xref: /cloud-hypervisor/arch/src/x86_64/smbios.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
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::fmt::{self, Display};
12 use std::mem;
13 use std::result;
14 use std::slice;
15 use vm_memory::ByteValued;
16 use vm_memory::{Address, Bytes, GuestAddress};
17 
18 #[allow(unused_variables)]
19 #[derive(Debug)]
20 pub enum Error {
21     /// There was too little guest memory to store the entire SMBIOS table.
22     NotEnoughMemory,
23     /// The SMBIOS table has too little address space to be stored.
24     AddressOverflow,
25     /// Failure while zeroing out the memory for the SMBIOS table.
26     Clear,
27     /// Failure to write SMBIOS entrypoint structure
28     WriteSmbiosEp,
29     /// Failure to write additional data to memory
30     WriteData,
31 }
32 
33 impl std::error::Error for Error {}
34 
35 impl Display for Error {
36     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37         use self::Error::*;
38 
39         let description = match self {
40             NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
41             AddressOverflow => "The SMBIOS table has too little address space to be stored",
42             Clear => "Failure while zeroing out the memory for the SMBIOS table",
43             WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
44             WriteData => "Failure to write additional data to memory",
45         };
46 
47         write!(f, "SMBIOS error: {}", description)
48     }
49 }
50 
51 pub type Result<T> = result::Result<T, Error>;
52 
53 // Constants sourced from SMBIOS Spec 3.2.0.
54 const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
55 const BIOS_INFORMATION: u8 = 0;
56 const SYSTEM_INFORMATION: u8 = 1;
57 const END_OF_TABLE: u8 = 127;
58 const PCI_SUPPORTED: u64 = 1 << 7;
59 const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
60 
61 fn compute_checksum<T: Copy>(v: &T) -> u8 {
62     // Safe because we are only reading the bytes within the size of the `T` reference `v`.
63     let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
64     let mut checksum: u8 = 0;
65     for i in v_slice.iter() {
66         checksum = checksum.wrapping_add(*i);
67     }
68     (!checksum).wrapping_add(1)
69 }
70 
71 #[repr(packed)]
72 #[derive(Default, Copy)]
73 pub struct Smbios30Entrypoint {
74     pub signature: [u8; 5usize],
75     pub checksum: u8,
76     pub length: u8,
77     pub majorver: u8,
78     pub minorver: u8,
79     pub docrev: u8,
80     pub revision: u8,
81     pub reserved: u8,
82     pub max_size: u32,
83     pub physptr: u64,
84 }
85 
86 impl Clone for Smbios30Entrypoint {
87     fn clone(&self) -> Self {
88         *self
89     }
90 }
91 
92 #[repr(packed)]
93 #[derive(Default, Copy)]
94 pub struct SmbiosBiosInfo {
95     pub typ: u8,
96     pub length: u8,
97     pub handle: u16,
98     pub vendor: u8,
99     pub version: u8,
100     pub start_addr: u16,
101     pub release_date: u8,
102     pub rom_size: u8,
103     pub characteristics: u64,
104     pub characteristics_ext1: u8,
105     pub characteristics_ext2: u8,
106 }
107 
108 impl Clone for SmbiosBiosInfo {
109     fn clone(&self) -> Self {
110         *self
111     }
112 }
113 
114 #[repr(packed)]
115 #[derive(Default, Copy)]
116 pub struct SmbiosSysInfo {
117     pub typ: u8,
118     pub length: u8,
119     pub handle: u16,
120     pub manufacturer: u8,
121     pub product_name: u8,
122     pub version: u8,
123     pub serial_number: u8,
124     pub uuid: [u8; 16usize],
125     pub wake_up_type: u8,
126     pub sku: u8,
127     pub family: u8,
128 }
129 
130 impl Clone for SmbiosSysInfo {
131     fn clone(&self) -> Self {
132         *self
133     }
134 }
135 
136 // SAFETY: These data structures only contain a series of integers
137 unsafe impl ByteValued for Smbios30Entrypoint {}
138 unsafe impl ByteValued for SmbiosBiosInfo {}
139 unsafe impl ByteValued for SmbiosSysInfo {}
140 
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 
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 
165 pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
166     let physptr = GuestAddress(SMBIOS_START)
167         .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
168         .ok_or(Error::NotEnoughMemory)?;
169     let mut curptr = physptr;
170     let mut handle = 0;
171 
172     {
173         handle += 1;
174         let smbios_biosinfo = SmbiosBiosInfo {
175             typ: BIOS_INFORMATION,
176             length: mem::size_of::<SmbiosBiosInfo>() as u8,
177             handle,
178             vendor: 1,  // First string written in this section
179             version: 2, // Second string written in this section
180             characteristics: PCI_SUPPORTED,
181             characteristics_ext2: IS_VIRTUAL_MACHINE,
182             ..Default::default()
183         };
184         curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
185         curptr = write_string(mem, "cloud-hypervisor", curptr)?;
186         curptr = write_string(mem, "0", curptr)?;
187         curptr = write_and_incr(mem, 0u8, curptr)?;
188     }
189 
190     {
191         handle += 1;
192         let smbios_sysinfo = SmbiosSysInfo {
193             typ: SYSTEM_INFORMATION,
194             length: mem::size_of::<SmbiosSysInfo>() as u8,
195             handle,
196             manufacturer: 1, // First string written in this section
197             product_name: 2, // Second string written in this section
198             ..Default::default()
199         };
200         curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
201         curptr = write_string(mem, "Cloud Hypervisor", curptr)?;
202         curptr = write_string(mem, "cloud-hypervisor", curptr)?;
203         curptr = write_and_incr(mem, 0u8, curptr)?;
204     }
205 
206     {
207         handle += 1;
208         let smbios_sysinfo = SmbiosSysInfo {
209             typ: END_OF_TABLE,
210             length: mem::size_of::<SmbiosSysInfo>() as u8,
211             handle,
212             ..Default::default()
213         };
214         curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
215         curptr = write_and_incr(mem, 0u8, curptr)?;
216     }
217 
218     {
219         let mut smbios_ep = Smbios30Entrypoint {
220             signature: *SM3_MAGIC_IDENT,
221             length: mem::size_of::<Smbios30Entrypoint>() as u8,
222             // SMBIOS rev 3.2.0
223             majorver: 0x03,
224             minorver: 0x02,
225             docrev: 0x00,
226             revision: 0x01, // SMBIOS 3.0
227             max_size: curptr.unchecked_offset_from(physptr) as u32,
228             physptr: physptr.0,
229             ..Default::default()
230         };
231         smbios_ep.checksum = compute_checksum(&smbios_ep);
232         mem.write_obj(smbios_ep, GuestAddress(SMBIOS_START))
233             .map_err(|_| Error::WriteSmbiosEp)?;
234     }
235 
236     Ok(curptr.unchecked_offset_from(physptr))
237 }
238 
239 #[cfg(test)]
240 mod tests {
241     use super::*;
242 
243     #[test]
244     fn struct_size() {
245         assert_eq!(
246             mem::size_of::<Smbios30Entrypoint>(),
247             0x18usize,
248             concat!("Size of: ", stringify!(Smbios30Entrypoint))
249         );
250         assert_eq!(
251             mem::size_of::<SmbiosBiosInfo>(),
252             0x14usize,
253             concat!("Size of: ", stringify!(SmbiosBiosInfo))
254         );
255         assert_eq!(
256             mem::size_of::<SmbiosSysInfo>(),
257             0x1busize,
258             concat!("Size of: ", stringify!(SmbiosSysInfo))
259         );
260     }
261 
262     #[test]
263     fn entrypoint_checksum() {
264         let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
265 
266         setup_smbios(&mem).unwrap();
267 
268         let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
269 
270         assert_eq!(compute_checksum(&smbios_ep), 0);
271     }
272 }
273