1 // Copyright © 2020 Intel Corporation
2 //
3 // Copyright 2019 The Chromium OS Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6 //
7 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
8
9 use std::{mem, result, slice};
10
11 use thiserror::Error;
12 use uuid::Uuid;
13 use vm_memory::{Address, ByteValued, Bytes, GuestAddress};
14
15 use crate::layout::SMBIOS_START;
16 use crate::GuestMemoryMmap;
17
18 #[derive(Debug, Error)]
19 pub enum Error {
20 /// There was too little guest memory to store the entire SMBIOS table.
21 #[error("There was too little guest memory to store the SMBIOS table")]
22 NotEnoughMemory,
23 /// The SMBIOS table has too little address space to be stored.
24 #[error("The SMBIOS table has too little address space to be stored")]
25 AddressOverflow,
26 /// Failure while zeroing out the memory for the SMBIOS table.
27 #[error("Failure while zeroing out the memory for the SMBIOS table")]
28 Clear,
29 /// Failure to write SMBIOS entrypoint structure
30 #[error("Failure to write SMBIOS entrypoint structure")]
31 WriteSmbiosEp,
32 /// Failure to write additional data to memory
33 #[error("Failure to write additional data to memory")]
34 WriteData,
35 /// Failure to parse uuid, uuid format may be error
36 #[error("Failure to parse uuid")]
37 ParseUuid(#[source] uuid::Error),
38 }
39
40 pub type Result<T> = result::Result<T, Error>;
41
42 // Constants sourced from SMBIOS Spec 3.2.0.
43 const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
44 const BIOS_INFORMATION: u8 = 0;
45 const SYSTEM_INFORMATION: u8 = 1;
46 const OEM_STRINGS: u8 = 11;
47 const END_OF_TABLE: u8 = 127;
48 const PCI_SUPPORTED: u64 = 1 << 7;
49 const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
50
compute_checksum<T: Copy>(v: &T) -> u851 fn compute_checksum<T: Copy>(v: &T) -> u8 {
52 // SAFETY: we are only reading the bytes within the size of the `T` reference `v`.
53 let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
54 let mut checksum: u8 = 0;
55 for i in v_slice.iter() {
56 checksum = checksum.wrapping_add(*i);
57 }
58 (!checksum).wrapping_add(1)
59 }
60
61 #[repr(C)]
62 #[repr(packed)]
63 #[derive(Default, Copy, Clone)]
64 struct Smbios30Entrypoint {
65 signature: [u8; 5usize],
66 checksum: u8,
67 length: u8,
68 majorver: u8,
69 minorver: u8,
70 docrev: u8,
71 revision: u8,
72 reserved: u8,
73 max_size: u32,
74 physptr: u64,
75 }
76
77 #[repr(C)]
78 #[repr(packed)]
79 #[derive(Default, Copy, Clone)]
80 struct SmbiosBiosInfo {
81 r#type: u8,
82 length: u8,
83 handle: u16,
84 vendor: u8,
85 version: u8,
86 start_addr: u16,
87 release_date: u8,
88 rom_size: u8,
89 characteristics: u64,
90 characteristics_ext1: u8,
91 characteristics_ext2: u8,
92 }
93
94 #[repr(C)]
95 #[repr(packed)]
96 #[derive(Default, Copy, Clone)]
97 struct SmbiosSysInfo {
98 r#type: u8,
99 length: u8,
100 handle: u16,
101 manufacturer: u8,
102 product_name: u8,
103 version: u8,
104 serial_number: u8,
105 uuid: [u8; 16usize],
106 wake_up_type: u8,
107 sku: u8,
108 family: u8,
109 }
110
111 #[repr(C)]
112 #[repr(packed)]
113 #[derive(Default, Copy, Clone)]
114 struct SmbiosOemStrings {
115 r#type: u8,
116 length: u8,
117 handle: u16,
118 count: u8,
119 }
120
121 #[repr(C)]
122 #[repr(packed)]
123 #[derive(Default, Copy, Clone)]
124 struct SmbiosEndOfTable {
125 r#type: u8,
126 length: u8,
127 handle: u16,
128 }
129
130 // SAFETY: data structure only contain a series of integers
131 unsafe impl ByteValued for Smbios30Entrypoint {}
132 // SAFETY: data structure only contain a series of integers
133 unsafe impl ByteValued for SmbiosBiosInfo {}
134 // SAFETY: data structure only contain a series of integers
135 unsafe impl ByteValued for SmbiosSysInfo {}
136 // SAFETY: data structure only contain a series of integers
137 unsafe impl ByteValued for SmbiosOemStrings {}
138 // SAFETY: data structure only contain a series of integers
139 unsafe impl ByteValued for SmbiosEndOfTable {}
140
write_and_incr<T: ByteValued>( mem: &GuestMemoryMmap, val: T, mut curptr: GuestAddress, ) -> Result<GuestAddress>141 fn write_and_incr<T: ByteValued>(
142 mem: &GuestMemoryMmap,
143 val: T,
144 mut curptr: GuestAddress,
145 ) -> Result<GuestAddress> {
146 mem.write_obj(val, curptr).map_err(|_| Error::WriteData)?;
147 curptr = curptr
148 .checked_add(mem::size_of::<T>() as u64)
149 .ok_or(Error::NotEnoughMemory)?;
150 Ok(curptr)
151 }
152
write_string( mem: &GuestMemoryMmap, val: &str, mut curptr: GuestAddress, ) -> Result<GuestAddress>153 fn write_string(
154 mem: &GuestMemoryMmap,
155 val: &str,
156 mut curptr: GuestAddress,
157 ) -> Result<GuestAddress> {
158 for c in val.as_bytes().iter() {
159 curptr = write_and_incr(mem, *c, curptr)?;
160 }
161 curptr = write_and_incr(mem, 0u8, curptr)?;
162 Ok(curptr)
163 }
164
setup_smbios( mem: &GuestMemoryMmap, serial_number: Option<&str>, uuid: Option<&str>, oem_strings: Option<&[&str]>, ) -> Result<u64>165 pub fn setup_smbios(
166 mem: &GuestMemoryMmap,
167 serial_number: Option<&str>,
168 uuid: Option<&str>,
169 oem_strings: Option<&[&str]>,
170 ) -> Result<u64> {
171 let physptr = GuestAddress(SMBIOS_START)
172 .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
173 .ok_or(Error::NotEnoughMemory)?;
174 let mut curptr = physptr;
175 let mut handle = 0;
176
177 {
178 handle += 1;
179 let smbios_biosinfo = SmbiosBiosInfo {
180 r#type: BIOS_INFORMATION,
181 length: mem::size_of::<SmbiosBiosInfo>() as u8,
182 handle,
183 vendor: 1, // First string written in this section
184 version: 2, // Second string written in this section
185 characteristics: PCI_SUPPORTED,
186 characteristics_ext2: IS_VIRTUAL_MACHINE,
187 ..Default::default()
188 };
189 curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
190 curptr = write_string(mem, "cloud-hypervisor", curptr)?;
191 curptr = write_string(mem, "0", curptr)?;
192 curptr = write_and_incr(mem, 0u8, curptr)?;
193 }
194
195 {
196 handle += 1;
197
198 let uuid_number = uuid
199 .map(Uuid::parse_str)
200 .transpose()
201 .map_err(Error::ParseUuid)?
202 .unwrap_or(Uuid::nil());
203 let smbios_sysinfo = SmbiosSysInfo {
204 r#type: SYSTEM_INFORMATION,
205 length: mem::size_of::<SmbiosSysInfo>() as u8,
206 handle,
207 manufacturer: 1, // First string written in this section
208 product_name: 2, // Second string written in this section
209 serial_number: serial_number.map(|_| 3).unwrap_or_default(), // 3rd string
210 uuid: uuid_number.to_bytes_le(), // set uuid
211 ..Default::default()
212 };
213 curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
214 curptr = write_string(mem, "Cloud Hypervisor", curptr)?;
215 curptr = write_string(mem, "cloud-hypervisor", curptr)?;
216 if let Some(serial_number) = serial_number {
217 curptr = write_string(mem, serial_number, curptr)?;
218 }
219 curptr = write_and_incr(mem, 0u8, curptr)?;
220 }
221
222 if let Some(oem_strings) = oem_strings {
223 handle += 1;
224
225 let smbios_oemstrings = SmbiosOemStrings {
226 r#type: OEM_STRINGS,
227 length: mem::size_of::<SmbiosOemStrings>() as u8,
228 handle,
229 count: oem_strings.len() as u8,
230 };
231
232 curptr = write_and_incr(mem, smbios_oemstrings, curptr)?;
233
234 for s in oem_strings {
235 curptr = write_string(mem, s, curptr)?;
236 }
237
238 curptr = write_and_incr(mem, 0u8, curptr)?;
239 }
240
241 {
242 handle += 1;
243 let smbios_end = SmbiosEndOfTable {
244 r#type: END_OF_TABLE,
245 length: mem::size_of::<SmbiosEndOfTable>() as u8,
246 handle,
247 };
248 curptr = write_and_incr(mem, smbios_end, curptr)?;
249 curptr = write_and_incr(mem, 0u8, curptr)?;
250 curptr = write_and_incr(mem, 0u8, curptr)?;
251 }
252
253 {
254 let mut smbios_ep = Smbios30Entrypoint {
255 signature: *SM3_MAGIC_IDENT,
256 length: mem::size_of::<Smbios30Entrypoint>() as u8,
257 // SMBIOS rev 3.2.0
258 majorver: 0x03,
259 minorver: 0x02,
260 docrev: 0x00,
261 revision: 0x01, // SMBIOS 3.0
262 max_size: curptr.unchecked_offset_from(physptr) as u32,
263 physptr: physptr.0,
264 ..Default::default()
265 };
266 smbios_ep.checksum = compute_checksum(&smbios_ep);
267 mem.write_obj(smbios_ep, GuestAddress(SMBIOS_START))
268 .map_err(|_| Error::WriteSmbiosEp)?;
269 }
270
271 Ok(curptr.unchecked_offset_from(physptr) + std::mem::size_of::<Smbios30Entrypoint>() as u64)
272 }
273
274 #[cfg(test)]
275 mod tests {
276 use super::*;
277
278 #[test]
struct_size()279 fn struct_size() {
280 assert_eq!(
281 mem::size_of::<Smbios30Entrypoint>(),
282 0x18usize,
283 concat!("Size of: ", stringify!(Smbios30Entrypoint))
284 );
285 assert_eq!(
286 mem::size_of::<SmbiosBiosInfo>(),
287 0x14usize,
288 concat!("Size of: ", stringify!(SmbiosBiosInfo))
289 );
290 assert_eq!(
291 mem::size_of::<SmbiosSysInfo>(),
292 0x1busize,
293 concat!("Size of: ", stringify!(SmbiosSysInfo))
294 );
295 }
296
297 #[test]
entrypoint_checksum()298 fn entrypoint_checksum() {
299 let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
300
301 setup_smbios(&mem, None, None, None).unwrap();
302
303 let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
304
305 assert_eq!(compute_checksum(&smbios_ep), 0);
306 }
307 }
308