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