extern crate alloc;
use crate::td_uefi_pi::pi::guid::Guid;
use alloc::string::String;
use core::{ptr::slice_from_raw_parts, str::FromStr};
use scroll::{Pread, Pwrite};
pub const TDX_METADATA_GUID_STR: &str = "E9EAF9F3-168E-44D5-A8EB-7F4D8738F6AE";
pub const TDX_METADATA_GUID: Guid = Guid::from_fields(
0xE9EAF9F3,
0x168E,
0x44D5,
[0xA8, 0xEB, 0x7F, 0x4D, 0x87, 0x38, 0xF6, 0xAE],
);
pub const TDX_METADATA_SIGNATURE: u32 = 0x46564454;
pub const TDX_METADATA_VERSION: u32 = 1;
pub const TDX_METADATA_OFFSET: u32 = 0x20;
pub const TDX_METADATA_GUID_LEN: u32 = 16;
pub const TDX_METADATA_DESCRIPTOR_LEN: u32 = 16;
pub const TDX_METADATA_SECTION_LEN: u32 = 32;
pub const TDX_METADATA_SECTION_TYPE_BFV: u32 = 0;
pub const TDX_METADATA_SECTION_TYPE_CFV: u32 = 1;
pub const TDX_METADATA_SECTION_TYPE_TD_HOB: u32 = 2;
pub const TDX_METADATA_SECTION_TYPE_TEMP_MEM: u32 = 3;
pub const TDX_METADATA_SECTION_TYPE_PERM_MEM: u32 = 4;
pub const TDX_METADATA_SECTION_TYPE_PAYLOAD: u32 = 5;
pub const TDX_METADATA_SECTION_TYPE_PAYLOAD_PARAM: u32 = 6;
pub const TDX_METADATA_SECTION_TYPE_TD_INFO: u32 = 7;
pub const TDX_METADATA_SECTION_TYPE_MAX: u32 = 8;
pub const TDX_METADATA_SECTION_TYPE_STRS: [&str; TDX_METADATA_SECTION_TYPE_MAX as usize] = [
"BFV",
"CFV",
"TD_HOB",
"TempMem",
"PermMem",
"Payload",
"PayloadParam",
"TdInfo",
];
pub const TDX_METADATA_ATTRIBUTES_EXTENDMR: u32 = 0x00000001;
pub const TDX_METADATA_ATTRIBUTES_PAGE_AUG: u32 = 0x00000002;
#[repr(C)]
#[derive(Debug, Pread, Pwrite)]
pub struct TdxMetadataDescriptor {
pub signature: u32,
pub length: u32,
pub version: u32,
pub number_of_section_entry: u32,
}
impl Default for TdxMetadataDescriptor {
fn default() -> Self {
TdxMetadataDescriptor {
signature: TDX_METADATA_SIGNATURE,
length: 16,
version: 1,
number_of_section_entry: 0,
}
}
}
impl TdxMetadataDescriptor {
pub fn set_sections(&mut self, sections: u32) {
assert!(sections < 0x10000);
self.number_of_section_entry = sections;
self.length = 16 + sections * 32;
}
pub fn is_valid(&self) -> bool {
let len = self.length;
!(self.signature != TDX_METADATA_SIGNATURE
|| self.version != 1
|| self.number_of_section_entry == 0
|| len < 16
|| (len - 16) % 32 != 0
|| (len - 16) / 32 != self.number_of_section_entry)
}
pub fn as_bytes(&self) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
self as *const TdxMetadataDescriptor as *const u8,
core::mem::size_of::<Self>(),
)
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Pwrite, Pread)]
pub struct TdxMetadataSection {
pub data_offset: u32,
pub raw_data_size: u32,
pub memory_address: u64,
pub memory_data_size: u64,
pub r#type: u32,
pub attributes: u32,
}
impl TdxMetadataSection {
pub fn get_type_name(r#type: u32) -> Option<String> {
if r#type >= TDX_METADATA_SECTION_TYPE_MAX {
None
} else {
Some(String::from(
TDX_METADATA_SECTION_TYPE_STRS[r#type as usize],
))
}
}
pub fn as_bytes(&self) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
self as *const TdxMetadataSection as *const u8,
core::mem::size_of::<Self>(),
)
}
}
}
#[repr(C)]
#[derive(Pwrite, Pread)]
pub struct TdxMetadataGuid {
pub guid: Guid,
}
impl TdxMetadataGuid {
pub fn is_valid(&self) -> bool {
let metadata_guid = Guid::from_str(TDX_METADATA_GUID_STR).unwrap();
metadata_guid == self.guid
}
pub fn as_bytes(&self) -> &[u8; 16] {
self.guid.as_bytes()
}
pub fn from_bytes(buffer: &[u8; 16]) -> Option<TdxMetadataGuid> {
let guid = Guid::from_bytes(buffer);
let metadata_guid = TdxMetadataGuid { guid };
if metadata_guid.is_valid() {
Some(metadata_guid)
} else {
None
}
}
}
impl Default for TdxMetadataGuid {
fn default() -> Self {
TdxMetadataGuid {
guid: Guid::from_str(TDX_METADATA_GUID_STR).unwrap(),
}
}
}
#[derive(Debug)]
pub enum TdxMetadataError {
InvalidSection,
}
pub fn validate_sections(sections: &[TdxMetadataSection]) -> Result<(), TdxMetadataError> {
let mut bfv_cnt = 0;
let mut bfv_start = 0;
let mut bfv_end = 0;
let mut hob_cnt = 0;
let mut perm_mem_cnt = 0;
let mut payload_cnt = 0;
let mut payload_param_cnt = 0;
let mut td_info_cnt = 0;
let mut td_info_start = 0;
let mut td_info_end = 0;
let check_data_memory_fields =
|data_offset: u32, data_size: u32, memory_address: u64, memory_size: u64| -> bool {
if data_size == 0 && data_offset != 0 {
return false;
}
if data_size != 0 && memory_size < data_size as u64 {
return false;
}
if (memory_address & 0xfff) != 0 {
return false;
}
true
};
for section in sections.iter() {
match section.r#type {
TDX_METADATA_SECTION_TYPE_BFV => {
if bfv_cnt == i32::MAX {
return Err(TdxMetadataError::InvalidSection);
}
bfv_cnt += 1;
if section.raw_data_size == 0 {
return Err(TdxMetadataError::InvalidSection);
}
if section.attributes != TDX_METADATA_ATTRIBUTES_EXTENDMR {
return Err(TdxMetadataError::InvalidSection);
}
if !check_data_memory_fields(
section.data_offset,
section.raw_data_size,
section.memory_address,
section.memory_data_size,
) {
return Err(TdxMetadataError::InvalidSection);
} else {
bfv_start = section.data_offset;
bfv_end = bfv_start + section.raw_data_size;
}
}
TDX_METADATA_SECTION_TYPE_CFV => {
if section.raw_data_size == 0 {
return Err(TdxMetadataError::InvalidSection);
}
if section.attributes != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if !check_data_memory_fields(
section.data_offset,
section.raw_data_size,
section.memory_address,
section.memory_data_size,
) {
return Err(TdxMetadataError::InvalidSection);
}
}
TDX_METADATA_SECTION_TYPE_TD_HOB => {
if hob_cnt == i32::MAX {
return Err(TdxMetadataError::InvalidSection);
}
hob_cnt += 1;
if hob_cnt > 1 {
return Err(TdxMetadataError::InvalidSection);
}
if section.raw_data_size != 0 || section.data_offset != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if section.attributes != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if !check_data_memory_fields(
section.data_offset,
section.raw_data_size,
section.memory_address,
section.memory_data_size,
) {
return Err(TdxMetadataError::InvalidSection);
}
}
TDX_METADATA_SECTION_TYPE_TEMP_MEM => {
if section.raw_data_size != 0 || section.data_offset != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if section.attributes != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if !check_data_memory_fields(
section.data_offset,
section.raw_data_size,
section.memory_address,
section.memory_data_size,
) {
return Err(TdxMetadataError::InvalidSection);
}
}
TDX_METADATA_SECTION_TYPE_PERM_MEM => {
if perm_mem_cnt == i32::MAX {
return Err(TdxMetadataError::InvalidSection);
}
perm_mem_cnt += 1;
if section.raw_data_size != 0 || section.data_offset != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if section.attributes != TDX_METADATA_ATTRIBUTES_PAGE_AUG {
return Err(TdxMetadataError::InvalidSection);
}
if !check_data_memory_fields(
section.data_offset,
section.raw_data_size,
section.memory_address,
section.memory_data_size,
) {
return Err(TdxMetadataError::InvalidSection);
}
}
TDX_METADATA_SECTION_TYPE_PAYLOAD => {
if payload_cnt == i32::MAX {
return Err(TdxMetadataError::InvalidSection);
}
payload_cnt += 1;
if payload_cnt > 1 {
return Err(TdxMetadataError::InvalidSection);
}
if section.attributes & (!TDX_METADATA_ATTRIBUTES_EXTENDMR) != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if !check_data_memory_fields(
section.data_offset,
section.raw_data_size,
section.memory_address,
section.memory_data_size,
) {
return Err(TdxMetadataError::InvalidSection);
}
}
TDX_METADATA_SECTION_TYPE_PAYLOAD_PARAM => {
if payload_param_cnt == i32::MAX {
return Err(TdxMetadataError::InvalidSection);
}
payload_param_cnt += 1;
if payload_param_cnt > 1 {
return Err(TdxMetadataError::InvalidSection);
}
if section.attributes != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if !check_data_memory_fields(
section.data_offset,
section.raw_data_size,
section.memory_address,
section.memory_data_size,
) {
return Err(TdxMetadataError::InvalidSection);
}
}
TDX_METADATA_SECTION_TYPE_TD_INFO => {
if td_info_cnt == i32::MAX {
return Err(TdxMetadataError::InvalidSection);
}
td_info_cnt += 1;
if td_info_cnt > 1 {
return Err(TdxMetadataError::InvalidSection);
}
if section.attributes != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if section.raw_data_size == 0 {
return Err(TdxMetadataError::InvalidSection);
} else {
td_info_start = section.data_offset;
td_info_end = td_info_start + section.raw_data_size;
}
if section.memory_address != 0 || section.memory_data_size != 0 {
return Err(TdxMetadataError::InvalidSection);
}
}
_ => {
return Err(TdxMetadataError::InvalidSection);
}
}
}
if bfv_cnt == 0 {
return Err(TdxMetadataError::InvalidSection);
}
if hob_cnt == 0 && perm_mem_cnt == 0 {
return Err(TdxMetadataError::InvalidSection);
}
if payload_cnt == 0 && payload_param_cnt != 0 {
return Err(TdxMetadataError::InvalidSection);
}
if td_info_cnt != 0
&& (td_info_start < bfv_start || td_info_start >= bfv_end || td_info_end > bfv_end)
{
return Err(TdxMetadataError::InvalidSection);
}
Ok(())
}
#[repr(C)]
#[derive(Default, Pwrite, Pread)]
pub struct TdxMetadataPtr {
pub ptr: u32,
}
impl TdxMetadataPtr {
pub fn as_bytes(&self) -> &[u8] {
unsafe {
&*slice_from_raw_parts(
self as *const TdxMetadataPtr as *const u8,
core::mem::size_of::<Self>(),
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use scroll::export::mem::size_of;
#[test]
fn ensure_data_struct_size() {
assert_eq!(size_of::<TdxMetadataDescriptor>(), 16);
assert_eq!(size_of::<TdxMetadataSection>(), 32);
assert_eq!(size_of::<TdxMetadataGuid>(), 16);
assert_eq!(size_of::<TdxMetadataPtr>(), 4);
}
#[test]
fn test_tdx_metadata_descriptor() {
let mut desc = TdxMetadataDescriptor::default();
assert_eq!(desc.signature, TDX_METADATA_SIGNATURE);
assert_eq!(desc.length, 16);
assert_eq!(desc.version, 1);
assert_eq!(desc.number_of_section_entry, 0);
assert_eq!(desc.is_valid(), false);
desc.set_sections(1);
assert_eq!(desc.signature, TDX_METADATA_SIGNATURE);
assert_eq!(desc.length, 48);
assert_eq!(desc.version, 1);
assert_eq!(desc.number_of_section_entry, 1);
assert_eq!(desc.is_valid(), true);
}
#[test]
fn test_tdx_metadata_guid() {
let tdx_metadata_guid: [u8; 16] = [
0xF3, 0xF9, 0xEA, 0xE9, 0x8e, 0x16, 0xD5, 0x44, 0xA8, 0xEB, 0x7F, 0x4D, 0x87, 0x38,
0xF6, 0xAE,
];
let invalid_tdx_metadata_guid: [u8; 16] = [
0xE9, 0xEA, 0xF9, 0xF3, 0x16, 0x8e, 0x44, 0xD5, 0xA8, 0xEB, 0x7F, 0x4D, 0x87, 0x38,
0xF6, 0xAE,
];
let guid = TdxMetadataGuid::default();
assert_eq!(&tdx_metadata_guid, guid.as_bytes());
assert_eq!(&tdx_metadata_guid, TDX_METADATA_GUID.as_bytes());
assert_eq!(guid.is_valid(), true);
let guid_pread: TdxMetadataGuid = tdx_metadata_guid.pread(0).unwrap();
assert_eq!(guid_pread.as_bytes(), guid.as_bytes());
let guid = TdxMetadataGuid::from_bytes(&tdx_metadata_guid).unwrap();
assert_eq!(guid.as_bytes(), &tdx_metadata_guid);
assert!(TdxMetadataGuid::from_bytes(&invalid_tdx_metadata_guid).is_none());
}
#[test]
fn test_tdx_metadata_section() {
assert_eq!(TdxMetadataSection::get_type_name(0).unwrap(), "BFV");
assert_eq!(TdxMetadataSection::get_type_name(1).unwrap(), "CFV");
assert_eq!(TdxMetadataSection::get_type_name(2).unwrap(), "TD_HOB");
assert_eq!(TdxMetadataSection::get_type_name(3).unwrap(), "TempMem");
assert_eq!(TdxMetadataSection::get_type_name(4).unwrap(), "PermMem");
assert_eq!(TdxMetadataSection::get_type_name(5).unwrap(), "Payload");
assert_eq!(
TdxMetadataSection::get_type_name(6).unwrap(),
"PayloadParam"
);
assert_eq!(TdxMetadataSection::get_type_name(7).unwrap(), "TdInfo");
assert!(TdxMetadataSection::get_type_name(8).is_none());
}
#[test]
fn test_validate_sections() {
let sections = [];
assert!(!validate_sections(§ions).is_ok());
let mut sections: [TdxMetadataSection; 7] = [TdxMetadataSection::default(); 7];
sections[0] = TdxMetadataSection {
data_offset: 0,
raw_data_size: 0xf7e000,
memory_address: 0xff082000,
memory_data_size: 0xf7e000,
attributes: 1,
r#type: TDX_METADATA_SECTION_TYPE_BFV,
};
sections[1] = TdxMetadataSection {
data_offset: 0,
raw_data_size: 0x40000,
memory_address: 0xff000000,
memory_data_size: 0x40000,
attributes: 0,
r#type: TDX_METADATA_SECTION_TYPE_CFV,
};
sections[2] = TdxMetadataSection {
data_offset: 0,
raw_data_size: 0,
memory_address: 0x820000,
memory_data_size: 0x20000,
attributes: 0,
r#type: TDX_METADATA_SECTION_TYPE_TD_HOB,
};
sections[3] = TdxMetadataSection {
data_offset: 0,
raw_data_size: 0,
memory_address: 0xFF040000,
memory_data_size: 0x1000,
attributes: 0,
r#type: TDX_METADATA_SECTION_TYPE_TEMP_MEM,
};
sections[4] = TdxMetadataSection {
data_offset: 0,
raw_data_size: 0,
memory_address: 0x1200000,
memory_data_size: 0x8000000,
attributes: 0,
r#type: TDX_METADATA_SECTION_TYPE_PAYLOAD,
};
sections[5] = TdxMetadataSection {
data_offset: 0,
raw_data_size: 0,
memory_address: 0x1100000,
memory_data_size: 0x100000,
attributes: 0,
r#type: TDX_METADATA_SECTION_TYPE_PAYLOAD_PARAM,
};
sections[6] = TdxMetadataSection {
data_offset: 0,
raw_data_size: 0x1000,
memory_address: 0,
memory_data_size: 0,
attributes: 0,
r#type: TDX_METADATA_SECTION_TYPE_TD_INFO,
};
assert!(validate_sections(§ions).is_ok());
sections[0].raw_data_size = 0;
assert!(!validate_sections(§ions).is_ok());
sections[0].raw_data_size = 0xf7e000;
sections[0].attributes = 0;
assert!(!validate_sections(§ions).is_ok());
sections[0].attributes = TDX_METADATA_ATTRIBUTES_EXTENDMR;
sections[0].memory_data_size = sections[0].raw_data_size as u64 - 1;
assert!(!validate_sections(§ions).is_ok());
sections[0].memory_data_size += 1;
sections[0].memory_address += 1;
assert!(!validate_sections(§ions).is_ok());
sections[0].memory_address -= 1;
sections[3].r#type = TDX_METADATA_SECTION_TYPE_BFV;
sections[3].attributes = TDX_METADATA_ATTRIBUTES_EXTENDMR;
sections[3].raw_data_size = sections[3].memory_data_size as u32;
assert!(validate_sections(§ions).is_ok());
sections[3].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
sections[3].attributes = 0;
sections[3].raw_data_size = 0;
sections[1].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
sections[1].raw_data_size = 0;
assert!(validate_sections(§ions).is_ok());
sections[1].r#type = TDX_METADATA_SECTION_TYPE_CFV;
assert!(!validate_sections(§ions).is_ok());
sections[1].raw_data_size = 0x40000;
sections[1].attributes = 1;
assert!(!validate_sections(§ions).is_ok());
sections[1].attributes = 0;
sections[1].memory_data_size = sections[1].raw_data_size as u64 - 1;
assert!(!validate_sections(§ions).is_ok());
sections[1].memory_data_size += 1;
sections[1].memory_address += 1;
assert!(!validate_sections(§ions).is_ok());
sections[1].memory_address -= 1;
sections[3].r#type = TDX_METADATA_SECTION_TYPE_CFV;
sections[3].raw_data_size = sections[3].memory_data_size as u32;
assert!(validate_sections(§ions).is_ok());
sections[3].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
sections[3].raw_data_size = 0;
sections[2].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
assert!(!validate_sections(§ions).is_ok());
sections[2].r#type = TDX_METADATA_SECTION_TYPE_TD_HOB;
sections[2].raw_data_size = 1;
assert!(!validate_sections(§ions).is_ok());
sections[2].raw_data_size = 0;
sections[2].data_offset = 1;
assert!(!validate_sections(§ions).is_ok());
sections[2].data_offset = 0;
sections[2].attributes = 1;
assert!(!validate_sections(§ions).is_ok());
sections[2].attributes = 0;
sections[2].memory_address += 1;
assert!(!validate_sections(§ions).is_ok());
sections[2].memory_address -= 1;
sections[3].r#type = TDX_METADATA_SECTION_TYPE_TD_HOB;
assert!(!validate_sections(§ions).is_ok());
sections[3].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
sections[3].raw_data_size = 1;
assert!(!validate_sections(§ions).is_ok());
sections[3].raw_data_size = 0;
sections[3].data_offset = 1;
assert!(!validate_sections(§ions).is_ok());
sections[3].data_offset = 0;
sections[3].attributes = 1;
assert!(!validate_sections(§ions).is_ok());
sections[3].attributes = 0;
sections[3].memory_address += 1;
assert!(!validate_sections(§ions).is_ok());
sections[3].memory_address -= 1;
sections[2].r#type = TDX_METADATA_SECTION_TYPE_PERM_MEM;
sections[2].attributes = TDX_METADATA_ATTRIBUTES_PAGE_AUG;
assert!(validate_sections(§ions).is_ok());
sections[2].raw_data_size = 1;
assert!(!validate_sections(§ions).is_ok());
sections[2].raw_data_size = 0;
sections[2].data_offset = 1;
assert!(!validate_sections(§ions).is_ok());
sections[2].data_offset = 0;
sections[2].attributes = 0;
assert!(!validate_sections(§ions).is_ok());
sections[2].attributes = TDX_METADATA_ATTRIBUTES_EXTENDMR;
assert!(!validate_sections(§ions).is_ok());
sections[2].attributes = TDX_METADATA_ATTRIBUTES_PAGE_AUG;
sections[2].memory_address += 1;
assert!(!validate_sections(§ions).is_ok());
sections[2].memory_address -= 1;
sections[3].r#type = TDX_METADATA_SECTION_TYPE_TD_HOB;
assert!(validate_sections(§ions).is_ok());
sections[3].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
sections[3].r#type = TDX_METADATA_SECTION_TYPE_PERM_MEM;
sections[3].attributes = TDX_METADATA_ATTRIBUTES_PAGE_AUG;
assert!(validate_sections(§ions).is_ok());
sections[3].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
sections[3].attributes = 0;
sections[4].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
assert!(!validate_sections(§ions).is_ok());
sections[5].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
assert!(validate_sections(§ions).is_ok());
sections[4].r#type = TDX_METADATA_SECTION_TYPE_PAYLOAD;
sections[5].r#type = TDX_METADATA_SECTION_TYPE_PAYLOAD_PARAM;
sections[4].attributes = 1;
assert!(validate_sections(§ions).is_ok());
sections[4].attributes = 2;
assert!(!validate_sections(§ions).is_ok());
sections[4].attributes = 0;
sections[4].data_offset = 1;
assert!(!validate_sections(§ions).is_ok());
sections[4].data_offset = 0;
sections[4].memory_address += 1;
assert!(!validate_sections(§ions).is_ok());
sections[4].memory_address -= 1;
sections[5].r#type = TDX_METADATA_SECTION_TYPE_PAYLOAD;
assert!(!validate_sections(§ions).is_ok());
sections[5].r#type = TDX_METADATA_SECTION_TYPE_PAYLOAD_PARAM;
sections[5].r#type = TDX_METADATA_SECTION_TYPE_TEMP_MEM;
assert!(validate_sections(§ions).is_ok());
sections[5].r#type = TDX_METADATA_SECTION_TYPE_PAYLOAD_PARAM;
sections[5].attributes = 1;
assert!(!validate_sections(§ions).is_ok());
sections[5].attributes = 0;
sections[5].memory_address += 1;
assert!(!validate_sections(§ions).is_ok());
sections[5].memory_address -= 1;
sections[3].r#type = TDX_METADATA_SECTION_TYPE_PAYLOAD_PARAM;
assert!(!validate_sections(§ions).is_ok());
sections[5].r#type = TDX_METADATA_SECTION_TYPE_MAX;
assert!(!validate_sections(§ions).is_ok());
}
#[test]
fn test_tdxmetadataptr() {
let ptr = TdxMetadataPtr { ptr: 0x1000 };
assert_eq!(ptr.as_bytes(), 0x1000_i32.to_le_bytes())
}
}