1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::fmt::{self, Display};
6 use std::mem;
7 use std::result;
8 use std::slice;
9 
10 use std::fs::OpenOptions;
11 use std::io::prelude::*;
12 use std::path::{Path, PathBuf};
13 
14 use data_model::DataInit;
15 use vm_memory::{GuestAddress, GuestMemory};
16 
17 #[derive(Debug)]
18 pub enum Error {
19     /// There was too little guest memory to store the entire SMBIOS table.
20     NotEnoughMemory,
21     /// The SMBIOS table has too little address space to be stored.
22     AddressOverflow,
23     /// Failure while zeroing out the memory for the SMBIOS table.
24     Clear,
25     /// Failure to write SMBIOS entrypoint structure
26     WriteSmbiosEp,
27     /// Failure to write additional data to memory
28     WriteData,
29     /// Failure while reading SMBIOS data file
30     IoFailed,
31     /// Incorrect or not readable host SMBIOS data
32     InvalidInput,
33     /// Invalid table entry point checksum
34     InvalidChecksum,
35 }
36 
37 impl std::error::Error for Error {}
38 
39 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result40     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41         use self::Error::*;
42 
43         let description = match self {
44             NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
45             AddressOverflow => "The SMBIOS table has too little address space to be stored",
46             Clear => "Failure while zeroing out the memory for the SMBIOS table",
47             WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
48             WriteData => "Failure to write additional data to memory",
49             IoFailed => "Failure while reading SMBIOS data file",
50             InvalidInput => "Failure to read host SMBIOS data",
51             InvalidChecksum => "Failure to verify host SMBIOS entry checksum",
52         };
53 
54         write!(f, "SMBIOS error: {}", description)
55     }
56 }
57 
58 pub type Result<T> = result::Result<T, Error>;
59 
60 const SMBIOS_START: u64 = 0xf0000; // First possible location per the spec.
61 
62 // Constants sourced from SMBIOS Spec 2.3.1.
63 const SM2_MAGIC_IDENT: &[u8; 4usize] = b"_SM_";
64 
65 // Constants sourced from SMBIOS Spec 3.2.0.
66 const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
67 const BIOS_INFORMATION: u8 = 0;
68 const SYSTEM_INFORMATION: u8 = 1;
69 const END_OF_TABLE: u8 = 127;
70 const PCI_SUPPORTED: u64 = 1 << 7;
71 const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
72 
compute_checksum<T: Copy>(v: &T) -> u873 fn compute_checksum<T: Copy>(v: &T) -> u8 {
74     // Safe because we are only reading the bytes within the size of the `T` reference `v`.
75     let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
76     let mut checksum: u8 = 0;
77     for i in v_slice.iter() {
78         checksum = checksum.wrapping_add(*i);
79     }
80     (!checksum).wrapping_add(1)
81 }
82 
83 #[repr(packed)]
84 #[derive(Default, Copy)]
85 pub struct Smbios23Intermediate {
86     pub signature: [u8; 5usize],
87     pub checksum: u8,
88     pub length: u16,
89     pub address: u32,
90     pub count: u16,
91     pub revision: u8,
92 }
93 
94 unsafe impl data_model::DataInit for Smbios23Intermediate {}
95 
96 impl Clone for Smbios23Intermediate {
clone(&self) -> Self97     fn clone(&self) -> Self {
98         *self
99     }
100 }
101 
102 #[repr(packed)]
103 #[derive(Default, Copy)]
104 pub struct Smbios23Entrypoint {
105     pub signature: [u8; 4usize],
106     pub checksum: u8,
107     pub length: u8,
108     pub majorver: u8,
109     pub minorver: u8,
110     pub max_size: u16,
111     pub revision: u8,
112     pub reserved: [u8; 5usize],
113     pub dmi: Smbios23Intermediate,
114 }
115 
116 unsafe impl data_model::DataInit for Smbios23Entrypoint {}
117 
118 impl Clone for Smbios23Entrypoint {
clone(&self) -> Self119     fn clone(&self) -> Self {
120         *self
121     }
122 }
123 
124 #[repr(packed)]
125 #[derive(Default, Copy)]
126 pub struct Smbios30Entrypoint {
127     pub signature: [u8; 5usize],
128     pub checksum: u8,
129     pub length: u8,
130     pub majorver: u8,
131     pub minorver: u8,
132     pub docrev: u8,
133     pub revision: u8,
134     pub reserved: u8,
135     pub max_size: u32,
136     pub physptr: u64,
137 }
138 unsafe impl data_model::DataInit for Smbios30Entrypoint {}
139 
140 impl Clone for Smbios30Entrypoint {
clone(&self) -> Self141     fn clone(&self) -> Self {
142         *self
143     }
144 }
145 
146 #[repr(packed)]
147 #[derive(Default, Copy)]
148 pub struct SmbiosBiosInfo {
149     pub typ: u8,
150     pub length: u8,
151     pub handle: u16,
152     pub vendor: u8,
153     pub version: u8,
154     pub start_addr: u16,
155     pub release_date: u8,
156     pub rom_size: u8,
157     pub characteristics: u64,
158     pub characteristics_ext1: u8,
159     pub characteristics_ext2: u8,
160 }
161 
162 impl Clone for SmbiosBiosInfo {
clone(&self) -> Self163     fn clone(&self) -> Self {
164         *self
165     }
166 }
167 
168 unsafe impl data_model::DataInit for SmbiosBiosInfo {}
169 
170 #[repr(packed)]
171 #[derive(Default, Copy)]
172 pub struct SmbiosSysInfo {
173     pub typ: u8,
174     pub length: u8,
175     pub handle: u16,
176     pub manufacturer: u8,
177     pub product_name: u8,
178     pub version: u8,
179     pub serial_number: u8,
180     pub uuid: [u8; 16usize],
181     pub wake_up_type: u8,
182     pub sku: u8,
183     pub family: u8,
184 }
185 
186 impl Clone for SmbiosSysInfo {
clone(&self) -> Self187     fn clone(&self) -> Self {
188         *self
189     }
190 }
191 
192 unsafe impl data_model::DataInit for SmbiosSysInfo {}
193 
write_and_incr<T: DataInit>( mem: &GuestMemory, val: T, mut curptr: GuestAddress, ) -> Result<GuestAddress>194 fn write_and_incr<T: DataInit>(
195     mem: &GuestMemory,
196     val: T,
197     mut curptr: GuestAddress,
198 ) -> Result<GuestAddress> {
199     mem.write_obj_at_addr(val, curptr)
200         .map_err(|_| Error::WriteData)?;
201     curptr = curptr
202         .checked_add(mem::size_of::<T>() as u64)
203         .ok_or(Error::NotEnoughMemory)?;
204     Ok(curptr)
205 }
206 
write_string(mem: &GuestMemory, val: &str, mut curptr: GuestAddress) -> Result<GuestAddress>207 fn write_string(mem: &GuestMemory, val: &str, mut curptr: GuestAddress) -> Result<GuestAddress> {
208     for c in val.as_bytes().iter() {
209         curptr = write_and_incr(mem, *c, curptr)?;
210     }
211     curptr = write_and_incr(mem, 0_u8, curptr)?;
212     Ok(curptr)
213 }
214 
setup_smbios_from_file(mem: &GuestMemory, path: &Path) -> Result<()>215 fn setup_smbios_from_file(mem: &GuestMemory, path: &Path) -> Result<()> {
216     let mut sme_path = PathBuf::from(path);
217     sme_path.push("smbios_entry_point");
218     let mut sme = Vec::new();
219     OpenOptions::new()
220         .read(true)
221         .open(&sme_path)
222         .map_err(|_| Error::IoFailed)?
223         .read_to_end(&mut sme)
224         .map_err(|_| Error::IoFailed)?;
225 
226     let mut dmi_path = PathBuf::from(path);
227     dmi_path.push("DMI");
228     let mut dmi = Vec::new();
229     OpenOptions::new()
230         .read(true)
231         .open(&dmi_path)
232         .map_err(|_| Error::IoFailed)?
233         .read_to_end(&mut dmi)
234         .map_err(|_| Error::IoFailed)?;
235 
236     // Try SMBIOS 3.0 format.
237     if sme.len() == mem::size_of::<Smbios30Entrypoint>() && sme.starts_with(SM3_MAGIC_IDENT) {
238         let mut smbios_ep = Smbios30Entrypoint::default();
239         smbios_ep.as_mut_slice().copy_from_slice(&sme);
240 
241         let physptr = GuestAddress(SMBIOS_START)
242             .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
243             .ok_or(Error::NotEnoughMemory)?;
244 
245         mem.write_at_addr(&dmi, physptr)
246             .map_err(|_| Error::NotEnoughMemory)?;
247 
248         // Update EP DMI location
249         smbios_ep.physptr = physptr.offset();
250         smbios_ep.checksum = 0;
251         smbios_ep.checksum = compute_checksum(&smbios_ep);
252 
253         mem.write_obj_at_addr(smbios_ep, GuestAddress(SMBIOS_START))
254             .map_err(|_| Error::NotEnoughMemory)?;
255 
256         return Ok(());
257     }
258 
259     // Try SMBIOS 2.3 format.
260     if sme.len() == mem::size_of::<Smbios23Entrypoint>() && sme.starts_with(SM2_MAGIC_IDENT) {
261         let mut smbios_ep = Smbios23Entrypoint::default();
262         smbios_ep.as_mut_slice().copy_from_slice(&sme);
263 
264         let physptr = GuestAddress(SMBIOS_START)
265             .checked_add(mem::size_of::<Smbios23Entrypoint>() as u64)
266             .ok_or(Error::NotEnoughMemory)?;
267 
268         mem.write_at_addr(&dmi, physptr)
269             .map_err(|_| Error::NotEnoughMemory)?;
270 
271         // Update EP DMI location
272         smbios_ep.dmi.address = physptr.offset() as u32;
273         smbios_ep.dmi.checksum = 0;
274         smbios_ep.dmi.checksum = compute_checksum(&smbios_ep.dmi);
275         smbios_ep.checksum = 0;
276         smbios_ep.checksum = compute_checksum(&smbios_ep);
277 
278         mem.write_obj_at_addr(smbios_ep, GuestAddress(SMBIOS_START))
279             .map_err(|_| Error::WriteSmbiosEp)?;
280 
281         return Ok(());
282     }
283 
284     Err(Error::InvalidInput)
285 }
286 
setup_smbios(mem: &GuestMemory, dmi_path: Option<PathBuf>) -> Result<()>287 pub fn setup_smbios(mem: &GuestMemory, dmi_path: Option<PathBuf>) -> Result<()> {
288     if let Some(dmi_path) = dmi_path {
289         return setup_smbios_from_file(mem, &dmi_path);
290     }
291 
292     let physptr = GuestAddress(SMBIOS_START)
293         .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
294         .ok_or(Error::NotEnoughMemory)?;
295     let mut curptr = physptr;
296     let mut handle = 0;
297 
298     {
299         handle += 1;
300         let smbios_biosinfo = SmbiosBiosInfo {
301             typ: BIOS_INFORMATION,
302             length: mem::size_of::<SmbiosBiosInfo>() as u8,
303             handle,
304             vendor: 1,  // First string written in this section
305             version: 2, // Second string written in this section
306             characteristics: PCI_SUPPORTED,
307             characteristics_ext2: IS_VIRTUAL_MACHINE,
308             ..Default::default()
309         };
310         curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
311         curptr = write_string(mem, "crosvm", curptr)?;
312         curptr = write_string(mem, "0", curptr)?;
313         curptr = write_and_incr(mem, 0_u8, curptr)?;
314     }
315 
316     {
317         handle += 1;
318         let smbios_sysinfo = SmbiosSysInfo {
319             typ: SYSTEM_INFORMATION,
320             length: mem::size_of::<SmbiosSysInfo>() as u8,
321             handle,
322             manufacturer: 1, // First string written in this section
323             product_name: 2, // Second string written in this section
324             ..Default::default()
325         };
326         curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
327         curptr = write_string(mem, "ChromiumOS", curptr)?;
328         curptr = write_string(mem, "crosvm", curptr)?;
329         curptr = write_and_incr(mem, 0u8, curptr)?;
330     }
331 
332     {
333         handle += 1;
334         let smbios_sysinfo = SmbiosSysInfo {
335             typ: END_OF_TABLE,
336             length: mem::size_of::<SmbiosSysInfo>() as u8,
337             handle,
338             ..Default::default()
339         };
340         curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
341         curptr = write_and_incr(mem, 0_u8, curptr)?;
342     }
343 
344     {
345         let mut smbios_ep = Smbios30Entrypoint::default();
346         smbios_ep.signature = *SM3_MAGIC_IDENT;
347         smbios_ep.length = mem::size_of::<Smbios30Entrypoint>() as u8;
348         // SMBIOS rev 3.2.0
349         smbios_ep.majorver = 0x03;
350         smbios_ep.minorver = 0x02;
351         smbios_ep.docrev = 0x00;
352         smbios_ep.revision = 0x01; // SMBIOS 3.0
353         smbios_ep.max_size = curptr.offset_from(physptr) as u32;
354         smbios_ep.physptr = physptr.offset();
355         smbios_ep.checksum = compute_checksum(&smbios_ep);
356         mem.write_obj_at_addr(smbios_ep, GuestAddress(SMBIOS_START))
357             .map_err(|_| Error::WriteSmbiosEp)?;
358     }
359 
360     Ok(())
361 }
362 
363 #[cfg(test)]
364 mod tests {
365     use super::*;
366 
367     #[test]
struct_size()368     fn struct_size() {
369         assert_eq!(
370             mem::size_of::<Smbios23Entrypoint>(),
371             0x1fusize,
372             concat!("Size of: ", stringify!(Smbios23Entrypoint))
373         );
374         assert_eq!(
375             mem::size_of::<Smbios30Entrypoint>(),
376             0x18usize,
377             concat!("Size of: ", stringify!(Smbios30Entrypoint))
378         );
379         assert_eq!(
380             mem::size_of::<SmbiosBiosInfo>(),
381             0x14usize,
382             concat!("Size of: ", stringify!(SmbiosBiosInfo))
383         );
384         assert_eq!(
385             mem::size_of::<SmbiosSysInfo>(),
386             0x1busize,
387             concat!("Size of: ", stringify!(SmbiosSysInfo))
388         );
389     }
390 
391     #[test]
entrypoint_checksum()392     fn entrypoint_checksum() {
393         let mem = GuestMemory::new(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
394 
395         // Use default 3.0 SMBIOS format.
396         setup_smbios(&mem, None).unwrap();
397 
398         let smbios_ep: Smbios30Entrypoint =
399             mem.read_obj_from_addr(GuestAddress(SMBIOS_START)).unwrap();
400 
401         assert_eq!(compute_checksum(&smbios_ep), 0);
402     }
403 }
404