• 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::fmt::{self, Display};
6 use std::mem;
7 use std::result;
8 use std::slice;
9 
10 use data_model::DataInit;
11 use sys_util::{GuestAddress, GuestMemory};
12 
13 #[derive(Debug)]
14 pub enum Error {
15     /// There was too little guest memory to store the entire SMBIOS table.
16     NotEnoughMemory,
17     /// The SMBIOS table has too little address space to be stored.
18     AddressOverflow,
19     /// Failure while zeroing out the memory for the SMBIOS table.
20     Clear,
21     /// Failure to write SMBIOS entrypoint structure
22     WriteSmbiosEp,
23     /// Failure to write additional data to memory
24     WriteData,
25 }
26 
27 impl std::error::Error for Error {}
28 
29 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result30     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31         use self::Error::*;
32 
33         let description = match self {
34             NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
35             AddressOverflow => "The SMBIOS table has too little address space to be stored",
36             Clear => "Failure while zeroing out the memory for the SMBIOS table",
37             WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
38             WriteData => "Failure to write additional data to memory",
39         };
40 
41         write!(f, "SMBIOS error: {}", description)
42     }
43 }
44 
45 pub type Result<T> = result::Result<T, Error>;
46 
47 const SMBIOS_START: u64 = 0xf0000; // First possible location per the spec.
48 
49 // Constants sourced from SMBIOS Spec 3.2.0.
50 const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
51 const BIOS_INFORMATION: u8 = 0;
52 const SYSTEM_INFORMATION: u8 = 1;
53 const PCI_SUPPORTED: u64 = 1 << 7;
54 const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
55 
compute_checksum<T: Copy>(v: &T) -> u856 fn compute_checksum<T: Copy>(v: &T) -> u8 {
57     // Safe because we are only reading the bytes within the size of the `T` reference `v`.
58     let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
59     let mut checksum: u8 = 0;
60     for i in v_slice.iter() {
61         checksum = checksum.wrapping_add(*i);
62     }
63     (!checksum).wrapping_add(1)
64 }
65 
66 #[repr(packed)]
67 #[derive(Default, Copy)]
68 pub struct Smbios30Entrypoint {
69     pub signature: [u8; 5usize],
70     pub checksum: u8,
71     pub length: u8,
72     pub majorver: u8,
73     pub minorver: u8,
74     pub docrev: u8,
75     pub revision: u8,
76     pub reserved: u8,
77     pub max_size: u32,
78     pub physptr: u64,
79 }
80 unsafe impl data_model::DataInit for Smbios30Entrypoint {}
81 
82 impl Clone for Smbios30Entrypoint {
clone(&self) -> Self83     fn clone(&self) -> Self {
84         *self
85     }
86 }
87 
88 #[repr(packed)]
89 #[derive(Default, Copy)]
90 pub struct SmbiosBiosInfo {
91     pub typ: u8,
92     pub length: u8,
93     pub handle: u16,
94     pub vendor: u8,
95     pub version: u8,
96     pub start_addr: u16,
97     pub release_date: u8,
98     pub rom_size: u8,
99     pub characteristics: u64,
100     pub characteristics_ext1: u8,
101     pub characteristics_ext2: u8,
102 }
103 
104 impl Clone for SmbiosBiosInfo {
clone(&self) -> Self105     fn clone(&self) -> Self {
106         *self
107     }
108 }
109 
110 unsafe impl data_model::DataInit for SmbiosBiosInfo {}
111 
112 #[repr(packed)]
113 #[derive(Default, Copy)]
114 pub struct SmbiosSysInfo {
115     pub typ: u8,
116     pub length: u8,
117     pub handle: u16,
118     pub manufacturer: u8,
119     pub product_name: u8,
120     pub version: u8,
121     pub serial_number: u8,
122     pub uuid: [u8; 16usize],
123     pub wake_up_type: u8,
124     pub sku: u8,
125     pub family: u8,
126 }
127 
128 impl Clone for SmbiosSysInfo {
clone(&self) -> Self129     fn clone(&self) -> Self {
130         *self
131     }
132 }
133 
134 unsafe impl data_model::DataInit for SmbiosSysInfo {}
135 
write_and_incr<T: DataInit>( mem: &GuestMemory, val: T, mut curptr: GuestAddress, ) -> Result<GuestAddress>136 fn write_and_incr<T: DataInit>(
137     mem: &GuestMemory,
138     val: T,
139     mut curptr: GuestAddress,
140 ) -> Result<GuestAddress> {
141     mem.write_obj_at_addr(val, curptr)
142         .map_err(|_| Error::WriteData)?;
143     curptr = curptr
144         .checked_add(mem::size_of::<T>() as u64)
145         .ok_or(Error::NotEnoughMemory)?;
146     Ok(curptr)
147 }
148 
write_string(mem: &GuestMemory, val: &str, mut curptr: GuestAddress) -> Result<GuestAddress>149 fn write_string(mem: &GuestMemory, val: &str, mut curptr: GuestAddress) -> Result<GuestAddress> {
150     for c in val.as_bytes().iter() {
151         curptr = write_and_incr(mem, *c, curptr)?;
152     }
153     curptr = write_and_incr(mem, 0 as u8, curptr)?;
154     Ok(curptr)
155 }
156 
setup_smbios(mem: &GuestMemory) -> Result<()>157 pub fn setup_smbios(mem: &GuestMemory) -> Result<()> {
158     let physptr = GuestAddress(SMBIOS_START)
159         .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
160         .ok_or(Error::NotEnoughMemory)?;
161     let mut curptr = physptr;
162     let mut handle = 0;
163 
164     {
165         handle += 1;
166         let mut smbios_biosinfo = SmbiosBiosInfo::default();
167         smbios_biosinfo.typ = BIOS_INFORMATION;
168         smbios_biosinfo.length = mem::size_of::<SmbiosBiosInfo>() as u8;
169         smbios_biosinfo.handle = handle;
170         smbios_biosinfo.vendor = 1; // First string written in this section
171         smbios_biosinfo.version = 2; // Second string written in this section
172         smbios_biosinfo.characteristics = PCI_SUPPORTED;
173         smbios_biosinfo.characteristics_ext2 = IS_VIRTUAL_MACHINE;
174         curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
175         curptr = write_string(mem, "crosvm", curptr)?;
176         curptr = write_string(mem, "0", curptr)?;
177         curptr = write_and_incr(mem, 0 as u8, curptr)?;
178     }
179 
180     {
181         handle += 1;
182         let mut smbios_sysinfo = SmbiosSysInfo::default();
183         smbios_sysinfo.typ = SYSTEM_INFORMATION;
184         smbios_sysinfo.length = mem::size_of::<SmbiosSysInfo>() as u8;
185         smbios_sysinfo.handle = handle;
186         smbios_sysinfo.manufacturer = 1; // First string written in this section
187         smbios_sysinfo.product_name = 2; // Second string written in this section
188         curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
189         curptr = write_string(mem, "ChromiumOS", curptr)?;
190         curptr = write_string(mem, "crosvm", curptr)?;
191         curptr = write_and_incr(mem, 0 as u8, curptr)?;
192     }
193 
194     {
195         let mut smbios_ep = Smbios30Entrypoint::default();
196         smbios_ep.signature = *SM3_MAGIC_IDENT;
197         smbios_ep.length = mem::size_of::<Smbios30Entrypoint>() as u8;
198         // SMBIOS rev 3.2.0
199         smbios_ep.majorver = 0x03;
200         smbios_ep.minorver = 0x02;
201         smbios_ep.docrev = 0x00;
202         smbios_ep.revision = 0x01; // SMBIOS 3.0
203         smbios_ep.max_size = curptr.offset_from(physptr) as u32;
204         smbios_ep.physptr = physptr.offset();
205         smbios_ep.checksum = compute_checksum(&smbios_ep);
206         mem.write_obj_at_addr(smbios_ep, GuestAddress(SMBIOS_START))
207             .map_err(|_| Error::WriteSmbiosEp)?;
208     }
209 
210     Ok(())
211 }
212 
213 #[cfg(test)]
214 mod tests {
215     use super::*;
216 
217     #[test]
struct_size()218     fn struct_size() {
219         assert_eq!(
220             mem::size_of::<Smbios30Entrypoint>(),
221             0x18usize,
222             concat!("Size of: ", stringify!(Smbios30Entrypoint))
223         );
224         assert_eq!(
225             mem::size_of::<SmbiosBiosInfo>(),
226             0x14usize,
227             concat!("Size of: ", stringify!(SmbiosBiosInfo))
228         );
229         assert_eq!(
230             mem::size_of::<SmbiosSysInfo>(),
231             0x1busize,
232             concat!("Size of: ", stringify!(SmbiosSysInfo))
233         );
234     }
235 
236     #[test]
entrypoint_checksum()237     fn entrypoint_checksum() {
238         let mem = GuestMemory::new(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
239 
240         setup_smbios(&mem).unwrap();
241 
242         let smbios_ep: Smbios30Entrypoint =
243             mem.read_obj_from_addr(GuestAddress(SMBIOS_START)).unwrap();
244 
245         assert_eq!(compute_checksum(&smbios_ep), 0);
246     }
247 }
248