1+ // Copyright © 2020, Oracle and/or its affiliates.
2+ //
13// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
24// SPDX-License-Identifier: Apache-2.0
35//
@@ -22,10 +24,14 @@ pub mod regs;
2224pub mod gen;
2325
2426use linux_loader:: configurator:: linux:: LinuxBootConfigurator ;
27+ use linux_loader:: configurator:: pvh:: PvhBootConfigurator ;
2528use linux_loader:: configurator:: { BootConfigurator , BootParams } ;
2629use linux_loader:: loader:: bootparam:: boot_params;
30+ use linux_loader:: loader:: elf:: start_info:: {
31+ hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
32+ } ;
2733
28- use crate :: arch:: InitrdConfig ;
34+ use crate :: arch:: { BootProtocol , InitrdConfig } ;
2935use crate :: device_manager:: resources:: ResourceAllocator ;
3036use crate :: utils:: u64_to_usize;
3137use crate :: vstate:: memory:: {
@@ -35,8 +41,10 @@ use crate::vstate::memory::{
3541// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
3642// Usable normal RAM
3743const E820_RAM : u32 = 1 ;
44+
3845// Reserved area that should be avoided during memory allocations
3946const E820_RESERVED : u32 = 2 ;
47+ const MEMMAP_TYPE_RAM : u32 = 1 ;
4048
4149/// Errors thrown while configuring x86_64 system.
4250#[ derive( Debug , PartialEq , Eq , thiserror:: Error , displaydoc:: Display ) ]
@@ -49,6 +57,12 @@ pub enum ConfigurationError {
4957 ZeroPageSetup ,
5058 /// Failed to compute initrd address.
5159 InitrdAddress ,
60+ /// Error writing module entry to guest memory.
61+ ModlistSetup ,
62+ /// Error writing memory map table to guest memory.
63+ MemmapTableSetup ,
64+ /// Error writing hvm_start_info to guest memory.
65+ StartInfoSetup ,
5266}
5367
5468const FIRST_ADDR_PAST_32BITS : u64 = 1 << 32 ;
@@ -110,13 +124,141 @@ pub fn initrd_load_addr(
110124/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
111125/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
112126/// * `num_cpus` - Number of virtual CPUs the guest will have.
127+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
113128pub fn configure_system (
114129 guest_mem : & GuestMemoryMmap ,
115130 resource_allocator : & mut ResourceAllocator ,
116131 cmdline_addr : GuestAddress ,
117132 cmdline_size : usize ,
118133 initrd : & Option < InitrdConfig > ,
119134 num_cpus : u8 ,
135+ boot_prot : BootProtocol ,
136+ ) -> Result < ( ) , ConfigurationError > {
137+ // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
138+ mptable:: setup_mptable ( guest_mem, resource_allocator, num_cpus)
139+ . map_err ( ConfigurationError :: MpTableSetup ) ?;
140+
141+ match boot_prot {
142+ BootProtocol :: PvhBoot => {
143+ configure_pvh ( guest_mem, cmdline_addr, initrd) ?;
144+ }
145+ BootProtocol :: LinuxBoot => {
146+ configure_64bit_boot ( guest_mem, cmdline_addr, cmdline_size, initrd) ?;
147+ }
148+ }
149+
150+ Ok ( ( ) )
151+ }
152+
153+ fn configure_pvh (
154+ guest_mem : & GuestMemoryMmap ,
155+ cmdline_addr : GuestAddress ,
156+ initrd : & Option < InitrdConfig > ,
157+ ) -> Result < ( ) , ConfigurationError > {
158+ const XEN_HVM_START_MAGIC_VALUE : u32 = 0x336e_c578 ;
159+ let first_addr_past_32bits = GuestAddress ( FIRST_ADDR_PAST_32BITS ) ;
160+ let end_32bit_gap_start = GuestAddress ( MMIO_MEM_START ) ;
161+ let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
162+
163+ // Vector to hold modules (currently either empty or holding initrd).
164+ let mut modules: Vec < hvm_modlist_entry > = Vec :: new ( ) ;
165+ if let Some ( initrd_config) = initrd {
166+ // The initrd has been written to guest memory already, here we just need to
167+ // create the module structure that describes it.
168+ modules. push ( hvm_modlist_entry {
169+ paddr : initrd_config. address . raw_value ( ) ,
170+ size : initrd_config. size as u64 ,
171+ ..Default :: default ( )
172+ } ) ;
173+ }
174+
175+ // Vector to hold the memory maps which needs to be written to guest memory
176+ // at MEMMAP_START after all of the mappings are recorded.
177+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
178+
179+ // Create the memory map entries.
180+ add_memmap_entry ( & mut memmap, 0 , layout:: SYSTEM_MEM_START , MEMMAP_TYPE_RAM ) ;
181+ add_memmap_entry (
182+ & mut memmap,
183+ layout:: SYSTEM_MEM_START ,
184+ layout:: SYSTEM_MEM_SIZE ,
185+ E820_RESERVED ,
186+ ) ;
187+ let last_addr = guest_mem. last_addr ( ) ;
188+ if last_addr < end_32bit_gap_start {
189+ add_memmap_entry (
190+ & mut memmap,
191+ himem_start. raw_value ( ) ,
192+ last_addr. unchecked_offset_from ( himem_start) + 1 ,
193+ MEMMAP_TYPE_RAM ,
194+ ) ;
195+ } else {
196+ add_memmap_entry (
197+ & mut memmap,
198+ himem_start. raw_value ( ) ,
199+ end_32bit_gap_start. unchecked_offset_from ( himem_start) ,
200+ MEMMAP_TYPE_RAM ,
201+ ) ;
202+
203+ if last_addr > first_addr_past_32bits {
204+ add_memmap_entry (
205+ & mut memmap,
206+ first_addr_past_32bits. raw_value ( ) ,
207+ last_addr. unchecked_offset_from ( first_addr_past_32bits) + 1 ,
208+ MEMMAP_TYPE_RAM ,
209+ ) ;
210+ }
211+ }
212+
213+ // Construct the hvm_start_info structure and serialize it into
214+ // boot_params. This will be stored at PVH_INFO_START address, and %rbx
215+ // will be initialized to contain PVH_INFO_START prior to starting the
216+ // guest, as required by the PVH ABI.
217+ #[ allow( clippy:: cast_possible_truncation) ] // the vec lenghts are single digit integers
218+ let mut start_info = hvm_start_info {
219+ magic : XEN_HVM_START_MAGIC_VALUE ,
220+ version : 1 ,
221+ cmdline_paddr : cmdline_addr. raw_value ( ) ,
222+ memmap_paddr : layout:: MEMMAP_START ,
223+ memmap_entries : memmap. len ( ) as u32 ,
224+ nr_modules : modules. len ( ) as u32 ,
225+ ..Default :: default ( )
226+ } ;
227+ if !modules. is_empty ( ) {
228+ start_info. modlist_paddr = layout:: MODLIST_START ;
229+ }
230+ let mut boot_params =
231+ BootParams :: new :: < hvm_start_info > ( & start_info, GuestAddress ( layout:: PVH_INFO_START ) ) ;
232+
233+ // Copy the vector with the memmap table to the MEMMAP_START address
234+ // which is already saved in the memmap_paddr field of hvm_start_info struct.
235+ boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap, GuestAddress ( layout:: MEMMAP_START ) ) ;
236+
237+ // Copy the vector with the modules list to the MODLIST_START address.
238+ // Note that we only set the modlist_paddr address if there is a nonzero
239+ // number of modules, but serializing an empty list is harmless.
240+ boot_params. set_modules :: < hvm_modlist_entry > ( & modules, GuestAddress ( layout:: MODLIST_START ) ) ;
241+
242+ // Write the hvm_start_info struct to guest memory.
243+ PvhBootConfigurator :: write_bootparams ( & boot_params, guest_mem)
244+ . map_err ( |_| ConfigurationError :: StartInfoSetup )
245+ }
246+
247+ fn add_memmap_entry ( memmap : & mut Vec < hvm_memmap_table_entry > , addr : u64 , size : u64 , mem_type : u32 ) {
248+ // Add the table entry to the vector
249+ memmap. push ( hvm_memmap_table_entry {
250+ addr,
251+ size,
252+ type_ : mem_type,
253+ reserved : 0 ,
254+ } ) ;
255+ }
256+
257+ fn configure_64bit_boot (
258+ guest_mem : & GuestMemoryMmap ,
259+ cmdline_addr : GuestAddress ,
260+ cmdline_size : usize ,
261+ initrd : & Option < InitrdConfig > ,
120262) -> Result < ( ) , ConfigurationError > {
121263 const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
122264 const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -127,9 +269,6 @@ pub fn configure_system(
127269
128270 let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
129271
130- // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
131- mptable:: setup_mptable ( guest_mem, resource_allocator, num_cpus) ?;
132-
133272 // Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
134273 let mut params = boot_params {
135274 acpi_rsdp_addr : layout:: RSDP_ADDR ,
@@ -245,8 +384,15 @@ mod tests {
245384 let no_vcpus = 4 ;
246385 let gm = single_region_mem ( 0x10000 ) ;
247386 let mut resource_allocator = ResourceAllocator :: new ( ) . unwrap ( ) ;
248- let config_err =
249- configure_system ( & gm, & mut resource_allocator, GuestAddress ( 0 ) , 0 , & None , 1 ) ;
387+ let config_err = configure_system (
388+ & gm,
389+ & mut resource_allocator,
390+ GuestAddress ( 0 ) ,
391+ 0 ,
392+ & None ,
393+ 1 ,
394+ BootProtocol :: LinuxBoot ,
395+ ) ;
250396 assert_eq ! (
251397 config_err. unwrap_err( ) ,
252398 super :: ConfigurationError :: MpTableSetup ( mptable:: MptableError :: NotEnoughMemory )
@@ -263,6 +409,17 @@ mod tests {
263409 0 ,
264410 & None ,
265411 no_vcpus,
412+ BootProtocol :: LinuxBoot ,
413+ )
414+ . unwrap ( ) ;
415+ configure_system (
416+ & gm,
417+ & mut resource_allocator,
418+ GuestAddress ( 0 ) ,
419+ 0 ,
420+ & None ,
421+ no_vcpus,
422+ BootProtocol :: PvhBoot ,
266423 )
267424 . unwrap ( ) ;
268425
@@ -277,6 +434,17 @@ mod tests {
277434 0 ,
278435 & None ,
279436 no_vcpus,
437+ BootProtocol :: LinuxBoot ,
438+ )
439+ . unwrap ( ) ;
440+ configure_system (
441+ & gm,
442+ & mut resource_allocator,
443+ GuestAddress ( 0 ) ,
444+ 0 ,
445+ & None ,
446+ no_vcpus,
447+ BootProtocol :: PvhBoot ,
280448 )
281449 . unwrap ( ) ;
282450
@@ -291,6 +459,17 @@ mod tests {
291459 0 ,
292460 & None ,
293461 no_vcpus,
462+ BootProtocol :: LinuxBoot ,
463+ )
464+ . unwrap ( ) ;
465+ configure_system (
466+ & gm,
467+ & mut resource_allocator,
468+ GuestAddress ( 0 ) ,
469+ 0 ,
470+ & None ,
471+ no_vcpus,
472+ BootProtocol :: PvhBoot ,
294473 )
295474 . unwrap ( ) ;
296475 }
@@ -334,4 +513,31 @@ mod tests {
334513 )
335514 . is_err( ) ) ;
336515 }
516+
517+ #[ test]
518+ fn test_add_memmap_entry ( ) {
519+ const MEMMAP_TYPE_RESERVED : u32 = 2 ;
520+
521+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
522+
523+ let expected_memmap = vec ! [
524+ hvm_memmap_table_entry {
525+ addr: 0x0 ,
526+ size: 0x1000 ,
527+ type_: MEMMAP_TYPE_RAM ,
528+ ..Default :: default ( )
529+ } ,
530+ hvm_memmap_table_entry {
531+ addr: 0x10000 ,
532+ size: 0xa000 ,
533+ type_: MEMMAP_TYPE_RESERVED ,
534+ ..Default :: default ( )
535+ } ,
536+ ] ;
537+
538+ add_memmap_entry ( & mut memmap, 0 , 0x1000 , MEMMAP_TYPE_RAM ) ;
539+ add_memmap_entry ( & mut memmap, 0x10000 , 0xa000 , MEMMAP_TYPE_RESERVED ) ;
540+
541+ assert_eq ! ( format!( "{:?}" , memmap) , format!( "{:?}" , expected_memmap) ) ;
542+ }
337543}
0 commit comments