linux_x86_64.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
use std::ffi::CStr;16
use std::fs::File;17
use std::io::{BufReader, Read, Seek, SeekFrom};18
use std::mem::{size_of, size_of_val};19
use std::path::Path;20
21
use snafu::ResultExt;22
use zerocopy::{FromZeros, IntoBytes};23
24
use crate::arch::layout::{25
APIC_START, BOOT_GDT_START, BOOT_PAGING_START, EBDA_START, KERNEL_CMDLINE_LIMIT,26
KERNEL_CMDLINE_START, KERNEL_IMAGE_START, LINUX_BOOT_PARAMS_START,27
};28
use crate::arch::msr::{ApicBase, Efer};29
use crate::arch::paging::Entry;30
use crate::arch::reg::{31
Cr0, Cr4, DtReg, DtRegVal, Reg, Rflags, SReg, SegAccess, SegReg, SegRegVal,32
};33
use crate::mem::mapped::RamBus;34
use crate::mem::{MemRegionEntry, MemRegionType};35
36
use crate::loader::linux::bootparams::{37
BootE820Entry, BootParams, E820_ACPI, E820_PMEM, E820_RAM, E820_RESERVED, MAGIC_AA55,38
MAGIC_HDRS, SETUP_HEADER_OFFSET, XLoadFlags,39
};40
use crate::loader::{Error, InitState, error, search_initramfs_address};41
42
// loading bzImage and ramdisk above 4G in 64bit.43
const MINIMAL_VERSION: u16 = 0x020c;44
45
pub fn load<P: AsRef<Path>>(46
memory: &RamBus,47
mem_regions: &[(u64, MemRegionEntry)],48
kernel: P,49
cmdline: Option<&CStr>,50
initramfs: Option<P>,51
) -> Result<InitState, Error> {52
let mut boot_params = BootParams::new_zeroed();53
let access_kernel = error::AccessFile {54
path: kernel.as_ref(),55
};56
let kernel = File::open(&kernel).context(access_kernel)?;57
let kernel_meta = kernel.metadata().context(access_kernel)?;58
let mut kernel = BufReader::new(kernel);59
60
kernel61
.seek(SeekFrom::Start(SETUP_HEADER_OFFSET))62
.context(access_kernel)?;63
kernel64
.read_exact(boot_params.hdr.as_mut_bytes())65
.context(access_kernel)?;66
67
// For backwards compatibility, if the setup_sects field contains 0,68
// the real value is 4.69
if boot_params.hdr.setup_sects == 0 {70
boot_params.hdr.setup_sects = 4;71
}72
73
if boot_params.hdr.boot_flag != MAGIC_AA55 {74
return error::MissingMagic {75
magic: MAGIC_AA55 as u64,76
found: boot_params.hdr.boot_flag as u64,77
}78
.fail();79
}80
if boot_params.hdr.header != MAGIC_HDRS {81
return error::MissingMagic {82
magic: MAGIC_HDRS as u64,83
found: boot_params.hdr.header as u64,84
}85
.fail();86
}87
if boot_params.hdr.version < MINIMAL_VERSION {88
return error::TooOld {89
name: "bzimage",90
min: MINIMAL_VERSION as u64,91
found: boot_params.hdr.version as u64,92
}93
.fail();94
}95
if !XLoadFlags::from_bits_retain(boot_params.hdr.xloadflags).contains(XLoadFlags::XLF_KERNEL_64)96
{97
return error::Not64Bit.fail();98
}99
if boot_params.hdr.relocatable_kernel == 0 {100
return error::NotRelocatable.fail();101
}102
103
boot_params.hdr.type_of_loader = 0xff;104
105
// load cmd line106
if let Some(cmdline) = cmdline {107
let cmdline = cmdline.to_bytes_with_nul();108
let cmdline_limit =109
std::cmp::min(boot_params.hdr.cmdline_size as u64, KERNEL_CMDLINE_LIMIT);110
if cmdline.len() as u64 > cmdline_limit {111
return error::CmdLineTooLong {112
len: cmdline.len(),113
limit: cmdline_limit,114
}115
.fail();116
}117
memory.write_range(KERNEL_CMDLINE_START, cmdline.len() as u64, cmdline)?;118
boot_params.hdr.cmdline_ptr = KERNEL_CMDLINE_START as u32;119
boot_params.ext_cmdline_ptr = (KERNEL_CMDLINE_START >> 32) as u32;120
}121
122
// load kernel image123
let kernel_offset = (boot_params.hdr.setup_sects as u64 + 1) * 512;124
kernel125
.seek(SeekFrom::Start(kernel_offset))126
.context(access_kernel)?;127
let kernel_size = kernel_meta.len() - kernel_offset;128
memory.write_range(KERNEL_IMAGE_START, kernel_size, kernel)?;129
130
// load initramfs131
let initramfs_range;132
if let Some(initramfs) = initramfs {133
let access_initramfs = error::AccessFile {134
path: initramfs.as_ref(),135
};136
let initramfs = File::open(&initramfs).context(access_initramfs)?;137
let initramfs_size = initramfs.metadata().context(access_initramfs)?.len();138
let initramfs_gpa = search_initramfs_address(139
mem_regions,140
initramfs_size,141
boot_params.hdr.initrd_addr_max as u64,142
)?;143
let initramfs_end = initramfs_gpa + initramfs_size;144
memory.write_range(initramfs_gpa, initramfs_size, initramfs)?;145
boot_params.hdr.ramdisk_image = initramfs_gpa as u32;146
boot_params.ext_ramdisk_image = (initramfs_gpa >> 32) as u32;147
boot_params.hdr.ramdisk_size = initramfs_size as u32;148
boot_params.ext_ramdisk_size = (initramfs_size >> 32) as u32;149
log::info!(150
"initramfs loaded at {:#x} - {:#x}, ",151
initramfs_gpa,152
initramfs_end - 1,153
);154
initramfs_range = Some(initramfs_gpa..initramfs_end);155
} else {156
initramfs_range = None;157
}158
159
// setup e820 table160
let mut region_index = 0;161
for (addr, region) in mem_regions.iter() {162
let type_ = match region.type_ {163
MemRegionType::Ram => E820_RAM,164
MemRegionType::Reserved => E820_RESERVED,165
MemRegionType::Acpi => E820_ACPI,166
MemRegionType::Pmem => E820_PMEM,167
MemRegionType::Hidden => continue,168
};169
boot_params.e820_table[region_index] = BootE820Entry {170
addr: *addr,171
size: region.size,172
type_,173
};174
region_index += 1;175
}176
boot_params.e820_entries = mem_regions.len() as u8;177
178
boot_params.acpi_rsdp_addr = EBDA_START;179
180
memory.write_t(LINUX_BOOT_PARAMS_START, &boot_params)?;181
182
// set up identity paging183
let pml4_start = BOOT_PAGING_START;184
let pdpt_start = pml4_start + 0x1000;185
let pml4e = (Entry::P | Entry::RW).bits() as u64 | pdpt_start;186
memory.write_t(pml4_start, &pml4e)?;187
let alignment = boot_params.hdr.kernel_alignment as u64;188
let runtime_start = (KERNEL_IMAGE_START + alignment - 1) & !(alignment - 1);189
let max_addr = std::cmp::max(190
runtime_start + boot_params.hdr.init_size as u64,191
std::cmp::max(192
LINUX_BOOT_PARAMS_START + size_of::<BootParams>() as u64,193
KERNEL_CMDLINE_START + KERNEL_CMDLINE_LIMIT,194
),195
);196
let num_page = (max_addr + (1 << 30) - 1) >> 30;197
for i in 0..num_page {198
let pdpte = (i << 30) | (Entry::P | Entry::RW | Entry::PS).bits() as u64;199
memory.write_t(pdpt_start + i * size_of::<u64>() as u64, &pdpte)?;200
}201
202
// set up gdt203
let boot_cs = SegRegVal {204
selector: 0x10,205
base: 0,206
limit: 0xfff_ffff,207
access: SegAccess(0xa09b),208
};209
let boot_ds = SegRegVal {210
selector: 0x18,211
base: 0,212
limit: 0xfff_ffff,213
access: SegAccess(0xc093),214
};215
let boot_tr = SegRegVal {216
selector: 0x20,217
base: 0,218
limit: 0,219
access: SegAccess(0x8b),220
};221
let boot_ldtr = SegRegVal {222
selector: 0x28,223
base: 0,224
limit: 0,225
access: SegAccess(0x82),226
};227
let gdt = [228
0,229
0,230
boot_cs.to_desc(),231
boot_ds.to_desc(),232
boot_tr.to_desc(),233
boot_ldtr.to_desc(),234
];235
let gdtr = DtRegVal {236
base: BOOT_GDT_START,237
limit: size_of_val(&gdt) as u16 - 1,238
};239
let idtr = DtRegVal { base: 0, limit: 0 };240
memory.write_t(BOOT_GDT_START, &gdt)?;241
242
let mut apic_base = ApicBase(APIC_START);243
apic_base.set_bsp(true);244
apic_base.set_xapic(true);245
apic_base.set_x2apic(true);246
247
Ok(InitState {248
regs: vec![249
(Reg::Rsi, LINUX_BOOT_PARAMS_START),250
(Reg::Rip, KERNEL_IMAGE_START + 0x200),251
(Reg::Rflags, Rflags::RESERVED_1.bits() as u64),252
],253
sregs: vec![254
(SReg::Efer, (Efer::LMA | Efer::LME).bits() as u64),255
(SReg::Cr0, (Cr0::NE | Cr0::PE | Cr0::PG).bits() as u64),256
(SReg::Cr3, pml4_start),257
(SReg::Cr4, Cr4::PAE.bits() as u64),258
(SReg::ApicBase, apic_base.0),259
],260
seg_regs: vec![261
(SegReg::Cs, boot_cs),262
(SegReg::Ds, boot_ds),263
(SegReg::Es, boot_ds),264
(SegReg::Fs, boot_ds),265
(SegReg::Gs, boot_ds),266
(SegReg::Ss, boot_ds),267
(SegReg::Tr, boot_tr),268
(SegReg::Ldtr, boot_ldtr),269
],270
dt_regs: vec![(DtReg::Gdtr, gdtr), (DtReg::Idtr, idtr)],271
initramfs: initramfs_range,272
})273
}274