Skip to main content

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