tmk_vmm/
load.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Support for loading a TMK into VM memory.
5
6use anyhow::Context as _;
7use fs_err::File;
8use guestmem::GuestMemory;
9use hvdef::Vtl;
10use loader::importer::GuestArch;
11use loader::importer::ImageLoad;
12use loader::importer::X86Register;
13use object::Endianness;
14use object::Object;
15use object::ObjectSection;
16use object::ObjectSegment as _;
17use std::fmt::Debug;
18use std::sync::Arc;
19use virt::VpIndex;
20use vm_topology::memory::MemoryLayout;
21use vm_topology::processor::ProcessorTopology;
22use vm_topology::processor::aarch64::Aarch64Topology;
23use vm_topology::processor::x86::X86Topology;
24use zerocopy::FromBytes as _;
25use zerocopy::FromZeros;
26use zerocopy::IntoBytes;
27
28/// Loads a TMK, returning the initial registers for the BSP.
29#[cfg_attr(not(guest_arch = "x86_64"), expect(dead_code))]
30pub fn load_x86(
31    memory_layout: &MemoryLayout,
32    guest_memory: &GuestMemory,
33    processor_topology: &ProcessorTopology<X86Topology>,
34    caps: &virt::x86::X86PartitionCapabilities,
35    tmk: &File,
36    test: &TestInfo,
37) -> anyhow::Result<Arc<virt::x86::X86InitialRegs>> {
38    let mut loader = vm_loader::Loader::new(guest_memory.clone(), memory_layout, Vtl::Vtl0);
39    let load_info = load_common(&mut loader, tmk, test)?;
40
41    let page_table_base = load_info.next_available_address;
42    let mut page_table_work_buffer: Vec<page_table::x64::PageTable> =
43        vec![page_table::x64::PageTable::new_zeroed(); page_table::x64::PAGE_TABLE_MAX_COUNT];
44    let mut page_tables: Vec<u8> = vec![0; page_table::x64::PAGE_TABLE_MAX_BYTES];
45    let page_table_builder = page_table::x64::IdentityMapBuilder::new(
46        page_table_base,
47        page_table::IdentityMapSize::Size4Gb,
48        page_table_work_buffer.as_mut_slice(),
49        page_tables.as_mut_slice(),
50    )?;
51    let page_tables = page_table_builder.build();
52    loader
53        .import_pages(
54            page_table_base >> 12,
55            page_tables.len() as u64 >> 12,
56            "page_tables",
57            loader::importer::BootPageAcceptance::Exclusive,
58            page_tables,
59        )
60        .context("failed to import page tables")?;
61
62    let gdt_base = page_table_base + page_tables.len() as u64;
63    loader::common::import_default_gdt(&mut loader, gdt_base >> 12)
64        .context("failed to import gdt")?;
65
66    let mut import_reg = |reg| {
67        loader
68            .import_vp_register(reg)
69            .context("failed to set register")
70    };
71    import_reg(X86Register::Cr0(x86defs::X64_CR0_PG | x86defs::X64_CR0_PE))?;
72    import_reg(X86Register::Cr3(page_table_base))?;
73    import_reg(X86Register::Cr4(x86defs::X64_CR4_PAE))?;
74    import_reg(X86Register::Efer(
75        x86defs::X64_EFER_SCE
76            | x86defs::X64_EFER_LME
77            | x86defs::X64_EFER_LMA
78            | x86defs::X64_EFER_NXE,
79    ))?;
80    import_reg(X86Register::Rip(load_info.entrypoint))?;
81    import_reg(X86Register::Rsi(load_info.param))?;
82
83    let regs = vm_loader::initial_regs::x86_initial_regs(
84        &loader.initial_regs(),
85        caps,
86        &processor_topology.vp_arch(VpIndex::BSP),
87    );
88    Ok(regs)
89}
90
91#[cfg_attr(not(guest_arch = "aarch64"), expect(dead_code))]
92pub fn load_aarch64(
93    memory_layout: &MemoryLayout,
94    guest_memory: &GuestMemory,
95    processor_topology: &ProcessorTopology<Aarch64Topology>,
96    caps: &virt::aarch64::Aarch64PartitionCapabilities,
97    tmk: &File,
98    test: &TestInfo,
99) -> anyhow::Result<Arc<virt::aarch64::Aarch64InitialRegs>> {
100    let mut loader = vm_loader::Loader::new(guest_memory.clone(), memory_layout, Vtl::Vtl0);
101    let load_info = load_common(&mut loader, tmk, test)?;
102
103    let mut import_reg = |reg| {
104        loader
105            .import_vp_register(reg)
106            .context("failed to set register")
107    };
108
109    import_reg(loader::importer::Aarch64Register::Pc(load_info.entrypoint))?;
110    import_reg(loader::importer::Aarch64Register::X0(load_info.param))?;
111    let regs = vm_loader::initial_regs::aarch64_initial_regs(
112        &loader.initial_regs(),
113        caps,
114        &processor_topology.vp_arch(VpIndex::BSP),
115    );
116
117    Ok(regs)
118}
119
120fn load_common<R: Debug + GuestArch>(
121    loader: &mut vm_loader::Loader<'_, R>,
122    tmk: &File,
123    test: &TestInfo,
124) -> anyhow::Result<LoadInfo> {
125    let load_info = loader::elf::load_static_elf(
126        loader,
127        &mut &*tmk,
128        0,
129        0x200000,
130        false,
131        loader::importer::BootPageAcceptance::Exclusive,
132        "tmk",
133    )
134    .context("failed to load tmk")?;
135
136    let start_input = tmk_protocol::StartInput {
137        command: crate::run::COMMAND_ADDRESS,
138        test_index: test.index,
139    };
140
141    let start_input_addr = load_info.next_available_address;
142
143    loader.import_pages(
144        start_input_addr >> 12,
145        1,
146        "start_input",
147        loader::importer::BootPageAcceptance::Exclusive,
148        start_input.as_bytes(),
149    )?;
150
151    Ok(LoadInfo {
152        entrypoint: load_info.entrypoint,
153        param: start_input_addr,
154        next_available_address: start_input_addr + 0x1000,
155    })
156}
157
158struct LoadInfo {
159    entrypoint: u64,
160    param: u64,
161    next_available_address: u64,
162}
163
164#[derive(Clone)]
165pub struct TestInfo {
166    pub name: String,
167    pub index: u64,
168}
169
170/// Enumerate the tests from a TMK binary.
171///
172/// The test definitions are stored as an array of
173/// [`tmk_protocol::TestDescriptor64`] in the "tmk_tests" section of the binary.
174pub fn enumerate_tests(tmk: &File) -> anyhow::Result<Vec<TestInfo>> {
175    let reader = object::ReadCache::new(tmk);
176    let file: object::read::elf::ElfFile64<'_, Endianness, _> =
177        object::read::elf::ElfFile::parse(&reader).context("failed to parse TMK")?;
178
179    let mut relocs = file
180        .dynamic_relocations()
181        .context("failed to find dynamic relocations")?
182        .collect::<Vec<_>>();
183    relocs.sort_by_key(|&(a, _)| a);
184
185    // Relocate address `v` that was loaded from address `addr`.
186    let reloc = |addr, v: u64| {
187        let r = relocs.binary_search_by_key(&addr, |&(a, _)| a);
188        match r {
189            Ok(i) => {
190                let reloc = &relocs[i].1;
191                v.wrapping_add_signed(reloc.addend())
192            }
193            Err(_) => v,
194        }
195    };
196
197    let section = file
198        .section_by_name("tmk_tests")
199        .context("failed to find tmk_tests section")?;
200    let data = section.data()?;
201    let descriptors = <[tmk_protocol::TestDescriptor64]>::ref_from_bytes(data)
202        .ok()
203        .context("failed to parse tmk_tests section")?;
204    let mut tests = Vec::with_capacity(descriptors.len());
205    for (i, t) in descriptors.iter().enumerate() {
206        let name_address = reloc(
207            section.address() + (i * size_of::<tmk_protocol::TestDescriptor64>()) as u64,
208            t.name,
209        );
210        let name = file
211            .segments()
212            .find_map(|s| s.data_range(name_address, t.name_len).transpose())
213            .context("failed to find name for test")?
214            .context("failed to parse tmk")?;
215        let name = core::str::from_utf8(name).context("failed to parse test name")?;
216
217        tests.push(TestInfo {
218            name: name.to_string(),
219            index: i as u64,
220        });
221    }
222
223    Ok(tests)
224}