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>(§ion, None).is_err());
251 /// let sections_start = GuestAddress(0x2000);
252 /// assert!(boot_params
253 /// .add_section::<Section>(§ion, Some(sections_start))
254 /// .is_ok());
255 /// // It can be overwritten...
256 /// assert_eq!(
257 /// boot_params
258 /// .add_section::<Section>(§ion, 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>(§ion, Some(sections_start.unchecked_sub(0x100)))
265 /// .is_err());
266 /// // Or appended...
267 /// assert_eq!(
268 /// boot_params.add_section::<Section>(§ion, 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}