mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-08-21 07:21:15 +00:00
arch: x86_64: Add basic SMBIOS support
Taken from crosvm: 44336b913126d73f9f8d6854f57aac92b5db809e and adapted for Cloud Hypervisor. This is basic and incomplete support but Linux correctly finds the DMI data based on this: root@clr-c6ed47bc1c9d473d9a3a8bddc50ee4cb ~ # dmesg | grep -i dmi [ 0.000000] DMI: Cloud Hypervisor cloud-hypervisor, BIOS 0 root@clr-c6ed47bc1c9d473d9a3a8bddc50ee4cb ~ # dmesg | grep -i smbio [ 0.000000] SMBIOS 3.2.0 present. Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
parent
24c051c663
commit
2c3c335de6
@ -70,6 +70,8 @@ pub const EBDA_START: GuestAddress = GuestAddress(0xa0000);
|
|||||||
// ACPI RSDP table
|
// ACPI RSDP table
|
||||||
pub const RSDP_POINTER: GuestAddress = EBDA_START;
|
pub const RSDP_POINTER: GuestAddress = EBDA_START;
|
||||||
|
|
||||||
|
pub const SMBIOS_START: u64 = 0xf0000; // First possible location per the spec.
|
||||||
|
|
||||||
// == End of "EBDA" range ==
|
// == End of "EBDA" range ==
|
||||||
|
|
||||||
// ** High RAM (start: 1MiB, length: 3071MiB) **
|
// ** High RAM (start: 1MiB, length: 3071MiB) **
|
||||||
|
@ -25,6 +25,7 @@ use vm_memory::{
|
|||||||
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||||
GuestMemoryMmap, GuestMemoryRegion, GuestUsize,
|
GuestMemoryMmap, GuestMemoryRegion, GuestUsize,
|
||||||
};
|
};
|
||||||
|
mod smbios;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum BootProtocol {
|
pub enum BootProtocol {
|
||||||
@ -114,6 +115,9 @@ pub enum Error {
|
|||||||
|
|
||||||
/// Cannot set the local interruption due to bad configuration.
|
/// Cannot set the local interruption due to bad configuration.
|
||||||
LocalIntConfiguration(anyhow::Error),
|
LocalIntConfiguration(anyhow::Error),
|
||||||
|
|
||||||
|
/// Error setting up SMBIOS table
|
||||||
|
SmbiosSetup(smbios::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for super::Error {
|
impl From<Error> for super::Error {
|
||||||
@ -296,6 +300,8 @@ pub fn configure_system(
|
|||||||
rsdp_addr: Option<GuestAddress>,
|
rsdp_addr: Option<GuestAddress>,
|
||||||
boot_prot: BootProtocol,
|
boot_prot: BootProtocol,
|
||||||
) -> super::Result<()> {
|
) -> super::Result<()> {
|
||||||
|
smbios::setup_smbios(guest_mem).map_err(Error::SmbiosSetup)?;
|
||||||
|
|
||||||
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
|
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
|
||||||
#[cfg(not(feature = "acpi"))]
|
#[cfg(not(feature = "acpi"))]
|
||||||
mptable::setup_mptable(guest_mem, _num_cpus).map_err(Error::MpTableSetup)?;
|
mptable::setup_mptable(guest_mem, _num_cpus).map_err(Error::MpTableSetup)?;
|
||||||
|
252
arch/src/x86_64/smbios.rs
Normal file
252
arch/src/x86_64/smbios.rs
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
// Copyright © 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
|
use layout::SMBIOS_START;
|
||||||
|
use std::fmt::{self, Display};
|
||||||
|
use std::mem;
|
||||||
|
use std::result;
|
||||||
|
use std::slice;
|
||||||
|
use vm_memory::ByteValued;
|
||||||
|
use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryMmap};
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[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>;
|
||||||
|
|
||||||
|
// Constants sourced from SMBIOS Spec 3.2.0.
|
||||||
|
const SM3_MAGIC_IDENT: &[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 ByteValued 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 ByteValued 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 ByteValued for SmbiosSysInfo {}
|
||||||
|
|
||||||
|
fn write_and_incr<T: ByteValued>(
|
||||||
|
mem: &GuestMemoryMmap,
|
||||||
|
val: T,
|
||||||
|
mut curptr: GuestAddress,
|
||||||
|
) -> Result<GuestAddress> {
|
||||||
|
mem.write_obj(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: &GuestMemoryMmap,
|
||||||
|
val: &str,
|
||||||
|
mut curptr: GuestAddress,
|
||||||
|
) -> Result<GuestAddress> {
|
||||||
|
for c in val.as_bytes().iter() {
|
||||||
|
curptr = write_and_incr(mem, *c, curptr)?;
|
||||||
|
}
|
||||||
|
curptr = write_and_incr(mem, 0 as u8, curptr)?;
|
||||||
|
Ok(curptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_smbios(mem: &GuestMemoryMmap) -> 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, "cloud-hypervisor", 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, "Cloud Hypervisor", curptr)?;
|
||||||
|
curptr = write_string(mem, "cloud-hypervisor", 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.unchecked_offset_from(physptr) as u32;
|
||||||
|
smbios_ep.physptr = physptr.0;
|
||||||
|
smbios_ep.checksum = compute_checksum(&smbios_ep);
|
||||||
|
mem.write_obj(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 = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
|
||||||
|
|
||||||
|
setup_smbios(&mem).unwrap();
|
||||||
|
|
||||||
|
let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(compute_checksum(&smbios_ep), 0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user