xref: /aosp_15_r20/external/crosvm/x86_64/src/bzimage.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2019 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! Loader for bzImage-format Linux kernels as described in
6*bb4ee6a4SAndroid Build Coastguard Worker //! <https://www.kernel.org/doc/Documentation/x86/boot.txt>
7*bb4ee6a4SAndroid Build Coastguard Worker 
8*bb4ee6a4SAndroid Build Coastguard Worker use std::cmp::Ordering;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::io;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::mem::offset_of;
11*bb4ee6a4SAndroid Build Coastguard Worker 
12*bb4ee6a4SAndroid Build Coastguard Worker use base::debug;
13*bb4ee6a4SAndroid Build Coastguard Worker use base::FileGetLen;
14*bb4ee6a4SAndroid Build Coastguard Worker use base::FileReadWriteAtVolatile;
15*bb4ee6a4SAndroid Build Coastguard Worker use base::VolatileSlice;
16*bb4ee6a4SAndroid Build Coastguard Worker use remain::sorted;
17*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error;
18*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestAddress;
19*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemory;
20*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemoryError;
21*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::AsBytes;
22*bb4ee6a4SAndroid Build Coastguard Worker 
23*bb4ee6a4SAndroid Build Coastguard Worker use crate::bootparam::boot_params;
24*bb4ee6a4SAndroid Build Coastguard Worker use crate::bootparam::XLF_KERNEL_64;
25*bb4ee6a4SAndroid Build Coastguard Worker use crate::CpuMode;
26*bb4ee6a4SAndroid Build Coastguard Worker use crate::KERNEL_32BIT_ENTRY_OFFSET;
27*bb4ee6a4SAndroid Build Coastguard Worker use crate::KERNEL_64BIT_ENTRY_OFFSET;
28*bb4ee6a4SAndroid Build Coastguard Worker 
29*bb4ee6a4SAndroid Build Coastguard Worker #[sorted]
30*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Error, Debug)]
31*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error {
32*bb4ee6a4SAndroid Build Coastguard Worker     #[error("bad kernel header signature")]
33*bb4ee6a4SAndroid Build Coastguard Worker     BadSignature,
34*bb4ee6a4SAndroid Build Coastguard Worker     #[error("entry point out of range")]
35*bb4ee6a4SAndroid Build Coastguard Worker     EntryPointOutOfRange,
36*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unable to get kernel file size: {0}")]
37*bb4ee6a4SAndroid Build Coastguard Worker     GetFileLen(io::Error),
38*bb4ee6a4SAndroid Build Coastguard Worker     #[error("guest memory error {0}")]
39*bb4ee6a4SAndroid Build Coastguard Worker     GuestMemoryError(GuestMemoryError),
40*bb4ee6a4SAndroid Build Coastguard Worker     #[error("invalid setup_header_end value {0}")]
41*bb4ee6a4SAndroid Build Coastguard Worker     InvalidSetupHeaderEnd(usize),
42*bb4ee6a4SAndroid Build Coastguard Worker     #[error("invalid setup_sects value {0}")]
43*bb4ee6a4SAndroid Build Coastguard Worker     InvalidSetupSects(u8),
44*bb4ee6a4SAndroid Build Coastguard Worker     #[error("invalid syssize value {0}")]
45*bb4ee6a4SAndroid Build Coastguard Worker     InvalidSysSize(u32),
46*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unable to read boot_params: {0}")]
47*bb4ee6a4SAndroid Build Coastguard Worker     ReadBootParams(io::Error),
48*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unable to read header size: {0}")]
49*bb4ee6a4SAndroid Build Coastguard Worker     ReadHeaderSize(io::Error),
50*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unable to read kernel image: {0}")]
51*bb4ee6a4SAndroid Build Coastguard Worker     ReadKernelImage(io::Error),
52*bb4ee6a4SAndroid Build Coastguard Worker }
53*bb4ee6a4SAndroid Build Coastguard Worker 
54*bb4ee6a4SAndroid Build Coastguard Worker pub type Result<T> = std::result::Result<T, Error>;
55*bb4ee6a4SAndroid Build Coastguard Worker 
56*bb4ee6a4SAndroid Build Coastguard Worker /// Loads a kernel from a bzImage to a slice
57*bb4ee6a4SAndroid Build Coastguard Worker ///
58*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments
59*bb4ee6a4SAndroid Build Coastguard Worker ///
60*bb4ee6a4SAndroid Build Coastguard Worker /// * `guest_mem` - The guest memory region the kernel is written to.
61*bb4ee6a4SAndroid Build Coastguard Worker /// * `kernel_start` - The offset into `guest_mem` at which to load the kernel.
62*bb4ee6a4SAndroid Build Coastguard Worker /// * `kernel_image` - Input bzImage.
load_bzimage<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, ) -> Result<(boot_params, u64, GuestAddress, CpuMode)> where F: FileReadWriteAtVolatile + FileGetLen,63*bb4ee6a4SAndroid Build Coastguard Worker pub fn load_bzimage<F>(
64*bb4ee6a4SAndroid Build Coastguard Worker     guest_mem: &GuestMemory,
65*bb4ee6a4SAndroid Build Coastguard Worker     kernel_start: GuestAddress,
66*bb4ee6a4SAndroid Build Coastguard Worker     kernel_image: &mut F,
67*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<(boot_params, u64, GuestAddress, CpuMode)>
68*bb4ee6a4SAndroid Build Coastguard Worker where
69*bb4ee6a4SAndroid Build Coastguard Worker     F: FileReadWriteAtVolatile + FileGetLen,
70*bb4ee6a4SAndroid Build Coastguard Worker {
71*bb4ee6a4SAndroid Build Coastguard Worker     let mut params = boot_params::default();
72*bb4ee6a4SAndroid Build Coastguard Worker 
73*bb4ee6a4SAndroid Build Coastguard Worker     // The start of setup header is defined by its offset within boot_params (0x01f1).
74*bb4ee6a4SAndroid Build Coastguard Worker     let setup_header_start = offset_of!(boot_params, hdr);
75*bb4ee6a4SAndroid Build Coastguard Worker 
76*bb4ee6a4SAndroid Build Coastguard Worker     // Per x86 Linux 64-bit boot protocol:
77*bb4ee6a4SAndroid Build Coastguard Worker     // "The end of setup header can be calculated as follows: 0x0202 + byte value at offset 0x0201"
78*bb4ee6a4SAndroid Build Coastguard Worker     let mut setup_size_byte = 0u8;
79*bb4ee6a4SAndroid Build Coastguard Worker     kernel_image
80*bb4ee6a4SAndroid Build Coastguard Worker         .read_exact_at_volatile(
81*bb4ee6a4SAndroid Build Coastguard Worker             VolatileSlice::new(std::slice::from_mut(&mut setup_size_byte)),
82*bb4ee6a4SAndroid Build Coastguard Worker             0x0201,
83*bb4ee6a4SAndroid Build Coastguard Worker         )
84*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::ReadHeaderSize)?;
85*bb4ee6a4SAndroid Build Coastguard Worker     let setup_header_end = 0x0202 + usize::from(setup_size_byte);
86*bb4ee6a4SAndroid Build Coastguard Worker 
87*bb4ee6a4SAndroid Build Coastguard Worker     debug!(
88*bb4ee6a4SAndroid Build Coastguard Worker         "setup_header file offset range: 0x{:04x}..0x{:04x}",
89*bb4ee6a4SAndroid Build Coastguard Worker         setup_header_start, setup_header_end,
90*bb4ee6a4SAndroid Build Coastguard Worker     );
91*bb4ee6a4SAndroid Build Coastguard Worker 
92*bb4ee6a4SAndroid Build Coastguard Worker     // Read `setup_header` into `boot_params`. The bzImage may have a different size of
93*bb4ee6a4SAndroid Build Coastguard Worker     // `setup_header`, so read directly into a byte slice of the outer `boot_params` structure
94*bb4ee6a4SAndroid Build Coastguard Worker     // rather than reading into `params.hdr`. The bounds check in `.get_mut()` will ensure we do not
95*bb4ee6a4SAndroid Build Coastguard Worker     // read beyond the end of `boot_params`.
96*bb4ee6a4SAndroid Build Coastguard Worker     let setup_header_slice = params
97*bb4ee6a4SAndroid Build Coastguard Worker         .as_bytes_mut()
98*bb4ee6a4SAndroid Build Coastguard Worker         .get_mut(setup_header_start..setup_header_end)
99*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or(Error::InvalidSetupHeaderEnd(setup_header_end))?;
100*bb4ee6a4SAndroid Build Coastguard Worker 
101*bb4ee6a4SAndroid Build Coastguard Worker     kernel_image
102*bb4ee6a4SAndroid Build Coastguard Worker         .read_exact_at_volatile(
103*bb4ee6a4SAndroid Build Coastguard Worker             VolatileSlice::new(setup_header_slice),
104*bb4ee6a4SAndroid Build Coastguard Worker             setup_header_start as u64,
105*bb4ee6a4SAndroid Build Coastguard Worker         )
106*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::ReadBootParams)?;
107*bb4ee6a4SAndroid Build Coastguard Worker 
108*bb4ee6a4SAndroid Build Coastguard Worker     // bzImage header signature "HdrS"
109*bb4ee6a4SAndroid Build Coastguard Worker     if params.hdr.header != 0x53726448 {
110*bb4ee6a4SAndroid Build Coastguard Worker         return Err(Error::BadSignature);
111*bb4ee6a4SAndroid Build Coastguard Worker     }
112*bb4ee6a4SAndroid Build Coastguard Worker 
113*bb4ee6a4SAndroid Build Coastguard Worker     let setup_sects = if params.hdr.setup_sects == 0 {
114*bb4ee6a4SAndroid Build Coastguard Worker         4u64
115*bb4ee6a4SAndroid Build Coastguard Worker     } else {
116*bb4ee6a4SAndroid Build Coastguard Worker         params.hdr.setup_sects as u64
117*bb4ee6a4SAndroid Build Coastguard Worker     };
118*bb4ee6a4SAndroid Build Coastguard Worker 
119*bb4ee6a4SAndroid Build Coastguard Worker     let kernel_offset = setup_sects
120*bb4ee6a4SAndroid Build Coastguard Worker         .checked_add(1)
121*bb4ee6a4SAndroid Build Coastguard Worker         .and_then(|sectors| sectors.checked_mul(512))
122*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or(Error::InvalidSetupSects(params.hdr.setup_sects))?;
123*bb4ee6a4SAndroid Build Coastguard Worker     let kernel_size = (params.hdr.syssize as usize)
124*bb4ee6a4SAndroid Build Coastguard Worker         .checked_mul(16)
125*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or(Error::InvalidSysSize(params.hdr.syssize))?;
126*bb4ee6a4SAndroid Build Coastguard Worker 
127*bb4ee6a4SAndroid Build Coastguard Worker     let file_size = kernel_image.get_len().map_err(Error::GetFileLen)?;
128*bb4ee6a4SAndroid Build Coastguard Worker     let load_size = file_size
129*bb4ee6a4SAndroid Build Coastguard Worker         .checked_sub(kernel_offset)
130*bb4ee6a4SAndroid Build Coastguard Worker         .and_then(|n| usize::try_from(n).ok())
131*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or(Error::InvalidSetupSects(params.hdr.setup_sects))?;
132*bb4ee6a4SAndroid Build Coastguard Worker 
133*bb4ee6a4SAndroid Build Coastguard Worker     match kernel_size.cmp(&load_size) {
134*bb4ee6a4SAndroid Build Coastguard Worker         Ordering::Greater => {
135*bb4ee6a4SAndroid Build Coastguard Worker             // `syssize` from header was larger than the actual file.
136*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::InvalidSysSize(params.hdr.syssize));
137*bb4ee6a4SAndroid Build Coastguard Worker         }
138*bb4ee6a4SAndroid Build Coastguard Worker         Ordering::Less => {
139*bb4ee6a4SAndroid Build Coastguard Worker             debug!(
140*bb4ee6a4SAndroid Build Coastguard Worker                 "loading {} extra bytes appended to bzImage",
141*bb4ee6a4SAndroid Build Coastguard Worker                 load_size - kernel_size
142*bb4ee6a4SAndroid Build Coastguard Worker             );
143*bb4ee6a4SAndroid Build Coastguard Worker         }
144*bb4ee6a4SAndroid Build Coastguard Worker         Ordering::Equal => {}
145*bb4ee6a4SAndroid Build Coastguard Worker     }
146*bb4ee6a4SAndroid Build Coastguard Worker 
147*bb4ee6a4SAndroid Build Coastguard Worker     // Load the whole kernel image to kernel_start
148*bb4ee6a4SAndroid Build Coastguard Worker     let guest_slice = guest_mem
149*bb4ee6a4SAndroid Build Coastguard Worker         .get_slice_at_addr(kernel_start, load_size)
150*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::GuestMemoryError)?;
151*bb4ee6a4SAndroid Build Coastguard Worker     kernel_image
152*bb4ee6a4SAndroid Build Coastguard Worker         .read_exact_at_volatile(guest_slice, kernel_offset)
153*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::ReadKernelImage)?;
154*bb4ee6a4SAndroid Build Coastguard Worker 
155*bb4ee6a4SAndroid Build Coastguard Worker     let (entry_offset, cpu_mode) = if params.hdr.xloadflags & XLF_KERNEL_64 != 0 {
156*bb4ee6a4SAndroid Build Coastguard Worker         (KERNEL_64BIT_ENTRY_OFFSET, CpuMode::LongMode)
157*bb4ee6a4SAndroid Build Coastguard Worker     } else {
158*bb4ee6a4SAndroid Build Coastguard Worker         (KERNEL_32BIT_ENTRY_OFFSET, CpuMode::FlatProtectedMode)
159*bb4ee6a4SAndroid Build Coastguard Worker     };
160*bb4ee6a4SAndroid Build Coastguard Worker 
161*bb4ee6a4SAndroid Build Coastguard Worker     let bzimage_entry = guest_mem
162*bb4ee6a4SAndroid Build Coastguard Worker         .checked_offset(kernel_start, entry_offset)
163*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or(Error::EntryPointOutOfRange)?;
164*bb4ee6a4SAndroid Build Coastguard Worker 
165*bb4ee6a4SAndroid Build Coastguard Worker     Ok((
166*bb4ee6a4SAndroid Build Coastguard Worker         params,
167*bb4ee6a4SAndroid Build Coastguard Worker         kernel_start.offset() + load_size as u64,
168*bb4ee6a4SAndroid Build Coastguard Worker         bzimage_entry,
169*bb4ee6a4SAndroid Build Coastguard Worker         cpu_mode,
170*bb4ee6a4SAndroid Build Coastguard Worker     ))
171*bb4ee6a4SAndroid Build Coastguard Worker }
172