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