• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Loader for bzImage-format Linux kernels as described in
6 //! <https://www.kernel.org/doc/Documentation/x86/boot.txt>
7 
8 use std::io;
9 
10 use base::debug;
11 use base::FileReadWriteAtVolatile;
12 use base::VolatileSlice;
13 use memoffset::offset_of;
14 use remain::sorted;
15 use thiserror::Error;
16 use vm_memory::GuestAddress;
17 use vm_memory::GuestMemory;
18 use vm_memory::GuestMemoryError;
19 use zerocopy::AsBytes;
20 
21 use crate::bootparam::boot_params;
22 
23 #[sorted]
24 #[derive(Error, Debug)]
25 pub enum Error {
26     #[error("bad kernel header signature")]
27     BadSignature,
28     #[error("guest memory error {0}")]
29     GuestMemoryError(GuestMemoryError),
30     #[error("invalid setup_header_end value {0}")]
31     InvalidSetupHeaderEnd(usize),
32     #[error("invalid setup_sects value {0}")]
33     InvalidSetupSects(u8),
34     #[error("invalid syssize value {0}")]
35     InvalidSysSize(u32),
36     #[error("unable to read boot_params: {0}")]
37     ReadBootParams(io::Error),
38     #[error("unable to read header size: {0}")]
39     ReadHeaderSize(io::Error),
40     #[error("unable to read kernel image: {0}")]
41     ReadKernelImage(io::Error),
42 }
43 
44 pub type Result<T> = std::result::Result<T, Error>;
45 
46 /// Loads a kernel from a bzImage to a slice
47 ///
48 /// # Arguments
49 ///
50 /// * `guest_mem` - The guest memory region the kernel is written to.
51 /// * `kernel_start` - The offset into `guest_mem` at which to load the kernel.
52 /// * `kernel_image` - Input bzImage.
load_bzimage<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, ) -> Result<(boot_params, u64)> where F: FileReadWriteAtVolatile,53 pub fn load_bzimage<F>(
54     guest_mem: &GuestMemory,
55     kernel_start: GuestAddress,
56     kernel_image: &mut F,
57 ) -> Result<(boot_params, u64)>
58 where
59     F: FileReadWriteAtVolatile,
60 {
61     let mut params = boot_params::default();
62 
63     // The start of setup header is defined by its offset within boot_params (0x01f1).
64     let setup_header_start = offset_of!(boot_params, hdr);
65 
66     // Per x86 Linux 64-bit boot protocol:
67     // "The end of setup header can be calculated as follows: 0x0202 + byte value at offset 0x0201"
68     let mut setup_size_byte = 0u8;
69     kernel_image
70         .read_exact_at_volatile(
71             VolatileSlice::new(std::slice::from_mut(&mut setup_size_byte)),
72             0x0201,
73         )
74         .map_err(Error::ReadHeaderSize)?;
75     let setup_header_end = 0x0202 + usize::from(setup_size_byte);
76 
77     debug!(
78         "setup_header file offset range: 0x{:04x}..0x{:04x}",
79         setup_header_start, setup_header_end,
80     );
81 
82     // Read `setup_header` into `boot_params`. The bzImage may have a different size of
83     // `setup_header`, so read directly into a byte slice of the outer `boot_params` structure
84     // rather than reading into `params.hdr`. The bounds check in `.get_mut()` will ensure we do not
85     // read beyond the end of `boot_params`.
86     let setup_header_slice = params
87         .as_bytes_mut()
88         .get_mut(setup_header_start..setup_header_end)
89         .ok_or(Error::InvalidSetupHeaderEnd(setup_header_end))?;
90 
91     kernel_image
92         .read_exact_at_volatile(
93             VolatileSlice::new(setup_header_slice),
94             setup_header_start as u64,
95         )
96         .map_err(Error::ReadBootParams)?;
97 
98     // bzImage header signature "HdrS"
99     if params.hdr.header != 0x53726448 {
100         return Err(Error::BadSignature);
101     }
102 
103     let setup_sects = if params.hdr.setup_sects == 0 {
104         4u64
105     } else {
106         params.hdr.setup_sects as u64
107     };
108 
109     let kernel_offset = setup_sects
110         .checked_add(1)
111         .and_then(|sectors| sectors.checked_mul(512))
112         .ok_or(Error::InvalidSetupSects(params.hdr.setup_sects))?;
113     let kernel_size = (params.hdr.syssize as usize)
114         .checked_mul(16)
115         .ok_or(Error::InvalidSysSize(params.hdr.syssize))?;
116 
117     // Load the whole kernel image to kernel_start
118     let guest_slice = guest_mem
119         .get_slice_at_addr(kernel_start, kernel_size)
120         .map_err(Error::GuestMemoryError)?;
121     kernel_image
122         .read_exact_at_volatile(guest_slice, kernel_offset)
123         .map_err(Error::ReadKernelImage)?;
124 
125     Ok((params, kernel_start.offset() + kernel_size as u64))
126 }
127