Skip to content

Commit d4626b2

Browse files
committed
Parse attributes in extended VTL0 APIs
VTL0 sends data to the secure kernel during and after initial boot. Use the new attributes parameter to extend load_kdata, allowing it to process data after boot and use the improved format for sending data from VTL0. Small, simple data buffers like certificates can be sent efficiently with the same APIs used for larger aggregated data like module info. The new data format is leveraged to reduce the size of the kernel string table passed from ~10MB (all of rodata) to ~250KB. The validate_module and validate_kexec APIs are extended to use the new attributes.
1 parent dc38d17 commit d4626b2

File tree

5 files changed

+626
-535
lines changed

5 files changed

+626
-535
lines changed

litebox_platform_lvbs/src/mshv/error.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,21 @@ pub enum VsmError {
152152

153153
#[error("symbol name contains invalid UTF-8")]
154154
SymbolNameInvalidUtf8,
155+
156+
#[error("invalid API attribute")]
157+
ApiAttrInvalid,
158+
159+
#[error("invalid symbol info type")]
160+
SymbolInfoTypeInvalid,
161+
162+
#[error("invalid permissions info type")]
163+
PermInfoTypeInvalid,
164+
165+
#[error("invalid patch type")]
166+
PatchTypeInvalid,
167+
168+
#[error("invalid data page")]
169+
DataPageInvalid,
155170
}
156171

157172
impl From<VerificationError> for VsmError {
@@ -217,7 +232,12 @@ impl From<VsmError> for Errno {
217232
| VsmError::SymbolNameInvalidUtf8
218233
| VsmError::SymbolNameNoTerminator
219234
| VsmError::CertificateDerLengthInvalid { .. }
220-
| VsmError::CertificateParseFailed => Errno::EINVAL,
235+
| VsmError::CertificateParseFailed
236+
| VsmError::ApiAttrInvalid
237+
| VsmError::SymbolInfoTypeInvalid
238+
| VsmError::PermInfoTypeInvalid
239+
| VsmError::DataPageInvalid
240+
| VsmError::PatchTypeInvalid => Errno::EINVAL,
221241

222242
// Signature verification failures delegate to VerificationError's Errno mapping
223243
VsmError::SignatureVerificationFailed(e) => Errno::from(e),

litebox_platform_lvbs/src/mshv/heki.rs

Lines changed: 29 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ use crate::{
77
};
88
use core::mem;
99
use litebox::utils::TruncateExt;
10+
use modular_bitfield::Specifier;
1011
use num_enum::TryFromPrimitive;
1112
use x86_64::{
12-
PhysAddr, VirtAddr,
13+
PhysAddr,
1314
structures::paging::{PageSize, Size4KiB},
1415
};
16+
use zerocopy::{FromBytes, Immutable, KnownLayout};
1517

1618
bitflags::bitflags! {
1719
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -42,8 +44,9 @@ pub(crate) fn mem_attr_to_hv_page_prot_flags(attr: MemAttr) -> HvPageProtFlags {
4244
flags
4345
}
4446

45-
#[derive(Default, Debug, TryFromPrimitive, PartialEq)]
46-
#[repr(u64)]
47+
#[derive(Default, Debug, TryFromPrimitive, PartialEq, Specifier)]
48+
#[bits = 16]
49+
#[repr(u16)]
4750
pub enum HekiKdataType {
4851
SystemCerts = 0,
4952
RevocationCerts = 1,
@@ -52,22 +55,36 @@ pub enum HekiKdataType {
5255
KernelData = 4,
5356
PatchInfo = 5,
5457
KexecTrampoline = 6,
58+
SymbolInfo = 7,
59+
ModuleInfo = 8,
60+
PermInfo = 9,
61+
KexecInfo = 10,
62+
DataPage = 0xff,
5563
#[default]
56-
Unknown = 0xffff_ffff_ffff_ffff,
64+
Unknown = 0xffff,
65+
}
66+
67+
#[derive(Debug, TryFromPrimitive, PartialEq)]
68+
#[repr(u16)]
69+
pub enum HekiSymbolInfoType {
70+
SymbolTable = 0,
71+
GplSymbolTable = 1,
72+
SymbolStringTable = 2,
73+
Unknown = 0xffff,
5774
}
5875

5976
#[derive(Default, Debug, TryFromPrimitive, PartialEq)]
60-
#[repr(u64)]
77+
#[repr(u16)]
6178
pub enum HekiKexecType {
6279
KexecImage = 0,
6380
KexecKernelBlob = 1,
6481
KexecPages = 2,
6582
#[default]
66-
Unknown = 0xffff_ffff_ffff_ffff,
83+
Unknown = 0xffff,
6784
}
6885

6986
#[derive(Clone, Copy, Default, Debug, TryFromPrimitive, PartialEq)]
70-
#[repr(u64)]
87+
#[repr(u16)]
7188
pub enum ModMemType {
7289
Text = 0,
7390
Data = 1,
@@ -79,7 +96,7 @@ pub enum ModMemType {
7996
ElfBuffer = 7,
8097
Patch = 8,
8198
#[default]
82-
Unknown = 0xffff_ffff_ffff_ffff,
99+
Unknown = 0xffff,
83100
}
84101

85102
pub(crate) fn mod_mem_type_to_mem_attr(mod_mem_type: ModMemType) -> MemAttr {
@@ -103,150 +120,6 @@ pub(crate) fn mod_mem_type_to_mem_attr(mod_mem_type: ModMemType) -> MemAttr {
103120
mem_attr
104121
}
105122

106-
/// `HekiRange` is a generic container for various types of memory ranges.
107-
/// It has an `attributes` field which can be interpreted differently based on the context like
108-
/// `MemAttr`, `KdataType`, `ModMemType`, or `KexecType`.
109-
#[derive(Default, Clone, Copy)]
110-
#[repr(C, packed)]
111-
pub struct HekiRange {
112-
pub va: u64,
113-
pub pa: u64,
114-
pub epa: u64,
115-
pub attributes: u64,
116-
}
117-
118-
impl HekiRange {
119-
#[inline]
120-
pub fn is_aligned<U>(&self, align: U) -> bool
121-
where
122-
U: Into<u64> + Copy,
123-
{
124-
let va = self.va;
125-
let pa = self.pa;
126-
let epa = self.epa;
127-
128-
VirtAddr::new(va).is_aligned(align)
129-
&& PhysAddr::new(pa).is_aligned(align)
130-
&& PhysAddr::new(epa).is_aligned(align)
131-
}
132-
133-
#[inline]
134-
pub fn mem_attr(&self) -> Option<MemAttr> {
135-
let attr = self.attributes;
136-
MemAttr::from_bits(attr)
137-
}
138-
139-
#[inline]
140-
pub fn mod_mem_type(&self) -> ModMemType {
141-
let attr = self.attributes;
142-
ModMemType::try_from(attr).unwrap_or(ModMemType::Unknown)
143-
}
144-
145-
#[inline]
146-
pub fn heki_kdata_type(&self) -> HekiKdataType {
147-
let attr = self.attributes;
148-
HekiKdataType::try_from(attr).unwrap_or(HekiKdataType::Unknown)
149-
}
150-
151-
#[inline]
152-
pub fn heki_kexec_type(&self) -> HekiKexecType {
153-
let attr = self.attributes;
154-
HekiKexecType::try_from(attr).unwrap_or(HekiKexecType::Unknown)
155-
}
156-
157-
pub fn is_valid(&self) -> bool {
158-
let va = self.va;
159-
let pa = self.pa;
160-
let epa = self.epa;
161-
let Ok(pa) = PhysAddr::try_new(pa) else {
162-
return false;
163-
};
164-
let Ok(epa) = PhysAddr::try_new(epa) else {
165-
return false;
166-
};
167-
!(VirtAddr::try_new(va).is_err()
168-
|| epa < pa
169-
|| (self.mem_attr().is_none()
170-
&& self.heki_kdata_type() == HekiKdataType::Unknown
171-
&& self.heki_kexec_type() == HekiKexecType::Unknown
172-
&& self.mod_mem_type() == ModMemType::Unknown))
173-
}
174-
}
175-
176-
impl core::fmt::Debug for HekiRange {
177-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178-
let va = self.va;
179-
let pa = self.pa;
180-
let epa = self.epa;
181-
let attr = self.attributes;
182-
f.debug_struct("HekiRange")
183-
.field("va", &format_args!("{va:#x}"))
184-
.field("pa", &format_args!("{pa:#x}"))
185-
.field("epa", &format_args!("{epa:#x}"))
186-
.field("attr", &format_args!("{attr:#x}"))
187-
.field("type", &format_args!("{:?}", self.heki_kdata_type()))
188-
.field("size", &format_args!("{:?}", self.epa - self.pa))
189-
.finish()
190-
}
191-
}
192-
193-
#[expect(clippy::cast_possible_truncation)]
194-
pub const HEKI_MAX_RANGES: usize =
195-
((PAGE_SIZE as u32 - u64::BITS * 3 / 8) / core::mem::size_of::<HekiRange>() as u32) as usize;
196-
197-
#[derive(Clone, Copy)]
198-
#[repr(align(4096))]
199-
#[repr(C)]
200-
pub struct HekiPage {
201-
pub next: *mut HekiPage,
202-
pub next_pa: u64,
203-
pub nranges: u64,
204-
pub ranges: [HekiRange; HEKI_MAX_RANGES],
205-
pad: u64,
206-
}
207-
208-
impl HekiPage {
209-
pub fn new() -> Self {
210-
HekiPage {
211-
next: core::ptr::null_mut(),
212-
..Default::default()
213-
}
214-
}
215-
216-
pub fn is_valid(&self) -> bool {
217-
if PhysAddr::try_new(self.next_pa).is_err() {
218-
return false;
219-
}
220-
let Some(nranges) = usize::try_from(self.nranges)
221-
.ok()
222-
.filter(|&n| n <= HEKI_MAX_RANGES)
223-
else {
224-
return false;
225-
};
226-
for heki_range in &self.ranges[..nranges] {
227-
if !heki_range.is_valid() {
228-
return false;
229-
}
230-
}
231-
true
232-
}
233-
}
234-
235-
impl Default for HekiPage {
236-
fn default() -> Self {
237-
Self::new()
238-
}
239-
}
240-
241-
impl<'a> IntoIterator for &'a HekiPage {
242-
type Item = &'a HekiRange;
243-
type IntoIter = core::slice::Iter<'a, HekiRange>;
244-
245-
fn into_iter(self) -> Self::IntoIter {
246-
self.ranges[..usize::try_from(self.nranges).unwrap_or(0)].iter()
247-
}
248-
}
249-
250123
#[derive(Default, Clone, Copy, Debug)]
251124
#[repr(C)]
252125
pub struct HekiPatch {
@@ -304,12 +177,12 @@ impl HekiPatch {
304177
}
305178
}
306179

307-
#[derive(Default, Clone, Copy, Debug, PartialEq)]
308-
#[repr(u32)]
180+
#[derive(Default, Clone, Copy, Debug, PartialEq, TryFromPrimitive)]
181+
#[repr(u16)]
309182
pub enum HekiPatchType {
310183
JumpLabel = 0,
311184
#[default]
312-
Unknown = 0xffff_ffff,
185+
Unknown = 0xffff,
313186
}
314187

315188
#[derive(Clone, Copy, Debug)]
@@ -348,6 +221,7 @@ impl HekiPatchInfo {
348221
}
349222
}
350223

224+
#[derive(FromBytes, KnownLayout, Immutable)]
351225
#[repr(C)]
352226
#[allow(clippy::struct_field_names)]
353227
// TODO: Account for kernel config changing the size and meaning of the field members
@@ -380,37 +254,3 @@ impl HekiKernelSymbol {
380254
}
381255
}
382256
}
383-
384-
#[repr(C)]
385-
#[allow(clippy::struct_field_names)]
386-
pub struct HekiKernelInfo {
387-
pub ksymtab_start: *const HekiKernelSymbol,
388-
pub ksymtab_end: *const HekiKernelSymbol,
389-
pub ksymtab_gpl_start: *const HekiKernelSymbol,
390-
pub ksymtab_gpl_end: *const HekiKernelSymbol,
391-
// Skip unused arch info
392-
}
393-
394-
impl HekiKernelInfo {
395-
const KINFO_LEN: usize = mem::size_of::<HekiKernelInfo>();
396-
397-
pub fn from_bytes(bytes: &[u8]) -> Result<Self, VsmError> {
398-
if bytes.len() < Self::KINFO_LEN {
399-
return Err(VsmError::BufferTooSmall("HekiKernelInfo"));
400-
}
401-
402-
#[allow(clippy::cast_ptr_alignment)]
403-
let kinfo_ptr = bytes.as_ptr().cast::<HekiKernelInfo>();
404-
assert!(kinfo_ptr.is_aligned(), "kinfo_ptr is not aligned");
405-
406-
// SAFETY: Casting from vtl0 buffer that contained the struct
407-
unsafe {
408-
Ok(HekiKernelInfo {
409-
ksymtab_start: (*kinfo_ptr).ksymtab_start,
410-
ksymtab_end: (*kinfo_ptr).ksymtab_end,
411-
ksymtab_gpl_start: (*kinfo_ptr).ksymtab_gpl_start,
412-
ksymtab_gpl_end: (*kinfo_ptr).ksymtab_gpl_end,
413-
})
414-
}
415-
}
416-
}

0 commit comments

Comments
 (0)