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 uuid::Uuid; 16 use vm_memory::ByteValued; 17 use vm_memory::{Address, Bytes, GuestAddress}; 18 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 /// Failure to parse uuid, uuid format may be error 32 ParseUuid(uuid::Error), 33 } 34 35 impl std::error::Error for Error {} 36 37 impl Display for Error { 38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 39 use self::Error::*; 40 41 let description = match self { 42 NotEnoughMemory => { 43 "There was too little guest memory to store the SMBIOS table".to_string() 44 } 45 AddressOverflow => { 46 "The SMBIOS table has too little address space to be stored".to_string() 47 } 48 Clear => "Failure while zeroing out the memory for the SMBIOS table".to_string(), 49 WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure".to_string(), 50 WriteData => "Failure to write additional data to memory".to_string(), 51 ParseUuid(e) => format!("Failure to parse uuid: {}", e), 52 }; 53 54 write!(f, "SMBIOS error: {}", description) 55 } 56 } 57 58 pub type Result<T> = result::Result<T, Error>; 59 60 // Constants sourced from SMBIOS Spec 3.2.0. 61 const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_"; 62 const BIOS_INFORMATION: u8 = 0; 63 const SYSTEM_INFORMATION: u8 = 1; 64 const OEM_STRINGS: u8 = 11; 65 const END_OF_TABLE: u8 = 127; 66 const PCI_SUPPORTED: u64 = 1 << 7; 67 const IS_VIRTUAL_MACHINE: u8 = 1 << 4; 68 69 fn compute_checksum<T: Copy>(v: &T) -> u8 { 70 // Safe because we are only reading the bytes within the size of the `T` reference `v`. 71 let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) }; 72 let mut checksum: u8 = 0; 73 for i in v_slice.iter() { 74 checksum = checksum.wrapping_add(*i); 75 } 76 (!checksum).wrapping_add(1) 77 } 78 79 #[repr(C)] 80 #[repr(packed)] 81 #[derive(Default, Copy, Clone)] 82 struct Smbios30Entrypoint { 83 signature: [u8; 5usize], 84 checksum: u8, 85 length: u8, 86 majorver: u8, 87 minorver: u8, 88 docrev: u8, 89 revision: u8, 90 reserved: u8, 91 max_size: u32, 92 physptr: u64, 93 } 94 95 #[repr(C)] 96 #[repr(packed)] 97 #[derive(Default, Copy, Clone)] 98 struct SmbiosBiosInfo { 99 r#type: u8, 100 length: u8, 101 handle: u16, 102 vendor: u8, 103 version: u8, 104 start_addr: u16, 105 release_date: u8, 106 rom_size: u8, 107 characteristics: u64, 108 characteristics_ext1: u8, 109 characteristics_ext2: u8, 110 } 111 112 #[repr(C)] 113 #[repr(packed)] 114 #[derive(Default, Copy, Clone)] 115 struct SmbiosSysInfo { 116 r#type: u8, 117 length: u8, 118 handle: u16, 119 manufacturer: u8, 120 product_name: u8, 121 version: u8, 122 serial_number: u8, 123 uuid: [u8; 16usize], 124 wake_up_type: u8, 125 sku: u8, 126 family: u8, 127 } 128 129 #[repr(C)] 130 #[repr(packed)] 131 #[derive(Default, Copy, Clone)] 132 struct SmbiosOemStrings { 133 r#type: u8, 134 length: u8, 135 handle: u16, 136 count: u8, 137 } 138 139 #[repr(C)] 140 #[repr(packed)] 141 #[derive(Default, Copy, Clone)] 142 struct SmbiosEndOfTable { 143 r#type: u8, 144 length: u8, 145 handle: u16, 146 } 147 148 // SAFETY: These data structures only contain a series of integers 149 unsafe impl ByteValued for Smbios30Entrypoint {} 150 unsafe impl ByteValued for SmbiosBiosInfo {} 151 unsafe impl ByteValued for SmbiosSysInfo {} 152 unsafe impl ByteValued for SmbiosOemStrings {} 153 unsafe impl ByteValued for SmbiosEndOfTable {} 154 155 fn write_and_incr<T: ByteValued>( 156 mem: &GuestMemoryMmap, 157 val: T, 158 mut curptr: GuestAddress, 159 ) -> Result<GuestAddress> { 160 mem.write_obj(val, curptr).map_err(|_| Error::WriteData)?; 161 curptr = curptr 162 .checked_add(mem::size_of::<T>() as u64) 163 .ok_or(Error::NotEnoughMemory)?; 164 Ok(curptr) 165 } 166 167 fn write_string( 168 mem: &GuestMemoryMmap, 169 val: &str, 170 mut curptr: GuestAddress, 171 ) -> Result<GuestAddress> { 172 for c in val.as_bytes().iter() { 173 curptr = write_and_incr(mem, *c, curptr)?; 174 } 175 curptr = write_and_incr(mem, 0u8, curptr)?; 176 Ok(curptr) 177 } 178 179 pub fn setup_smbios( 180 mem: &GuestMemoryMmap, 181 serial_number: Option<&str>, 182 uuid: Option<&str>, 183 oem_strings: Option<&[&str]>, 184 ) -> Result<u64> { 185 let physptr = GuestAddress(SMBIOS_START) 186 .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64) 187 .ok_or(Error::NotEnoughMemory)?; 188 let mut curptr = physptr; 189 let mut handle = 0; 190 191 { 192 handle += 1; 193 let smbios_biosinfo = SmbiosBiosInfo { 194 r#type: BIOS_INFORMATION, 195 length: mem::size_of::<SmbiosBiosInfo>() as u8, 196 handle, 197 vendor: 1, // First string written in this section 198 version: 2, // Second string written in this section 199 characteristics: PCI_SUPPORTED, 200 characteristics_ext2: IS_VIRTUAL_MACHINE, 201 ..Default::default() 202 }; 203 curptr = write_and_incr(mem, smbios_biosinfo, curptr)?; 204 curptr = write_string(mem, "cloud-hypervisor", curptr)?; 205 curptr = write_string(mem, "0", curptr)?; 206 curptr = write_and_incr(mem, 0u8, curptr)?; 207 } 208 209 { 210 handle += 1; 211 212 let uuid_number = uuid 213 .map(Uuid::parse_str) 214 .transpose() 215 .map_err(Error::ParseUuid)? 216 .unwrap_or(Uuid::nil()); 217 let smbios_sysinfo = SmbiosSysInfo { 218 r#type: SYSTEM_INFORMATION, 219 length: mem::size_of::<SmbiosSysInfo>() as u8, 220 handle, 221 manufacturer: 1, // First string written in this section 222 product_name: 2, // Second string written in this section 223 serial_number: serial_number.map(|_| 3).unwrap_or_default(), // 3rd string 224 uuid: uuid_number.to_bytes_le(), // set uuid 225 ..Default::default() 226 }; 227 curptr = write_and_incr(mem, smbios_sysinfo, curptr)?; 228 curptr = write_string(mem, "Cloud Hypervisor", curptr)?; 229 curptr = write_string(mem, "cloud-hypervisor", curptr)?; 230 if let Some(serial_number) = serial_number { 231 curptr = write_string(mem, serial_number, curptr)?; 232 } 233 curptr = write_and_incr(mem, 0u8, curptr)?; 234 } 235 236 if let Some(oem_strings) = oem_strings { 237 handle += 1; 238 239 let smbios_oemstrings = SmbiosOemStrings { 240 r#type: OEM_STRINGS, 241 length: mem::size_of::<SmbiosOemStrings>() as u8, 242 handle, 243 count: oem_strings.len() as u8, 244 }; 245 246 curptr = write_and_incr(mem, smbios_oemstrings, curptr)?; 247 248 for s in oem_strings { 249 curptr = write_string(mem, s, curptr)?; 250 } 251 252 curptr = write_and_incr(mem, 0u8, curptr)?; 253 } 254 255 { 256 handle += 1; 257 let smbios_end = SmbiosEndOfTable { 258 r#type: END_OF_TABLE, 259 length: mem::size_of::<SmbiosEndOfTable>() as u8, 260 handle, 261 }; 262 curptr = write_and_incr(mem, smbios_end, curptr)?; 263 curptr = write_and_incr(mem, 0u8, curptr)?; 264 curptr = write_and_incr(mem, 0u8, curptr)?; 265 } 266 267 { 268 let mut smbios_ep = Smbios30Entrypoint { 269 signature: *SM3_MAGIC_IDENT, 270 length: mem::size_of::<Smbios30Entrypoint>() as u8, 271 // SMBIOS rev 3.2.0 272 majorver: 0x03, 273 minorver: 0x02, 274 docrev: 0x00, 275 revision: 0x01, // SMBIOS 3.0 276 max_size: curptr.unchecked_offset_from(physptr) as u32, 277 physptr: physptr.0, 278 ..Default::default() 279 }; 280 smbios_ep.checksum = compute_checksum(&smbios_ep); 281 mem.write_obj(smbios_ep, GuestAddress(SMBIOS_START)) 282 .map_err(|_| Error::WriteSmbiosEp)?; 283 } 284 285 Ok(curptr.unchecked_offset_from(physptr) + std::mem::size_of::<Smbios30Entrypoint>() as u64) 286 } 287 288 #[cfg(test)] 289 mod tests { 290 use super::*; 291 292 #[test] 293 fn struct_size() { 294 assert_eq!( 295 mem::size_of::<Smbios30Entrypoint>(), 296 0x18usize, 297 concat!("Size of: ", stringify!(Smbios30Entrypoint)) 298 ); 299 assert_eq!( 300 mem::size_of::<SmbiosBiosInfo>(), 301 0x14usize, 302 concat!("Size of: ", stringify!(SmbiosBiosInfo)) 303 ); 304 assert_eq!( 305 mem::size_of::<SmbiosSysInfo>(), 306 0x1busize, 307 concat!("Size of: ", stringify!(SmbiosSysInfo)) 308 ); 309 } 310 311 #[test] 312 fn entrypoint_checksum() { 313 let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap(); 314 315 setup_smbios(&mem, None, None, None).unwrap(); 316 317 let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap(); 318 319 assert_eq!(compute_checksum(&smbios_ep), 0); 320 } 321 } 322