igvmfilegen/vp_context_builder/
vbs.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! VBS VP context builder.
5
6use crate::file_loader::DEFAULT_COMPATIBILITY_MASK;
7use crate::vp_context_builder::VpContextBuilder;
8use crate::vp_context_builder::VpContextState;
9use hvdef::Vtl;
10use igvm::FileDataSerializer;
11use igvm::IgvmDirectiveHeader;
12use igvm_defs::PAGE_SIZE_4K;
13use loader::importer::Aarch64Register;
14use loader::importer::X86Register;
15use std::fmt::Debug;
16use std::mem::discriminant;
17
18/// A trait used to specialize behavior based on the register type. Different
19/// architectures need to do different conversions and emit different
20/// [`IgvmDirectiveHeader`] types.
21pub trait VbsRegister: Sized {
22    /// Convert the list of registers into the corresponding
23    /// [`IgvmDirectiveHeader`] for this architecture.
24    fn into_igvm_header(vtl: Vtl, list: &[Self]) -> IgvmDirectiveHeader;
25}
26
27impl VbsRegister for X86Register {
28    fn into_igvm_header(vtl: Vtl, list: &[Self]) -> IgvmDirectiveHeader {
29        IgvmDirectiveHeader::X64VbsVpContext {
30            registers: list
31                .iter()
32                .map(|&reg| reg.into())
33                .collect::<Vec<igvm::registers::X86Register>>(),
34            vtl: (vtl as u8).try_into().expect("vtl should be valid"),
35            compatibility_mask: DEFAULT_COMPATIBILITY_MASK,
36        }
37    }
38}
39
40impl VbsRegister for Aarch64Register {
41    fn into_igvm_header(vtl: Vtl, list: &[Self]) -> IgvmDirectiveHeader {
42        IgvmDirectiveHeader::AArch64VbsVpContext {
43            registers: list
44                .iter()
45                .map(|&reg| reg.into())
46                .collect::<Vec<igvm::registers::AArch64Register>>(),
47            vtl: (vtl as u8).try_into().expect("vtl should be valid"),
48            compatibility_mask: DEFAULT_COMPATIBILITY_MASK,
49        }
50    }
51}
52
53#[derive(Debug, Clone)]
54pub struct VbsVpContext<R: VbsRegister> {
55    /// The registers set for this VP.
56    registers: Vec<R>,
57    /// The VTL this VP context is for.
58    vtl: Vtl,
59}
60
61impl<R: VbsRegister> VbsVpContext<R> {
62    pub fn new(vtl: Vtl) -> Self {
63        Self {
64            registers: Vec::new(),
65            vtl,
66        }
67    }
68
69    /// Returns this VP context encoded as a serialized page of data, in IGVM
70    /// directive format.
71    pub fn as_page(&self) -> Vec<u8> {
72        let header = R::into_igvm_header(self.vtl, &self.registers);
73        // Serialize the same binary format as an IGVM header, but instead to be deposited as page data.
74        let mut variable_header = Vec::new();
75        let mut file_data = FileDataSerializer::new(0);
76        header
77            .write_binary_header(&mut variable_header, &mut file_data)
78            .expect("registers should be valid");
79
80        let file_data = file_data.take();
81
82        assert!(file_data.len() <= PAGE_SIZE_4K as usize);
83
84        file_data
85    }
86}
87
88impl<R: VbsRegister> VpContextBuilder for VbsVpContext<R> {
89    type Register = R;
90
91    fn import_vp_register(&mut self, register: R) {
92        // Check for duplicate register
93        assert!(
94            !self
95                .registers
96                .iter()
97                .any(|reg| discriminant(reg) == discriminant(&register)),
98            "duplicate register import"
99        );
100
101        self.registers.push(register);
102    }
103
104    fn set_vp_context_memory(&mut self, _page_base: u64) {
105        unimplemented!("not supported for VBS");
106    }
107
108    fn finalize(&mut self, state: &mut Vec<VpContextState>) {
109        if self.registers.is_empty() {
110            return;
111        }
112        // Serialize as a VP context IGVM header.
113        state.push(VpContextState::Directive(R::into_igvm_header(
114            self.vtl,
115            &self.registers,
116        )));
117    }
118}