mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-03-20 07:58:55 +00:00
Adding a new method to the TdHob structure so that we can easily insert a HOB_PAYLOAD_INFO_TABLE into the HOB. Signed-off-by: Jiaqi Gao <jiaqi.gao@intel.com> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
454 lines
13 KiB
Rust
454 lines
13 KiB
Rust
// Copyright © 2021 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
use crate::GuestMemoryMmap;
|
|
use std::fs::File;
|
|
use std::io::{Read, Seek, SeekFrom};
|
|
use thiserror::Error;
|
|
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError};
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum TdvfError {
|
|
#[error("Failed read TDVF descriptor: {0}")]
|
|
ReadDescriptor(#[source] std::io::Error),
|
|
#[error("Failed read TDVF descriptor offset: {0}")]
|
|
ReadDescriptorOffset(#[source] std::io::Error),
|
|
#[error("Invalid descriptor signature")]
|
|
InvalidDescriptorSignature,
|
|
#[error("Invalid descriptor size")]
|
|
InvalidDescriptorSize,
|
|
#[error("Invalid descriptor version")]
|
|
InvalidDescriptorVersion,
|
|
#[error("Failed to write HOB details to guest memory: {0}")]
|
|
GuestMemoryWriteHob(#[source] GuestMemoryError),
|
|
}
|
|
|
|
// TDVF_DESCRIPTOR
|
|
#[repr(packed)]
|
|
#[derive(Default)]
|
|
pub struct TdvfDescriptor {
|
|
signature: [u8; 4],
|
|
length: u32,
|
|
version: u32,
|
|
num_sections: u32, // NumberOfSectionEntry
|
|
}
|
|
|
|
// TDVF_SECTION
|
|
#[repr(packed)]
|
|
#[derive(Clone, Copy, Default, Debug)]
|
|
pub struct TdvfSection {
|
|
pub data_offset: u32,
|
|
pub data_size: u32, // RawDataSize
|
|
pub address: u64, // MemoryAddress
|
|
pub size: u64, // MemoryDataSize
|
|
pub r#type: TdvfSectionType,
|
|
pub attributes: u32,
|
|
}
|
|
|
|
#[repr(u32)]
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum TdvfSectionType {
|
|
Bfv,
|
|
Cfv,
|
|
TdHob,
|
|
TempMem,
|
|
PermMem,
|
|
Payload,
|
|
PayloadParam,
|
|
Reserved = 0xffffffff,
|
|
}
|
|
|
|
impl Default for TdvfSectionType {
|
|
fn default() -> Self {
|
|
TdvfSectionType::Reserved
|
|
}
|
|
}
|
|
|
|
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
|
|
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
|
// the end of the file.
|
|
// See "TDVF Metadata Pointer" in "TDX Virtual Firmware Design Guide
|
|
file.seek(SeekFrom::End(-0x20))
|
|
.map_err(TdvfError::ReadDescriptorOffset)?;
|
|
|
|
let mut descriptor_offset: [u8; 4] = [0; 4];
|
|
file.read_exact(&mut descriptor_offset)
|
|
.map_err(TdvfError::ReadDescriptorOffset)?;
|
|
let descriptor_offset = u32::from_le_bytes(descriptor_offset) as u64;
|
|
|
|
file.seek(SeekFrom::Start(descriptor_offset))
|
|
.map_err(TdvfError::ReadDescriptor)?;
|
|
|
|
let mut descriptor: TdvfDescriptor = Default::default();
|
|
// Safe as we read exactly the size of the descriptor header
|
|
file.read_exact(unsafe {
|
|
std::slice::from_raw_parts_mut(
|
|
&mut descriptor as *mut _ as *mut u8,
|
|
std::mem::size_of::<TdvfDescriptor>(),
|
|
)
|
|
})
|
|
.map_err(TdvfError::ReadDescriptor)?;
|
|
|
|
if &descriptor.signature != b"TDVF" {
|
|
return Err(TdvfError::InvalidDescriptorSignature);
|
|
}
|
|
|
|
if descriptor.length as usize
|
|
!= std::mem::size_of::<TdvfDescriptor>()
|
|
+ std::mem::size_of::<TdvfSection>() * descriptor.num_sections as usize
|
|
{
|
|
return Err(TdvfError::InvalidDescriptorSize);
|
|
}
|
|
|
|
if descriptor.version != 1 {
|
|
return Err(TdvfError::InvalidDescriptorVersion);
|
|
}
|
|
|
|
let mut sections = Vec::new();
|
|
sections.resize_with(descriptor.num_sections as usize, TdvfSection::default);
|
|
|
|
// Safe as we read exactly the advertised sections
|
|
file.read_exact(unsafe {
|
|
std::slice::from_raw_parts_mut(
|
|
sections.as_mut_ptr() as *mut u8,
|
|
descriptor.num_sections as usize * std::mem::size_of::<TdvfSection>(),
|
|
)
|
|
})
|
|
.map_err(TdvfError::ReadDescriptor)?;
|
|
|
|
Ok(sections)
|
|
}
|
|
|
|
#[repr(u16)]
|
|
#[derive(Copy, Clone, Debug)]
|
|
enum HobType {
|
|
Handoff = 0x1,
|
|
ResourceDescriptor = 0x3,
|
|
GuidExtension = 0x4,
|
|
Unused = 0xfffe,
|
|
EndOfHobList = 0xffff,
|
|
}
|
|
|
|
impl Default for HobType {
|
|
fn default() -> Self {
|
|
HobType::Unused
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Default, Debug)]
|
|
struct HobHeader {
|
|
r#type: HobType,
|
|
length: u16,
|
|
reserved: u32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Default, Debug)]
|
|
struct HobHandoffInfoTable {
|
|
header: HobHeader,
|
|
version: u32,
|
|
boot_mode: u32,
|
|
efi_memory_top: u64,
|
|
efi_memory_bottom: u64,
|
|
efi_free_memory_top: u64,
|
|
efi_free_memory_bottom: u64,
|
|
efi_end_of_hob_list: u64,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Default, Debug)]
|
|
struct EfiGuid {
|
|
data1: u32,
|
|
data2: u16,
|
|
data3: u16,
|
|
data4: [u8; 8],
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Default, Debug)]
|
|
struct HobResourceDescriptor {
|
|
header: HobHeader,
|
|
owner: EfiGuid,
|
|
resource_type: u32,
|
|
resource_attribute: u32,
|
|
physical_start: u64,
|
|
resource_length: u64,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Default, Debug)]
|
|
struct HobGuidType {
|
|
header: HobHeader,
|
|
name: EfiGuid,
|
|
}
|
|
|
|
#[repr(u32)]
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum PayloadImageType {
|
|
ExecutablePayload,
|
|
BzImage,
|
|
RawVmLinux,
|
|
}
|
|
|
|
impl Default for PayloadImageType {
|
|
fn default() -> Self {
|
|
PayloadImageType::ExecutablePayload
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Default, Debug)]
|
|
pub struct PayloadInfo {
|
|
pub image_type: PayloadImageType,
|
|
pub entry_point: u64,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Default, Debug)]
|
|
struct TdPayload {
|
|
guid_type: HobGuidType,
|
|
payload_info: PayloadInfo,
|
|
}
|
|
|
|
// SAFETY: These data structures only contain a series of integers
|
|
unsafe impl ByteValued for HobHeader {}
|
|
unsafe impl ByteValued for HobHandoffInfoTable {}
|
|
unsafe impl ByteValued for HobResourceDescriptor {}
|
|
unsafe impl ByteValued for HobGuidType {}
|
|
unsafe impl ByteValued for PayloadInfo {}
|
|
unsafe impl ByteValued for TdPayload {}
|
|
|
|
pub struct TdHob {
|
|
start_offset: u64,
|
|
current_offset: u64,
|
|
}
|
|
|
|
fn align_hob(v: u64) -> u64 {
|
|
(v + 7) / 8 * 8
|
|
}
|
|
|
|
impl TdHob {
|
|
fn update_offset<T>(&mut self) {
|
|
self.current_offset = align_hob(self.current_offset + std::mem::size_of::<T>() as u64)
|
|
}
|
|
|
|
pub fn start(offset: u64) -> TdHob {
|
|
// Leave a gap to place the HandoffTable at the start as it can only be filled in later
|
|
let mut hob = TdHob {
|
|
start_offset: offset,
|
|
current_offset: offset,
|
|
};
|
|
hob.update_offset::<HobHandoffInfoTable>();
|
|
hob
|
|
}
|
|
|
|
pub fn finish(&mut self, mem: &GuestMemoryMmap) -> Result<(), TdvfError> {
|
|
// Write end
|
|
let end = HobHeader {
|
|
r#type: HobType::EndOfHobList,
|
|
length: std::mem::size_of::<HobHeader>() as u16,
|
|
reserved: 0,
|
|
};
|
|
info!("Writing HOB end {:x} {:x?}", self.current_offset, end);
|
|
mem.write_obj(end, GuestAddress(self.current_offset))
|
|
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
|
self.update_offset::<HobHeader>();
|
|
|
|
// Write handoff, delayed as it needs end of HOB list
|
|
let efi_end_of_hob_list = self.current_offset;
|
|
let handoff = HobHandoffInfoTable {
|
|
header: HobHeader {
|
|
r#type: HobType::Handoff,
|
|
length: std::mem::size_of::<HobHandoffInfoTable>() as u16,
|
|
reserved: 0,
|
|
},
|
|
version: 0x9,
|
|
boot_mode: 0,
|
|
efi_memory_top: 0,
|
|
efi_memory_bottom: 0,
|
|
efi_free_memory_top: 0,
|
|
efi_free_memory_bottom: 0,
|
|
efi_end_of_hob_list,
|
|
};
|
|
info!("Writing HOB start {:x} {:x?}", self.start_offset, handoff);
|
|
mem.write_obj(handoff, GuestAddress(self.start_offset))
|
|
.map_err(TdvfError::GuestMemoryWriteHob)
|
|
}
|
|
|
|
pub fn add_resource(
|
|
&mut self,
|
|
mem: &GuestMemoryMmap,
|
|
physical_start: u64,
|
|
resource_length: u64,
|
|
resource_type: u32,
|
|
resource_attribute: u32,
|
|
) -> Result<(), TdvfError> {
|
|
let resource_descriptor = HobResourceDescriptor {
|
|
header: HobHeader {
|
|
r#type: HobType::ResourceDescriptor,
|
|
length: std::mem::size_of::<HobResourceDescriptor>() as u16,
|
|
reserved: 0,
|
|
},
|
|
owner: EfiGuid::default(),
|
|
resource_type,
|
|
resource_attribute,
|
|
physical_start,
|
|
resource_length,
|
|
};
|
|
info!(
|
|
"Writing HOB resource {:x} {:x?}",
|
|
self.current_offset, resource_descriptor
|
|
);
|
|
mem.write_obj(resource_descriptor, GuestAddress(self.current_offset))
|
|
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
|
self.update_offset::<HobResourceDescriptor>();
|
|
Ok(())
|
|
}
|
|
|
|
pub fn add_memory_resource(
|
|
&mut self,
|
|
mem: &GuestMemoryMmap,
|
|
physical_start: u64,
|
|
resource_length: u64,
|
|
ram: bool,
|
|
) -> Result<(), TdvfError> {
|
|
self.add_resource(
|
|
mem,
|
|
physical_start,
|
|
resource_length,
|
|
if ram {
|
|
0 /* EFI_RESOURCE_SYSTEM_MEMORY */
|
|
} else {
|
|
0x5 /*EFI_RESOURCE_MEMORY_RESERVED */
|
|
},
|
|
/* TODO:
|
|
* QEMU currently fills it in like this:
|
|
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_ENCRYPTED | EFI_RESOURCE_ATTRIBUTE_TESTED
|
|
* which differs from the spec (due to TDVF implementation issue?)
|
|
*/
|
|
0x04000007,
|
|
)
|
|
}
|
|
|
|
pub fn add_mmio_resource(
|
|
&mut self,
|
|
mem: &GuestMemoryMmap,
|
|
physical_start: u64,
|
|
resource_length: u64,
|
|
) -> Result<(), TdvfError> {
|
|
self.add_resource(
|
|
mem,
|
|
physical_start,
|
|
resource_length,
|
|
0x1, /* EFI_RESOURCE_MEMORY_MAPPED_IO */
|
|
/*
|
|
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE
|
|
*/
|
|
0x403,
|
|
)
|
|
}
|
|
|
|
pub fn add_acpi_table(
|
|
&mut self,
|
|
mem: &GuestMemoryMmap,
|
|
table_content: &[u8],
|
|
) -> Result<(), TdvfError> {
|
|
// We already know the HobGuidType size is 8 bytes multiple, but we
|
|
// need the total size to be 8 bytes multiple. That is why the ACPI
|
|
// table size must be 8 bytes multiple as well.
|
|
let length = std::mem::size_of::<HobGuidType>() as u16
|
|
+ align_hob(table_content.len() as u64) as u16;
|
|
let hob_guid_type = HobGuidType {
|
|
header: HobHeader {
|
|
r#type: HobType::GuidExtension,
|
|
length,
|
|
reserved: 0,
|
|
},
|
|
// ACPI_TABLE_HOB_GUID
|
|
// 0x6a0c5870, 0xd4ed, 0x44f4, {0xa1, 0x35, 0xdd, 0x23, 0x8b, 0x6f, 0xc, 0x8d }
|
|
name: EfiGuid {
|
|
data1: 0x6a0c_5870,
|
|
data2: 0xd4ed,
|
|
data3: 0x44f4,
|
|
data4: [0xa1, 0x35, 0xdd, 0x23, 0x8b, 0x6f, 0xc, 0x8d],
|
|
},
|
|
};
|
|
info!(
|
|
"Writing HOB ACPI table {:x} {:x?} {:x?}",
|
|
self.current_offset, hob_guid_type, table_content
|
|
);
|
|
mem.write_obj(hob_guid_type, GuestAddress(self.current_offset))
|
|
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
|
let current_offset = self.current_offset + std::mem::size_of::<HobGuidType>() as u64;
|
|
|
|
// In case the table is quite large, let's make sure we can handle
|
|
// retrying until everything has been correctly copied.
|
|
let mut offset: usize = 0;
|
|
loop {
|
|
let bytes_written = mem
|
|
.write(
|
|
&table_content[offset..],
|
|
GuestAddress(current_offset + offset as u64),
|
|
)
|
|
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
|
offset += bytes_written;
|
|
if offset >= table_content.len() {
|
|
break;
|
|
}
|
|
}
|
|
self.current_offset += length as u64;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn add_payload(
|
|
&mut self,
|
|
mem: &GuestMemoryMmap,
|
|
payload_info: PayloadInfo,
|
|
) -> Result<(), TdvfError> {
|
|
let payload = TdPayload {
|
|
guid_type: HobGuidType {
|
|
header: HobHeader {
|
|
r#type: HobType::GuidExtension,
|
|
length: std::mem::size_of::<TdPayload>() as u16,
|
|
reserved: 0,
|
|
},
|
|
// HOB_PAYLOAD_INFO_GUID
|
|
// 0xb96fa412, 0x461f, 0x4be3, {0x8c, 0xd, 0xad, 0x80, 0x5a, 0x49, 0x7a, 0xc0
|
|
name: EfiGuid {
|
|
data1: 0xb96f_a412,
|
|
data2: 0x461f,
|
|
data3: 0x4be3,
|
|
data4: [0x8c, 0xd, 0xad, 0x80, 0x5a, 0x49, 0x7a, 0xc0],
|
|
},
|
|
},
|
|
payload_info,
|
|
};
|
|
info!(
|
|
"Writing HOB TD_PAYLOAD {:x} {:x?}",
|
|
self.current_offset, payload
|
|
);
|
|
mem.write_obj(payload, GuestAddress(self.current_offset))
|
|
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
|
self.update_offset::<TdPayload>();
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn test_parse_tdvf_sections() {
|
|
let mut f = std::fs::File::open("tdvf.fd").unwrap();
|
|
let sections = parse_tdvf_sections(&mut f).unwrap();
|
|
for section in sections {
|
|
eprintln!("{:x?}", section)
|
|
}
|
|
}
|
|
}
|