mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-11 04:26:38 +00:00
Move GuestAddress and GuestMemory to a new crate for VM memory. This will make separating sys_util and crosvm independent making it easier to use sys_util functions outside of crosvm. Change-Id: I12e14948ea85754dfa6267b3a3fb32b77ef6796e Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2311251 Auto-Submit: Dylan Reid <dgreid@chromium.org> Commit-Queue: Dylan Reid <dgreid@chromium.org> Tested-by: Dylan Reid <dgreid@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
247 lines
7.3 KiB
Rust
247 lines
7.3 KiB
Rust
// 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 vm_memory::{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: &[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, 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);
|
|
}
|
|
}
|