xen.rs0.00%
1
// Copyright 2024 Google LLC2
//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 at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// 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 and13
// limitations under the License.14
15
pub mod start_info;16
17
use std::ffi::CStr;18
use std::fs::File;19
use std::io::{BufReader, Read, Seek, SeekFrom};20
use std::mem::{offset_of, size_of, size_of_val};21
use std::path::Path;22
23
use snafu::ResultExt;24
use zerocopy::{FromZeros, Immutable, IntoBytes};25
26
use crate::align_up;27
use crate::arch::layout::{28
APIC_START, BOOT_GDT_START, EBDA_START, HVM_START_INFO_START, KERNEL_CMDLINE_LIMIT,29
KERNEL_CMDLINE_START,30
};31
use crate::arch::msr::ApicBase;32
use crate::arch::reg::{Cr0, DtReg, DtRegVal, Reg, Rflags, SReg, SegAccess, SegReg, SegRegVal};33
use 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
};37
use 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
};41
use crate::loader::{InitState, Result, error, search_initramfs_address};42
use crate::mem::mapped::RamBus;43
use crate::mem::{MemRegionEntry, MemRegionType};44
45
use self::start_info::{HvmMemmapTableEntry, HvmModlistEntry, HvmStartInfo};46
47
pub const XEN_ELFNOTE_PHYS32_ENTRY: u32 = 18;48
49
#[repr(C)]50
#[derive(Debug, IntoBytes, Default, Immutable)]51
struct StartInfoPage {52
start_info: HvmStartInfo,53
initramfs: HvmModlistEntry,54
memory_map: [HvmMemmapTableEntry; 32],55
}56
57
fn 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.html103
pub 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 kernel116
let mut elf_header = Elf64Header::new_zeroed();117
kernel118
.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
kernel135
.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
kernel140
.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
kernel156
.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
kernel165
.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
kernel169
.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 line205
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 initramfs219
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 - 1238
);239
initramfs_range = Some(initramfs_gpa..initramfs_end);240
} else {241
initramfs_range = None;242
}243
244
// setup memory mapping table245
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 gdt269
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