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// 
@@ -17,14 +19,20 @@ pub mod msr;
1719pub  mod  regs; 
1820
1921use  linux_loader:: configurator:: linux:: LinuxBootConfigurator ; 
22+ use  linux_loader:: configurator:: pvh:: PvhBootConfigurator ; 
2023use  linux_loader:: configurator:: { BootConfigurator ,  BootParams } ; 
2124use  linux_loader:: loader:: bootparam:: boot_params; 
25+ use  linux_loader:: loader:: elf:: start_info:: { 
26+     hvm_memmap_table_entry,  hvm_modlist_entry,  hvm_start_info, 
27+ } ; 
2228use  vm_memory:: { Address ,  GuestAddress ,  GuestMemory ,  GuestMemoryMmap ,  GuestMemoryRegion } ; 
2329
30+ use  super :: BootProtocol ; 
2431use  crate :: InitrdConfig ; 
2532
2633// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31 
2734const  E820_RAM :  u32  = 1 ; 
35+ const  MEMMAP_TYPE_RAM :  u32  = 1 ; 
2836
2937/// Errors thrown while configuring x86_64 system. 
3038#[ derive( Debug ,  PartialEq ,  Eq ,  derive_more:: From ) ]  
@@ -37,6 +45,12 @@ pub enum Error {
3745     ZeroPageSetup , 
3846    /// Failed to compute initrd address. 
3947     InitrdAddress , 
48+     /// Error writing module entry to guest memory. 
49+      ModlistSetup , 
50+     /// Error writing memory map table to guest memory. 
51+      MemmapTableSetup , 
52+     /// Error writing hvm_start_info to guest memory. 
53+      StartInfoSetup , 
4054} 
4155
4256// Where BIOS/VGA magic would live on a real PC. 
@@ -97,12 +111,139 @@ pub fn initrd_load_addr(guest_mem: &GuestMemoryMmap, initrd_size: usize) -> supe
97111/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator. 
98112/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`. 
99113/// * `num_cpus` - Number of virtual CPUs the guest will have. 
114+ /// * `boot_prot` - Boot protocol that will be used to boot the guest. 
100115pub  fn  configure_system ( 
101116    guest_mem :  & GuestMemoryMmap , 
102117    cmdline_addr :  GuestAddress , 
103118    cmdline_size :  usize , 
104119    initrd :  & Option < InitrdConfig > , 
105120    num_cpus :  u8 , 
121+     boot_prot :  BootProtocol , 
122+ )  -> super :: Result < ( ) >  { 
123+     // Note that this puts the mptable at the last 1k of Linux's 640k base RAM 
124+     mptable:: setup_mptable ( guest_mem,  num_cpus) . map_err ( Error :: MpTableSetup ) ?; 
125+ 
126+     match  boot_prot { 
127+         BootProtocol :: PvhBoot  => { 
128+             configure_pvh ( guest_mem,  cmdline_addr,  initrd) ?; 
129+         } 
130+         BootProtocol :: LinuxBoot  => { 
131+             configure_64bit_boot ( guest_mem,  cmdline_addr,  cmdline_size,  initrd) ?; 
132+         } 
133+     } 
134+ 
135+     Ok ( ( ) ) 
136+ } 
137+ 
138+ fn  configure_pvh ( 
139+     guest_mem :  & GuestMemoryMmap , 
140+     cmdline_addr :  GuestAddress , 
141+     initrd :  & Option < InitrdConfig > , 
142+ )  -> super :: Result < ( ) >  { 
143+     const  XEN_HVM_START_MAGIC_VALUE :  u32  = 0x336e_c578 ; 
144+     let  first_addr_past_32bits = GuestAddress ( FIRST_ADDR_PAST_32BITS ) ; 
145+     let  end_32bit_gap_start = GuestAddress ( MMIO_MEM_START ) ; 
146+     let  himem_start = GuestAddress ( layout:: HIMEM_START ) ; 
147+ 
148+     // Vector to hold modules (currently either empty or holding initrd). 
149+     let  mut  modules:  Vec < hvm_modlist_entry >  = Vec :: new ( ) ; 
150+     if  let  Some ( initrd_config)  = initrd { 
151+         // The initrd has been written to guest memory already, here we just need to 
152+         // create the module structure that describes it. 
153+         modules. push ( hvm_modlist_entry  { 
154+             paddr :  initrd_config. address . raw_value ( ) , 
155+             size :  initrd_config. size  as  u64 , 
156+             ..Default :: default ( ) 
157+         } ) ; 
158+     } 
159+ 
160+     // Vector to hold the memory maps which needs to be written to guest memory 
161+     // at MEMMAP_START after all of the mappings are recorded. 
162+     let  mut  memmap:  Vec < hvm_memmap_table_entry >  = Vec :: new ( ) ; 
163+ 
164+     // Create the memory map entries. 
165+     add_memmap_entry ( & mut  memmap,  0 ,  EBDA_START ,  MEMMAP_TYPE_RAM ) ?; 
166+     let  last_addr = guest_mem. last_addr ( ) ; 
167+     if  last_addr < end_32bit_gap_start { 
168+         add_memmap_entry ( 
169+             & mut  memmap, 
170+             himem_start. raw_value ( )  as  u64 , 
171+             last_addr. unchecked_offset_from ( himem_start)  as  u64  + 1 , 
172+             MEMMAP_TYPE_RAM , 
173+         ) ?; 
174+     }  else  { 
175+         add_memmap_entry ( 
176+             & mut  memmap, 
177+             himem_start. raw_value ( ) , 
178+             end_32bit_gap_start. unchecked_offset_from ( himem_start) , 
179+             MEMMAP_TYPE_RAM , 
180+         ) ?; 
181+ 
182+         if  last_addr > first_addr_past_32bits { 
183+             add_memmap_entry ( 
184+                 & mut  memmap, 
185+                 first_addr_past_32bits. raw_value ( ) , 
186+                 last_addr. unchecked_offset_from ( first_addr_past_32bits)  + 1 , 
187+                 MEMMAP_TYPE_RAM , 
188+             ) ?; 
189+         } 
190+     } 
191+ 
192+     // Construct the hvm_start_info structure and serialize it into 
193+     // boot_params.  This will be stored at PVH_INFO_START address, and %rbx 
194+     // will be initialized to contain PVH_INFO_START prior to starting the 
195+     // guest, as required by the PVH ABI. 
196+     let  mut  start_info = hvm_start_info  { 
197+         magic :  XEN_HVM_START_MAGIC_VALUE , 
198+         version :  1 , 
199+         cmdline_paddr :  cmdline_addr. raw_value ( ) , 
200+         memmap_paddr :  layout:: MEMMAP_START , 
201+         memmap_entries :  memmap. len ( )  as  u32 , 
202+         nr_modules :  modules. len ( )  as  u32 , 
203+         ..Default :: default ( ) 
204+     } ; 
205+     if  !modules. is_empty ( )  { 
206+         start_info. modlist_paddr  = layout:: MODLIST_START ; 
207+     } 
208+     let  mut  boot_params =
209+         BootParams :: new :: < hvm_start_info > ( & start_info,  GuestAddress ( layout:: PVH_INFO_START ) ) ; 
210+ 
211+     // Copy the vector with the memmap table to the MEMMAP_START address 
212+     // which is already saved in the memmap_paddr field of hvm_start_info struct. 
213+     boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap,  GuestAddress ( layout:: MEMMAP_START ) ) ; 
214+ 
215+     // Copy the vector with the modules list to the MODLIST_START address. 
216+     // Note that we only set the modlist_paddr address if there is a nonzero 
217+     // number of modules, but serializing an empty list is harmless. 
218+     boot_params. set_modules :: < hvm_modlist_entry > ( & modules,  GuestAddress ( layout:: MODLIST_START ) ) ; 
219+ 
220+     // Write the hvm_start_info struct to guest memory. 
221+     PvhBootConfigurator :: write_bootparams ( & boot_params,  guest_mem) 
222+         . map_err ( |_| Error :: StartInfoSetup ) 
223+ } 
224+ 
225+ fn  add_memmap_entry ( 
226+     memmap :  & mut  Vec < hvm_memmap_table_entry > , 
227+     addr :  u64 , 
228+     size :  u64 , 
229+     mem_type :  u32 , 
230+ )  -> super :: Result < ( ) >  { 
231+     // Add the table entry to the vector 
232+     memmap. push ( hvm_memmap_table_entry  { 
233+         addr, 
234+         size, 
235+         type_ :  mem_type, 
236+         reserved :  0 , 
237+     } ) ; 
238+ 
239+     Ok ( ( ) ) 
240+ } 
241+ 
242+ fn  configure_64bit_boot ( 
243+     guest_mem :  & GuestMemoryMmap , 
244+     cmdline_addr :  GuestAddress , 
245+     cmdline_size :  usize , 
246+     initrd :  & Option < InitrdConfig > , 
106247)  -> super :: Result < ( ) >  { 
107248    const  KERNEL_BOOT_FLAG_MAGIC :  u16  = 0xaa55 ; 
108249    const  KERNEL_HDR_MAGIC :  u32  = 0x5372_6448 ; 
@@ -113,9 +254,6 @@ pub fn configure_system(
113254
114255    let  himem_start = GuestAddress ( layout:: HIMEM_START ) ; 
115256
116-     // Note that this puts the mptable at the last 1k of Linux's 640k base RAM 
117-     mptable:: setup_mptable ( guest_mem,  num_cpus) ?; 
118- 
119257    let  mut  params = boot_params:: default ( ) ; 
120258
121259    params. hdr . type_of_loader  = KERNEL_LOADER_OTHER ; 
@@ -218,7 +356,8 @@ mod tests {
218356        let  gm =
219357            vm_memory:: test_utils:: create_anon_guest_memory ( & [ ( GuestAddress ( 0 ) ,  0x10000 ) ] ,  false ) 
220358                . unwrap ( ) ; 
221-         let  config_err = configure_system ( & gm,  GuestAddress ( 0 ) ,  0 ,  & None ,  1 ) ; 
359+         let  config_err =
360+             configure_system ( & gm,  GuestAddress ( 0 ) ,  0 ,  & None ,  1 ,  BootProtocol :: LinuxBoot ) ; 
222361        assert ! ( config_err. is_err( ) ) ; 
223362        assert_eq ! ( 
224363            config_err. unwrap_err( ) , 
@@ -229,19 +368,70 @@ mod tests {
229368        let  mem_size = 128  << 20 ; 
230369        let  arch_mem_regions = arch_memory_regions ( mem_size) ; 
231370        let  gm = vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions,  false ) . unwrap ( ) ; 
232-         configure_system ( & gm,  GuestAddress ( 0 ) ,  0 ,  & None ,  no_vcpus) . unwrap ( ) ; 
371+         configure_system ( 
372+             & gm, 
373+             GuestAddress ( 0 ) , 
374+             0 , 
375+             & None , 
376+             no_vcpus, 
377+             BootProtocol :: LinuxBoot , 
378+         ) 
379+         . unwrap ( ) ; 
380+         configure_system ( 
381+             & gm, 
382+             GuestAddress ( 0 ) , 
383+             0 , 
384+             & None , 
385+             no_vcpus, 
386+             BootProtocol :: PvhBoot , 
387+         ) 
388+         . unwrap ( ) ; 
233389
234390        // Now assigning some memory that is equal to the start of the 32bit memory hole. 
235391        let  mem_size = 3328  << 20 ; 
236392        let  arch_mem_regions = arch_memory_regions ( mem_size) ; 
237393        let  gm = vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions,  false ) . unwrap ( ) ; 
238-         configure_system ( & gm,  GuestAddress ( 0 ) ,  0 ,  & None ,  no_vcpus) . unwrap ( ) ; 
394+         configure_system ( 
395+             & gm, 
396+             GuestAddress ( 0 ) , 
397+             0 , 
398+             & None , 
399+             no_vcpus, 
400+             BootProtocol :: LinuxBoot , 
401+         ) 
402+         . unwrap ( ) ; 
403+         configure_system ( 
404+             & gm, 
405+             GuestAddress ( 0 ) , 
406+             0 , 
407+             & None , 
408+             no_vcpus, 
409+             BootProtocol :: PvhBoot , 
410+         ) 
411+         . unwrap ( ) ; 
239412
240413        // Now assigning some memory that falls after the 32bit memory hole. 
241414        let  mem_size = 3330  << 20 ; 
242415        let  arch_mem_regions = arch_memory_regions ( mem_size) ; 
243416        let  gm = vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions,  false ) . unwrap ( ) ; 
244-         configure_system ( & gm,  GuestAddress ( 0 ) ,  0 ,  & None ,  no_vcpus) . unwrap ( ) ; 
417+         configure_system ( 
418+             & gm, 
419+             GuestAddress ( 0 ) , 
420+             0 , 
421+             & None , 
422+             no_vcpus, 
423+             BootProtocol :: LinuxBoot , 
424+         ) 
425+         . unwrap ( ) ; 
426+         configure_system ( 
427+             & gm, 
428+             GuestAddress ( 0 ) , 
429+             0 , 
430+             & None , 
431+             no_vcpus, 
432+             BootProtocol :: PvhBoot , 
433+         ) 
434+         . unwrap ( ) ; 
245435    } 
246436
247437    #[ test]  
@@ -283,4 +473,31 @@ mod tests {
283473        ) 
284474        . is_err( ) ) ; 
285475    } 
476+ 
477+     #[ test]  
478+     fn  test_add_memmap_entry ( )  { 
479+         const  MEMMAP_TYPE_RESERVED :  u32  = 2 ; 
480+ 
481+         let  mut  memmap:  Vec < hvm_memmap_table_entry >  = Vec :: new ( ) ; 
482+ 
483+         let  expected_memmap = vec ! [ 
484+             hvm_memmap_table_entry { 
485+                 addr:  0x0 , 
486+                 size:  0x1000 , 
487+                 type_:  MEMMAP_TYPE_RAM , 
488+                 ..Default :: default ( ) 
489+             } , 
490+             hvm_memmap_table_entry { 
491+                 addr:  0x10000 , 
492+                 size:  0xa000 , 
493+                 type_:  MEMMAP_TYPE_RESERVED , 
494+                 ..Default :: default ( ) 
495+             } , 
496+         ] ; 
497+ 
498+         add_memmap_entry ( & mut  memmap,  0 ,  0x1000 ,  MEMMAP_TYPE_RAM ) . unwrap ( ) ; 
499+         add_memmap_entry ( & mut  memmap,  0x10000 ,  0xa000 ,  MEMMAP_TYPE_RESERVED ) . unwrap ( ) ; 
500+ 
501+         assert_eq ! ( format!( "{:?}" ,  memmap) ,  format!( "{:?}" ,  expected_memmap) ) ; 
502+     } 
286503} 
0 commit comments