linux_loader/configurator/x86_64/
linux.rs

1// Copyright © 2020, Oracle and/or its affiliates.
2//
3// Copyright (c) 2019 Intel Corporation. All rights reserved.
4// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5//
6// Copyright 2017 The Chromium OS Authors. All rights reserved.
7// Use of this source code is governed by a BSD-style license that can be
8// found in the LICENSE-BSD-3-Clause file.
9//
10// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
11
12//! Traits and structs for configuring and loading boot parameters on `x86_64` using the Linux
13//! boot protocol.
14
15use vm_memory::{Bytes, GuestMemory};
16
17use crate::configurator::{BootConfigurator, BootParams, Error as BootConfiguratorError, Result};
18
19use std::fmt;
20
21/// Boot configurator for the Linux boot protocol.
22pub struct LinuxBootConfigurator {}
23
24/// Errors specific to the Linux boot protocol configuration.
25#[derive(Debug, PartialEq, Eq)]
26pub enum Error {
27    /// The zero page extends past the end of guest memory.
28    ZeroPagePastRamEnd,
29    /// Error writing to the zero page of guest memory.
30    ZeroPageSetup,
31}
32
33impl fmt::Display for Error {
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        use Error::*;
36        let desc = match self {
37            ZeroPagePastRamEnd => "the zero page extends past the end of guest memory.",
38            ZeroPageSetup => "error writing to the zero page of guest memory.",
39        };
40
41        write!(f, "Linux Boot Configurator: {}", desc,)
42    }
43}
44
45impl std::error::Error for Error {}
46
47impl From<Error> for BootConfiguratorError {
48    fn from(err: Error) -> Self {
49        BootConfiguratorError::Linux(err)
50    }
51}
52
53impl BootConfigurator for LinuxBootConfigurator {
54    /// Writes the boot parameters (configured elsewhere) into guest memory.
55    ///
56    /// # Arguments
57    ///
58    /// * `params` - boot parameters. The header contains a [`boot_params`] struct. The `sections`
59    ///              and `modules` are unused.
60    /// * `guest_memory` - guest's physical memory.
61    ///
62    /// # Examples
63    ///
64    /// ```rust
65    /// # extern crate vm_memory;
66    /// # use linux_loader::configurator::{BootConfigurator, BootParams};
67    /// # use linux_loader::configurator::linux::LinuxBootConfigurator;
68    /// # use linux_loader::loader::bootparam::boot_params;
69    /// # use vm_memory::{Address, ByteValued, GuestMemory, GuestMemoryMmap, GuestAddress};
70    /// # const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
71    /// # const KERNEL_HDR_MAGIC: u32 = 0x53726448;
72    /// # const KERNEL_LOADER_OTHER: u8 = 0xff;
73    /// # const KERNEL_MIN_ALIGNMENT_BYTES: u32 = 0x1000000;
74    /// # const MEM_SIZE: u64 = 0x100_0000;
75    /// # fn create_guest_memory() -> GuestMemoryMmap {
76    /// #   GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
77    /// # }
78    /// fn build_bootparams() -> boot_params {
79    ///     let mut params = boot_params::default();
80    ///     params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
81    ///     params.hdr.header = KERNEL_HDR_MAGIC;
82    ///     params.hdr.kernel_alignment = KERNEL_MIN_ALIGNMENT_BYTES;
83    ///     params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
84    ///     params
85    /// }
86    ///
87    /// fn main() {
88    /// #   let zero_page_addr = GuestAddress(0x30000);
89    ///     let guest_memory = create_guest_memory();
90    ///     let params = build_bootparams();
91    ///     let mut bootparams = BootParams::new::<boot_params>(&params, zero_page_addr);
92    ///     LinuxBootConfigurator::write_bootparams::<GuestMemoryMmap>(&bootparams, &guest_memory)
93    ///         .unwrap();
94    /// }
95    /// ```
96    ///
97    /// [`boot_params`]: ../loader/bootparam/struct.boot_params.html
98    fn write_bootparams<M>(params: &BootParams, guest_memory: &M) -> Result<()>
99    where
100        M: GuestMemory,
101    {
102        // The VMM has filled a `boot_params` struct and its e820 map.
103        // This will be written in guest memory at the zero page.
104        guest_memory
105            .checked_offset(params.header_start, params.header.len())
106            .ok_or(Error::ZeroPagePastRamEnd)?;
107        guest_memory
108            .write_slice(params.header.as_slice(), params.header_start)
109            .map_err(|_| Error::ZeroPageSetup)?;
110
111        Ok(())
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use crate::loader_gen::bootparam::boot_params;
119    use std::mem;
120    use vm_memory::{Address, GuestAddress, GuestMemoryMmap};
121
122    const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
123    const KERNEL_HDR_MAGIC: u32 = 0x53726448;
124    const KERNEL_LOADER_OTHER: u8 = 0xff;
125    const KERNEL_MIN_ALIGNMENT_BYTES: u32 = 0x1000000;
126    const MEM_SIZE: u64 = 0x100_0000;
127
128    fn create_guest_mem() -> GuestMemoryMmap {
129        GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
130    }
131
132    fn build_bootparams_common() -> boot_params {
133        let mut params = boot_params::default();
134        params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
135        params.hdr.header = KERNEL_HDR_MAGIC;
136        params.hdr.kernel_alignment = KERNEL_MIN_ALIGNMENT_BYTES;
137        params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
138        params
139    }
140
141    #[test]
142    fn test_configure_linux_boot() {
143        let zero_page_addr = GuestAddress(0x30000);
144
145        let params = build_bootparams_common();
146        // This is where we'd append e820 entries, cmdline, PCI, ACPI etc.
147
148        let guest_memory = create_guest_mem();
149
150        // Error case: boot params don't fit in guest memory (zero page address too close to end).
151        let bad_zeropg_addr = GuestAddress(
152            guest_memory.last_addr().raw_value() - mem::size_of::<boot_params>() as u64 + 1,
153        );
154        let mut bootparams = BootParams::new::<boot_params>(&params, bad_zeropg_addr);
155        assert_eq!(
156            LinuxBootConfigurator::write_bootparams::<GuestMemoryMmap>(&bootparams, &guest_memory,)
157                .err(),
158            Some(Error::ZeroPagePastRamEnd.into()),
159        );
160
161        // Success case.
162        bootparams.header_start = zero_page_addr;
163        assert!(LinuxBootConfigurator::write_bootparams::<GuestMemoryMmap>(
164            &bootparams,
165            &guest_memory,
166        )
167        .is_ok());
168    }
169
170    #[test]
171    fn test_error_messages() {
172        assert_eq!(
173            format!("{}", Error::ZeroPagePastRamEnd),
174            "Linux Boot Configurator: the zero page extends past the end of guest memory."
175        );
176        assert_eq!(
177            format!("{}", Error::ZeroPageSetup),
178            "Linux Boot Configurator: error writing to the zero page of guest memory."
179        );
180    }
181}