1 // Copyright © 2020 Intel Corporation 2 // 3 // Copyright 2019 The Chromium OS Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 // 7 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 8 9 use crate::layout::SMBIOS_START; 10 use crate::GuestMemoryMmap; 11 use std::fmt::{self, Display}; 12 use std::mem; 13 use std::result; 14 use std::slice; 15 use vm_memory::ByteValued; 16 use vm_memory::{Address, Bytes, GuestAddress}; 17 18 #[allow(unused_variables)] 19 #[derive(Debug)] 20 pub enum Error { 21 /// There was too little guest memory to store the entire SMBIOS table. 22 NotEnoughMemory, 23 /// The SMBIOS table has too little address space to be stored. 24 AddressOverflow, 25 /// Failure while zeroing out the memory for the SMBIOS table. 26 Clear, 27 /// Failure to write SMBIOS entrypoint structure 28 WriteSmbiosEp, 29 /// Failure to write additional data to memory 30 WriteData, 31 } 32 33 impl std::error::Error for Error {} 34 35 impl Display for Error { 36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 37 use self::Error::*; 38 39 let description = match self { 40 NotEnoughMemory => "There was too little guest memory to store the SMBIOS table", 41 AddressOverflow => "The SMBIOS table has too little address space to be stored", 42 Clear => "Failure while zeroing out the memory for the SMBIOS table", 43 WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure", 44 WriteData => "Failure to write additional data to memory", 45 }; 46 47 write!(f, "SMBIOS error: {}", description) 48 } 49 } 50 51 pub type Result<T> = result::Result<T, Error>; 52 53 // Constants sourced from SMBIOS Spec 3.2.0. 54 const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_"; 55 const BIOS_INFORMATION: u8 = 0; 56 const SYSTEM_INFORMATION: u8 = 1; 57 const END_OF_TABLE: u8 = 127; 58 const PCI_SUPPORTED: u64 = 1 << 7; 59 const IS_VIRTUAL_MACHINE: u8 = 1 << 4; 60 61 fn compute_checksum<T: Copy>(v: &T) -> u8 { 62 // Safe because we are only reading the bytes within the size of the `T` reference `v`. 63 let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) }; 64 let mut checksum: u8 = 0; 65 for i in v_slice.iter() { 66 checksum = checksum.wrapping_add(*i); 67 } 68 (!checksum).wrapping_add(1) 69 } 70 71 #[repr(packed)] 72 #[derive(Default, Copy)] 73 pub struct Smbios30Entrypoint { 74 pub signature: [u8; 5usize], 75 pub checksum: u8, 76 pub length: u8, 77 pub majorver: u8, 78 pub minorver: u8, 79 pub docrev: u8, 80 pub revision: u8, 81 pub reserved: u8, 82 pub max_size: u32, 83 pub physptr: u64, 84 } 85 86 impl Clone for Smbios30Entrypoint { 87 fn clone(&self) -> Self { 88 *self 89 } 90 } 91 92 #[repr(packed)] 93 #[derive(Default, Copy)] 94 pub struct SmbiosBiosInfo { 95 pub typ: u8, 96 pub length: u8, 97 pub handle: u16, 98 pub vendor: u8, 99 pub version: u8, 100 pub start_addr: u16, 101 pub release_date: u8, 102 pub rom_size: u8, 103 pub characteristics: u64, 104 pub characteristics_ext1: u8, 105 pub characteristics_ext2: u8, 106 } 107 108 impl Clone for SmbiosBiosInfo { 109 fn clone(&self) -> Self { 110 *self 111 } 112 } 113 114 #[repr(packed)] 115 #[derive(Default, Copy)] 116 pub struct SmbiosSysInfo { 117 pub typ: u8, 118 pub length: u8, 119 pub handle: u16, 120 pub manufacturer: u8, 121 pub product_name: u8, 122 pub version: u8, 123 pub serial_number: u8, 124 pub uuid: [u8; 16usize], 125 pub wake_up_type: u8, 126 pub sku: u8, 127 pub family: u8, 128 } 129 130 impl Clone for SmbiosSysInfo { 131 fn clone(&self) -> Self { 132 *self 133 } 134 } 135 136 // SAFETY: These data structures only contain a series of integers 137 unsafe impl ByteValued for Smbios30Entrypoint {} 138 unsafe impl ByteValued for SmbiosBiosInfo {} 139 unsafe impl ByteValued for SmbiosSysInfo {} 140 141 fn write_and_incr<T: ByteValued>( 142 mem: &GuestMemoryMmap, 143 val: T, 144 mut curptr: GuestAddress, 145 ) -> Result<GuestAddress> { 146 mem.write_obj(val, curptr).map_err(|_| Error::WriteData)?; 147 curptr = curptr 148 .checked_add(mem::size_of::<T>() as u64) 149 .ok_or(Error::NotEnoughMemory)?; 150 Ok(curptr) 151 } 152 153 fn write_string( 154 mem: &GuestMemoryMmap, 155 val: &str, 156 mut curptr: GuestAddress, 157 ) -> Result<GuestAddress> { 158 for c in val.as_bytes().iter() { 159 curptr = write_and_incr(mem, *c, curptr)?; 160 } 161 curptr = write_and_incr(mem, 0u8, curptr)?; 162 Ok(curptr) 163 } 164 165 pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> { 166 let physptr = GuestAddress(SMBIOS_START) 167 .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64) 168 .ok_or(Error::NotEnoughMemory)?; 169 let mut curptr = physptr; 170 let mut handle = 0; 171 172 { 173 handle += 1; 174 let smbios_biosinfo = SmbiosBiosInfo { 175 typ: BIOS_INFORMATION, 176 length: mem::size_of::<SmbiosBiosInfo>() as u8, 177 handle, 178 vendor: 1, // First string written in this section 179 version: 2, // Second string written in this section 180 characteristics: PCI_SUPPORTED, 181 characteristics_ext2: IS_VIRTUAL_MACHINE, 182 ..Default::default() 183 }; 184 curptr = write_and_incr(mem, smbios_biosinfo, curptr)?; 185 curptr = write_string(mem, "cloud-hypervisor", curptr)?; 186 curptr = write_string(mem, "0", curptr)?; 187 curptr = write_and_incr(mem, 0u8, curptr)?; 188 } 189 190 { 191 handle += 1; 192 let smbios_sysinfo = SmbiosSysInfo { 193 typ: SYSTEM_INFORMATION, 194 length: mem::size_of::<SmbiosSysInfo>() as u8, 195 handle, 196 manufacturer: 1, // First string written in this section 197 product_name: 2, // Second string written in this section 198 ..Default::default() 199 }; 200 curptr = write_and_incr(mem, smbios_sysinfo, curptr)?; 201 curptr = write_string(mem, "Cloud Hypervisor", curptr)?; 202 curptr = write_string(mem, "cloud-hypervisor", curptr)?; 203 curptr = write_and_incr(mem, 0u8, curptr)?; 204 } 205 206 { 207 handle += 1; 208 let smbios_sysinfo = SmbiosSysInfo { 209 typ: END_OF_TABLE, 210 length: mem::size_of::<SmbiosSysInfo>() as u8, 211 handle, 212 ..Default::default() 213 }; 214 curptr = write_and_incr(mem, smbios_sysinfo, curptr)?; 215 curptr = write_and_incr(mem, 0u8, curptr)?; 216 } 217 218 { 219 let mut smbios_ep = Smbios30Entrypoint { 220 signature: *SM3_MAGIC_IDENT, 221 length: mem::size_of::<Smbios30Entrypoint>() as u8, 222 // SMBIOS rev 3.2.0 223 majorver: 0x03, 224 minorver: 0x02, 225 docrev: 0x00, 226 revision: 0x01, // SMBIOS 3.0 227 max_size: curptr.unchecked_offset_from(physptr) as u32, 228 physptr: physptr.0, 229 ..Default::default() 230 }; 231 smbios_ep.checksum = compute_checksum(&smbios_ep); 232 mem.write_obj(smbios_ep, GuestAddress(SMBIOS_START)) 233 .map_err(|_| Error::WriteSmbiosEp)?; 234 } 235 236 Ok(curptr.unchecked_offset_from(physptr)) 237 } 238 239 #[cfg(test)] 240 mod tests { 241 use super::*; 242 243 #[test] 244 fn struct_size() { 245 assert_eq!( 246 mem::size_of::<Smbios30Entrypoint>(), 247 0x18usize, 248 concat!("Size of: ", stringify!(Smbios30Entrypoint)) 249 ); 250 assert_eq!( 251 mem::size_of::<SmbiosBiosInfo>(), 252 0x14usize, 253 concat!("Size of: ", stringify!(SmbiosBiosInfo)) 254 ); 255 assert_eq!( 256 mem::size_of::<SmbiosSysInfo>(), 257 0x1busize, 258 concat!("Size of: ", stringify!(SmbiosSysInfo)) 259 ); 260 } 261 262 #[test] 263 fn entrypoint_checksum() { 264 let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap(); 265 266 setup_smbios(&mem).unwrap(); 267 268 let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap(); 269 270 assert_eq!(compute_checksum(&smbios_ep), 0); 271 } 272 } 273