linux_loader/configurator/
mod.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.
13//! - [BootConfigurator](trait.BootConfigurator.html): configure boot parameters.
14//! - [LinuxBootConfigurator](linux/struct.LinuxBootConfigurator.html): Linux boot protocol
15//!   parameters configurator.
16//! - [PvhBootConfigurator](pvh/struct.PvhBootConfigurator.html): PVH boot protocol parameters
17//!   configurator.
18
19#![cfg(any(feature = "elf", feature = "pe", feature = "bzimage"))]
20
21use vm_memory::{Address, ByteValued, GuestAddress, GuestMemory};
22
23use std::fmt;
24
25#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
26mod x86_64;
27#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
28pub use x86_64::*;
29
30#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
31pub mod fdt;
32
33use std::cmp::max;
34use std::mem::size_of;
35
36/// Errors specific to boot protocol configuration.
37#[derive(Debug, PartialEq, Eq)]
38pub enum Error {
39    /// Errors specific to the Linux boot protocol configuration.
40    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
41    Linux(linux::Error),
42    /// Errors specific to the PVH boot protocol configuration.
43    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
44    Pvh(pvh::Error),
45    /// Errors specific to device tree boot configuration.
46    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
47    Fdt(fdt::Error),
48
49    /// Boot parameter was specified without its starting address in guest memory.
50    MissingStartAddress,
51    /// Boot parameter address overflows.
52    Overflow,
53    /// Boot parameter address precedes the starting address.
54    InvalidAddress,
55}
56
57impl fmt::Display for Error {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        use Error::*;
60        let desc = match self {
61            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
62            Linux(ref _e) => "failed to configure boot parameter by Linux Boot protocol.",
63            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
64            Pvh(ref _e) => "failed to configure boot parameter by PVH.",
65            #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
66            Fdt(ref _e) => "failed to configure boot parameter by FDT.",
67
68            MissingStartAddress => {
69                "boot parameter was specified without its starting address in guest memory."
70            }
71            Overflow => "boot parameter address overflows.",
72            InvalidAddress => "boot parameter address precedes the starting address.",
73        };
74
75        write!(f, "Boot Configurator: {}", desc)
76    }
77}
78
79impl std::error::Error for Error {
80    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
81        use Error::*;
82        match self {
83            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
84            Linux(ref e) => Some(e),
85            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
86            Pvh(ref e) => Some(e),
87            #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
88            Fdt(ref e) => Some(e),
89
90            MissingStartAddress => None,
91            Overflow => None,
92            InvalidAddress => None,
93        }
94    }
95}
96
97/// Specialized [`Result`] type for the boot configurator.
98///
99/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
100pub type Result<T> = std::result::Result<T, Error>;
101
102/// Trait that defines interfaces for building (TBD) and configuring boot parameters.
103///
104/// Currently, this trait exposes a single function which writes user-provided boot parameters into
105/// guest memory at the user-specified addresses. It's meant to be called after the kernel is
106/// loaded and after the boot parameters are built externally (in the VMM).
107///
108/// This trait will be extended with additional functionality to build boot parameters.
109pub trait BootConfigurator {
110    /// Writes the boot parameters (configured elsewhere) into guest memory.
111    ///
112    /// The arguments are split into `header` and `sections` to accommodate different boot
113    /// protocols like Linux boot and PVH. In Linux boot, the e820 map could be considered as
114    /// `sections`, but it's already encapsulated in the `boot_params` and thus all the boot
115    /// parameters are passed through a single struct. In PVH, the memory map table is separated
116    /// from the `hvm_start_info` struct, therefore it's passed separately.
117    ///
118    /// # Arguments
119    ///
120    /// * `params` - struct containing the header section of the boot parameters, additional
121    ///              sections and modules, and their associated addresses in guest memory. These
122    ///              vary with the boot protocol used.
123    /// * `guest_memory` - guest's physical memory.
124    fn write_bootparams<M>(params: &BootParams, guest_memory: &M) -> Result<()>
125    where
126        M: GuestMemory;
127}
128
129/// Boot parameters to be written in guest memory.
130#[derive(Clone)]
131pub struct BootParams {
132    /// "Header section", always written in guest memory irrespective of boot protocol.
133    pub header: Vec<u8>,
134    /// Header section address.
135    pub header_start: GuestAddress,
136    /// Optional sections containing boot configurations (e.g. E820 map).
137    pub sections: Option<Vec<u8>>,
138    /// Sections starting address.
139    pub sections_start: Option<GuestAddress>,
140    /// Optional modules specified at boot configuration time.
141    pub modules: Option<Vec<u8>>,
142    /// Modules starting address.
143    pub modules_start: Option<GuestAddress>,
144}
145
146impl BootParams {
147    /// Creates a new [`BootParams`](struct.BootParams.html) struct with the specified header.
148    ///
149    /// # Arguments
150    ///
151    /// * `header` - [`ByteValued`] representation of mandatory boot parameters.
152    /// * `header_addr` - address in guest memory where `header` will be written.
153    ///
154    /// # Examples
155    ///
156    /// ```rust
157    /// # use linux_loader::configurator::BootParams;
158    /// # use vm_memory::{GuestAddress, ByteValued};
159    /// # #[derive(Clone, Copy, Default)]
160    /// # struct Header;
161    /// # unsafe impl ByteValued for Header {}
162    /// let boot_params = BootParams::new(&Header::default(), GuestAddress(0x1000));
163    /// ```
164    ///
165    /// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
166    pub fn new<T: ByteValued>(header: &T, header_addr: GuestAddress) -> Self {
167        BootParams {
168            header: header.as_slice().to_vec(),
169            header_start: header_addr,
170            sections: None,
171            sections_start: None,
172            modules: None,
173            modules_start: None,
174        }
175    }
176
177    /// Sets or overwrites the boot sections and associated memory address.
178    ///
179    /// Unused on `aarch64` and `riscv64` for the Linux boot protocol.
180    /// For the PVH boot protocol, the sections specify the memory map table in
181    /// [`hvm_memmap_table_entry`] structs.
182    ///
183    /// # Arguments
184    ///
185    /// * `sections` - vector of [`ByteValued`] boot configurations.
186    /// * `sections_addr` - address where the sections will be written in guest memory.
187    ///
188    /// # Examples
189    ///
190    /// ```rust
191    /// # use linux_loader::configurator::BootParams;
192    /// # use vm_memory::{ByteValued, GuestAddress};
193    /// # #[derive(Clone, Copy, Default)]
194    /// # struct Header;
195    /// # unsafe impl ByteValued for Header {}
196    /// # #[derive(Clone, Copy, Default)]
197    /// # struct Section;
198    /// # unsafe impl ByteValued for Section {}
199    /// let mut boot_params = BootParams::new(&Header::default(), GuestAddress(0x1000));
200    /// let mut sections: Vec<Section> = vec![Section::default()];
201    /// boot_params.set_sections(sections.as_slice(), GuestAddress(0x2000));
202    /// // Another call overwrites the sections.
203    /// sections.clear();
204    /// boot_params.set_sections(sections.as_slice(), GuestAddress(0x3000));
205    /// assert_eq!(boot_params.sections.unwrap().len(), 0);
206    /// assert_eq!(boot_params.sections_start.unwrap(), GuestAddress(0x3000));
207    /// ```
208    ///
209    /// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
210    /// [`hvm_memmap_table_entry`]: ../loader/elf/start_info/struct.hvm_memmap_table_entry.html
211    pub fn set_sections<T: ByteValued>(&mut self, sections: &[T], sections_addr: GuestAddress) {
212        self.sections = Some(
213            sections
214                .iter()
215                .flat_map(|section| section.as_slice().to_vec())
216                .collect(),
217        );
218        self.sections_start = Some(sections_addr);
219    }
220
221    /// Adds a boot section at the specified address (if specified and valid), or appends it.
222    ///
223    /// It's up to the caller to ensure that the section will not overlap with existing content
224    /// or leave a gap past the current sections in the list.
225    ///
226    /// # Arguments
227    ///
228    /// * `section` - [`ByteValued`] boot section element.
229    /// * `section_addr` - optional address for the section in guest memory.
230    ///
231    /// # Returns
232    ///
233    /// Starting address of the section in guest memory, or an error.
234    ///
235    /// # Examples
236    ///
237    /// ```rust
238    /// # use linux_loader::configurator::BootParams;
239    /// # use vm_memory::{Address, GuestAddress, ByteValued};
240    /// # use std::mem::size_of;
241    /// # #[derive(Clone, Copy, Default)]
242    /// # struct Header;
243    /// # unsafe impl ByteValued for Header {}
244    /// # #[derive(Clone, Copy, Default)]
245    /// # struct Section;
246    /// # unsafe impl ByteValued for Section {}
247    /// let mut boot_params = BootParams::new(&Header::default(), GuestAddress(0x1000));
248    /// let section = Section::default();
249    /// // Sections start address needs to be configured first.
250    /// assert!(boot_params.add_section::<Section>(&section, None).is_err());
251    /// let sections_start = GuestAddress(0x2000);
252    /// assert!(boot_params
253    ///     .add_section::<Section>(&section, Some(sections_start))
254    ///     .is_ok());
255    /// // It can be overwritten...
256    /// assert_eq!(
257    ///     boot_params
258    ///         .add_section::<Section>(&section, Some(sections_start))
259    ///         .unwrap(),
260    ///     sections_start
261    /// );
262    /// // But only if the address is valid.
263    /// assert!(boot_params
264    ///     .add_section::<Section>(&section, Some(sections_start.unchecked_sub(0x100)))
265    ///     .is_err());
266    /// // Or appended...
267    /// assert_eq!(
268    ///     boot_params.add_section::<Section>(&section, None).unwrap(),
269    ///     sections_start.unchecked_add(size_of::<Section>() as u64)
270    /// );
271    /// ```
272    ///
273    /// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
274    pub fn add_section<T: ByteValued>(
275        &mut self,
276        section: &T,
277        section_addr: Option<GuestAddress>,
278    ) -> Result<GuestAddress> {
279        Self::add_boot_parameter_to_list(
280            section,
281            section_addr,
282            self.sections.get_or_insert(vec![]),
283            &mut self.sections_start,
284        )
285    }
286
287    /// Sets or overwrites the boot modules and associated memory address.
288    ///
289    /// Unused on `aarch64` and `riscv64` for the Linux boot protocol.
290    /// For the PVH boot protocol, the modules are specified in [`hvm_modlist_entry`] structs.
291    ///
292    /// # Arguments
293    ///
294    /// * `modules` - vector of [`ByteValued`] boot configurations.
295    /// * `modules_addr` - address where the modules will be written in guest memory.
296    ///
297    /// # Examples
298    ///
299    /// ```rust
300    /// # use linux_loader::configurator::BootParams;
301    /// # #[derive(Clone, Copy, Default)]
302    /// # struct Header;
303    /// # unsafe impl ByteValued for Header {}
304    /// # #[derive(Clone, Copy, Default)]
305    /// # struct Module;
306    /// # unsafe impl ByteValued for Module {}
307    /// # use vm_memory::{GuestAddress, ByteValued};
308    /// let mut boot_params = BootParams::new(&Header::default(), GuestAddress(0x1000));
309    /// let mut modules: Vec<Module> = vec![Module::default()];
310    /// boot_params.set_modules(modules.as_slice(), GuestAddress(0x2000));
311    /// // Another call overwrites the sections.
312    /// modules.clear();
313    /// boot_params.set_modules(modules.as_slice(), GuestAddress(0x3000));
314    /// assert_eq!(boot_params.modules.unwrap().len(), 0);
315    /// assert_eq!(boot_params.modules_start.unwrap(), GuestAddress(0x3000));
316    /// ```
317    ///
318    /// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
319    /// [`hvm_modlist_entry`]: ../loader/elf/start_info/struct.hvm_modlist_entry.html
320    pub fn set_modules<T: ByteValued>(&mut self, modules: &[T], modules_addr: GuestAddress) {
321        self.modules = Some(
322            modules
323                .iter()
324                .flat_map(|module| module.as_slice().to_vec())
325                .collect(),
326        );
327        self.modules_start = Some(modules_addr);
328    }
329
330    /// Adds a boot module at the specified address (if specified and valid), or appends it.
331    ///
332    /// It's up to the caller to ensure that the module will not overlap with existing content
333    /// or leave a gap past the current modules in the list.
334    ///
335    /// # Arguments
336    ///
337    /// * `module` - [`ByteValued`] boot module element.
338    /// * `module_addr` - optional address for the module in guest memory.
339    ///
340    /// # Returns
341    ///
342    /// Starting address of the module in guest memory, or an error.
343    ///
344    /// # Examples
345    ///
346    /// ```rust
347    /// # use linux_loader::configurator::BootParams;
348    /// # use vm_memory::{Address, GuestAddress, ByteValued};
349    /// # use std::mem::size_of;
350    /// # #[derive(Clone, Copy, Default)]
351    /// # struct Header;
352    /// # unsafe impl ByteValued for Header {}
353    /// # #[derive(Clone, Copy, Default)]
354    /// # struct Module;
355    /// # unsafe impl ByteValued for Module {}
356    /// let mut boot_params = BootParams::new(&Header::default(), GuestAddress(0x1000));
357    /// let module = Module::default();
358    /// // Modules start address needs to be configured first.
359    /// assert!(boot_params.add_module::<Module>(&module, None).is_err());
360    /// let modules_start = GuestAddress(0x2000);
361    /// assert!(boot_params
362    ///     .add_module::<Module>(&module, Some(modules_start))
363    ///     .is_ok());
364    /// // It can be overwritten...
365    /// assert_eq!(
366    ///     boot_params
367    ///         .add_module::<Module>(&module, Some(modules_start))
368    ///         .unwrap(),
369    ///     modules_start
370    /// );
371    /// // But only if the address is valid.
372    /// assert!(boot_params
373    ///     .add_module::<Module>(&module, Some(modules_start.unchecked_sub(0x100)))
374    ///     .is_err());
375    /// // Or appended...
376    /// assert_eq!(
377    ///     boot_params.add_module::<Module>(&module, None).unwrap(),
378    ///     modules_start.unchecked_add(size_of::<Module>() as u64)
379    /// );
380    /// ```
381    ///
382    /// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
383    pub fn add_module<T: ByteValued>(
384        &mut self,
385        module: &T,
386        module_addr: Option<GuestAddress>,
387    ) -> Result<GuestAddress> {
388        Self::add_boot_parameter_to_list(
389            module,
390            module_addr,
391            self.modules.get_or_insert(vec![]),
392            &mut self.modules_start,
393        )
394    }
395
396    /// Adds a boot parameter (section or module) to a byte buffer.
397    ///
398    /// Initializes the buffer and corresponding starting address, if necessary.
399    fn add_boot_parameter_to_list<T: ByteValued>(
400        elem: &T,
401        elem_start_opt: Option<GuestAddress>,
402        bytes_acc: &mut Vec<u8>,
403        list_start_opt: &mut Option<GuestAddress>,
404    ) -> Result<GuestAddress> {
405        if list_start_opt.is_none() {
406            *list_start_opt = elem_start_opt;
407        }
408        let list_start = list_start_opt.ok_or(Error::MissingStartAddress)?;
409        let elem_start = elem_start_opt.unwrap_or(
410            list_start
411                .checked_add(bytes_acc.len() as u64)
412                .ok_or(Error::Overflow)?,
413        );
414        let elem_off = elem_start
415            .checked_offset_from(list_start)
416            .ok_or(Error::InvalidAddress)? as usize;
417        let elem_end = elem_off + size_of::<T>();
418        bytes_acc.resize(max(elem_end, bytes_acc.len()), 0);
419        bytes_acc.splice(elem_off..elem_end, elem.as_slice().iter().cloned());
420        Ok(elem_start)
421    }
422}
423
424#[cfg(test)]
425mod tests {
426    #![allow(clippy::undocumented_unsafe_blocks)]
427    use super::*;
428
429    #[derive(Clone, Copy, Default)]
430    struct Foobar {
431        _foo: [u8; 5],
432    }
433
434    unsafe impl ByteValued for Foobar {}
435
436    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
437    struct DummyHeader {
438        _dummy: u64,
439    }
440
441    unsafe impl ByteValued for DummyHeader {}
442
443    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
444    struct DummySection {
445        _dummy: u64,
446    }
447
448    unsafe impl ByteValued for DummySection {}
449
450    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
451    struct DummyModule {
452        _dummy: u64,
453    }
454
455    unsafe impl ByteValued for DummyModule {}
456
457    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
458    struct OtherDummyModule {
459        _dummy: u64,
460    }
461
462    unsafe impl ByteValued for OtherDummyModule {}
463
464    #[test]
465    fn test_error_messages() {
466        #[cfg(target_arch = "x86_64")]
467        {
468            // Linux
469            assert_eq!(
470                format!("{}", Error::Linux(linux::Error::ZeroPagePastRamEnd)),
471                "Boot Configurator: failed to configure boot parameter by Linux Boot protocol."
472            );
473            assert_eq!(
474                format!("{}", Error::Linux(linux::Error::ZeroPageSetup)),
475                "Boot Configurator: failed to configure boot parameter by Linux Boot protocol."
476            );
477
478            // PVH
479            assert_eq!(
480                format!("{}", Error::Pvh(pvh::Error::MemmapTableMissing)),
481                "Boot Configurator: failed to configure boot parameter by PVH."
482            );
483            assert_eq!(
484                format!("{}", Error::Pvh(pvh::Error::MemmapTablePastRamEnd)),
485                "Boot Configurator: failed to configure boot parameter by PVH."
486            );
487            assert_eq!(
488                format!("{}", Error::Pvh(pvh::Error::MemmapTableSetup)),
489                "Boot Configurator: failed to configure boot parameter by PVH."
490            );
491            assert_eq!(
492                format!("{}", Error::Pvh(pvh::Error::StartInfoPastRamEnd)),
493                "Boot Configurator: failed to configure boot parameter by PVH."
494            );
495            assert_eq!(
496                format!("{}", Error::Pvh(pvh::Error::StartInfoSetup)),
497                "Boot Configurator: failed to configure boot parameter by PVH."
498            );
499        }
500
501        #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
502        // FDT
503        assert_eq!(
504            format!("{}", Error::Fdt(fdt::Error::WriteFDTToMemory)),
505            "Boot Configurator: failed to configure boot parameter by FDT."
506        );
507
508        assert_eq!(
509            format!("{}", Error::MissingStartAddress),
510            "Boot Configurator: \
511             boot parameter was specified without its starting address in guest memory."
512        );
513        assert_eq!(
514            format!("{}", Error::Overflow),
515            "Boot Configurator: boot parameter address overflows."
516        );
517        assert_eq!(
518            format!("{}", Error::InvalidAddress),
519            "Boot Configurator: boot parameter address precedes the starting address."
520        );
521    }
522
523    #[test]
524    fn test_bootparam_list_addition() {
525        let mut accumulator: Vec<u8> = vec![];
526        let start = GuestAddress(0x1000);
527        let element = Foobar::default();
528
529        // Error case: start address not specified.
530        assert_eq!(
531            format!(
532                "{:?}",
533                BootParams::add_boot_parameter_to_list(&element, None, &mut accumulator, &mut None)
534                    .err()
535            ),
536            "Some(MissingStartAddress)"
537        );
538
539        // Success case: start address is set, element address not specified - will be appended.
540        assert_eq!(
541            BootParams::add_boot_parameter_to_list(
542                &element,
543                None,
544                &mut accumulator,
545                &mut Some(start)
546            )
547            .unwrap(),
548            start
549        );
550        assert_eq!(accumulator, element.as_slice().to_vec());
551
552        // Success case: start address is unset, element address is specified.
553        let mut list_start_opt: Option<GuestAddress> = None;
554        assert_eq!(
555            BootParams::add_boot_parameter_to_list(
556                &element,
557                Some(start),
558                &mut accumulator,
559                &mut list_start_opt
560            )
561            .unwrap(),
562            start
563        );
564        assert_eq!(list_start_opt, Some(start));
565        assert_eq!(accumulator, element.as_slice().to_vec());
566
567        // Error case: start address is set, element address is specified, but precedes start.
568        assert_eq!(
569            format!(
570                "{:?}",
571                BootParams::add_boot_parameter_to_list(
572                    &element,
573                    Some(start.unchecked_sub(0x100)),
574                    &mut accumulator,
575                    &mut list_start_opt
576                )
577                .err()
578            ),
579            "Some(InvalidAddress)"
580        );
581
582        // Success case: start address is set, element address is specified and valid.
583
584        // Case 1: element falls in the middle of the accumulator.
585        accumulator.clear();
586        // Start by adding 2 elements.
587        assert!(BootParams::add_boot_parameter_to_list(
588            &element,
589            None,
590            &mut accumulator,
591            &mut list_start_opt
592        )
593        .is_ok());
594        assert!(BootParams::add_boot_parameter_to_list(
595            &Foobar {
596                _foo: [2, 2, 2, 3, 3]
597            },
598            None,
599            &mut accumulator,
600            &mut list_start_opt
601        )
602        .is_ok());
603        // Sanity check.
604        #[rustfmt::skip]
605        assert_eq!(
606            accumulator,
607            &[
608                0, 0, 0, 0, 0,  // elem 0
609                2, 2, 2, 3, 3,  // elem 1
610            ]
611        );
612
613        // Add a 3rd one that overlaps with the middle of element 1.
614        assert!(BootParams::add_boot_parameter_to_list(
615            &Foobar { _foo: [1u8; 5] },
616            Some(start.unchecked_add(size_of::<Foobar>() as u64 + 3)),
617            &mut accumulator,
618            &mut list_start_opt
619        )
620        .is_ok());
621        #[rustfmt::skip]
622        assert_eq!(
623            accumulator,
624            &[
625                0, 0, 0, 0, 0,              // elem 0
626                2, 2, 2,                    // elem 1 cut short
627                1, 1, 1, 1, 1,              // elem 2
628            ]
629        );
630        assert_eq!(accumulator.len(), 13)
631    }
632
633    #[test]
634    fn test_bootparams() {
635        // Test building bootparams from header.
636        let hdr = DummyHeader::default();
637        let hdr_addr = GuestAddress(0x1000);
638        let mut bootparams = BootParams::new(&hdr, hdr_addr);
639        assert_eq!(bootparams.header, hdr.as_slice());
640        assert_eq!(bootparams.header_start, hdr_addr);
641
642        // Test setting sections.
643        let sections = vec![DummySection::default(); 2];
644        let sections_addr = GuestAddress(0x2000);
645        bootparams.set_sections::<DummySection>(sections.as_slice(), sections_addr);
646        assert_eq!(
647            bootparams.sections,
648            Some(vec![0u8; 2 * size_of::<DummySection>()])
649        );
650        assert_eq!(bootparams.sections_start, Some(sections_addr));
651
652        // Test overwriting sections.
653        let sections = vec![DummySection::default(); 3];
654        let sections_addr = GuestAddress(0x3000);
655        bootparams.set_sections::<DummySection>(sections.as_slice(), sections_addr);
656        assert_eq!(
657            bootparams.sections,
658            Some(vec![0u8; 3 * size_of::<DummySection>()])
659        );
660        assert_eq!(bootparams.sections_start, Some(sections_addr));
661
662        // Test appending a new section.
663        assert_eq!(
664            bootparams.add_section::<DummySection>(&DummySection::default(), None),
665            Ok(sections_addr.unchecked_add(3 * size_of::<DummySection>() as u64))
666        );
667        assert_eq!(
668            bootparams.sections,
669            Some(vec![0u8; 4 * size_of::<DummySection>()])
670        );
671        assert_eq!(bootparams.sections_start, Some(sections_addr));
672
673        // Test setting modules.
674        let modules = vec![DummyModule::default(); 2];
675        let modules_addr = GuestAddress(0x4000);
676        bootparams.set_modules::<DummyModule>(modules.as_slice(), modules_addr);
677        assert_eq!(
678            bootparams.modules,
679            Some(vec![0u8; 2 * size_of::<DummyModule>()])
680        );
681        assert_eq!(bootparams.modules_start, Some(modules_addr));
682
683        // Test overwriting modules.
684        let modules = vec![DummyModule::default(); 3];
685        let modules_addr = GuestAddress(0x5000);
686        bootparams.set_modules::<DummyModule>(modules.as_slice(), modules_addr);
687        assert_eq!(
688            bootparams.modules,
689            Some(vec![0u8; 3 * size_of::<DummyModule>()])
690        );
691        assert_eq!(bootparams.modules_start, Some(modules_addr));
692
693        // Test appending a new module.
694        assert_eq!(
695            bootparams.add_module::<DummyModule>(&DummyModule::default(), None),
696            Ok(modules_addr.unchecked_add(3 * size_of::<DummyModule>() as u64))
697        );
698
699        // Test appending a new module of a different type.
700        assert_eq!(
701            bootparams.add_module::<OtherDummyModule>(&OtherDummyModule::default(), None),
702            Ok(modules_addr.unchecked_add(
703                3 * size_of::<DummyModule>() as u64 + size_of::<OtherDummyModule>() as u64
704            ))
705        );
706    }
707}