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