summary refs log tree commit diff
path: root/x86_64
diff options
context:
space:
mode:
Diffstat (limited to 'x86_64')
-rw-r--r--x86_64/src/lib.rs5
-rw-r--r--x86_64/src/smbios.rs247
2 files changed, 252 insertions, 0 deletions
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index 5229f63..84483a5 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -51,6 +51,7 @@ mod gdt;
 mod interrupts;
 mod mptable;
 mod regs;
+mod smbios;
 
 use std::error::Error as StdError;
 use std::ffi::{CStr, CString};
@@ -103,6 +104,7 @@ pub enum Error {
     SetupMptable(mptable::Error),
     SetupMsrs(regs::Error),
     SetupRegs(regs::Error),
+    SetupSmbios(smbios::Error),
     SetupSregs(regs::Error),
     ZeroPagePastRamEnd,
     ZeroPageSetup,
@@ -144,6 +146,7 @@ impl Display for Error {
             SetupMptable(e) => write!(f, "failed to set up mptable: {}", e),
             SetupMsrs(e) => write!(f, "failed to set up MSRs: {}", e),
             SetupRegs(e) => write!(f, "failed to set up registers: {}", e),
+            SetupSmbios(e) => write!(f, "failed to set up SMBIOS: {}", e),
             SetupSregs(e) => write!(f, "failed to set up sregs: {}", e),
             ZeroPagePastRamEnd => write!(f, "the zero page extends past the end of guest_mem"),
             ZeroPageSetup => write!(f, "error writing the zero page of guest memory"),
@@ -190,6 +193,8 @@ fn configure_system(
     // Note that this puts the mptable at 0x0 in guest physical memory.
     mptable::setup_mptable(guest_mem, num_cpus, pci_irqs).map_err(Error::SetupMptable)?;
 
+    smbios::setup_smbios(guest_mem).map_err(Error::SetupSmbios)?;
+
     let mut params: boot_params = Default::default();
 
     params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
diff --git a/x86_64/src/smbios.rs b/x86_64/src/smbios.rs
new file mode 100644
index 0000000..491ad4a
--- /dev/null
+++ b/x86_64/src/smbios.rs
@@ -0,0 +1,247 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::fmt::{self, Display};
+use std::mem;
+use std::result;
+use std::slice;
+
+use data_model::DataInit;
+use sys_util::{GuestAddress, GuestMemory};
+
+#[derive(Debug)]
+pub enum Error {
+    /// There was too little guest memory to store the entire SMBIOS table.
+    NotEnoughMemory,
+    /// The SMBIOS table has too little address space to be stored.
+    AddressOverflow,
+    /// Failure while zeroing out the memory for the SMBIOS table.
+    Clear,
+    /// Failure to write SMBIOS entrypoint structure
+    WriteSmbiosEp,
+    /// Failure to write additional data to memory
+    WriteData,
+}
+
+impl std::error::Error for Error {}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        let description = match self {
+            NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
+            AddressOverflow => "The SMBIOS table has too little address space to be stored",
+            Clear => "Failure while zeroing out the memory for the SMBIOS table",
+            WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
+            WriteData => "Failure to write additional data to memory",
+        };
+
+        write!(f, "SMBIOS error: {}", description)
+    }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+const SMBIOS_START: u64 = 0xf0000; // First possible location per the spec.
+
+// Constants sourced from SMBIOS Spec 3.2.0.
+const SM3_MAGIC_IDENT: &'static [u8; 5usize] = b"_SM3_";
+const BIOS_INFORMATION: u8 = 0;
+const SYSTEM_INFORMATION: u8 = 1;
+const PCI_SUPPORTED: u64 = 1 << 7;
+const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
+
+fn compute_checksum<T: Copy>(v: &T) -> u8 {
+    // Safe because we are only reading the bytes within the size of the `T` reference `v`.
+    let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
+    let mut checksum: u8 = 0;
+    for i in v_slice.iter() {
+        checksum = checksum.wrapping_add(*i);
+    }
+    (!checksum).wrapping_add(1)
+}
+
+#[repr(packed)]
+#[derive(Default, Copy)]
+pub struct Smbios30Entrypoint {
+    pub signature: [u8; 5usize],
+    pub checksum: u8,
+    pub length: u8,
+    pub majorver: u8,
+    pub minorver: u8,
+    pub docrev: u8,
+    pub revision: u8,
+    pub reserved: u8,
+    pub max_size: u32,
+    pub physptr: u64,
+}
+unsafe impl data_model::DataInit for Smbios30Entrypoint {}
+
+impl Clone for Smbios30Entrypoint {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+#[repr(packed)]
+#[derive(Default, Copy)]
+pub struct SmbiosBiosInfo {
+    pub typ: u8,
+    pub length: u8,
+    pub handle: u16,
+    pub vendor: u8,
+    pub version: u8,
+    pub start_addr: u16,
+    pub release_date: u8,
+    pub rom_size: u8,
+    pub characteristics: u64,
+    pub characteristics_ext1: u8,
+    pub characteristics_ext2: u8,
+}
+
+impl Clone for SmbiosBiosInfo {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+unsafe impl data_model::DataInit for SmbiosBiosInfo {}
+
+#[repr(packed)]
+#[derive(Default, Copy)]
+pub struct SmbiosSysInfo {
+    pub typ: u8,
+    pub length: u8,
+    pub handle: u16,
+    pub manufacturer: u8,
+    pub product_name: u8,
+    pub version: u8,
+    pub serial_number: u8,
+    pub uuid: [u8; 16usize],
+    pub wake_up_type: u8,
+    pub sku: u8,
+    pub family: u8,
+}
+
+impl Clone for SmbiosSysInfo {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+unsafe impl data_model::DataInit for SmbiosSysInfo {}
+
+fn write_and_incr<T: DataInit>(
+    mem: &GuestMemory,
+    val: T,
+    mut curptr: GuestAddress,
+) -> Result<GuestAddress> {
+    mem.write_obj_at_addr(val, curptr)
+        .map_err(|_| Error::WriteData)?;
+    curptr = curptr
+        .checked_add(mem::size_of::<T>() as u64)
+        .ok_or(Error::NotEnoughMemory)?;
+    Ok(curptr)
+}
+
+fn write_string(mem: &GuestMemory, val: &str, mut curptr: GuestAddress) -> Result<GuestAddress> {
+    for c in val.as_bytes().iter() {
+        curptr = write_and_incr(mem, c.clone(), curptr)?;
+    }
+    curptr = write_and_incr(mem, 0 as u8, curptr)?;
+    Ok(curptr)
+}
+
+pub fn setup_smbios(mem: &GuestMemory) -> Result<()> {
+    let physptr = GuestAddress(SMBIOS_START)
+        .checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
+        .ok_or(Error::NotEnoughMemory)?;
+    let mut curptr = physptr;
+    let mut handle = 0;
+
+    {
+        handle += 1;
+        let mut smbios_biosinfo = SmbiosBiosInfo::default();
+        smbios_biosinfo.typ = BIOS_INFORMATION;
+        smbios_biosinfo.length = mem::size_of::<SmbiosBiosInfo>() as u8;
+        smbios_biosinfo.handle = handle;
+        smbios_biosinfo.vendor = 1; // First string written in this section
+        smbios_biosinfo.version = 2; // Second string written in this section
+        smbios_biosinfo.characteristics = PCI_SUPPORTED;
+        smbios_biosinfo.characteristics_ext2 = IS_VIRTUAL_MACHINE;
+        curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
+        curptr = write_string(mem, "crosvm", curptr)?;
+        curptr = write_string(mem, "0", curptr)?;
+        curptr = write_and_incr(mem, 0 as u8, curptr)?;
+    }
+
+    {
+        handle += 1;
+        let mut smbios_sysinfo = SmbiosSysInfo::default();
+        smbios_sysinfo.typ = SYSTEM_INFORMATION;
+        smbios_sysinfo.length = mem::size_of::<SmbiosSysInfo>() as u8;
+        smbios_sysinfo.handle = handle;
+        smbios_sysinfo.manufacturer = 1; // First string written in this section
+        smbios_sysinfo.product_name = 2; // Second string written in this section
+        curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
+        curptr = write_string(mem, "ChromiumOS", curptr)?;
+        curptr = write_string(mem, "crosvm", curptr)?;
+        curptr = write_and_incr(mem, 0 as u8, curptr)?;
+    }
+
+    {
+        let mut smbios_ep = Smbios30Entrypoint::default();
+        smbios_ep.signature = *SM3_MAGIC_IDENT;
+        smbios_ep.length = mem::size_of::<Smbios30Entrypoint>() as u8;
+        // SMBIOS rev 3.2.0
+        smbios_ep.majorver = 0x03;
+        smbios_ep.minorver = 0x02;
+        smbios_ep.docrev = 0x00;
+        smbios_ep.revision = 0x01; // SMBIOS 3.0
+        smbios_ep.max_size = curptr.offset_from(physptr) as u32;
+        smbios_ep.physptr = physptr.offset();
+        smbios_ep.checksum = compute_checksum(&smbios_ep);
+        mem.write_obj_at_addr(smbios_ep, GuestAddress(SMBIOS_START))
+            .map_err(|_| Error::WriteSmbiosEp)?;
+    }
+
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn struct_size() {
+        assert_eq!(
+            mem::size_of::<Smbios30Entrypoint>(),
+            0x18usize,
+            concat!("Size of: ", stringify!(Smbios30Entrypoint))
+        );
+        assert_eq!(
+            mem::size_of::<SmbiosBiosInfo>(),
+            0x14usize,
+            concat!("Size of: ", stringify!(SmbiosBiosInfo))
+        );
+        assert_eq!(
+            mem::size_of::<SmbiosSysInfo>(),
+            0x1busize,
+            concat!("Size of: ", stringify!(SmbiosSysInfo))
+        );
+    }
+
+    #[test]
+    fn entrypoint_checksum() {
+        let mem = GuestMemory::new(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
+
+        setup_smbios(&mem).unwrap();
+
+        let smbios_ep: Smbios30Entrypoint =
+            mem.read_obj_from_addr(GuestAddress(SMBIOS_START)).unwrap();
+
+        assert_eq!(compute_checksum(&smbios_ep), 0);
+    }
+}