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