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