mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
hypervisor, vmm: Build and pass SNP ID block to launch finish
Add KvmSevSnpIdBlock and KvmSevSnpIdAuth structs matching the AMD SEV-SNP Firmware ABI Spec (Rev 1.58), and build them from the IGVM SNP ID block directive during launch finish. This properly populates id_block_uaddr/id_auth_uaddr in KVM_SEV_SNP_LAUNCH_FINISH and derives auth_key_en from the assembled author key, matching QEMU's behavior. Thread the guest policy from sev_snp_init to launch_finish via an atomic on KvmVm so the ID block gets the correct policy value. Also track has_snp_id_block in IgvmLoadedInfo to enable the ID block based on whether the IGVM file actually contains one, rather than hardcoding it for KVM. Signed-off-by: Ruben Hakobyan <hruben@meta.com>
This commit is contained in:
committed by
Rob Bradford
parent
bfab43e252
commit
c97d635d40
@@ -563,6 +563,8 @@ pub struct KvmVm {
|
||||
msrs: Vec<MsrEntry>,
|
||||
#[cfg(all(feature = "sev_snp", target_arch = "x86_64"))]
|
||||
sev_fd: Option<x86_64::sev::SevFd>,
|
||||
#[cfg(all(feature = "sev_snp", target_arch = "x86_64"))]
|
||||
snp_guest_policy: std::sync::OnceLock<u64>,
|
||||
dirty_log_slots: RwLock<HashMap<u32, KvmDirtyLogSlot>>,
|
||||
guest_memfds: Option<RwLock<HashMap<u32, OwnedFd>>>,
|
||||
}
|
||||
@@ -689,7 +691,11 @@ impl vm::Vm for KvmVm {
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.launch_start(&self.fd, guest_policy)
|
||||
.map_err(|e| vm::HypervisorVmError::InitializeSevSnp(e.into()))
|
||||
.map_err(|e| vm::HypervisorVmError::InitializeSevSnp(e.into()))?;
|
||||
self.snp_guest_policy
|
||||
.set(guest_policy.into_bits())
|
||||
.expect("sev_snp_init called more than once");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "sev_snp", target_arch = "x86_64"))]
|
||||
@@ -736,15 +742,22 @@ impl vm::Vm for KvmVm {
|
||||
snp_id_block: igvm_defs::IGVM_VHS_SNP_ID_BLOCK,
|
||||
host_data: [u8; 32],
|
||||
id_block_enabled: u8,
|
||||
auth_key_enabled: u8,
|
||||
) -> vm::Result<()> {
|
||||
let guest_policy = *self
|
||||
.snp_guest_policy
|
||||
.get()
|
||||
.expect("complete_isolated_import called before sev_snp_init");
|
||||
self.sev_fd
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.launch_finish(
|
||||
&self.fd,
|
||||
&snp_id_block,
|
||||
host_data,
|
||||
id_block_enabled,
|
||||
snp_id_block.author_key_enabled,
|
||||
auth_key_enabled,
|
||||
guest_policy,
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::CompleteIsolatedImport(e.into()))
|
||||
}
|
||||
@@ -1606,6 +1619,8 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
dirty_log_slots: RwLock::new(HashMap::new()),
|
||||
#[cfg(feature = "sev_snp")]
|
||||
sev_fd,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
snp_guest_policy: std::sync::OnceLock::new(),
|
||||
guest_memfds,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ use std::os::fd::{AsRawFd, OwnedFd};
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::path::Path;
|
||||
|
||||
use igvm_defs::SnpPolicy;
|
||||
use igvm_defs::{IGVM_VHS_SNP_ID_BLOCK, SnpPolicy};
|
||||
use kvm_bindings::kvm_sev_cmd;
|
||||
use kvm_ioctls::VmFd;
|
||||
use log::{debug, error, info};
|
||||
use vmm_sys_util::errno;
|
||||
use zerocopy::{FromZeros, Immutable, IntoBytes};
|
||||
|
||||
pub(crate) type Result<T> = std::result::Result<T, errno::Error>;
|
||||
|
||||
@@ -96,6 +97,88 @@ pub(crate) struct KvmSevSnpLaunchFinish {
|
||||
pub pad1: [u64; 4],
|
||||
}
|
||||
|
||||
// See AMD Spec Section 8.18 — Structure of the ID Block
|
||||
// https://docs.amd.com/v/u/en-US/56860_PUB_1.58_SEV_SNP
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, IntoBytes, Immutable)]
|
||||
pub(crate) struct KvmSevSnpIdBlock {
|
||||
pub ld: [u8; 48],
|
||||
pub family_id: [u8; 16],
|
||||
pub image_id: [u8; 16],
|
||||
pub version: u32,
|
||||
pub guest_svn: u32,
|
||||
pub policy: u64,
|
||||
}
|
||||
|
||||
// See AMD Spec Section 8.18 — Layout of the ID Authentication Information Structure
|
||||
// https://docs.amd.com/v/u/en-US/56860_PUB_1.58_SEV_SNP
|
||||
#[repr(C)]
|
||||
#[derive(Clone, FromZeros, IntoBytes, Immutable)]
|
||||
pub(crate) struct KvmSevSnpIdAuth {
|
||||
pub id_key_alg: u32,
|
||||
pub auth_key_algo: u32,
|
||||
pub reserved: [u8; 56],
|
||||
pub id_block_sig: [u8; 512],
|
||||
pub id_key: [u8; 1028],
|
||||
pub reserved2: [u8; 60],
|
||||
pub id_key_sig: [u8; 512],
|
||||
pub author_key: [u8; 1028],
|
||||
pub reserved3: [u8; 892],
|
||||
}
|
||||
|
||||
// Must be 1
|
||||
// AMD SEV-SNP Firmware ABI, Section 8.18 — Structure of the ID Block
|
||||
// https://docs.amd.com/v/u/en-US/56860_PUB_1.58_SEV_SNP
|
||||
const IGVM_SEV_ID_BLOCK_VERSION: u32 = 1;
|
||||
|
||||
fn build_id_block(snp_id_block: &IGVM_VHS_SNP_ID_BLOCK, guest_policy: u64) -> KvmSevSnpIdBlock {
|
||||
KvmSevSnpIdBlock {
|
||||
ld: snp_id_block.ld,
|
||||
family_id: snp_id_block.family_id,
|
||||
image_id: snp_id_block.image_id,
|
||||
version: IGVM_SEV_ID_BLOCK_VERSION,
|
||||
guest_svn: snp_id_block.guest_svn,
|
||||
policy: guest_policy,
|
||||
}
|
||||
}
|
||||
|
||||
// SEV-SNP Firmware ABI Spec Chapter 10: Format for an ECDSA P-384 Public Key
|
||||
// https://docs.amd.com/v/u/en-US/56860_PUB_1.58_SEV_SNP
|
||||
fn serialize_public_key(curve: u32, qx: &[u8; 72], qy: &[u8; 72]) -> [u8; 1028] {
|
||||
let mut key = [0u8; 0x404];
|
||||
key[..0x004].copy_from_slice(&curve.to_le_bytes());
|
||||
key[0x004..0x04C].copy_from_slice(qx);
|
||||
key[0x04C..0x094].copy_from_slice(qy);
|
||||
key
|
||||
}
|
||||
|
||||
fn build_id_auth(snp_id_block: &IGVM_VHS_SNP_ID_BLOCK) -> KvmSevSnpIdAuth {
|
||||
let mut id_auth = KvmSevSnpIdAuth::new_zeroed();
|
||||
|
||||
id_auth.id_key_alg = snp_id_block.id_key_algorithm;
|
||||
id_auth.auth_key_algo = snp_id_block.author_key_algorithm;
|
||||
|
||||
let sig = snp_id_block.id_key_signature.as_bytes();
|
||||
id_auth.id_block_sig[..sig.len()].copy_from_slice(sig);
|
||||
|
||||
id_auth.id_key = serialize_public_key(
|
||||
snp_id_block.id_public_key.curve,
|
||||
&snp_id_block.id_public_key.qx,
|
||||
&snp_id_block.id_public_key.qy,
|
||||
);
|
||||
|
||||
let sig = snp_id_block.author_key_signature.as_bytes();
|
||||
id_auth.id_key_sig[..sig.len()].copy_from_slice(sig);
|
||||
|
||||
id_auth.author_key = serialize_public_key(
|
||||
snp_id_block.author_public_key.curve,
|
||||
&snp_id_block.author_public_key.qx,
|
||||
&snp_id_block.author_public_key.qy,
|
||||
);
|
||||
|
||||
id_auth
|
||||
}
|
||||
|
||||
impl SevFd {
|
||||
pub(crate) fn new(sev_path: impl AsRef<Path>) -> Result<Self> {
|
||||
let file = OpenOptions::new()
|
||||
@@ -132,9 +215,6 @@ impl SevFd {
|
||||
}
|
||||
|
||||
pub(crate) fn launch_start(&self, vm: &VmFd, guest_policy: SnpPolicy) -> Result<()> {
|
||||
// See AMD Spec Section 4.3 - Guest Policy
|
||||
// Bit 17 is reserved and has to be one.
|
||||
// https://docs.amd.com/v/u/en-US/56860_PUB_1.58_SEV_SNP
|
||||
let mut start: KvmSevSnpLaunchStart = KvmSevSnpLaunchStart {
|
||||
policy: guest_policy.into_bits(),
|
||||
..Default::default()
|
||||
@@ -177,14 +257,21 @@ impl SevFd {
|
||||
pub(crate) fn launch_finish(
|
||||
&self,
|
||||
vm: &VmFd,
|
||||
snp_id_block: &IGVM_VHS_SNP_ID_BLOCK,
|
||||
host_data: [u8; 32],
|
||||
id_block_en: u8,
|
||||
auth_key_en: u8,
|
||||
guest_policy: u64,
|
||||
) -> Result<()> {
|
||||
let id_block = build_id_block(snp_id_block, guest_policy);
|
||||
let id_auth = build_id_auth(snp_id_block);
|
||||
|
||||
let mut finish = KvmSevSnpLaunchFinish {
|
||||
host_data,
|
||||
id_block_uaddr: id_block.as_bytes().as_ptr() as u64,
|
||||
id_auth_uaddr: id_auth.as_bytes().as_ptr() as u64,
|
||||
id_block_en,
|
||||
auth_key_en,
|
||||
host_data,
|
||||
..Default::default()
|
||||
};
|
||||
let mut sev_cmd = kvm_sev_cmd {
|
||||
@@ -194,7 +281,108 @@ impl SevFd {
|
||||
..Default::default()
|
||||
};
|
||||
let flags = finish.flags;
|
||||
debug!("Calling KVM_SEV_SNP_LAUNCH_FINISH, flags: {flags}");
|
||||
debug!(
|
||||
"KVM_SEV_SNP_LAUNCH_FINISH: id_block_en={id_block_en}, auth_key_en={auth_key_en}, policy={guest_policy:#x}, flags={flags}"
|
||||
);
|
||||
sev_op(vm, &mut sev_cmd, "KVM_SEV_SNP_LAUNCH_FINISH")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::mem::size_of;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn make_test_igvm_id_block() -> IGVM_VHS_SNP_ID_BLOCK {
|
||||
let mut block = IGVM_VHS_SNP_ID_BLOCK::new_zeroed();
|
||||
block.ld[0] = 0xAA;
|
||||
block.ld[47] = 0xBB;
|
||||
block.family_id[0] = 0x01;
|
||||
block.image_id[0] = 0x02;
|
||||
block.version = 42;
|
||||
block.guest_svn = 7;
|
||||
block.id_key_algorithm = 1;
|
||||
block.author_key_algorithm = 1;
|
||||
block.id_key_signature.r_comp[0] = 0x10;
|
||||
block.id_key_signature.s_comp[0] = 0x20;
|
||||
block.id_public_key.curve = 2;
|
||||
block.id_public_key.qx[0] = 0x30;
|
||||
block.id_public_key.qy[0] = 0x40;
|
||||
block.author_key_signature.r_comp[0] = 0x50;
|
||||
block.author_key_signature.s_comp[0] = 0x60;
|
||||
block.author_public_key.curve = 2;
|
||||
block.author_public_key.qx[0] = 0x70;
|
||||
block.author_public_key.qy[0] = 0x80;
|
||||
block
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn id_block_struct_sizes() {
|
||||
assert_eq!(size_of::<KvmSevSnpIdBlock>(), 96);
|
||||
assert_eq!(size_of::<KvmSevSnpIdAuth>(), 4096);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_id_block_maps_fields_correctly() {
|
||||
let igvm = make_test_igvm_id_block();
|
||||
let policy = 0x30000u64;
|
||||
let id_block = build_id_block(&igvm, policy);
|
||||
|
||||
assert_eq!(id_block.ld, igvm.ld);
|
||||
assert_eq!(id_block.family_id, igvm.family_id);
|
||||
assert_eq!(id_block.image_id, igvm.image_id);
|
||||
assert_eq!(id_block.version, IGVM_SEV_ID_BLOCK_VERSION);
|
||||
assert_eq!(id_block.guest_svn, igvm.guest_svn);
|
||||
assert_eq!(id_block.policy, policy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_id_block_policy_at_offset_88() {
|
||||
let igvm = IGVM_VHS_SNP_ID_BLOCK::new_zeroed();
|
||||
let policy = 0xDEAD_BEEF_CAFE_BABEu64;
|
||||
let id_block = build_id_block(&igvm, policy);
|
||||
let bytes = id_block.as_bytes();
|
||||
assert_eq!(&bytes[88..96], &policy.to_le_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_id_auth_maps_signatures_correctly() {
|
||||
let igvm = make_test_igvm_id_block();
|
||||
let id_auth = build_id_auth(&igvm);
|
||||
|
||||
assert_eq!(id_auth.id_key_alg, igvm.id_key_algorithm);
|
||||
assert_eq!(id_auth.auth_key_algo, igvm.author_key_algorithm);
|
||||
|
||||
assert_eq!(id_auth.id_block_sig[0], 0x10);
|
||||
assert_eq!(id_auth.id_block_sig[72], 0x20);
|
||||
assert!(id_auth.id_block_sig[144..].iter().all(|&b| b == 0));
|
||||
|
||||
assert_eq!(id_auth.id_key_sig[0], 0x50);
|
||||
assert_eq!(id_auth.id_key_sig[72], 0x60);
|
||||
assert!(id_auth.id_key_sig[144..].iter().all(|&b| b == 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_id_auth_serializes_public_keys() {
|
||||
let igvm = make_test_igvm_id_block();
|
||||
let id_auth = build_id_auth(&igvm);
|
||||
|
||||
assert_eq!(&id_auth.id_key[..4], &2u32.to_le_bytes());
|
||||
assert_eq!(id_auth.id_key[4], 0x30);
|
||||
assert_eq!(id_auth.id_key[76], 0x40);
|
||||
assert!(id_auth.id_key[148..].iter().all(|&b| b == 0));
|
||||
|
||||
assert_eq!(&id_auth.author_key[..4], &2u32.to_le_bytes());
|
||||
assert_eq!(id_auth.author_key[4], 0x70);
|
||||
assert_eq!(id_auth.author_key[76], 0x80);
|
||||
assert!(id_auth.author_key[148..].iter().all(|&b| b == 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_id_auth_zeroed_input() {
|
||||
let igvm = IGVM_VHS_SNP_ID_BLOCK::new_zeroed();
|
||||
let id_auth = build_id_auth(&igvm);
|
||||
assert!(id_auth.as_bytes().iter().all(|&b| b == 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2318,6 +2318,7 @@ impl vm::Vm for MshvVm {
|
||||
snp_id_block: IGVM_VHS_SNP_ID_BLOCK,
|
||||
host_data: [u8; 32],
|
||||
id_block_enabled: u8,
|
||||
auth_key_enabled: u8,
|
||||
) -> vm::Result<()> {
|
||||
let mut auth_info = hv_snp_id_auth_info {
|
||||
id_key_algorithm: snp_id_block.id_key_algorithm,
|
||||
@@ -2351,7 +2352,7 @@ impl vm::Vm for MshvVm {
|
||||
id_auth_info: auth_info,
|
||||
host_data,
|
||||
id_block_enabled,
|
||||
author_key_enabled: 0,
|
||||
author_key_enabled: auth_key_enabled,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -440,6 +440,7 @@ pub trait Vm: Send + Sync + Any {
|
||||
_snp_id_block: IGVM_VHS_SNP_ID_BLOCK,
|
||||
_host_data: [u8; 32],
|
||||
_id_block_enabled: u8,
|
||||
_auth_key_enabled: u8,
|
||||
) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user