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