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,19 @@ 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
26- use crate :: arch:: InitrdConfig ;
32+ use crate :: arch:: { BootProtocol , InitrdConfig } ;
2733
2834// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
2935const E820_RAM : u32 = 1 ;
36+ const MEMMAP_TYPE_RAM : u32 = 1 ;
3037
3138/// Errors thrown while configuring x86_64 system.
3239#[ derive( Debug , PartialEq , Eq , derive_more:: From ) ]
@@ -39,6 +46,12 @@ pub enum ConfigurationError {
3946 ZeroPageSetup ,
4047 /// Failed to compute initrd address.
4148 InitrdAddress ,
49+ /// Error writing module entry to guest memory.
50+ ModlistSetup ,
51+ /// Error writing memory map table to guest memory.
52+ MemmapTableSetup ,
53+ /// Error writing hvm_start_info to guest memory.
54+ StartInfoSetup ,
4255}
4356
4457// Where BIOS/VGA magic would live on a real PC.
@@ -102,12 +115,139 @@ pub fn initrd_load_addr(
102115/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
103116/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
104117/// * `num_cpus` - Number of virtual CPUs the guest will have.
118+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
105119pub fn configure_system (
106120 guest_mem : & GuestMemoryMmap ,
107121 cmdline_addr : GuestAddress ,
108122 cmdline_size : usize ,
109123 initrd : & Option < InitrdConfig > ,
110124 num_cpus : u8 ,
125+ boot_prot : BootProtocol ,
126+ ) -> Result < ( ) , ConfigurationError > {
127+ // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
128+ mptable:: setup_mptable ( guest_mem, num_cpus) . map_err ( ConfigurationError :: MpTableSetup ) ?;
129+
130+ match boot_prot {
131+ BootProtocol :: PvhBoot => {
132+ configure_pvh ( guest_mem, cmdline_addr, initrd) ?;
133+ }
134+ BootProtocol :: LinuxBoot => {
135+ configure_64bit_boot ( guest_mem, cmdline_addr, cmdline_size, initrd) ?;
136+ }
137+ }
138+
139+ Ok ( ( ) )
140+ }
141+
142+ fn configure_pvh (
143+ guest_mem : & GuestMemoryMmap ,
144+ cmdline_addr : GuestAddress ,
145+ initrd : & Option < InitrdConfig > ,
146+ ) -> Result < ( ) , ConfigurationError > {
147+ const XEN_HVM_START_MAGIC_VALUE : u32 = 0x336e_c578 ;
148+ let first_addr_past_32bits = GuestAddress ( FIRST_ADDR_PAST_32BITS ) ;
149+ let end_32bit_gap_start = GuestAddress ( MMIO_MEM_START ) ;
150+ let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
151+
152+ // Vector to hold modules (currently either empty or holding initrd).
153+ let mut modules: Vec < hvm_modlist_entry > = Vec :: new ( ) ;
154+ if let Some ( initrd_config) = initrd {
155+ // The initrd has been written to guest memory already, here we just need to
156+ // create the module structure that describes it.
157+ modules. push ( hvm_modlist_entry {
158+ paddr : initrd_config. address . raw_value ( ) ,
159+ size : initrd_config. size as u64 ,
160+ ..Default :: default ( )
161+ } ) ;
162+ }
163+
164+ // Vector to hold the memory maps which needs to be written to guest memory
165+ // at MEMMAP_START after all of the mappings are recorded.
166+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
167+
168+ // Create the memory map entries.
169+ add_memmap_entry ( & mut memmap, 0 , EBDA_START , MEMMAP_TYPE_RAM ) ?;
170+ let last_addr = guest_mem. last_addr ( ) ;
171+ if last_addr < end_32bit_gap_start {
172+ add_memmap_entry (
173+ & mut memmap,
174+ himem_start. raw_value ( ) ,
175+ last_addr. unchecked_offset_from ( himem_start) + 1 ,
176+ MEMMAP_TYPE_RAM ,
177+ ) ?;
178+ } else {
179+ add_memmap_entry (
180+ & mut memmap,
181+ himem_start. raw_value ( ) ,
182+ end_32bit_gap_start. unchecked_offset_from ( himem_start) ,
183+ MEMMAP_TYPE_RAM ,
184+ ) ?;
185+
186+ if last_addr > first_addr_past_32bits {
187+ add_memmap_entry (
188+ & mut memmap,
189+ first_addr_past_32bits. raw_value ( ) ,
190+ last_addr. unchecked_offset_from ( first_addr_past_32bits) + 1 ,
191+ MEMMAP_TYPE_RAM ,
192+ ) ?;
193+ }
194+ }
195+
196+ // Construct the hvm_start_info structure and serialize it into
197+ // boot_params. This will be stored at PVH_INFO_START address, and %rbx
198+ // will be initialized to contain PVH_INFO_START prior to starting the
199+ // guest, as required by the PVH ABI.
200+ let mut start_info = hvm_start_info {
201+ magic : XEN_HVM_START_MAGIC_VALUE ,
202+ version : 1 ,
203+ cmdline_paddr : cmdline_addr. raw_value ( ) ,
204+ memmap_paddr : layout:: MEMMAP_START ,
205+ memmap_entries : memmap. len ( ) as u32 ,
206+ nr_modules : modules. len ( ) as u32 ,
207+ ..Default :: default ( )
208+ } ;
209+ if !modules. is_empty ( ) {
210+ start_info. modlist_paddr = layout:: MODLIST_START ;
211+ }
212+ let mut boot_params =
213+ BootParams :: new :: < hvm_start_info > ( & start_info, GuestAddress ( layout:: PVH_INFO_START ) ) ;
214+
215+ // Copy the vector with the memmap table to the MEMMAP_START address
216+ // which is already saved in the memmap_paddr field of hvm_start_info struct.
217+ boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap, GuestAddress ( layout:: MEMMAP_START ) ) ;
218+
219+ // Copy the vector with the modules list to the MODLIST_START address.
220+ // Note that we only set the modlist_paddr address if there is a nonzero
221+ // number of modules, but serializing an empty list is harmless.
222+ boot_params. set_modules :: < hvm_modlist_entry > ( & modules, GuestAddress ( layout:: MODLIST_START ) ) ;
223+
224+ // Write the hvm_start_info struct to guest memory.
225+ PvhBootConfigurator :: write_bootparams ( & boot_params, guest_mem)
226+ . map_err ( |_| ConfigurationError :: StartInfoSetup )
227+ }
228+
229+ fn add_memmap_entry (
230+ memmap : & mut Vec < hvm_memmap_table_entry > ,
231+ addr : u64 ,
232+ size : u64 ,
233+ mem_type : u32 ,
234+ ) -> Result < ( ) , ConfigurationError > {
235+ // Add the table entry to the vector
236+ memmap. push ( hvm_memmap_table_entry {
237+ addr,
238+ size,
239+ type_ : mem_type,
240+ reserved : 0 ,
241+ } ) ;
242+
243+ Ok ( ( ) )
244+ }
245+
246+ fn configure_64bit_boot (
247+ guest_mem : & GuestMemoryMmap ,
248+ cmdline_addr : GuestAddress ,
249+ cmdline_size : usize ,
250+ initrd : & Option < InitrdConfig > ,
111251) -> Result < ( ) , ConfigurationError > {
112252 const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
113253 const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -118,9 +258,6 @@ pub fn configure_system(
118258
119259 let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
120260
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-
124261 let mut params = boot_params:: default ( ) ;
125262
126263 params. hdr . type_of_loader = KERNEL_LOADER_OTHER ;
@@ -225,7 +362,8 @@ mod tests {
225362 false ,
226363 )
227364 . unwrap ( ) ;
228- let config_err = configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , 1 ) ;
365+ let config_err =
366+ configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , 1 , BootProtocol :: LinuxBoot ) ;
229367 assert ! ( config_err. is_err( ) ) ;
230368 assert_eq ! (
231369 config_err. unwrap_err( ) ,
@@ -237,21 +375,72 @@ mod tests {
237375 let arch_mem_regions = arch_memory_regions ( mem_size) ;
238376 let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
239377 . unwrap ( ) ;
240- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
378+ configure_system (
379+ & gm,
380+ GuestAddress ( 0 ) ,
381+ 0 ,
382+ & None ,
383+ no_vcpus,
384+ BootProtocol :: LinuxBoot ,
385+ )
386+ . unwrap ( ) ;
387+ configure_system (
388+ & gm,
389+ GuestAddress ( 0 ) ,
390+ 0 ,
391+ & None ,
392+ no_vcpus,
393+ BootProtocol :: PvhBoot ,
394+ )
395+ . unwrap ( ) ;
241396
242397 // Now assigning some memory that is equal to the start of the 32bit memory hole.
243398 let mem_size = 3328 << 20 ;
244399 let arch_mem_regions = arch_memory_regions ( mem_size) ;
245400 let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
246401 . unwrap ( ) ;
247- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
402+ configure_system (
403+ & gm,
404+ GuestAddress ( 0 ) ,
405+ 0 ,
406+ & None ,
407+ no_vcpus,
408+ BootProtocol :: LinuxBoot ,
409+ )
410+ . unwrap ( ) ;
411+ configure_system (
412+ & gm,
413+ GuestAddress ( 0 ) ,
414+ 0 ,
415+ & None ,
416+ no_vcpus,
417+ BootProtocol :: PvhBoot ,
418+ )
419+ . unwrap ( ) ;
248420
249421 // Now assigning some memory that falls after the 32bit memory hole.
250422 let mem_size = 3330 << 20 ;
251423 let arch_mem_regions = arch_memory_regions ( mem_size) ;
252424 let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
253425 . unwrap ( ) ;
254- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
426+ configure_system (
427+ & gm,
428+ GuestAddress ( 0 ) ,
429+ 0 ,
430+ & None ,
431+ no_vcpus,
432+ BootProtocol :: LinuxBoot ,
433+ )
434+ . unwrap ( ) ;
435+ configure_system (
436+ & gm,
437+ GuestAddress ( 0 ) ,
438+ 0 ,
439+ & None ,
440+ no_vcpus,
441+ BootProtocol :: PvhBoot ,
442+ )
443+ . unwrap ( ) ;
255444 }
256445
257446 #[ test]
@@ -293,4 +482,31 @@ mod tests {
293482 )
294483 . is_err( ) ) ;
295484 }
485+
486+ #[ test]
487+ fn test_add_memmap_entry ( ) {
488+ const MEMMAP_TYPE_RESERVED : u32 = 2 ;
489+
490+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
491+
492+ let expected_memmap = vec ! [
493+ hvm_memmap_table_entry {
494+ addr: 0x0 ,
495+ size: 0x1000 ,
496+ type_: MEMMAP_TYPE_RAM ,
497+ ..Default :: default ( )
498+ } ,
499+ hvm_memmap_table_entry {
500+ addr: 0x10000 ,
501+ size: 0xa000 ,
502+ type_: MEMMAP_TYPE_RESERVED ,
503+ ..Default :: default ( )
504+ } ,
505+ ] ;
506+
507+ add_memmap_entry ( & mut memmap, 0 , 0x1000 , MEMMAP_TYPE_RAM ) . unwrap ( ) ;
508+ add_memmap_entry ( & mut memmap, 0x10000 , 0xa000 , MEMMAP_TYPE_RESERVED ) . unwrap ( ) ;
509+
510+ assert_eq ! ( format!( "{:?}" , memmap) , format!( "{:?}" , expected_memmap) ) ;
511+ }
296512}
0 commit comments