Skip to content

Commit

Permalink
add bitflags for a few CPUID registers
Browse files Browse the repository at this point in the history
  • Loading branch information
gjcolombo committed Oct 8, 2024
1 parent 0fcad4c commit 3a5752a
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/cpuid-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ license = "MPL-2.0"
edition = "2021"

[dependencies]
bitflags.workspace = true
propolis_api_types = {workspace = true, optional = true}
propolis_types.workspace = true
thiserror.workspace = true
Expand Down
196 changes: 196 additions & 0 deletions crates/cpuid-utils/src/bits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! Bitflags and constants that provide symbolic names for the various bits in
//! various CPUID leaves.
//!
//! Definitions here are taken from the AMD Architecture Programmer's Manual,
//! volume 3, appendix E (Publication 24594, revision 3.36, March 2024).
bitflags::bitflags! {
/// Leaf 1 ecx: instruction feature identifiers.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Leaf1Ecx: u32 {
const SSE3 = 1 << 0;
const PCLMULQDQ = 1 << 1;
const MONITOR = 1 << 3;
const SSSE3 = 1 << 9;
const FMA = 1 << 12;
const CMPXCHG16B = 1 << 13;
const SSE41 = 1 << 19;
const SSE42 = 1 << 20;
const X2APIC = 1 << 21;
const MOVBE = 1 << 22;
const POPCNT = 1 << 23;
const AES = 1 << 25;
const XSAVE = 1 << 26;
const OSXSAVE = 1 << 27;
const AVX = 1 << 28;
const F16C = 1 << 29;
const RDRAND = 1 << 30;
const HV_GUEST = 1 << 31;

const ALL_FLAGS = Self::SSE3.bits() | Self::PCLMULQDQ.bits() |
Self::MONITOR.bits() | Self::SSSE3.bits() | Self::FMA.bits() |
Self::CMPXCHG16B.bits() | Self::SSE41.bits() | Self::SSE42.bits() |
Self::X2APIC.bits() | Self::MOVBE.bits() | Self::POPCNT.bits() |
Self::AES.bits() | Self::XSAVE.bits() | Self::OSXSAVE.bits() |
Self::AVX.bits() | Self::F16C.bits() | Self::RDRAND.bits() |
Self::HV_GUEST.bits();
}

/// Leaf 1 edx: Instruction feature identifiers.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Leaf1Edx: u32 {
const FPU = 1 << 0;
const VME = 1 << 1;
const DE = 1 << 2;
const PSE = 1 << 3;
const TSC = 1 << 4;
const MSR = 1 << 5;
const PAE = 1 << 6;
const MCE = 1 << 7;
const CMPXCHG8B = 1 << 8;
const APIC = 1 << 9;
const SYSENTER = 1 << 11;
const MTRR = 1 << 12;
const PGE = 1 << 13;
const MCA = 1 << 14;
const CMOV = 1 << 15;
const PAT = 1 << 16;
const PSE36 = 1 << 17;
const CLFLUSH = 1 << 19;
const MMX = 1 << 23;
const FXSR = 1 << 24;
const SSE = 1 << 25;
const SSE2 = 1 << 26;
const HTT = 1 << 28;

const ALL_FLAGS = Self::FPU.bits() | Self::VME.bits() |
Self::DE.bits() | Self::PSE.bits() | Self::TSC.bits() |
Self::MSR.bits() | Self::PAE.bits() | Self::MCE.bits() |
Self::CMPXCHG8B.bits() | Self::APIC.bits() | Self::SYSENTER.bits() |
Self::MTRR.bits() | Self::PGE.bits() | Self::MCA.bits() |
Self::CMOV.bits() | Self::PAT.bits() | Self::PSE36.bits() |
Self::CLFLUSH.bits() | Self::MMX.bits() | Self::FXSR.bits() |
Self::SSE.bits() | Self::SSE2.bits() | Self::HTT.bits();
}

/// Leaf 7 subleaf 0 ebx: instruction feature identifiers.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Leaf7Sub0Ebx: u32 {
const FSGSBASE = 1 << 0;
const BMI1 = 1 << 3;
const AVX2 = 1 << 5;
const SMEP = 1 << 7;
const BMI2 = 1 << 8;
const INVPCID = 1 << 10;
const PQM = 1 << 12;
const PQE = 1 << 15;
const RDSEED = 1 << 18;
const ADX = 1 << 19;
const SMAP = 1 << 20;
const CLFLUSHOPT = 1 << 23;
const CLWB = 1 << 24;
const SHA = 1 << 29;

const ALL_FLAGS = Self::FSGSBASE.bits() |
Self::BMI1.bits() | Self::AVX2.bits() | Self::SMEP.bits() |
Self::BMI2.bits() | Self::INVPCID.bits() | Self::PQM.bits() |
Self::PQE.bits() | Self::RDSEED.bits() | Self::ADX.bits() |
Self::SMAP.bits() | Self::CLFLUSHOPT.bits() | Self::CLWB.bits() |
Self::SHA.bits();
}

/// Leaf 0x8000_0001 ecx: Extended processor feature identifiers.
///
/// NOTE: These definitions are AMD-specific.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AmdExtLeaf1Ecx: u32 {
const LAHF = 1 << 0;
const CMP_LEGACY = 1 << 1;
const SVM = 1 << 2;
const EXT_APIC_SPACE = 1 << 3;
const ALT_MOV_CR8 = 1 << 4;
const ABM = 1 << 5;
const SSE4A = 1 << 6;
const MISALIGN_SSE = 1 << 7;
const THREED_NOW_PREFETCH = 1 << 8;
const OSVW = 1 << 9;
const IBS = 1 << 10;
const XOP = 1 << 11;
const SKINIT = 1 << 12;
const WDT = 1 << 13;
const LWP = 1 << 15;
const FMA4 = 1 << 16;
const TCE = 1 << 17;
const TBM = 1 << 21;
const TOPOLOGY_EXT = 1 << 22;
const PMC_EXT_CORE = 1 << 23;
const PMC_EXT_NB = 1 << 24;
const DATA_ACCESS_BP = 1 << 26;
const PERF_TSC = 1 << 27;
const PMC_EXT_LLC = 1 << 28;
const MONITORX = 1 << 29;
const DATA_BP_ADDR_MASK_EXT = 1 << 30;

const ALL_FLAGS = Self::LAHF.bits() | Self::CMP_LEGACY.bits() |
Self::SVM.bits() | Self::EXT_APIC_SPACE.bits() |
Self::ALT_MOV_CR8.bits() | Self::ABM.bits() | Self::SSE4A.bits() |
Self::MISALIGN_SSE.bits() | Self::THREED_NOW_PREFETCH.bits() |
Self::OSVW.bits() | Self::IBS.bits() | Self::XOP.bits() |
Self::SKINIT.bits() | Self::WDT.bits() | Self::LWP.bits() |
Self::FMA4.bits() | Self::TCE.bits() | Self::TBM.bits() |
Self::TOPOLOGY_EXT.bits() | Self::PMC_EXT_CORE.bits() |
Self::PMC_EXT_NB.bits() | Self::DATA_ACCESS_BP.bits() |
Self::PERF_TSC.bits() | Self::PMC_EXT_LLC.bits() |
Self::MONITORX.bits() | Self::DATA_BP_ADDR_MASK_EXT.bits();
}

/// Leaf 0x8000_0001 edx: Extended processor feature identifiers.
///
/// NOTE: These definitions are AMD-specific.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AmdExtLeaf1Edx: u32 {
const FPU = Leaf1Edx::FPU.bits();
const VME = Leaf1Edx::VME.bits();
const DE = Leaf1Edx::DE.bits();
const PSE = Leaf1Edx::PSE.bits();
const TSC = Leaf1Edx::TSC.bits();
const MSR = Leaf1Edx::MSR.bits();
const PAE = Leaf1Edx::PAE.bits();
const MCE = Leaf1Edx::MCE.bits();
const CMPXCHG8B = Leaf1Edx::CMPXCHG8B.bits();
const APIC = Leaf1Edx::APIC.bits();
const SYSCALL = 1 << 11;
const MTRR = Leaf1Edx::MTRR.bits();
const PGE = Leaf1Edx::PGE.bits();
const MCA = Leaf1Edx::MCA.bits();
const CMOV = Leaf1Edx::CMOV.bits();
const PAT = Leaf1Edx::PAT.bits();
const PSE36 = Leaf1Edx::PSE36.bits();
const NX = 1 << 20;
const MMX_EXT = 1 << 22;
const MMX = 1 << 23;
const FXSAVE = 1 << 24;
const FXSAVE_OPT = 1 << 25;
const GB_PAGE = 1 << 26;
const RDTSCP = 1 << 27;
const LONG_MODE = 1 << 29;
const THREED_NOW_EXT = 1 << 30;
const THREED_NOW = 1 << 31;

const ALL_FLAGS = Self::FPU.bits() | Self::VME.bits() |
Self::DE.bits() | Self::PSE.bits() | Self::TSC.bits() |
Self::MSR.bits() | Self::PAE.bits() | Self::MCE.bits() |
Self::CMPXCHG8B.bits() | Self::APIC.bits() | Self::SYSCALL.bits() |
Self::MTRR.bits() | Self::PGE.bits() | Self::MCA.bits() |
Self::CMOV.bits() | Self::PAT.bits() | Self::PSE36.bits() |
Self::NX.bits() | Self::MMX_EXT.bits() | Self::MMX.bits() |
Self::FXSAVE.bits() | Self::FXSAVE_OPT.bits() |
Self::GB_PAGE.bits() | Self::RDTSCP.bits() |
Self::LONG_MODE.bits() | Self::THREED_NOW_EXT.bits() |
Self::THREED_NOW.bits();
}
}
2 changes: 2 additions & 0 deletions crates/cpuid-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use std::{
pub use propolis_types::{CpuidIdent, CpuidValues, CpuidVendor};
use thiserror::Error;

pub mod bits;

#[cfg(feature = "instance-spec")]
mod instance_spec;

Expand Down
35 changes: 27 additions & 8 deletions phd-tests/tests/src/cpuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ async fn cpuid_instance_spec_round_trip_test(ctx: &Framework) {

#[phd_testcase]
async fn cpuid_boot_test(ctx: &Framework) {
use cpuid_utils::bits::*;

// This test verifies that plumbing a reasonably sensible-looking set of
// CPUID values into the guest produces a bootable guest.
//
Expand Down Expand Up @@ -88,15 +90,24 @@ async fn cpuid_boot_test(ctx: &Framework) {
// Report that leaf 7 is the last available standard leaf.
host_cpuid.get_mut(&CpuidIdent::leaf(0)).unwrap().eax = 7;

// Mask off feature bits that the Oxide platform won't support. See RFD
// 314. These are masks (and not assignments) so that features the host CPU
// doesn't support won't appear in the guest's feature masks.
// Mask off bits that a minimum-viable Oxide platform doesn't support or
// can't (or won't) expose to guests. See RFD 314 for further discussion of
// how these masks were chosen.
//
// These are masks (and not assignments) so that, if the host processor
// doesn't support a feature contained in an `ALL_FLAGS` value, the
let leaf_1 = host_cpuid.get_mut(&CpuidIdent::leaf(1)).unwrap();
leaf_1.ecx &= 0xF6F8_3203;
leaf_1.edx &= 0x178B_FBFF;
leaf_1.ecx &=
(Leaf1Ecx::ALL_FLAGS & !(Leaf1Ecx::OSXSAVE | Leaf1Ecx::MONITOR)).bits();
leaf_1.edx &= Leaf1Edx::ALL_FLAGS.bits();
let leaf_7 = host_cpuid.get_mut(&CpuidIdent::leaf(7)).unwrap();
leaf_7.eax = 0;
leaf_7.ebx &= 0x219C_03A9;
leaf_7.ebx &= (Leaf7Sub0Ebx::ALL_FLAGS
& !(Leaf7Sub0Ebx::PQM
| Leaf7Sub0Ebx::PQE
| Leaf7Sub0Ebx::RDSEED
| Leaf7Sub0Ebx::INVPCID))
.bits();
leaf_7.ecx = 0;
leaf_7.edx = 0;

Expand All @@ -106,8 +117,16 @@ async fn cpuid_boot_test(ctx: &Framework) {
0x8000_0004;
let ext_leaf_1 =
host_cpuid.get_mut(&CpuidIdent::leaf(0x8000_0001)).unwrap();
ext_leaf_1.ecx &= 0x4440_01F0;
ext_leaf_1.edx &= 0x27D3_FBFF;
ext_leaf_1.ecx &= (AmdExtLeaf1Ecx::ALT_MOV_CR8
| AmdExtLeaf1Ecx::ABM
| AmdExtLeaf1Ecx::SSE4A
| AmdExtLeaf1Ecx::MISALIGN_SSE
| AmdExtLeaf1Ecx::THREED_NOW_PREFETCH
| AmdExtLeaf1Ecx::DATA_ACCESS_BP
| AmdExtLeaf1Ecx::DATA_BP_ADDR_MASK_EXT)
.bits();
ext_leaf_1.edx &=
(AmdExtLeaf1Edx::ALL_FLAGS & !AmdExtLeaf1Edx::RDTSCP).bits();

// Test the plumbing by pumping a fake processor brand string into extended
// leaves 2-4 and seeing if the guest recognizes it.
Expand Down

0 comments on commit 3a5752a

Please sign in to comment.