Skip to main content

linux_loader/configurator/x86_64/
pvh.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 PVH boot
13//! protocol.
14
15#![cfg(any(feature = "elf", feature = "bzimage"))]
16
17use vm_memory::{ByteValued, Bytes, GuestMemoryBackend};
18
19use crate::configurator::{BootConfigurator, BootParams, Error as BootConfiguratorError, Result};
20use crate::loader_gen::start_info::{hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info};
21
22use std::fmt;
23
24/// Boot configurator for the PVH boot protocol.
25pub struct PvhBootConfigurator {}
26
27/// Errors specific to the PVH boot protocol configuration.
28#[derive(Debug, PartialEq, Eq)]
29pub enum Error {
30    /// The starting address for the memory map wasn't passed to the boot configurator.
31    MemmapTableAddressMissing,
32    /// No memory map wasn't passed to the boot configurator.
33    MemmapTableMissing,
34    /// The memory map table extends past the end of guest memory.
35    MemmapTablePastRamEnd,
36    /// Error writing memory map table to guest memory.
37    MemmapTableSetup,
38    /// The hvm_start_info structure extends past the end of guest memory.
39    StartInfoPastRamEnd,
40    /// Error writing hvm_start_info to guest memory.
41    StartInfoSetup,
42    /// The starting address for the modules descriptions wasn't passed to the boot configurator.
43    ModulesAddressMissing,
44    /// Error writing module descriptions to guest memory.
45    ModulesSetup,
46}
47
48impl fmt::Display for Error {
49    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50        use Error::*;
51        let desc = match self {
52            MemmapTableAddressMissing => {
53                "the starting address for the memory map wasn't passed to the boot configurator."
54            }
55            MemmapTableMissing => "no memory map was passed to the boot configurator.",
56            MemmapTablePastRamEnd => "the memory map table extends past the end of guest memory.",
57            MemmapTableSetup => "error writing memory map table to guest memory.",
58            StartInfoPastRamEnd => {
59                "the hvm_start_info structure extends past the end of guest memory."
60            }
61            StartInfoSetup => "error writing hvm_start_info to guest memory.",
62            ModulesAddressMissing => "the starting address for the modules descriptions wasn't passed to the boot configurator.",
63            ModulesSetup => "error writing module descriptions to guest memory.",
64        };
65
66        write!(f, "PVH Boot Configurator: {}", desc)
67    }
68}
69
70impl std::error::Error for Error {}
71
72impl From<Error> for BootConfiguratorError {
73    fn from(err: Error) -> Self {
74        BootConfiguratorError::Pvh(err)
75    }
76}
77
78// SAFETY: The layout of the structure is fixed and can be initialized by
79// reading its content from byte array.
80unsafe impl ByteValued for hvm_start_info {}
81
82// SAFETY: The layout of the structure is fixed and can be initialized by
83// reading its content from byte array.
84unsafe impl ByteValued for hvm_memmap_table_entry {}
85
86// SAFETY: The layout of the structure is fixed and can be initialized by
87// reading its content from byte array.
88unsafe impl ByteValued for hvm_modlist_entry {}
89
90impl BootConfigurator for PvhBootConfigurator {
91    /// Writes the boot parameters (configured elsewhere) into guest memory.
92    ///
93    /// # Arguments
94    ///
95    /// * `params` - boot parameters. The header contains a [`hvm_start_info`] struct. The
96    ///   sections contain the memory map in a vector of [`hvm_memmap_table_entry`]
97    ///   structs. The modules, if specified, contain [`hvm_modlist_entry`] structs.
98    /// * `guest_memory` - guest's physical memory.
99    ///
100    /// [`hvm_start_info`]: ../loader/elf/start_info/struct.hvm_start_info.html
101    /// [`hvm_memmap_table_entry`]: ../loader/elf/start_info/struct.hvm_memmap_table_entry.html
102    /// [`hvm_modlist_entry`]: ../loader/elf/start_info/struct.hvm_modlist_entry.html
103    ///
104    /// # Examples
105    ///
106    /// ```rust
107    /// # extern crate vm_memory;
108    /// # use linux_loader::configurator::{BootConfigurator, BootParams};
109    /// # use linux_loader::configurator::pvh::PvhBootConfigurator;
110    /// # use linux_loader::loader::elf::start_info::{hvm_start_info, hvm_memmap_table_entry};
111    /// # use vm_memory::{Address, ByteValued, GuestMemoryBackend, GuestMemoryMmap, GuestAddress};
112    /// # const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578;
113    /// # const MEM_SIZE: u64 = 0x100_0000;
114    /// # const E820_RAM: u32 = 1;
115    /// fn create_guest_memory() -> GuestMemoryMmap {
116    ///     GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
117    /// }
118    ///
119    /// fn build_boot_params() -> (hvm_start_info, Vec<hvm_memmap_table_entry>) {
120    ///     let mut start_info = hvm_start_info::default();
121    ///     let memmap_entry = hvm_memmap_table_entry {
122    ///         addr: 0x7000,
123    ///         size: 0,
124    ///         type_: E820_RAM,
125    ///         reserved: 0,
126    ///     };
127    ///     start_info.magic = XEN_HVM_START_MAGIC_VALUE;
128    ///     start_info.version = 1;
129    ///     start_info.nr_modules = 0;
130    ///     start_info.memmap_entries = 0;
131    ///     (start_info, vec![memmap_entry])
132    /// }
133    ///
134    /// fn main() {
135    ///     let guest_mem = create_guest_memory();
136    ///     let (mut start_info, memmap_entries) = build_boot_params();
137    ///     let start_info_addr = GuestAddress(0x6000);
138    ///     let memmap_addr = GuestAddress(0x7000);
139    ///     start_info.memmap_paddr = memmap_addr.raw_value();
140    ///
141    ///     let mut boot_params = BootParams::new::<hvm_start_info>(&start_info, start_info_addr);
142    ///     boot_params.set_sections::<hvm_memmap_table_entry>(&memmap_entries, memmap_addr);
143    ///     PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(&boot_params, &guest_mem).unwrap();
144    /// }
145    /// ```
146    fn write_bootparams<M>(params: &BootParams, guest_memory: &M) -> Result<()>
147    where
148        M: GuestMemoryBackend,
149    {
150        // The VMM has filled an `hvm_start_info` struct and a `Vec<hvm_memmap_table_entry>`
151        // and has passed them on to this function.
152        // The `hvm_start_info` will be written at `addr` and the memmap entries at
153        // `start_info.0.memmap_paddr`.
154        let memmap = params.sections.as_ref().ok_or(Error::MemmapTableMissing)?;
155        let memmap_addr = params
156            .sections_start
157            .ok_or(Error::MemmapTableAddressMissing)?;
158
159        guest_memory
160            .checked_offset(memmap_addr, memmap.len())
161            .ok_or(Error::MemmapTablePastRamEnd)?;
162        guest_memory
163            .write_slice(memmap.as_slice(), memmap_addr)
164            .map_err(|_| Error::MemmapTableSetup)?;
165
166        guest_memory
167            .checked_offset(params.header_start, params.header.len())
168            .ok_or(Error::StartInfoPastRamEnd)?;
169        guest_memory
170            .write_slice(params.header.as_slice(), params.header_start)
171            .map_err(|_| Error::StartInfoSetup)?;
172
173        if let Some(modules) = params.modules.as_ref() {
174            let modules_addr = params.modules_start.ok_or(Error::ModulesAddressMissing)?;
175            guest_memory
176                .write_slice(modules.as_slice(), modules_addr)
177                .map_err(|_| Error::ModulesSetup)?;
178        }
179
180        Ok(())
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use std::mem;
188    use vm_memory::{Address, GuestAddress, GuestMemoryMmap};
189
190    const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578;
191    const MEM_SIZE: u64 = 0x100_0000;
192    const E820_RAM: u32 = 1;
193
194    fn create_guest_mem() -> GuestMemoryMmap {
195        GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
196    }
197
198    fn build_bootparams_common() -> (
199        hvm_start_info,
200        Vec<hvm_memmap_table_entry>,
201        Vec<hvm_modlist_entry>,
202    ) {
203        let mut start_info = hvm_start_info::default();
204        let memmap_entry = hvm_memmap_table_entry {
205            addr: 0x7000,
206            size: 0,
207            type_: E820_RAM,
208            reserved: 0,
209        };
210
211        let modlist_entry = hvm_modlist_entry {
212            paddr: 0x10000,
213            size: 0x100,
214            cmdline_paddr: 0,
215            reserved: 0,
216        };
217
218        start_info.magic = XEN_HVM_START_MAGIC_VALUE;
219        start_info.version = 1;
220        start_info.nr_modules = 0;
221        start_info.memmap_entries = 0;
222
223        (start_info, vec![memmap_entry], vec![modlist_entry])
224    }
225
226    #[test]
227    fn test_configure_pvh_boot() {
228        let (mut start_info, memmap_entries, modlist_entries) = build_bootparams_common();
229        let guest_memory = create_guest_mem();
230
231        let start_info_addr = GuestAddress(0x6000);
232        let memmap_addr = GuestAddress(0x7000);
233        let modlist_addr = GuestAddress(0x6040);
234        start_info.memmap_paddr = memmap_addr.raw_value();
235
236        let mut boot_params = BootParams::new::<hvm_start_info>(&start_info, start_info_addr);
237
238        // Error case: configure without memory map.
239        assert_eq!(
240            PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(&boot_params, &guest_memory,)
241                .err(),
242            Some(Error::MemmapTableMissing.into())
243        );
244
245        // Error case: start_info doesn't fit in guest memory.
246        let bad_start_info_addr = GuestAddress(
247            guest_memory.last_addr().raw_value() - mem::size_of::<hvm_start_info>() as u64 + 1,
248        );
249        boot_params.set_sections::<hvm_memmap_table_entry>(&memmap_entries, memmap_addr);
250        boot_params.header_start = bad_start_info_addr;
251        assert_eq!(
252            PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(&boot_params, &guest_memory,)
253                .err(),
254            Some(Error::StartInfoPastRamEnd.into())
255        );
256
257        // Error case: memory map doesn't fit in guest memory.
258        let himem_start = GuestAddress(0x100000);
259        boot_params.header_start = himem_start;
260        let bad_memmap_addr = GuestAddress(
261            guest_memory.last_addr().raw_value() - mem::size_of::<hvm_memmap_table_entry>() as u64
262                + 1,
263        );
264        boot_params.set_sections::<hvm_memmap_table_entry>(&memmap_entries, bad_memmap_addr);
265
266        assert_eq!(
267            PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(&boot_params, &guest_memory,)
268                .err(),
269            Some(Error::MemmapTablePastRamEnd.into())
270        );
271
272        boot_params.set_sections::<hvm_memmap_table_entry>(&memmap_entries, memmap_addr);
273        boot_params.set_modules::<hvm_modlist_entry>(&modlist_entries, modlist_addr);
274        assert!(PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(
275            &boot_params,
276            &guest_memory,
277        )
278        .is_ok());
279    }
280
281    #[test]
282    fn test_error_messages() {
283        assert_eq!(
284            format!("{}", Error::MemmapTableMissing),
285            "PVH Boot Configurator: no memory map was passed to the boot configurator."
286        );
287        assert_eq!(
288            format!("{}", Error::MemmapTablePastRamEnd),
289            "PVH Boot Configurator: the memory map table extends past the end of guest memory."
290        );
291        assert_eq!(
292            format!("{}", Error::MemmapTableSetup),
293            "PVH Boot Configurator: error writing memory map table to guest memory."
294        );
295        assert_eq!(format!("{}", Error::StartInfoPastRamEnd), "PVH Boot Configurator: the hvm_start_info structure extends past the end of guest memory.");
296        assert_eq!(
297            format!("{}", Error::StartInfoSetup),
298            "PVH Boot Configurator: error writing hvm_start_info to guest memory."
299        );
300    }
301}