From 2e543bfc56a3549b6939f3c5d9a32c1ba90caaf5 Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Mon, 9 Feb 2026 13:23:24 -0800 Subject: [PATCH] refactor(sev): move OVMF structs and functions to mod firmware Signed-off-by: Changyuan Lyu --- alioth/src/board/board_x86_64.rs | 113 +++--------------- alioth/src/firmware/firmware.rs | 2 + alioth/src/firmware/ovmf/ovmf.rs | 19 +++ .../firmware/ovmf/ovmf_x86_64/ovmf_x86_64.rs | 50 ++++++++ alioth/src/firmware/ovmf/ovmf_x86_64/sev.rs | 75 ++++++++++++ 5 files changed, 161 insertions(+), 98 deletions(-) create mode 100644 alioth/src/firmware/ovmf/ovmf.rs create mode 100644 alioth/src/firmware/ovmf/ovmf_x86_64/ovmf_x86_64.rs create mode 100644 alioth/src/firmware/ovmf/ovmf_x86_64/sev.rs diff --git a/alioth/src/board/board_x86_64.rs b/alioth/src/board/board_x86_64.rs index 12762c4f..aefcb63d 100644 --- a/alioth/src/board/board_x86_64.rs +++ b/alioth/src/board/board_x86_64.rs @@ -22,7 +22,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use parking_lot::Mutex; use snafu::ResultExt; -use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes}; +use zerocopy::{FromBytes, FromZeros, IntoBytes}; use crate::arch::cpuid::CpuidIn; use crate::arch::layout::{ @@ -41,6 +41,11 @@ use crate::firmware::acpi::reg::{FadtReset, FadtSleepControl}; use crate::firmware::acpi::{ AcpiTable, create_fadt, create_madt, create_mcfg, create_rsdp, create_xsdt, }; +use crate::firmware::ovmf::parse_data; +use crate::firmware::ovmf::sev::{ + GUID_SEV_ES_RESET_BLOCK, GUID_SEV_METADATA, SevDescType, SevMetaData, SevMetadataDesc, + SnpCpuidFunc, SnpCpuidInfo, +}; use crate::hv::{Coco, Hypervisor, Vcpu, Vm}; use crate::loader::{Executable, InitState, Payload, firmware}; use crate::mem::mapped::ArcMemPages; @@ -214,7 +219,8 @@ where Coco::AmdSnp { .. } => {} _ => return, } - let ap_eip = parse_data_from_fw(fw.as_slice(), &SEV_ES_RESET_BLOCK_GUID).unwrap(); + let ap_eip = parse_data(fw.as_slice(), &GUID_SEV_ES_RESET_BLOCK).unwrap(); + let ap_eip = u32::read_from_bytes(ap_eip).unwrap(); self.arch.sev_ap_eip.store(ap_eip, Ordering::Release); } @@ -224,9 +230,9 @@ where let ram = ram_bus.lock_layout(); let (desc, _) = SevMetadataDesc::read_from_prefix(&fw_range[offset..]).unwrap(); let snp_page_type = match desc.type_ { - SEV_DESC_TYPE_SNP_SEC_MEM => SnpPageType::Unmeasured, - SEV_DESC_TYPE_SNP_SECRETS => SnpPageType::Secrets, - SEV_DESC_TYPE_CPUID => { + SevDescType::SNP_DESC_MEM => SnpPageType::Unmeasured, + SevDescType::SNP_SECRETS => SnpPageType::Secrets, + SevDescType::CPUID => { assert!(desc.len as usize >= size_of::()); assert!(cpuid_table.entries.len() >= self.arch.cpuids.len()); cpuid_table.count = self.arch.cpuids.len() as u32; @@ -244,7 +250,7 @@ where let mut ret = self .vm .snp_launch_update(range_bytes, desc.base as _, snp_page_type); - if ret.is_err() && desc.type_ == SEV_DESC_TYPE_CPUID { + 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()) { if set != got { @@ -289,9 +295,9 @@ where } Coco::AmdSnp { .. } => { let fw_range = fw.as_slice_mut(); - let metadata_offset_r: u32 = - parse_data_from_fw(fw_range, &SEV_METADATA_GUID).unwrap(); - let metadata_offset = fw_range.len() - metadata_offset_r as usize; + let metadata_offset_r = parse_data(fw_range, &GUID_SEV_METADATA).unwrap(); + let metadata_offset = + fw_range.len() - u32::read_from_bytes(metadata_offset_r).unwrap() as usize; let (metadata, _) = SevMetaData::read_from_prefix(&fw_range[metadata_offset..]).unwrap(); let desc_offset = metadata_offset + size_of::(); @@ -588,95 +594,6 @@ const DSDT_TEMPLATE: [u8; 352] = [ const DSDT_OFFSET_PCI_QWORD_MEM: usize = 0x12b; -const GUID_TABLE_FOOTER_R_OFFSET: usize = 48; - -const GUID_SIZE: usize = 16; - -const GUID_TABLE_FOOTER_GUID: [u8; GUID_SIZE] = [ - 0xDE, 0x82, 0xB5, 0x96, 0xB2, 0x1F, 0xF7, 0x45, 0xBA, 0xEA, 0xA3, 0x66, 0xC5, 0x5A, 0x08, 0x2D, -]; - -const SEV_ES_RESET_BLOCK_GUID: [u8; GUID_SIZE] = [ - 0xde, 0x71, 0xf7, 0x00, 0x7e, 0x1a, 0xcb, 0x4f, 0x89, 0x0e, 0x68, 0xc7, 0x7e, 0x2f, 0xb4, 0x4e, -]; - -const SEV_METADATA_GUID: [u8; GUID_SIZE] = [ - 0x66, 0x65, 0x88, 0xdc, 0x4a, 0x98, 0x98, 0x47, 0xA7, 0x5e, 0x55, 0x85, 0xa7, 0xbf, 0x67, 0xcc, -]; - -#[derive(Debug, FromBytes, IntoBytes, Immutable)] -#[repr(C)] -struct SevMetaData { - signature: u32, - len: u32, - version: u32, - num_desc: u32, -} - -pub const SEV_DESC_TYPE_SNP_SEC_MEM: u32 = 1; -pub const SEV_DESC_TYPE_SNP_SECRETS: u32 = 2; -pub const SEV_DESC_TYPE_CPUID: u32 = 3; - -#[derive(Debug, FromBytes, IntoBytes, Immutable)] -#[repr(C)] -struct SevMetadataDesc { - base: u32, - len: u32, - type_: u32, -} - -pub fn parse_data_from_fw(blob: &[u8], guid: &[u8; GUID_SIZE]) -> Option -where - T: FromBytes, -{ - let offset_table_footer = blob.len().checked_sub(GUID_TABLE_FOOTER_R_OFFSET)?; - if !blob[offset_table_footer..].starts_with(&GUID_TABLE_FOOTER_GUID) { - return None; - } - let offset_table_len = offset_table_footer.checked_sub(size_of::())?; - let (table_len, _) = u16::read_from_prefix(&blob[offset_table_len..]).ok()?; - let offset_table_end = offset_table_len.checked_sub( - (table_len as usize) - .checked_sub(size_of::())? - .checked_sub(GUID_SIZE)?, - )?; - let mut current = offset_table_len; - while current > offset_table_end { - let offset_len = current.checked_sub(GUID_SIZE + size_of::())?; - if blob[(offset_len + 2)..].starts_with(guid) { - return T::read_from_prefix(&blob[offset_len.checked_sub(size_of::())?..]) - .map(|(n, _)| n) - .ok(); - } - let (table_len, _) = u16::read_from_prefix(&blob[offset_len..]).ok()?; - current = current.checked_sub(table_len as usize)?; - } - None -} - -#[repr(C)] -#[derive(Debug, Clone, PartialEq, Eq, FromBytes, IntoBytes, Immutable)] -pub struct SnpCpuidFunc { - pub eax_in: u32, - pub ecx_in: u32, - pub xcr0_in: u64, - pub xss_in: u64, - pub eax: u32, - pub ebx: u32, - pub ecx: u32, - pub edx: u32, - pub reserved: u64, -} - -#[repr(C)] -#[derive(Debug, Clone, FromBytes, IntoBytes, Immutable)] -pub struct SnpCpuidInfo { - pub count: u32, - pub _reserved1: u32, - pub _reserved2: u64, - pub entries: [SnpCpuidFunc; 64], -} - #[cfg(test)] #[path = "board_x86_64_test.rs"] mod tests; diff --git a/alioth/src/firmware/firmware.rs b/alioth/src/firmware/firmware.rs index fa63ba1c..e2ea0ab9 100644 --- a/alioth/src/firmware/firmware.rs +++ b/alioth/src/firmware/firmware.rs @@ -17,3 +17,5 @@ pub mod acpi; #[path = "dt/dt.rs"] pub mod dt; +#[path = "ovmf/ovmf.rs"] +pub mod ovmf; diff --git a/alioth/src/firmware/ovmf/ovmf.rs b/alioth/src/firmware/ovmf/ovmf.rs new file mode 100644 index 00000000..66ccd218 --- /dev/null +++ b/alioth/src/firmware/ovmf/ovmf.rs @@ -0,0 +1,19 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[path = "ovmf_x86_64/ovmf_x86_64.rs"] +pub mod x86_64; + +#[cfg(target_arch = "x86_64")] +pub use self::x86_64::*; diff --git a/alioth/src/firmware/ovmf/ovmf_x86_64/ovmf_x86_64.rs b/alioth/src/firmware/ovmf/ovmf_x86_64/ovmf_x86_64.rs new file mode 100644 index 00000000..3fff8210 --- /dev/null +++ b/alioth/src/firmware/ovmf/ovmf_x86_64/ovmf_x86_64.rs @@ -0,0 +1,50 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod sev; + +use zerocopy::FromBytes; + +pub const GUID_SIZE: usize = 16; + +pub const OFFSET_R_LENGTH_GUID_TABLE: usize = 50; +pub const GUID_TABLE_FOOTER: [u8; GUID_SIZE] = [ + 0xDE, 0x82, 0xB5, 0x96, 0xB2, 0x1F, 0xF7, 0x45, 0xBA, 0xEA, 0xA3, 0x66, 0xC5, 0x5A, 0x08, 0x2D, +]; + +pub fn parse_data<'a>(blob: &'a [u8], target: &[u8; GUID_SIZE]) -> Option<&'a [u8]> { + let offset_table_len = blob.len().checked_sub(OFFSET_R_LENGTH_GUID_TABLE)?; + // `table_len` is the total length of the table, including the footer + let (table_len, guid) = u16::read_from_prefix(&blob[offset_table_len..]).ok()?; + if !guid.starts_with(&GUID_TABLE_FOOTER) { + return None; + } + let body_len = (table_len as usize).checked_sub(size_of::() + GUID_SIZE)?; + let offset_table_start = offset_table_len.checked_sub(body_len)?; + // Every entry in the table has the following structure: + // - Actual entry content + // - size_of::() bytes for the length of the entry + // - GUID_SIZE bytes for the GUID + let mut offset_entry_len = offset_table_len.checked_sub(GUID_SIZE + size_of::())?; + while offset_entry_len >= offset_table_start { + let (len_entry, guid) = u16::read_from_prefix(&blob[offset_entry_len..]).ok()?; + if guid.starts_with(target) { + let len_content = (len_entry as usize).checked_sub(GUID_SIZE + size_of::())?; + let offset_content = offset_entry_len.checked_sub(len_content)?; + return Some(&blob[offset_content..offset_entry_len]); + } + offset_entry_len = offset_entry_len.checked_sub(len_entry as usize)?; + } + None +} diff --git a/alioth/src/firmware/ovmf/ovmf_x86_64/sev.rs b/alioth/src/firmware/ovmf/ovmf_x86_64/sev.rs new file mode 100644 index 00000000..73d9b78b --- /dev/null +++ b/alioth/src/firmware/ovmf/ovmf_x86_64/sev.rs @@ -0,0 +1,75 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use zerocopy::{FromBytes, Immutable, IntoBytes}; + +use crate::consts; +use crate::firmware::ovmf::x86_64::GUID_SIZE; + +pub const GUID_SEV_ES_RESET_BLOCK: [u8; GUID_SIZE] = [ + 0xde, 0x71, 0xf7, 0x00, 0x7e, 0x1a, 0xcb, 0x4f, 0x89, 0x0e, 0x68, 0xc7, 0x7e, 0x2f, 0xb4, 0x4e, +]; + +pub const GUID_SEV_METADATA: [u8; GUID_SIZE] = [ + 0x66, 0x65, 0x88, 0xdc, 0x4a, 0x98, 0x98, 0x47, 0xA7, 0x5e, 0x55, 0x85, 0xa7, 0xbf, 0x67, 0xcc, +]; + +#[derive(Debug, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct SevMetaData { + pub signature: u32, + pub len: u32, + pub version: u32, + pub num_desc: u32, +} + +consts! { + #[derive(FromBytes, IntoBytes, Immutable)] + pub struct SevDescType(u32) { + SNP_DESC_MEM = 1; + SNP_SECRETS = 2; + CPUID = 3; + } +} + +#[derive(Debug, FromBytes, IntoBytes, Immutable)] +#[repr(C)] +pub struct SevMetadataDesc { + pub base: u32, + pub len: u32, + pub type_: SevDescType, +} + +#[repr(C)] +#[derive(Debug, Clone, PartialEq, Eq, FromBytes, IntoBytes, Immutable)] +pub struct SnpCpuidFunc { + pub eax_in: u32, + pub ecx_in: u32, + pub xcr0_in: u64, + pub xss_in: u64, + pub eax: u32, + pub ebx: u32, + pub ecx: u32, + pub edx: u32, + pub reserved: u64, +} + +#[repr(C)] +#[derive(Debug, Clone, FromBytes, IntoBytes, Immutable)] +pub struct SnpCpuidInfo { + pub count: u32, + pub _reserved1: u32, + pub _reserved2: u64, + pub entries: [SnpCpuidFunc; 64], +}