Alioth Code Coverage

linux_x86_64.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
15use std::ffi::CStr;
16use std::fs::File;
17use std::io::{BufReader, Read, Seek, SeekFrom};
18use std::mem::{size_of, size_of_val};
19use std::path::Path;
20
21use snafu::ResultExt;
22use zerocopy::{FromZeros, IntoBytes};
23
24use 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};
28use crate::arch::msr::{ApicBase, Efer};
29use crate::arch::paging::Entry;
30use crate::arch::reg::{
31 Cr0, Cr4, DtReg, DtRegVal, Reg, Rflags, SReg, SegAccess, SegReg, SegRegVal,
32};
33use crate::mem::mapped::RamBus;
34use crate::mem::{MemRegionEntry, MemRegionType};
35
36use 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};
40use crate::loader::{Error, InitState, error, search_initramfs_address};
41
42// loading bzImage and ramdisk above 4G in 64bit.
43const MINIMAL_VERSION: u16 = 0x020c;
44
45pub 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 kernel
61 .seek(SeekFrom::Start(SETUP_HEADER_OFFSET))
62 .context(access_kernel)?;
63 kernel
64 .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 line
106 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 image
123 let kernel_offset = (boot_params.hdr.setup_sects as u64 + 1) * 512;
124 kernel
125 .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 initramfs
131 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 table
160 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 paging
183 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 gdt
203 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