diff --git a/alioth/src/arch/x86_64/cpuid.rs b/alioth/src/arch/x86_64/cpuid.rs index 9252e9e0..adb301aa 100644 --- a/alioth/src/arch/x86_64/cpuid.rs +++ b/alioth/src/arch/x86_64/cpuid.rs @@ -12,8 +12,64 @@ // See the License for the specific language governing permissions and // limitations under the License. +use bitfield::bitfield; + +use crate::bitflags; + #[derive(Debug, Default, Clone, Hash, PartialEq, Eq)] pub struct CpuidIn { pub func: u32, pub index: Option, } + +bitflags! { + pub struct Cpuid1Ecx(u32) { + TSC_DEADLINE = 1 << 24; + HYPERVISOR = 1 << 31; + } +} + +bitflags! { + pub struct Cpuid7Index0Ebx(u32) { + TSC_ADJUST = 1 << 1; + } +} + +bitflags! { + pub struct Cpuid7Index0Edx(u32) { + IBRS_IBPB = 1 << 26; + SPEC_CTRL_ST_PREDICTORS = 1 << 27; + L1D_FLUSH_INTERFACE = 1 << 28; + ARCH_CAPABILITIES = 1 << 29; + CORE_CAPABILITIES = 1 << 30; + SPEC_CTRL_SSBD = 1 << 31; + } +} + +bitflags! { + pub struct CpuidExt8Ebx(u32) { + SSBD_VIRT_SPEC_CTRL = 1 << 25; + } +} + +bitflags! { + pub struct CpuidExt1fEAx(u32) { + SEV = 1 << 1; + SEV_ES = 1 << 3; + SEV_SNP = 1 << 4; + } +} + +bitfield! { + pub struct CpuidExt1fEbx(u32); + impl new; + pub u8, cbit_pos, set_cbit_pos : 5, 0; + pub u8, phys_addr_reduction, set_phys_addr_reduction : 11, 6; + pub u8, num_vmpl, set_num_vmpl : 15, 12; +} + +bitflags! { + pub struct CpuidExt21EAx(u32) { + NO_SMM_CTL_MSR = 1 << 9; + } +} diff --git a/alioth/src/board/board_x86_64/board_x86_64.rs b/alioth/src/board/board_x86_64/board_x86_64.rs index 00f7463f..3919edfa 100644 --- a/alioth/src/board/board_x86_64/board_x86_64.rs +++ b/alioth/src/board/board_x86_64/board_x86_64.rs @@ -26,7 +26,7 @@ use parking_lot::Mutex; use snafu::ResultExt; use zerocopy::{FromZeros, IntoBytes}; -use crate::arch::cpuid::CpuidIn; +use crate::arch::cpuid::{Cpuid1Ecx, CpuidIn}; use crate::arch::layout::{ BIOS_DATA_END, EBDA_END, EBDA_START, IOAPIC_START, MEM_64_START, PORT_ACPI_RESET, PORT_ACPI_SLEEP_CONTROL, PORT_ACPI_TIMER, PORT_CMOS_REG, RAM_32_SIZE, @@ -125,7 +125,7 @@ impl ArchBoard { let Some(out) = cpuids.get_mut(&leaf1) else { return error::MissingCpuid { leaf: leaf1 }.fail(); }; - out.ecx |= (1 << 24) | (1 << 31); + out.ecx |= (Cpuid1Ecx::TSC_DEADLINE | Cpuid1Ecx::HYPERVISOR).bits(); let leaf_8000_0000 = __cpuid(0x8000_0000); cpuids.insert( @@ -143,28 +143,10 @@ impl ArchBoard { cpuids.insert(CpuidIn { func, index: None }, host_cpuid); } - if matches!( - &config.coco, - Some(Coco::AmdSev { .. } | Coco::AmdSnp { .. }) - ) { - // AMD Volume 3, section E.4.17. - let leaf = CpuidIn { - func: 0x8000_001f, - index: None, - }; - let Some(out) = cpuids.get_mut(&leaf) else { - return error::MissingCpuid { leaf }.fail(); - }; - let host_ebx = __cpuid(leaf.func).ebx; - // set PhysAddrReduction to 1 - out.ebx = (1 << 6) | (host_ebx & 0x3f); - out.ecx = 0; - out.edx = 0; - if let Some(Coco::AmdSev { policy }) = &config.coco { - out.eax = if policy.es() { 0x2 | 0x8 } else { 0x2 }; - } else if let Some(Coco::AmdSnp { .. }) = &config.coco { - out.eax = 0x2 | 0x8 | 0x10; - } + if let Some(coco) = &config.coco + && matches!(coco, Coco::AmdSev { .. } | Coco::AmdSnp { .. }) + { + sev::adjust_cpuid(coco, &mut cpuids)?; } Ok(Self { diff --git a/alioth/src/board/board_x86_64/sev.rs b/alioth/src/board/board_x86_64/sev.rs index a5f88dce..a788902e 100644 --- a/alioth/src/board/board_x86_64/sev.rs +++ b/alioth/src/board/board_x86_64/sev.rs @@ -12,23 +12,107 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::arch::x86_64::{__cpuid, CpuidResult}; +use std::collections::HashMap; use std::iter::zip; use std::sync::Arc; use std::sync::atomic::Ordering; use zerocopy::FromZeros; +use crate::arch::cpuid::{ + Cpuid1Ecx, Cpuid7Index0Ebx, Cpuid7Index0Edx, CpuidExt1fEAx, CpuidExt1fEbx, CpuidExt8Ebx, + CpuidExt21EAx, CpuidIn, +}; use crate::arch::layout::MEM_64_START; use crate::arch::reg::{Reg, SegAccess, SegReg, SegRegVal}; use crate::arch::sev::{SevPolicy, SnpPageType, SnpPolicy}; -use crate::board::{Board, Result}; +use crate::board::{Board, Result, error}; use crate::firmware::ovmf::sev::{ SevDescType, SevMetadataDesc, SnpCpuidFunc, SnpCpuidInfo, parse_desc, parse_sev_ap_eip, }; -use crate::hv::{Vcpu, Vm, VmMemory}; +use crate::hv::{Coco, Vcpu, Vm, VmMemory}; use crate::mem::mapped::ArcMemPages; use crate::mem::{self, LayoutChanged, MarkPrivateMemory}; +pub fn adjust_cpuid(coco: &Coco, cpuids: &mut HashMap) -> Result<()> { + // AMD Volume 3, section E.4.17. + let in_ = CpuidIn { + func: 0x8000_001f, + index: None, + }; + let Some(out) = cpuids.get_mut(&in_) else { + return error::MissingCpuid { leaf: in_ }.fail(); + }; + let host_ebx = CpuidExt1fEbx(__cpuid(in_.func).ebx); + out.ebx = CpuidExt1fEbx::new(host_ebx.cbit_pos(), 1, 0).0; + out.ecx = 0; + out.edx = 0; + if let Coco::AmdSev { policy } = coco { + out.eax = if policy.es() { + (CpuidExt1fEAx::SEV | CpuidExt1fEAx::SEV_ES).bits() + } else { + CpuidExt1fEAx::SEV.bits() + }; + } else if let Coco::AmdSnp { .. } = coco { + out.eax = (CpuidExt1fEAx::SEV | CpuidExt1fEAx::SEV_ES | CpuidExt1fEAx::SEV_SNP).bits() + } + + if let Coco::AmdSnp { .. } = coco { + snp_adjust_cpuids(cpuids); + } + + Ok(()) +} + +fn snp_adjust_cpuids(cpuids: &mut HashMap) { + let in_ = CpuidIn { + func: 0x1, + index: None, + }; + if let Some(out) = cpuids.get_mut(&in_) { + out.ecx &= !Cpuid1Ecx::TSC_DEADLINE.bits() + }; + + let in_ = CpuidIn { + func: 0x7, + index: Some(0), + }; + if let Some(out) = cpuids.get_mut(&in_) { + out.ebx &= !Cpuid7Index0Ebx::TSC_ADJUST.bits(); + out.edx &= !(Cpuid7Index0Edx::IBRS_IBPB + | Cpuid7Index0Edx::SPEC_CTRL_ST_PREDICTORS + | Cpuid7Index0Edx::L1D_FLUSH_INTERFACE + | Cpuid7Index0Edx::ARCH_CAPABILITIES + | Cpuid7Index0Edx::CORE_CAPABILITIES + | Cpuid7Index0Edx::SPEC_CTRL_SSBD) + .bits() + } + + let in_ = CpuidIn { + func: 0x8000_0008, + index: None, + }; + if let Some(out) = cpuids.get_mut(&in_) { + out.ebx &= !CpuidExt8Ebx::SSBD_VIRT_SPEC_CTRL.bits(); + } + + let in_ = CpuidIn { + func: 0x8000_0021, + index: None, + }; + if let Some(out) = cpuids.get_mut(&in_) { + out.eax &= !CpuidExt21EAx::NO_SMM_CTL_MSR.bits(); + } + + for index in 0..=4 { + cpuids.remove(&CpuidIn { + func: 0x8000_0026, + index: Some(index), + }); + } +} + impl Board where V: Vm, @@ -59,7 +143,7 @@ where let mut cpuid_table = SnpCpuidInfo::new_zeroed(); let ram_bus = self.memory.ram_bus(); let ram = ram_bus.lock_layout(); - let snp_page_type = match desc.type_ { + let page_type = match desc.type_ { SevDescType::SNP_DESC_MEM => SnpPageType::UNMEASURED, SevDescType::SNP_SECRETS => SnpPageType::SECRETS, SevDescType::CPUID => { @@ -73,24 +157,18 @@ where _ => unimplemented!(), }; let range_ref = ram.get_slice::(desc.base as u64, desc.len as u64)?; - let range_bytes = + let bytes = unsafe { std::slice::from_raw_parts_mut(range_ref.as_ptr() as _, range_ref.len()) }; self.memory .mark_private_memory(desc.base as _, desc.len as _, true)?; - let mut ret = self - .vm - .snp_launch_update(range_bytes, desc.base as _, snp_page_type); + let ret = self.vm.snp_launch_update(bytes, desc.base as _, page_type); if ret.is_err() && desc.type_ == SevDescType::CPUID { let updated_cpuid: SnpCpuidInfo = ram.read_t(desc.base as _)?; - for (set, got) in zip(cpuid_table.entries.iter(), updated_cpuid.entries.iter()) { + for (set, got) in zip(&cpuid_table.entries, &updated_cpuid.entries) { if set != got { log::error!("set {set:#x?}, but firmware expects {got:#x?}"); } } - ram.write_t(desc.base as _, &updated_cpuid)?; - ret = self - .vm - .snp_launch_update(range_bytes, desc.base as _, snp_page_type); } ret?; Ok(())