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