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