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//
@@ -19,14 +21,20 @@ pub mod msr;
1921pub mod regs;
2022
2123use linux_loader:: configurator:: linux:: LinuxBootConfigurator ;
24+ use linux_loader:: configurator:: pvh:: PvhBootConfigurator ;
2225use linux_loader:: configurator:: { BootConfigurator , BootParams } ;
2326use linux_loader:: loader:: bootparam:: boot_params;
27+ use linux_loader:: loader:: elf:: start_info:: {
28+ hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
29+ } ;
2430use utils:: vm_memory:: { Address , GuestAddress , GuestMemory , GuestMemoryMmap , GuestMemoryRegion } ;
2531
32+ use super :: BootProtocol ;
2633use crate :: arch:: InitrdConfig ;
2734
2835// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
2936const E820_RAM : u32 = 1 ;
37+ const MEMMAP_TYPE_RAM : u32 = 1 ;
3038
3139/// Errors thrown while configuring x86_64 system.
3240#[ derive( Debug , PartialEq , Eq , derive_more:: From ) ]
@@ -39,6 +47,12 @@ pub enum ConfigurationError {
3947 ZeroPageSetup ,
4048 /// Failed to compute initrd address.
4149 InitrdAddress ,
50+ /// Error writing module entry to guest memory.
51+ ModlistSetup ,
52+ /// Error writing memory map table to guest memory.
53+ MemmapTableSetup ,
54+ /// Error writing hvm_start_info to guest memory.
55+ StartInfoSetup ,
4256}
4357
4458// Where BIOS/VGA magic would live on a real PC.
@@ -102,12 +116,139 @@ pub fn initrd_load_addr(
102116/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
103117/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
104118/// * `num_cpus` - Number of virtual CPUs the guest will have.
119+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
105120pub fn configure_system (
106121 guest_mem : & GuestMemoryMmap ,
107122 cmdline_addr : GuestAddress ,
108123 cmdline_size : usize ,
109124 initrd : & Option < InitrdConfig > ,
110125 num_cpus : u8 ,
126+ boot_prot : BootProtocol ,
127+ ) -> Result < ( ) , ConfigurationError > {
128+ // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
129+ mptable:: setup_mptable ( guest_mem, num_cpus) . map_err ( ConfigurationError :: MpTableSetup ) ?;
130+
131+ match boot_prot {
132+ BootProtocol :: PvhBoot => {
133+ configure_pvh ( guest_mem, cmdline_addr, initrd) ?;
134+ }
135+ BootProtocol :: LinuxBoot => {
136+ configure_64bit_boot ( guest_mem, cmdline_addr, cmdline_size, initrd) ?;
137+ }
138+ }
139+
140+ Ok ( ( ) )
141+ }
142+
143+ fn configure_pvh (
144+ guest_mem : & GuestMemoryMmap ,
145+ cmdline_addr : GuestAddress ,
146+ initrd : & Option < InitrdConfig > ,
147+ ) -> Result < ( ) , ConfigurationError > {
148+ const XEN_HVM_START_MAGIC_VALUE : u32 = 0x336e_c578 ;
149+ let first_addr_past_32bits = GuestAddress ( FIRST_ADDR_PAST_32BITS ) ;
150+ let end_32bit_gap_start = GuestAddress ( MMIO_MEM_START ) ;
151+ let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
152+
153+ // Vector to hold modules (currently either empty or holding initrd).
154+ let mut modules: Vec < hvm_modlist_entry > = Vec :: new ( ) ;
155+ if let Some ( initrd_config) = initrd {
156+ // The initrd has been written to guest memory already, here we just need to
157+ // create the module structure that describes it.
158+ modules. push ( hvm_modlist_entry {
159+ paddr : initrd_config. address . raw_value ( ) ,
160+ size : initrd_config. size as u64 ,
161+ ..Default :: default ( )
162+ } ) ;
163+ }
164+
165+ // Vector to hold the memory maps which needs to be written to guest memory
166+ // at MEMMAP_START after all of the mappings are recorded.
167+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
168+
169+ // Create the memory map entries.
170+ add_memmap_entry ( & mut memmap, 0 , EBDA_START , MEMMAP_TYPE_RAM ) ?;
171+ let last_addr = guest_mem. last_addr ( ) ;
172+ if last_addr < end_32bit_gap_start {
173+ add_memmap_entry (
174+ & mut memmap,
175+ himem_start. raw_value ( ) ,
176+ last_addr. unchecked_offset_from ( himem_start) + 1 ,
177+ MEMMAP_TYPE_RAM ,
178+ ) ?;
179+ } else {
180+ add_memmap_entry (
181+ & mut memmap,
182+ himem_start. raw_value ( ) ,
183+ end_32bit_gap_start. unchecked_offset_from ( himem_start) ,
184+ MEMMAP_TYPE_RAM ,
185+ ) ?;
186+
187+ if last_addr > first_addr_past_32bits {
188+ add_memmap_entry (
189+ & mut memmap,
190+ first_addr_past_32bits. raw_value ( ) ,
191+ last_addr. unchecked_offset_from ( first_addr_past_32bits) + 1 ,
192+ MEMMAP_TYPE_RAM ,
193+ ) ?;
194+ }
195+ }
196+
197+ // Construct the hvm_start_info structure and serialize it into
198+ // boot_params. This will be stored at PVH_INFO_START address, and %rbx
199+ // will be initialized to contain PVH_INFO_START prior to starting the
200+ // guest, as required by the PVH ABI.
201+ let mut start_info = hvm_start_info {
202+ magic : XEN_HVM_START_MAGIC_VALUE ,
203+ version : 1 ,
204+ cmdline_paddr : cmdline_addr. raw_value ( ) ,
205+ memmap_paddr : layout:: MEMMAP_START ,
206+ memmap_entries : memmap. len ( ) as u32 ,
207+ nr_modules : modules. len ( ) as u32 ,
208+ ..Default :: default ( )
209+ } ;
210+ if !modules. is_empty ( ) {
211+ start_info. modlist_paddr = layout:: MODLIST_START ;
212+ }
213+ let mut boot_params =
214+ BootParams :: new :: < hvm_start_info > ( & start_info, GuestAddress ( layout:: PVH_INFO_START ) ) ;
215+
216+ // Copy the vector with the memmap table to the MEMMAP_START address
217+ // which is already saved in the memmap_paddr field of hvm_start_info struct.
218+ boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap, GuestAddress ( layout:: MEMMAP_START ) ) ;
219+
220+ // Copy the vector with the modules list to the MODLIST_START address.
221+ // Note that we only set the modlist_paddr address if there is a nonzero
222+ // number of modules, but serializing an empty list is harmless.
223+ boot_params. set_modules :: < hvm_modlist_entry > ( & modules, GuestAddress ( layout:: MODLIST_START ) ) ;
224+
225+ // Write the hvm_start_info struct to guest memory.
226+ PvhBootConfigurator :: write_bootparams ( & boot_params, guest_mem)
227+ . map_err ( |_| ConfigurationError :: StartInfoSetup )
228+ }
229+
230+ fn add_memmap_entry (
231+ memmap : & mut Vec < hvm_memmap_table_entry > ,
232+ addr : u64 ,
233+ size : u64 ,
234+ mem_type : u32 ,
235+ ) -> Result < ( ) , ConfigurationError > {
236+ // Add the table entry to the vector
237+ memmap. push ( hvm_memmap_table_entry {
238+ addr,
239+ size,
240+ type_ : mem_type,
241+ reserved : 0 ,
242+ } ) ;
243+
244+ Ok ( ( ) )
245+ }
246+
247+ fn configure_64bit_boot (
248+ guest_mem : & GuestMemoryMmap ,
249+ cmdline_addr : GuestAddress ,
250+ cmdline_size : usize ,
251+ initrd : & Option < InitrdConfig > ,
111252) -> Result < ( ) , ConfigurationError > {
112253 const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
113254 const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -118,9 +259,6 @@ pub fn configure_system(
118259
119260 let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
120261
121- // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
122- mptable:: setup_mptable ( guest_mem, num_cpus) ?;
123-
124262 let mut params = boot_params:: default ( ) ;
125263
126264 params. hdr . type_of_loader = KERNEL_LOADER_OTHER ;
@@ -225,7 +363,8 @@ mod tests {
225363 false ,
226364 )
227365 . unwrap ( ) ;
228- let config_err = configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , 1 ) ;
366+ let config_err =
367+ configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , 1 , BootProtocol :: LinuxBoot ) ;
229368 assert ! ( config_err. is_err( ) ) ;
230369 assert_eq ! (
231370 config_err. unwrap_err( ) ,
@@ -237,21 +376,72 @@ mod tests {
237376 let arch_mem_regions = arch_memory_regions ( mem_size) ;
238377 let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
239378 . unwrap ( ) ;
240- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
379+ configure_system (
380+ & gm,
381+ GuestAddress ( 0 ) ,
382+ 0 ,
383+ & None ,
384+ no_vcpus,
385+ BootProtocol :: LinuxBoot ,
386+ )
387+ . unwrap ( ) ;
388+ configure_system (
389+ & gm,
390+ GuestAddress ( 0 ) ,
391+ 0 ,
392+ & None ,
393+ no_vcpus,
394+ BootProtocol :: PvhBoot ,
395+ )
396+ . unwrap ( ) ;
241397
242398 // Now assigning some memory that is equal to the start of the 32bit memory hole.
243399 let mem_size = 3328 << 20 ;
244400 let arch_mem_regions = arch_memory_regions ( mem_size) ;
245401 let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
246402 . unwrap ( ) ;
247- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
403+ configure_system (
404+ & gm,
405+ GuestAddress ( 0 ) ,
406+ 0 ,
407+ & None ,
408+ no_vcpus,
409+ BootProtocol :: LinuxBoot ,
410+ )
411+ . unwrap ( ) ;
412+ configure_system (
413+ & gm,
414+ GuestAddress ( 0 ) ,
415+ 0 ,
416+ & None ,
417+ no_vcpus,
418+ BootProtocol :: PvhBoot ,
419+ )
420+ . unwrap ( ) ;
248421
249422 // Now assigning some memory that falls after the 32bit memory hole.
250423 let mem_size = 3330 << 20 ;
251424 let arch_mem_regions = arch_memory_regions ( mem_size) ;
252425 let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
253426 . unwrap ( ) ;
254- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
427+ configure_system (
428+ & gm,
429+ GuestAddress ( 0 ) ,
430+ 0 ,
431+ & None ,
432+ no_vcpus,
433+ BootProtocol :: LinuxBoot ,
434+ )
435+ . unwrap ( ) ;
436+ configure_system (
437+ & gm,
438+ GuestAddress ( 0 ) ,
439+ 0 ,
440+ & None ,
441+ no_vcpus,
442+ BootProtocol :: PvhBoot ,
443+ )
444+ . unwrap ( ) ;
255445 }
256446
257447 #[ test]
@@ -293,4 +483,31 @@ mod tests {
293483 )
294484 . is_err( ) ) ;
295485 }
486+
487+ #[ test]
488+ fn test_add_memmap_entry ( ) {
489+ const MEMMAP_TYPE_RESERVED : u32 = 2 ;
490+
491+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
492+
493+ let expected_memmap = vec ! [
494+ hvm_memmap_table_entry {
495+ addr: 0x0 ,
496+ size: 0x1000 ,
497+ type_: MEMMAP_TYPE_RAM ,
498+ ..Default :: default ( )
499+ } ,
500+ hvm_memmap_table_entry {
501+ addr: 0x10000 ,
502+ size: 0xa000 ,
503+ type_: MEMMAP_TYPE_RESERVED ,
504+ ..Default :: default ( )
505+ } ,
506+ ] ;
507+
508+ add_memmap_entry ( & mut memmap, 0 , 0x1000 , MEMMAP_TYPE_RAM ) . unwrap ( ) ;
509+ add_memmap_entry ( & mut memmap, 0x10000 , 0xa000 , MEMMAP_TYPE_RESERVED ) . unwrap ( ) ;
510+
511+ assert_eq ! ( format!( "{:?}" , memmap) , format!( "{:?}" , expected_memmap) ) ;
512+ }
296513}
0 commit comments