xref: /aosp_15_r20/external/crosvm/kernel_loader/src/multiboot.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2024 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 //! Multiboot kernel loader
6*bb4ee6a4SAndroid Build Coastguard Worker //!
7*bb4ee6a4SAndroid Build Coastguard Worker //! Only Multiboot (version 0.6.96) is supported, not Multiboot2.
8*bb4ee6a4SAndroid Build Coastguard Worker 
9*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::mem::size_of;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::num::NonZeroU32;
12*bb4ee6a4SAndroid Build Coastguard Worker 
13*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
14*bb4ee6a4SAndroid Build Coastguard Worker use base::trace;
15*bb4ee6a4SAndroid Build Coastguard Worker use base::FileReadWriteAtVolatile;
16*bb4ee6a4SAndroid Build Coastguard Worker use base::VolatileSlice;
17*bb4ee6a4SAndroid Build Coastguard Worker use resources::AddressRange;
18*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestAddress;
19*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemory;
20*bb4ee6a4SAndroid Build Coastguard Worker 
21*bb4ee6a4SAndroid Build Coastguard Worker use crate::Error;
22*bb4ee6a4SAndroid Build Coastguard Worker use crate::LoadedKernel;
23*bb4ee6a4SAndroid Build Coastguard Worker use crate::Result;
24*bb4ee6a4SAndroid Build Coastguard Worker 
25*bb4ee6a4SAndroid Build Coastguard Worker /// Multiboot header retrieved from a kernel image.
26*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Debug)]
27*bb4ee6a4SAndroid Build Coastguard Worker pub struct MultibootKernel {
28*bb4ee6a4SAndroid Build Coastguard Worker     /// Byte offset of the beginning of the multiboot header in the kernel image.
29*bb4ee6a4SAndroid Build Coastguard Worker     pub offset: u32,
30*bb4ee6a4SAndroid Build Coastguard Worker 
31*bb4ee6a4SAndroid Build Coastguard Worker     /// Kernel requires that boot modules are aligned to 4 KB.
32*bb4ee6a4SAndroid Build Coastguard Worker     pub boot_modules_page_aligned: bool,
33*bb4ee6a4SAndroid Build Coastguard Worker 
34*bb4ee6a4SAndroid Build Coastguard Worker     /// Kernel requires available memory information (`mem_*` fields).
35*bb4ee6a4SAndroid Build Coastguard Worker     pub need_available_memory: bool,
36*bb4ee6a4SAndroid Build Coastguard Worker 
37*bb4ee6a4SAndroid Build Coastguard Worker     /// Kernel load address.
38*bb4ee6a4SAndroid Build Coastguard Worker     ///
39*bb4ee6a4SAndroid Build Coastguard Worker     /// If present, this overrides any other executable format headers (e.g. ELF).
40*bb4ee6a4SAndroid Build Coastguard Worker     pub load: Option<MultibootLoad>,
41*bb4ee6a4SAndroid Build Coastguard Worker 
42*bb4ee6a4SAndroid Build Coastguard Worker     /// Kernel preferred video mode.
43*bb4ee6a4SAndroid Build Coastguard Worker     ///
44*bb4ee6a4SAndroid Build Coastguard Worker     /// If present, the kernel also requires information about the video mode table.
45*bb4ee6a4SAndroid Build Coastguard Worker     pub preferred_video_mode: Option<MultibootVideoMode>,
46*bb4ee6a4SAndroid Build Coastguard Worker }
47*bb4ee6a4SAndroid Build Coastguard Worker 
48*bb4ee6a4SAndroid Build Coastguard Worker /// Multiboot kernel load parameters.
49*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Debug)]
50*bb4ee6a4SAndroid Build Coastguard Worker pub struct MultibootLoad {
51*bb4ee6a4SAndroid Build Coastguard Worker     /// File byte offset to load the kernel's code and initialized data from.
52*bb4ee6a4SAndroid Build Coastguard Worker     pub file_load_offset: u64,
53*bb4ee6a4SAndroid Build Coastguard Worker 
54*bb4ee6a4SAndroid Build Coastguard Worker     /// Number of bytes to read from the file at `file_load_offset`.
55*bb4ee6a4SAndroid Build Coastguard Worker     pub file_load_size: usize,
56*bb4ee6a4SAndroid Build Coastguard Worker 
57*bb4ee6a4SAndroid Build Coastguard Worker     /// Physical memory address where the kernel should be loaded.
58*bb4ee6a4SAndroid Build Coastguard Worker     pub load_addr: GuestAddress,
59*bb4ee6a4SAndroid Build Coastguard Worker 
60*bb4ee6a4SAndroid Build Coastguard Worker     /// Physical address of the kernel entry point.
61*bb4ee6a4SAndroid Build Coastguard Worker     pub entry_addr: GuestAddress,
62*bb4ee6a4SAndroid Build Coastguard Worker 
63*bb4ee6a4SAndroid Build Coastguard Worker     /// BSS physical memory starting address to zero fill, if present in kernel.
64*bb4ee6a4SAndroid Build Coastguard Worker     pub bss_addr: Option<GuestAddress>,
65*bb4ee6a4SAndroid Build Coastguard Worker 
66*bb4ee6a4SAndroid Build Coastguard Worker     /// BSS size in bytes (0 if no BSS region is present).
67*bb4ee6a4SAndroid Build Coastguard Worker     pub bss_size: usize,
68*bb4ee6a4SAndroid Build Coastguard Worker }
69*bb4ee6a4SAndroid Build Coastguard Worker 
70*bb4ee6a4SAndroid Build Coastguard Worker /// Multiboot kernel video mode specification.
71*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Debug)]
72*bb4ee6a4SAndroid Build Coastguard Worker pub struct MultibootVideoMode {
73*bb4ee6a4SAndroid Build Coastguard Worker     /// Preferred video mode type (text or graphics).
74*bb4ee6a4SAndroid Build Coastguard Worker     pub mode_type: MultibootVideoModeType,
75*bb4ee6a4SAndroid Build Coastguard Worker 
76*bb4ee6a4SAndroid Build Coastguard Worker     /// Width of the requested mode.
77*bb4ee6a4SAndroid Build Coastguard Worker     ///
78*bb4ee6a4SAndroid Build Coastguard Worker     /// For text modes, this is in units of characters. For graphics modes, this is in units of
79*bb4ee6a4SAndroid Build Coastguard Worker     /// pixels.
80*bb4ee6a4SAndroid Build Coastguard Worker     pub width: Option<NonZeroU32>,
81*bb4ee6a4SAndroid Build Coastguard Worker 
82*bb4ee6a4SAndroid Build Coastguard Worker     /// Height of the requested mode.
83*bb4ee6a4SAndroid Build Coastguard Worker     ///
84*bb4ee6a4SAndroid Build Coastguard Worker     /// For text modes, this is in units of characters. For graphics modes, this is in units of
85*bb4ee6a4SAndroid Build Coastguard Worker     /// pixels.
86*bb4ee6a4SAndroid Build Coastguard Worker     pub height: Option<NonZeroU32>,
87*bb4ee6a4SAndroid Build Coastguard Worker 
88*bb4ee6a4SAndroid Build Coastguard Worker     /// Requested bits per pixel (only relevant in graphics modes).
89*bb4ee6a4SAndroid Build Coastguard Worker     pub depth: Option<NonZeroU32>,
90*bb4ee6a4SAndroid Build Coastguard Worker }
91*bb4ee6a4SAndroid Build Coastguard Worker 
92*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug)]
93*bb4ee6a4SAndroid Build Coastguard Worker pub enum MultibootVideoModeType {
94*bb4ee6a4SAndroid Build Coastguard Worker     LinearGraphics,
95*bb4ee6a4SAndroid Build Coastguard Worker     EgaText,
96*bb4ee6a4SAndroid Build Coastguard Worker     Other(u32),
97*bb4ee6a4SAndroid Build Coastguard Worker }
98*bb4ee6a4SAndroid Build Coastguard Worker 
99*bb4ee6a4SAndroid Build Coastguard Worker /// Scan the provided kernel file to find a Multiboot header, if present.
100*bb4ee6a4SAndroid Build Coastguard Worker ///
101*bb4ee6a4SAndroid Build Coastguard Worker /// # Returns
102*bb4ee6a4SAndroid Build Coastguard Worker ///
103*bb4ee6a4SAndroid Build Coastguard Worker /// - `Ok(None)`: kernel file did not contain a Multiboot header.
104*bb4ee6a4SAndroid Build Coastguard Worker /// - `Ok(Some(...))`: kernel file contained a valid Multiboot header, which is returned.
105*bb4ee6a4SAndroid Build Coastguard Worker /// - `Err(...)`: kernel file contained a Multiboot header with a valid checksum but other fields in
106*bb4ee6a4SAndroid Build Coastguard Worker ///   the header were invalid.
multiboot_header_from_file(kernel_file: &mut File) -> Result<Option<MultibootKernel>>107*bb4ee6a4SAndroid Build Coastguard Worker pub fn multiboot_header_from_file(kernel_file: &mut File) -> Result<Option<MultibootKernel>> {
108*bb4ee6a4SAndroid Build Coastguard Worker     const MIN_HEADER_SIZE: usize = 3 * size_of::<u32>();
109*bb4ee6a4SAndroid Build Coastguard Worker     const ALIGNMENT: usize = 4;
110*bb4ee6a4SAndroid Build Coastguard Worker 
111*bb4ee6a4SAndroid Build Coastguard Worker     // Read up to 8192 bytes from the beginning of the file.
112*bb4ee6a4SAndroid Build Coastguard Worker     let kernel_file_len = kernel_file.metadata().map_err(|_| Error::ReadHeader)?.len();
113*bb4ee6a4SAndroid Build Coastguard Worker     let kernel_prefix_len = kernel_file_len.min(8192) as usize;
114*bb4ee6a4SAndroid Build Coastguard Worker 
115*bb4ee6a4SAndroid Build Coastguard Worker     if kernel_prefix_len < MIN_HEADER_SIZE {
116*bb4ee6a4SAndroid Build Coastguard Worker         return Ok(None);
117*bb4ee6a4SAndroid Build Coastguard Worker     }
118*bb4ee6a4SAndroid Build Coastguard Worker 
119*bb4ee6a4SAndroid Build Coastguard Worker     let mut kernel_bytes = vec![0u8; kernel_prefix_len];
120*bb4ee6a4SAndroid Build Coastguard Worker     kernel_file
121*bb4ee6a4SAndroid Build Coastguard Worker         .read_exact_at_volatile(VolatileSlice::new(&mut kernel_bytes), 0)
122*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(|_| Error::ReadHeader)?;
123*bb4ee6a4SAndroid Build Coastguard Worker 
124*bb4ee6a4SAndroid Build Coastguard Worker     for offset in (0..kernel_prefix_len).step_by(ALIGNMENT) {
125*bb4ee6a4SAndroid Build Coastguard Worker         let Some(hdr) = kernel_bytes.get(offset..) else {
126*bb4ee6a4SAndroid Build Coastguard Worker             break;
127*bb4ee6a4SAndroid Build Coastguard Worker         };
128*bb4ee6a4SAndroid Build Coastguard Worker         match multiboot_header(hdr, offset as u64, kernel_file_len) {
129*bb4ee6a4SAndroid Build Coastguard Worker             Ok(None) => continue,
130*bb4ee6a4SAndroid Build Coastguard Worker             Ok(Some(multiboot)) => return Ok(Some(multiboot)),
131*bb4ee6a4SAndroid Build Coastguard Worker             Err(e) => return Err(e),
132*bb4ee6a4SAndroid Build Coastguard Worker         }
133*bb4ee6a4SAndroid Build Coastguard Worker     }
134*bb4ee6a4SAndroid Build Coastguard Worker 
135*bb4ee6a4SAndroid Build Coastguard Worker     // The file did not contain a valid Multiboot header.
136*bb4ee6a4SAndroid Build Coastguard Worker     Ok(None)
137*bb4ee6a4SAndroid Build Coastguard Worker }
138*bb4ee6a4SAndroid Build Coastguard Worker 
139*bb4ee6a4SAndroid Build Coastguard Worker /// Attempt to parse a Multiboot header from the prefix of a slice.
140*bb4ee6a4SAndroid Build Coastguard Worker ///
141*bb4ee6a4SAndroid Build Coastguard Worker /// # Returns
142*bb4ee6a4SAndroid Build Coastguard Worker ///
143*bb4ee6a4SAndroid Build Coastguard Worker /// - `Ok(None)`: no multiboot header here.
144*bb4ee6a4SAndroid Build Coastguard Worker /// - `Ok(Some(...))`: valid multiboot header is returned.
145*bb4ee6a4SAndroid Build Coastguard Worker /// - `Err(...)`: valid multiboot header checksum at this position in the file (meaning this is the
146*bb4ee6a4SAndroid Build Coastguard Worker ///   real header location), but there is an invalid field later in the multiboot header (e.g. an
147*bb4ee6a4SAndroid Build Coastguard Worker ///   impossible combination of load addresses).
multiboot_header( hdr: &[u8], offset: u64, kernel_file_len: u64, ) -> Result<Option<MultibootKernel>>148*bb4ee6a4SAndroid Build Coastguard Worker fn multiboot_header(
149*bb4ee6a4SAndroid Build Coastguard Worker     hdr: &[u8],
150*bb4ee6a4SAndroid Build Coastguard Worker     offset: u64,
151*bb4ee6a4SAndroid Build Coastguard Worker     kernel_file_len: u64,
152*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<Option<MultibootKernel>> {
153*bb4ee6a4SAndroid Build Coastguard Worker     const MAGIC: u32 = 0x1BADB002;
154*bb4ee6a4SAndroid Build Coastguard Worker 
155*bb4ee6a4SAndroid Build Coastguard Worker     let Ok(magic) = get_le32(hdr, 0) else {
156*bb4ee6a4SAndroid Build Coastguard Worker         return Ok(None);
157*bb4ee6a4SAndroid Build Coastguard Worker     };
158*bb4ee6a4SAndroid Build Coastguard Worker     if magic != MAGIC {
159*bb4ee6a4SAndroid Build Coastguard Worker         return Ok(None);
160*bb4ee6a4SAndroid Build Coastguard Worker     }
161*bb4ee6a4SAndroid Build Coastguard Worker 
162*bb4ee6a4SAndroid Build Coastguard Worker     // Failing to read these fields means we ran out of data at the end of the slice and did not
163*bb4ee6a4SAndroid Build Coastguard Worker     // actually find a Multiboot header, so return `Ok(None)` to indicate no Multiboot header was
164*bb4ee6a4SAndroid Build Coastguard Worker     // found instead of using `?`, which would return an error.
165*bb4ee6a4SAndroid Build Coastguard Worker     let Ok(flags) = get_le32(hdr, 4) else {
166*bb4ee6a4SAndroid Build Coastguard Worker         return Ok(None);
167*bb4ee6a4SAndroid Build Coastguard Worker     };
168*bb4ee6a4SAndroid Build Coastguard Worker     let Ok(checksum) = get_le32(hdr, 8) else {
169*bb4ee6a4SAndroid Build Coastguard Worker         return Ok(None);
170*bb4ee6a4SAndroid Build Coastguard Worker     };
171*bb4ee6a4SAndroid Build Coastguard Worker 
172*bb4ee6a4SAndroid Build Coastguard Worker     if magic.wrapping_add(flags).wrapping_add(checksum) != 0 {
173*bb4ee6a4SAndroid Build Coastguard Worker         // Checksum did not match, so this is not a real Multiboot header. Keep searching.
174*bb4ee6a4SAndroid Build Coastguard Worker         return Ok(None);
175*bb4ee6a4SAndroid Build Coastguard Worker     }
176*bb4ee6a4SAndroid Build Coastguard Worker 
177*bb4ee6a4SAndroid Build Coastguard Worker     trace!("found Multiboot header with valid checksum at {offset:#X}");
178*bb4ee6a4SAndroid Build Coastguard Worker 
179*bb4ee6a4SAndroid Build Coastguard Worker     const F_BOOT_MODULE_PAGE_ALIGN: u32 = 1 << 0;
180*bb4ee6a4SAndroid Build Coastguard Worker     const F_AVAILABLE_MEMORY: u32 = 1 << 1;
181*bb4ee6a4SAndroid Build Coastguard Worker     const F_VIDEO_MODE: u32 = 1 << 2;
182*bb4ee6a4SAndroid Build Coastguard Worker     const F_ADDRESS: u32 = 1 << 16;
183*bb4ee6a4SAndroid Build Coastguard Worker 
184*bb4ee6a4SAndroid Build Coastguard Worker     const KNOWN_FLAGS: u32 =
185*bb4ee6a4SAndroid Build Coastguard Worker         F_BOOT_MODULE_PAGE_ALIGN | F_AVAILABLE_MEMORY | F_VIDEO_MODE | F_ADDRESS;
186*bb4ee6a4SAndroid Build Coastguard Worker 
187*bb4ee6a4SAndroid Build Coastguard Worker     let unknown_flags = flags & !KNOWN_FLAGS;
188*bb4ee6a4SAndroid Build Coastguard Worker     if unknown_flags != 0 {
189*bb4ee6a4SAndroid Build Coastguard Worker         error!("unknown flags {unknown_flags:#X}");
190*bb4ee6a4SAndroid Build Coastguard Worker         return Err(Error::InvalidFlags);
191*bb4ee6a4SAndroid Build Coastguard Worker     }
192*bb4ee6a4SAndroid Build Coastguard Worker 
193*bb4ee6a4SAndroid Build Coastguard Worker     let boot_modules_page_aligned = flags & F_BOOT_MODULE_PAGE_ALIGN != 0;
194*bb4ee6a4SAndroid Build Coastguard Worker     let need_available_memory = flags & F_AVAILABLE_MEMORY != 0;
195*bb4ee6a4SAndroid Build Coastguard Worker     let need_video_mode_table = flags & F_VIDEO_MODE != 0;
196*bb4ee6a4SAndroid Build Coastguard Worker     let load_address_available = flags & F_ADDRESS != 0;
197*bb4ee6a4SAndroid Build Coastguard Worker 
198*bb4ee6a4SAndroid Build Coastguard Worker     let load = if load_address_available {
199*bb4ee6a4SAndroid Build Coastguard Worker         let header_addr = get_le32(hdr, 12)?;
200*bb4ee6a4SAndroid Build Coastguard Worker         let load_addr = get_le32(hdr, 16)?;
201*bb4ee6a4SAndroid Build Coastguard Worker         let load_end_addr = get_le32(hdr, 20)?;
202*bb4ee6a4SAndroid Build Coastguard Worker         let bss_end_addr = get_le32(hdr, 24)?;
203*bb4ee6a4SAndroid Build Coastguard Worker         let entry_addr = get_le32(hdr, 28)?;
204*bb4ee6a4SAndroid Build Coastguard Worker 
205*bb4ee6a4SAndroid Build Coastguard Worker         if header_addr < load_addr {
206*bb4ee6a4SAndroid Build Coastguard Worker             error!("header_addr {header_addr:#X} < load_addr {load_addr:#X}");
207*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::InvalidKernelOffset);
208*bb4ee6a4SAndroid Build Coastguard Worker         }
209*bb4ee6a4SAndroid Build Coastguard Worker 
210*bb4ee6a4SAndroid Build Coastguard Worker         // The beginning of the area to load from the file starts `load_offset` bytes before the
211*bb4ee6a4SAndroid Build Coastguard Worker         // multiboot header.
212*bb4ee6a4SAndroid Build Coastguard Worker         let load_offset = u64::from(header_addr - load_addr);
213*bb4ee6a4SAndroid Build Coastguard Worker         if load_offset > offset {
214*bb4ee6a4SAndroid Build Coastguard Worker             error!("load_offset {load_offset:#X} > offset {offset:#X}");
215*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::InvalidKernelOffset);
216*bb4ee6a4SAndroid Build Coastguard Worker         }
217*bb4ee6a4SAndroid Build Coastguard Worker         let file_load_offset = offset - load_offset;
218*bb4ee6a4SAndroid Build Coastguard Worker 
219*bb4ee6a4SAndroid Build Coastguard Worker         let file_load_size = if load_end_addr == 0 {
220*bb4ee6a4SAndroid Build Coastguard Worker             // Zero `load_end_addr` means the loadable data extends to the end of the file.
221*bb4ee6a4SAndroid Build Coastguard Worker             (kernel_file_len - file_load_offset)
222*bb4ee6a4SAndroid Build Coastguard Worker                 .try_into()
223*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(|_| Error::InvalidKernelOffset)?
224*bb4ee6a4SAndroid Build Coastguard Worker         } else if load_end_addr < load_addr {
225*bb4ee6a4SAndroid Build Coastguard Worker             error!("load_end_addr {load_end_addr:#X} < load_addr {load_addr:#X}");
226*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::InvalidKernelOffset);
227*bb4ee6a4SAndroid Build Coastguard Worker         } else {
228*bb4ee6a4SAndroid Build Coastguard Worker             load_end_addr - load_addr
229*bb4ee6a4SAndroid Build Coastguard Worker         };
230*bb4ee6a4SAndroid Build Coastguard Worker 
231*bb4ee6a4SAndroid Build Coastguard Worker         let load_end_addr = load_addr
232*bb4ee6a4SAndroid Build Coastguard Worker             .checked_add(file_load_size)
233*bb4ee6a4SAndroid Build Coastguard Worker             .ok_or(Error::InvalidKernelOffset)?;
234*bb4ee6a4SAndroid Build Coastguard Worker 
235*bb4ee6a4SAndroid Build Coastguard Worker         // The bss region immediately follows the load-from-file region in memory.
236*bb4ee6a4SAndroid Build Coastguard Worker         let bss_addr = load_addr + file_load_size;
237*bb4ee6a4SAndroid Build Coastguard Worker 
238*bb4ee6a4SAndroid Build Coastguard Worker         let bss_size = if bss_end_addr == 0 {
239*bb4ee6a4SAndroid Build Coastguard Worker             // Zero `bss_end_addr` means no bss segment is present.
240*bb4ee6a4SAndroid Build Coastguard Worker             0
241*bb4ee6a4SAndroid Build Coastguard Worker         } else if bss_end_addr < bss_addr {
242*bb4ee6a4SAndroid Build Coastguard Worker             error!("bss_end_addr {bss_end_addr:#X} < bss_addr {bss_addr:#X}");
243*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::InvalidKernelOffset);
244*bb4ee6a4SAndroid Build Coastguard Worker         } else {
245*bb4ee6a4SAndroid Build Coastguard Worker             bss_end_addr - bss_addr
246*bb4ee6a4SAndroid Build Coastguard Worker         };
247*bb4ee6a4SAndroid Build Coastguard Worker 
248*bb4ee6a4SAndroid Build Coastguard Worker         let bss_addr = if bss_size > 0 {
249*bb4ee6a4SAndroid Build Coastguard Worker             Some(GuestAddress(bss_addr.into()))
250*bb4ee6a4SAndroid Build Coastguard Worker         } else {
251*bb4ee6a4SAndroid Build Coastguard Worker             None
252*bb4ee6a4SAndroid Build Coastguard Worker         };
253*bb4ee6a4SAndroid Build Coastguard Worker 
254*bb4ee6a4SAndroid Build Coastguard Worker         if entry_addr < load_addr || entry_addr >= load_end_addr {
255*bb4ee6a4SAndroid Build Coastguard Worker             error!(
256*bb4ee6a4SAndroid Build Coastguard Worker                 "entry_addr {entry_addr:#X} not in load range {load_addr:#X}..{load_end_addr:#X}"
257*bb4ee6a4SAndroid Build Coastguard Worker             );
258*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::InvalidKernelOffset);
259*bb4ee6a4SAndroid Build Coastguard Worker         }
260*bb4ee6a4SAndroid Build Coastguard Worker 
261*bb4ee6a4SAndroid Build Coastguard Worker         Some(MultibootLoad {
262*bb4ee6a4SAndroid Build Coastguard Worker             file_load_offset,
263*bb4ee6a4SAndroid Build Coastguard Worker             file_load_size: file_load_size as usize,
264*bb4ee6a4SAndroid Build Coastguard Worker             load_addr: GuestAddress(load_addr.into()),
265*bb4ee6a4SAndroid Build Coastguard Worker             entry_addr: GuestAddress(entry_addr.into()),
266*bb4ee6a4SAndroid Build Coastguard Worker             bss_addr,
267*bb4ee6a4SAndroid Build Coastguard Worker             bss_size: bss_size as usize,
268*bb4ee6a4SAndroid Build Coastguard Worker         })
269*bb4ee6a4SAndroid Build Coastguard Worker     } else {
270*bb4ee6a4SAndroid Build Coastguard Worker         None
271*bb4ee6a4SAndroid Build Coastguard Worker     };
272*bb4ee6a4SAndroid Build Coastguard Worker 
273*bb4ee6a4SAndroid Build Coastguard Worker     let preferred_video_mode = if need_video_mode_table {
274*bb4ee6a4SAndroid Build Coastguard Worker         let mode_type = get_le32(hdr, 32)?;
275*bb4ee6a4SAndroid Build Coastguard Worker         let width = get_le32(hdr, 36)?;
276*bb4ee6a4SAndroid Build Coastguard Worker         let height = get_le32(hdr, 40)?;
277*bb4ee6a4SAndroid Build Coastguard Worker         let depth = get_le32(hdr, 44)?;
278*bb4ee6a4SAndroid Build Coastguard Worker 
279*bb4ee6a4SAndroid Build Coastguard Worker         let mode_type = match mode_type {
280*bb4ee6a4SAndroid Build Coastguard Worker             0 => MultibootVideoModeType::LinearGraphics,
281*bb4ee6a4SAndroid Build Coastguard Worker             1 => MultibootVideoModeType::EgaText,
282*bb4ee6a4SAndroid Build Coastguard Worker             _ => MultibootVideoModeType::Other(mode_type),
283*bb4ee6a4SAndroid Build Coastguard Worker         };
284*bb4ee6a4SAndroid Build Coastguard Worker 
285*bb4ee6a4SAndroid Build Coastguard Worker         Some(MultibootVideoMode {
286*bb4ee6a4SAndroid Build Coastguard Worker             mode_type,
287*bb4ee6a4SAndroid Build Coastguard Worker             width: NonZeroU32::new(width),
288*bb4ee6a4SAndroid Build Coastguard Worker             height: NonZeroU32::new(height),
289*bb4ee6a4SAndroid Build Coastguard Worker             depth: NonZeroU32::new(depth),
290*bb4ee6a4SAndroid Build Coastguard Worker         })
291*bb4ee6a4SAndroid Build Coastguard Worker     } else {
292*bb4ee6a4SAndroid Build Coastguard Worker         None
293*bb4ee6a4SAndroid Build Coastguard Worker     };
294*bb4ee6a4SAndroid Build Coastguard Worker 
295*bb4ee6a4SAndroid Build Coastguard Worker     let multiboot = MultibootKernel {
296*bb4ee6a4SAndroid Build Coastguard Worker         offset: offset as u32,
297*bb4ee6a4SAndroid Build Coastguard Worker         boot_modules_page_aligned,
298*bb4ee6a4SAndroid Build Coastguard Worker         need_available_memory,
299*bb4ee6a4SAndroid Build Coastguard Worker         load,
300*bb4ee6a4SAndroid Build Coastguard Worker         preferred_video_mode,
301*bb4ee6a4SAndroid Build Coastguard Worker     };
302*bb4ee6a4SAndroid Build Coastguard Worker 
303*bb4ee6a4SAndroid Build Coastguard Worker     trace!("validated header: {multiboot:?}");
304*bb4ee6a4SAndroid Build Coastguard Worker 
305*bb4ee6a4SAndroid Build Coastguard Worker     Ok(Some(multiboot))
306*bb4ee6a4SAndroid Build Coastguard Worker }
307*bb4ee6a4SAndroid Build Coastguard Worker 
get_le32(bytes: &[u8], offset: usize) -> Result<u32>308*bb4ee6a4SAndroid Build Coastguard Worker fn get_le32(bytes: &[u8], offset: usize) -> Result<u32> {
309*bb4ee6a4SAndroid Build Coastguard Worker     let le32_bytes = bytes.get(offset..offset + 4).ok_or(Error::ReadHeader)?;
310*bb4ee6a4SAndroid Build Coastguard Worker     // This can't fail because the slice is always 4 bytes long.
311*bb4ee6a4SAndroid Build Coastguard Worker     let le32_array: [u8; 4] = le32_bytes.try_into().unwrap();
312*bb4ee6a4SAndroid Build Coastguard Worker     Ok(u32::from_le_bytes(le32_array))
313*bb4ee6a4SAndroid Build Coastguard Worker }
314*bb4ee6a4SAndroid Build Coastguard Worker 
315*bb4ee6a4SAndroid Build Coastguard Worker /// Load a Multiboot kernel image into memory.
316*bb4ee6a4SAndroid Build Coastguard Worker ///
317*bb4ee6a4SAndroid Build Coastguard Worker /// The `MultibootLoad` information can be retrieved from the optional `load` field of a
318*bb4ee6a4SAndroid Build Coastguard Worker /// `MultibootKernel` returned by [`multiboot_header_from_file()`].
load_multiboot<F>( guest_mem: &GuestMemory, kernel_image: &mut F, multiboot_load: &MultibootLoad, ) -> Result<LoadedKernel> where F: FileReadWriteAtVolatile,319*bb4ee6a4SAndroid Build Coastguard Worker pub fn load_multiboot<F>(
320*bb4ee6a4SAndroid Build Coastguard Worker     guest_mem: &GuestMemory,
321*bb4ee6a4SAndroid Build Coastguard Worker     kernel_image: &mut F,
322*bb4ee6a4SAndroid Build Coastguard Worker     multiboot_load: &MultibootLoad,
323*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<LoadedKernel>
324*bb4ee6a4SAndroid Build Coastguard Worker where
325*bb4ee6a4SAndroid Build Coastguard Worker     F: FileReadWriteAtVolatile,
326*bb4ee6a4SAndroid Build Coastguard Worker {
327*bb4ee6a4SAndroid Build Coastguard Worker     let guest_slice = guest_mem
328*bb4ee6a4SAndroid Build Coastguard Worker         .get_slice_at_addr(multiboot_load.load_addr, multiboot_load.file_load_size)
329*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(|_| Error::ReadKernelImage)?;
330*bb4ee6a4SAndroid Build Coastguard Worker     kernel_image
331*bb4ee6a4SAndroid Build Coastguard Worker         .read_exact_at_volatile(guest_slice, multiboot_load.file_load_offset)
332*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(|_| Error::ReadKernelImage)?;
333*bb4ee6a4SAndroid Build Coastguard Worker 
334*bb4ee6a4SAndroid Build Coastguard Worker     if let Some(bss_addr) = multiboot_load.bss_addr {
335*bb4ee6a4SAndroid Build Coastguard Worker         let bss_slice = guest_mem
336*bb4ee6a4SAndroid Build Coastguard Worker             .get_slice_at_addr(bss_addr, multiboot_load.bss_size)
337*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(|_| Error::ReadKernelImage)?;
338*bb4ee6a4SAndroid Build Coastguard Worker         bss_slice.write_bytes(0);
339*bb4ee6a4SAndroid Build Coastguard Worker     }
340*bb4ee6a4SAndroid Build Coastguard Worker 
341*bb4ee6a4SAndroid Build Coastguard Worker     let size: u64 = multiboot_load
342*bb4ee6a4SAndroid Build Coastguard Worker         .file_load_size
343*bb4ee6a4SAndroid Build Coastguard Worker         .checked_add(multiboot_load.bss_size)
344*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or(Error::InvalidProgramHeaderSize)?
345*bb4ee6a4SAndroid Build Coastguard Worker         .try_into()
346*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(|_| Error::InvalidProgramHeaderSize)?;
347*bb4ee6a4SAndroid Build Coastguard Worker 
348*bb4ee6a4SAndroid Build Coastguard Worker     let address_range = AddressRange::from_start_and_size(multiboot_load.load_addr.offset(), size)
349*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or(Error::InvalidProgramHeaderSize)?;
350*bb4ee6a4SAndroid Build Coastguard Worker 
351*bb4ee6a4SAndroid Build Coastguard Worker     Ok(LoadedKernel {
352*bb4ee6a4SAndroid Build Coastguard Worker         address_range,
353*bb4ee6a4SAndroid Build Coastguard Worker         size,
354*bb4ee6a4SAndroid Build Coastguard Worker         entry: multiboot_load.entry_addr,
355*bb4ee6a4SAndroid Build Coastguard Worker     })
356*bb4ee6a4SAndroid Build Coastguard Worker }
357