tdx.rs71.01%
1
// Copyright 2026 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
use std::cmp::min;16
use std::io::Write;17
18
use snafu::ResultExt;19
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};20
21
use crate::firmware::ovmf::x86_64::{GUID_SIZE, parse_data};22
use crate::firmware::uefi::{23
HOB_HANDOFF_TABLE_VERSION, HobGenericHeader, HobHandoffInfoTable, HobResourceDesc,24
HobResourceType, HobType, ResourceAttr,25
};26
use crate::firmware::{Result, error};27
use crate::mem::{MemRegionEntry, MemRegionType};28
use crate::{bitflags, consts};29
30
pub const GUID_TDX_METADATA_OFFSET: [u8; GUID_SIZE] = [31
0x35, 0x65, 0x7a, 0xe4, 0x4a, 0x98, 0x98, 0x47, 0x86, 0x5e, 0x46, 0x85, 0xa7, 0xbf, 0x8e, 0xc2,32
];33
pub const TDVF_SIGNATURE: u32 = u32::from_le_bytes(*b"TDVF");34
pub const TDVF_VERSION: u32 = 1;35
36
#[repr(C)]37
#[derive(Debug, Clone, Default, KnownLayout, Immutable, FromBytes, IntoBytes)]38
pub struct TdvfMetadata {39
pub signature: u32,40
pub length: u32,41
pub version: u32,42
pub number_of_entries: u32,43
}44
45
consts! {46
#[derive(Default, KnownLayout, Immutable, FromBytes, IntoBytes)]47
pub struct TdvfSectionType(u32) {48
BFV = 0;49
CFV = 1;50
TD_HOB = 2;51
TEMP_MEM = 3;52
}53
}54
55
bitflags! {56
#[derive(Default, KnownLayout, Immutable, FromBytes, IntoBytes)]57
pub struct TdvfSectionAttr(u32) {58
MR_EXTEND = 1 << 0;59
PAGE_AUG = 1 << 1;60
}61
}62
63
#[repr(C)]64
#[derive(Debug, Clone, Default, KnownLayout, Immutable, FromBytes, IntoBytes)]65
pub struct TdvfSectionEntry {66
pub data_offset: u32,67
pub data_size: u32,68
pub address: u64,69
pub size: u64,70
pub r#type: TdvfSectionType,71
pub attributes: TdvfSectionAttr,72
}73
74
pub fn parse_entries(data: &[u8]) -> Result<&[TdvfSectionEntry]> {75
let Some(offset_r) = parse_data(data, &GUID_TDX_METADATA_OFFSET) else {76
return error::MissingMetadata {77
name: "TdvfMetadata",78
}79
.fail();80
};81
let Ok(offset_r) = u32::read_from_bytes(offset_r) else {82
return error::InvalidLayout.fail();83
};84
let offset = data.len() - offset_r as usize;85
let Ok((metadata, remain)) = TdvfMetadata::ref_from_prefix(&data[offset..]) else {86
return error::InvalidLayout.fail();87
};88
if metadata.signature != TDVF_SIGNATURE {89
return error::MissingTdvfSignature {90
got: metadata.signature,91
}92
.fail();93
}94
if metadata.version != TDVF_VERSION {95
return error::MissingTdvfVersion {96
got: metadata.version,97
}98
.fail();99
}100
let Ok((entries, _)) = <[TdvfSectionEntry]>::ref_from_prefix_with_elems(101
remain,102
metadata.number_of_entries as usize,103
) else {104
return error::InvalidLayout.fail();105
};106
Ok(entries)107
}108
109
fn create_hob_mem_resources(3x110
entries: &[(u64, MemRegionEntry)],3x111
accepted: &[(u64, u64)],3x112
mut op: impl FnMut(HobResourceDesc) -> Result<()>,3x113
) -> Result<()> {3x114
let tmpl = HobResourceDesc {3x115
hdr: HobGenericHeader {3x116
r#type: HobType::RESOURCE_DESCRIPTOR,3x117
length: size_of::<HobResourceDesc>() as u16,3x118
reserved: 0,3x119
},3x120
owner: [0; 16],3x121
attr: ResourceAttr::PRESENT | ResourceAttr::INIT | ResourceAttr::TESTED,3x122
..Default::default()3x123
};3x124
let mut iter_e = entries.iter();3x125
let mut iter_a = accepted.iter();3x126
let mut section = iter_a.next().copied();3x127
let mut entry = iter_e.next().copied();3x128
loop {129
match (&mut entry, &mut section) {24x130
(None, None) => break,3x131
(None, Some((start, size))) => {132
log::error!(133
"Section [{start:x}, {:x}) is not covered by system memory",134
*start + *size135
);136
return error::UncoveredTdvfSection.fail();137
}138
(Some((_, e)), _) if e.type_ != MemRegionType::Ram => {21x139
entry = iter_e.next().copied();9x140
continue;9x141
}142
(Some((s, e)), None) => {3x143
op(HobResourceDesc {3x144
r#type: HobResourceType::MEMORY_UNACCEPTED,3x145
address: *s,3x146
len: e.size,3x147
..tmpl.clone()3x148
})?;3x149
entry = iter_e.next().copied();3x150
}151
(Some((s, e)), Some((start, size))) => {9x152
if let Some(len) = min(*s, *start + *size).checked_sub(*start)9x153
&& len > 06x154
{155
*size = len;156
entry = None;157
// Jump to branch (Some, None)158
continue;159
}9x160
if let Some(len) = min(*s + e.size, *start).checked_sub(*s)9x161
&& len > 09x162
{163
op(HobResourceDesc {3x164
r#type: HobResourceType::MEMORY_UNACCEPTED,3x165
address: *s,3x166
len,3x167
..tmpl.clone()3x168
})?;3x169
*s += len;3x170
e.size -= len;3x171
}6x172
if let Some(len) = min(*s + e.size, *start + *size).checked_sub(*start)9x173
&& len > 09x174
{175
op(HobResourceDesc {9x176
r#type: HobResourceType::SYSTEM_MEMORY,9x177
address: *start,9x178
len,9x179
..tmpl.clone()9x180
})?;9x181
*start += len;9x182
*size -= len;9x183
*s += len;9x184
e.size -= len9x185
};186
if *size == 0 {9x187
section = iter_a.next().copied();9x188
};9x189
if e.size == 0 {9x190
entry = iter_e.next().copied();191
}9x192
}193
}194
}195
Ok(())3x196
}3x197
198
pub fn create_hob(3x199
buffer: &mut [u8],3x200
hob_phys: u64,3x201
entries: &mut [(u64, MemRegionEntry)],3x202
accepted: &mut [(u64, u64)],3x203
) -> Result<()> {3x204
entries.sort_by_key(|(s, _)| *s);3x205
accepted.sort();3x206
207
let Ok((table, mut dst)) = HobHandoffInfoTable::mut_from_prefix(buffer) else {3x208
return error::InvalidLayout.fail();209
};210
211
let mut desc_size = 0;3x212
create_hob_mem_resources(entries, accepted, |d| {15x213
desc_size += size_of_val(&d);15x214
dst.write_all(d.as_bytes()).context(error::WriteHob)15x215
})?;15x216
217
let end = HobGenericHeader {3x218
r#type: HobType::END_OF_HOB_LIST,3x219
length: size_of::<HobGenericHeader>() as u16,3x220
reserved: 0,3x221
};3x222
dst.write_all(end.as_bytes()).context(error::WriteHob)?;3x223
224
*table = HobHandoffInfoTable {3x225
hdr: HobGenericHeader {3x226
r#type: HobType::HANDOFF,3x227
length: size_of::<HobHandoffInfoTable>() as u16,3x228
reserved: 0,3x229
},3x230
version: HOB_HANDOFF_TABLE_VERSION,3x231
end_of_hob_list: hob_phys + (size_of_val(table) + desc_size + size_of_val(&end)) as u64,3x232
..Default::default()3x233
};3x234
235
Ok(())3x236
}3x237
238
#[cfg(test)]239
#[path = "tdx_test.rs"]240
mod tests;241