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