Alioth Code Coverage

xen.rs0.00%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub mod start_info;
16
17use std::ffi::CStr;
18use std::fs::File;
19use std::io::{BufReader, Read, Seek, SeekFrom};
20use std::mem::{offset_of, size_of, size_of_val};
21use std::path::Path;
22
23use snafu::ResultExt;
24use zerocopy::{FromZeros, Immutable, IntoBytes};
25
26use crate::align_up;
27use crate::arch::layout::{
28 APIC_START, BOOT_GDT_START, EBDA_START, HVM_START_INFO_START, KERNEL_CMDLINE_LIMIT,
29 KERNEL_CMDLINE_START,
30};
31use crate::arch::msr::ApicBase;
32use crate::arch::reg::{Cr0, DtReg, DtRegVal, Reg, Rflags, SReg, SegAccess, SegReg, SegRegVal};
33use crate::loader::elf::{
34 ELF_HEADER_MAGIC, ELF_IDENT_CLASS_64, ELF_IDENT_LITTLE_ENDIAN, Elf64Header, Elf64Note,
35 Elf64ProgramHeader, Elf64SectionHeader, PT_NOTE, SHT_NOTE,
36};
37use crate::loader::xen::start_info::{
38 XEN_HVM_MEMMAP_TYPE_ACPI, XEN_HVM_MEMMAP_TYPE_PMEM, XEN_HVM_MEMMAP_TYPE_RAM,
39 XEN_HVM_MEMMAP_TYPE_RESERVED, XEN_HVM_START_INFO_V1, XEN_HVM_START_MAGIC_VALUE,
40};
41use crate::loader::{InitState, Result, error, search_initramfs_address};
42use crate::mem::mapped::RamBus;
43use crate::mem::{MemRegionEntry, MemRegionType};
44
45use self::start_info::{HvmMemmapTableEntry, HvmModlistEntry, HvmStartInfo};
46
47pub const XEN_ELFNOTE_PHYS32_ENTRY: u32 = 18;
48
49#[repr(C)]
50#[derive(Debug, IntoBytes, Default, Immutable)]
51struct StartInfoPage {
52 start_info: HvmStartInfo,
53 initramfs: HvmModlistEntry,
54 memory_map: [HvmMemmapTableEntry; 32],
55}
56
57fn search_pvh_note<F: Read + Seek>(
58 file: &mut F,
59 offset: u64,
60 size: u64,
61 align: u64,
62) -> std::io::Result<Option<u64>> {
63 if align.count_ones() > 1 {
64 return Err(std::io::Error::new(
65 std::io::ErrorKind::InvalidData,
66 format!("align = {align}, not a power of 2"),
67 ));
68 }
69 let align_bits = std::cmp::max(align, 1).trailing_zeros();
70 let mut pos = 0;
71 while pos < size {
72 file.seek(SeekFrom::Start(offset + pos))?;
73 let mut header = Elf64Note::new_zeroed();
74 file.read_exact(header.as_mut_bytes())?;
75 pos += size_of::<Elf64Note>() as u64;
76 pos += align_up!(header.desc_sz as u64, align_bits);
77 pos += align_up!(header.name_sz as u64, align_bits);
78 if header.type_ != XEN_ELFNOTE_PHYS32_ENTRY {
79 continue;
80 }
81 file.seek(SeekFrom::Current(
82 align_up!(header.name_sz as u64, align_bits) as i64,
83 ))?;
84 match header.desc_sz {
85 4 => {
86 let mut entry_point = 0u32;
87 file.read_exact(entry_point.as_mut_bytes())?;
88 return Ok(Some(entry_point as u64));
89 }
90 8 => {
91 let mut entry_point = 0u64;
92 file.read_exact(entry_point.as_mut_bytes())?;
93 return Ok(Some(entry_point));
94 }
95 _ => {}
96 }
97 }
98
99 Ok(None)
100}
101
102// https://xenbits.xen.org/docs/4.18-testing/misc/pvh.html
103pub fn load<P: AsRef<Path>>(
104 memory: &RamBus,
105 mem_regions: &[(u64, MemRegionEntry)],
106 kernel: P,
107 cmdline: Option<&CStr>,
108 initramfs: Option<P>,
109) -> Result<InitState> {
110 let access_kernel = error::AccessFile {
111 path: kernel.as_ref(),
112 };
113 let mut kernel = BufReader::new(File::open(&kernel).context(access_kernel)?);
114
115 // load kernel
116 let mut elf_header = Elf64Header::new_zeroed();
117 kernel
118 .read_exact(elf_header.as_mut_bytes())
119 .context(access_kernel)?;
120 if elf_header.ident_magic != ELF_HEADER_MAGIC {
121 return error::MissingMagic {
122 magic: u32::from_ne_bytes(ELF_HEADER_MAGIC) as u64,
123 found: u32::from_ne_bytes(elf_header.ident_magic) as u64,
124 }
125 .fail();
126 }
127 if elf_header.ident_class != ELF_IDENT_CLASS_64 {
128 return error::Not64Bit.fail();
129 }
130 assert_eq!(elf_header.ident_data, ELF_IDENT_LITTLE_ENDIAN);
131
132 let mut pvh_entry = None;
133
134 kernel
135 .seek(SeekFrom::Start(elf_header.ph_off))
136 .context(access_kernel)?;
137 let mut program_header =
138 Elf64ProgramHeader::new_vec_zeroed(elf_header.ph_num as usize).unwrap();
139 kernel
140 .read_exact(program_header.as_mut_bytes())
141 .context(access_kernel)?;
142 for program_header in program_header.iter() {
143 if program_header.type_ == PT_NOTE && pvh_entry.is_none() {
144 pvh_entry = search_pvh_note(
145 &mut kernel,
146 program_header.offset,
147 program_header.file_sz,
148 program_header.align,
149 )
150 .context(access_kernel)?;
151 }
152 if program_header.file_sz > 0 {
153 let addr = program_header.paddr;
154 let size = program_header.file_sz;
155 kernel
156 .seek(SeekFrom::Start(program_header.offset))
157 .context(access_kernel)?;
158 memory.write_range(addr, size, &mut kernel)?;
159 log::info!("loaded at {:#x?}-{:#x?}", addr, addr + size);
160 }
161 }
162
163 if pvh_entry.is_none() && elf_header.sh_num > 0 {
164 kernel
165 .seek(SeekFrom::Start(elf_header.sh_off))
166 .context(access_kernel)?;
167 let mut sections = Elf64SectionHeader::new_vec_zeroed(elf_header.sh_num as usize).unwrap();
168 kernel
169 .read_exact(sections.as_mut_bytes())
170 .context(access_kernel)?;
171 for section in sections.iter() {
172 if section.type_ != SHT_NOTE {
173 continue;
174 }
175 pvh_entry = search_pvh_note(
176 &mut kernel,
177 section.offset,
178 section.size,
179 section.addr_align,
180 )
181 .context(access_kernel)?;
182 if pvh_entry.is_some() {
183 break;
184 }
185 }
186 }
187
188 let Some(entry_point) = pvh_entry else {
189 return error::NoEntryPoint.fail();
190 };
191 log::info!("PVH entry = {entry_point:#x?}");
192
193 let mut start_info_page = StartInfoPage {
194 start_info: HvmStartInfo {
195 magic: XEN_HVM_START_MAGIC_VALUE,
196 version: XEN_HVM_START_INFO_V1,
197 cmdline_paddr: KERNEL_CMDLINE_START,
198 rsdp_paddr: EBDA_START,
199 ..Default::default()
200 },
201 ..Default::default()
202 };
203
204 // load cmd line
205 if let Some(cmdline) = cmdline {
206 let cmdline = cmdline.to_bytes_with_nul();
207 if cmdline.len() as u64 > KERNEL_CMDLINE_LIMIT {
208 return error::CmdLineTooLong {
209 len: cmdline.len(),
210 limit: KERNEL_CMDLINE_LIMIT,
211 }
212 .fail();
213 }
214 memory.write_range(KERNEL_CMDLINE_START, cmdline.len() as u64, cmdline)?;
215 start_info_page.start_info.cmdline_paddr = KERNEL_CMDLINE_START;
216 }
217
218 // load initramfs
219 let initramfs_range;
220 if let Some(initramfs) = initramfs {
221 let access_initramfs = error::AccessFile {
222 path: initramfs.as_ref(),
223 };
224 let initramfs = File::open(&initramfs).context(access_initramfs)?;
225 let initramfs_size = initramfs.metadata().context(access_initramfs)?.len();
226 let initramfs_gpa = search_initramfs_address(mem_regions, initramfs_size, 2 << 30)?;
227 let initramfs_end = initramfs_gpa + initramfs_size;
228 memory.write_range(initramfs_gpa, initramfs_size, initramfs)?;
229 start_info_page.start_info.nr_modules = 1;
230 start_info_page.start_info.modlist_paddr =
231 HVM_START_INFO_START + offset_of!(StartInfoPage, initramfs) as u64;
232 start_info_page.initramfs.paddr = initramfs_gpa as u64;
233 start_info_page.initramfs.size = initramfs_size;
234 log::info!(
235 "initramfs loaded at {:#x} - {:#x}, ",
236 initramfs_gpa,
237 initramfs_end - 1
238 );
239 initramfs_range = Some(initramfs_gpa..initramfs_end);
240 } else {
241 initramfs_range = None;
242 }
243
244 // setup memory mapping table
245 let mut index = 0;
246 for (addr, region) in mem_regions.iter() {
247 let type_ = match region.type_ {
248 MemRegionType::Ram => XEN_HVM_MEMMAP_TYPE_RAM,
249 MemRegionType::Reserved => XEN_HVM_MEMMAP_TYPE_RESERVED,
250 MemRegionType::Acpi => XEN_HVM_MEMMAP_TYPE_ACPI,
251 MemRegionType::Pmem => XEN_HVM_MEMMAP_TYPE_PMEM,
252 MemRegionType::Hidden => continue,
253 };
254 start_info_page.memory_map[index] = HvmMemmapTableEntry {
255 addr: *addr,
256 size: region.size,
257 type_,
258 reserved: 0,
259 };
260 index += 1;
261 }
262 start_info_page.start_info.memmap_entries = index as u32;
263 start_info_page.start_info.memmap_paddr =
264 HVM_START_INFO_START + offset_of!(StartInfoPage, memory_map) as u64;
265
266 memory.write_t(HVM_START_INFO_START, &start_info_page)?;
267
268 // set up gdt
269 let boot_cs = SegRegVal {
270 selector: 0x10,
271 base: 0,
272 limit: 0xfff_ffff,
273 access: SegAccess(0xc09b),
274 };
275 let boot_ds = SegRegVal {
276 selector: 0x18,
277 base: 0,
278 limit: 0xfff_ffff,
279 access: SegAccess(0xc093),
280 };
281 let boot_tr = SegRegVal {
282 selector: 0x20,
283 base: 0,
284 limit: 0x67,
285 access: SegAccess(0x8b),
286 };
287 let boot_ldtr = SegRegVal {
288 selector: 0x28,
289 base: 0,
290 limit: 0,
291 access: SegAccess(0x82),
292 };
293 let gdt = [
294 0,
295 0,
296 boot_cs.to_desc(),
297 boot_ds.to_desc(),
298 boot_tr.to_desc(),
299 boot_ldtr.to_desc(),
300 ];
301 let gdtr = DtRegVal {
302 base: BOOT_GDT_START,
303 limit: size_of_val(&gdt) as u16 - 1,
304 };
305 memory.write_t(BOOT_GDT_START, &gdt)?;
306
307 let idtr = DtRegVal { base: 0, limit: 0 };
308
309 let mut apic_base = ApicBase(APIC_START);
310 apic_base.set_bsp(true);
311 apic_base.set_xapic(true);
312 apic_base.set_x2apic(true);
313
314 Ok(InitState {
315 regs: vec![
316 (Reg::Rbx, HVM_START_INFO_START),
317 (Reg::Rflags, Rflags::RESERVED_1.bits() as u64),
318 (Reg::Rip, entry_point),
319 ],
320 sregs: vec![
321 (SReg::Cr0, Cr0::PE.bits() as u64),
322 (SReg::Cr4, 0),
323 (SReg::Efer, 0),
324 (SReg::ApicBase, apic_base.0),
325 ],
326 seg_regs: vec![
327 (SegReg::Cs, boot_cs),
328 (SegReg::Ds, boot_ds),
329 (SegReg::Es, boot_ds),
330 (SegReg::Fs, boot_ds),
331 (SegReg::Gs, boot_ds),
332 (SegReg::Ss, boot_ds),
333 (SegReg::Tr, boot_tr),
334 (SegReg::Ldtr, boot_ldtr),
335 ],
336 dt_regs: vec![(DtReg::Gdtr, gdtr), (DtReg::Idtr, idtr)],
337 initramfs: initramfs_range,
338 })
339}
340