1use 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#[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
170pub 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 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}