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,15 +19,41 @@ 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;
22- use vm_memory:: { Address , GuestAddress , GuestMemory , GuestMemoryMmap , GuestMemoryRegion } ;
23-
25+ use linux_loader:: loader:: elf:: start_info:: {
26+ hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
27+ } ;
28+ use vm_memory:: {
29+ Address , ByteValued , GuestAddress , GuestMemory , GuestMemoryMmap , GuestMemoryRegion ,
30+ } ;
31+
32+ use super :: BootProtocol ;
2433use crate :: InitrdConfig ;
2534
2635// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
2736const E820_RAM : u32 = 1 ;
2837
38+ // Workaround for the Rust orphan rules that guarantee trait coherence by wrapping the foreign type
39+ // in a tuple structure. Same approach is used by boot_params and BootParamsWrapper.
40+ #[ derive( Copy , Clone , Default ) ]
41+ struct StartInfoWrapper ( hvm_start_info ) ;
42+
43+ #[ derive( Copy , Clone , Default ) ]
44+ struct MemmapTableEntryWrapper ( hvm_memmap_table_entry ) ;
45+
46+ #[ derive( Copy , Clone , Default ) ]
47+ struct ModlistEntryWrapper ( hvm_modlist_entry ) ;
48+
49+ // It is safe to initialize the following structures. They are wrappers over the structures
50+ // defined by the start_info module, all of which are formed by fields of integer values.
51+ unsafe impl ByteValued for StartInfoWrapper { }
52+ unsafe impl ByteValued for MemmapTableEntryWrapper { }
53+ unsafe impl ByteValued for ModlistEntryWrapper { }
54+
55+ const MEMMAP_TYPE_RAM : u32 = 1 ;
56+
2957/// Errors thrown while configuring x86_64 system.
3058#[ derive( Debug , PartialEq , derive_more:: From ) ]
3159pub enum Error {
@@ -37,6 +65,12 @@ pub enum Error {
3765 ZeroPageSetup ,
3866 /// Failed to compute initrd address.
3967 InitrdAddress ,
68+ /// Error writing module entry to guest memory.
69+ ModlistSetup ,
70+ /// Error writing memory map table to guest memory.
71+ MemmapTableSetup ,
72+ /// Error writing hvm_start_info to guest memory.
73+ StartInfoSetup ,
4074}
4175
4276// Where BIOS/VGA magic would live on a real PC.
@@ -97,12 +131,139 @@ pub fn initrd_load_addr(guest_mem: &GuestMemoryMmap, initrd_size: usize) -> supe
97131/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
98132/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
99133/// * `num_cpus` - Number of virtual CPUs the guest will have.
134+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
100135pub fn configure_system (
101136 guest_mem : & GuestMemoryMmap ,
102137 cmdline_addr : GuestAddress ,
103138 cmdline_size : usize ,
104139 initrd : & Option < InitrdConfig > ,
105140 num_cpus : u8 ,
141+ boot_prot : BootProtocol ,
142+ ) -> super :: Result < ( ) > {
143+ // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
144+ mptable:: setup_mptable ( guest_mem, num_cpus) . map_err ( Error :: MpTableSetup ) ?;
145+
146+ match boot_prot {
147+ BootProtocol :: PvhBoot => {
148+ configure_pvh ( guest_mem, cmdline_addr, initrd) ?;
149+ }
150+ BootProtocol :: LinuxBoot => {
151+ configure_64bit_boot ( guest_mem, cmdline_addr, cmdline_size, initrd) ?;
152+ }
153+ }
154+
155+ Ok ( ( ) )
156+ }
157+
158+ fn configure_pvh (
159+ guest_mem : & GuestMemoryMmap ,
160+ cmdline_addr : GuestAddress ,
161+ initrd : & Option < InitrdConfig > ,
162+ ) -> super :: Result < ( ) > {
163+ const XEN_HVM_START_MAGIC_VALUE : u32 = 0x336e_c578 ;
164+ let first_addr_past_32bits = GuestAddress ( FIRST_ADDR_PAST_32BITS ) ;
165+ let end_32bit_gap_start = GuestAddress ( MMIO_MEM_START ) ;
166+ let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
167+
168+ // Vector to hold modules (currently either empty or holding initrd).
169+ let mut modules: Vec < hvm_modlist_entry > = Vec :: new ( ) ;
170+ if let Some ( initrd_config) = initrd {
171+ // The initrd has been written to guest memory already, here we just need to
172+ // create the module structure that describes it.
173+ modules. push ( hvm_modlist_entry {
174+ paddr : initrd_config. address . raw_value ( ) ,
175+ size : initrd_config. size as u64 ,
176+ ..Default :: default ( )
177+ } ) ;
178+ }
179+
180+ // Vector to hold the memory maps which needs to be written to guest memory
181+ // at MEMMAP_START after all of the mappings are recorded.
182+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
183+
184+ // Create the memory map entries.
185+ add_memmap_entry ( & mut memmap, 0 , EBDA_START , MEMMAP_TYPE_RAM ) ?;
186+ let last_addr = guest_mem. last_addr ( ) ;
187+ if last_addr < end_32bit_gap_start {
188+ add_memmap_entry (
189+ & mut memmap,
190+ himem_start. raw_value ( ) as u64 ,
191+ last_addr. unchecked_offset_from ( himem_start) as u64 + 1 ,
192+ MEMMAP_TYPE_RAM ,
193+ ) ?;
194+ } else {
195+ add_memmap_entry (
196+ & mut memmap,
197+ himem_start. raw_value ( ) ,
198+ end_32bit_gap_start. unchecked_offset_from ( himem_start) ,
199+ MEMMAP_TYPE_RAM ,
200+ ) ?;
201+
202+ if last_addr > first_addr_past_32bits {
203+ add_memmap_entry (
204+ & mut memmap,
205+ first_addr_past_32bits. raw_value ( ) ,
206+ last_addr. unchecked_offset_from ( first_addr_past_32bits) + 1 ,
207+ MEMMAP_TYPE_RAM ,
208+ ) ?;
209+ }
210+ }
211+
212+ // Construct the hvm_start_info structure and serialize it into
213+ // boot_params. This will be stored at PVH_INFO_START address, and %rbx
214+ // will be initialized to contain PVH_INFO_START prior to starting the
215+ // guest, as required by the PVH ABI.
216+ let mut start_info = hvm_start_info {
217+ magic : XEN_HVM_START_MAGIC_VALUE ,
218+ version : 1 ,
219+ cmdline_paddr : cmdline_addr. raw_value ( ) ,
220+ memmap_paddr : layout:: MEMMAP_START ,
221+ memmap_entries : memmap. len ( ) as u32 ,
222+ nr_modules : modules. len ( ) as u32 ,
223+ ..Default :: default ( )
224+ } ;
225+ if !modules. is_empty ( ) {
226+ start_info. modlist_paddr = layout:: MODLIST_START ;
227+ }
228+ let mut boot_params =
229+ BootParams :: new :: < hvm_start_info > ( & start_info, GuestAddress ( layout:: PVH_INFO_START ) ) ;
230+
231+ // Copy the vector with the memmap table to the MEMMAP_START address
232+ // which is already saved in the memmap_paddr field of hvm_start_info struct.
233+ boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap, GuestAddress ( layout:: MEMMAP_START ) ) ;
234+
235+ // Copy the vector with the modules list to the MODLIST_START address.
236+ // Note that we only set the modlist_paddr address if there is a nonzero
237+ // number of modules, but serializing an empty list is harmless.
238+ boot_params. set_modules :: < hvm_modlist_entry > ( & modules, GuestAddress ( layout:: MODLIST_START ) ) ;
239+
240+ // Write the hvm_start_info struct to guest memory.
241+ PvhBootConfigurator :: write_bootparams ( & boot_params, guest_mem)
242+ . map_err ( |_| Error :: StartInfoSetup )
243+ }
244+
245+ fn add_memmap_entry (
246+ memmap : & mut Vec < hvm_memmap_table_entry > ,
247+ addr : u64 ,
248+ size : u64 ,
249+ mem_type : u32 ,
250+ ) -> super :: Result < ( ) > {
251+ // Add the table entry to the vector
252+ memmap. push ( hvm_memmap_table_entry {
253+ addr,
254+ size,
255+ type_ : mem_type,
256+ reserved : 0 ,
257+ } ) ;
258+
259+ Ok ( ( ) )
260+ }
261+
262+ fn configure_64bit_boot (
263+ guest_mem : & GuestMemoryMmap ,
264+ cmdline_addr : GuestAddress ,
265+ cmdline_size : usize ,
266+ initrd : & Option < InitrdConfig > ,
106267) -> super :: Result < ( ) > {
107268 const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
108269 const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -113,9 +274,6 @@ pub fn configure_system(
113274
114275 let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
115276
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-
119277 let mut params = boot_params:: default ( ) ;
120278
121279 params. hdr . type_of_loader = KERNEL_LOADER_OTHER ;
@@ -218,7 +376,8 @@ mod tests {
218376 let gm =
219377 vm_memory:: test_utils:: create_anon_guest_memory ( & [ ( GuestAddress ( 0 ) , 0x10000 ) ] , false )
220378 . unwrap ( ) ;
221- let config_err = configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , 1 ) ;
379+ let config_err =
380+ configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , 1 , BootProtocol :: LinuxBoot ) ;
222381 assert ! ( config_err. is_err( ) ) ;
223382 assert_eq ! (
224383 config_err. unwrap_err( ) ,
@@ -229,19 +388,70 @@ mod tests {
229388 let mem_size = 128 << 20 ;
230389 let arch_mem_regions = arch_memory_regions ( mem_size) ;
231390 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 ( ) ;
391+ configure_system (
392+ & gm,
393+ GuestAddress ( 0 ) ,
394+ 0 ,
395+ & None ,
396+ no_vcpus,
397+ BootProtocol :: LinuxBoot ,
398+ )
399+ . unwrap ( ) ;
400+ configure_system (
401+ & gm,
402+ GuestAddress ( 0 ) ,
403+ 0 ,
404+ & None ,
405+ no_vcpus,
406+ BootProtocol :: PvhBoot ,
407+ )
408+ . unwrap ( ) ;
233409
234410 // Now assigning some memory that is equal to the start of the 32bit memory hole.
235411 let mem_size = 3328 << 20 ;
236412 let arch_mem_regions = arch_memory_regions ( mem_size) ;
237413 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 ( ) ;
414+ configure_system (
415+ & gm,
416+ GuestAddress ( 0 ) ,
417+ 0 ,
418+ & None ,
419+ no_vcpus,
420+ BootProtocol :: LinuxBoot ,
421+ )
422+ . unwrap ( ) ;
423+ configure_system (
424+ & gm,
425+ GuestAddress ( 0 ) ,
426+ 0 ,
427+ & None ,
428+ no_vcpus,
429+ BootProtocol :: PvhBoot ,
430+ )
431+ . unwrap ( ) ;
239432
240433 // Now assigning some memory that falls after the 32bit memory hole.
241434 let mem_size = 3330 << 20 ;
242435 let arch_mem_regions = arch_memory_regions ( mem_size) ;
243436 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 ( ) ;
437+ configure_system (
438+ & gm,
439+ GuestAddress ( 0 ) ,
440+ 0 ,
441+ & None ,
442+ no_vcpus,
443+ BootProtocol :: LinuxBoot ,
444+ )
445+ . unwrap ( ) ;
446+ configure_system (
447+ & gm,
448+ GuestAddress ( 0 ) ,
449+ 0 ,
450+ & None ,
451+ no_vcpus,
452+ BootProtocol :: PvhBoot ,
453+ )
454+ . unwrap ( ) ;
245455 }
246456
247457 #[ test]
@@ -283,4 +493,31 @@ mod tests {
283493 )
284494 . is_err( ) ) ;
285495 }
496+
497+ #[ test]
498+ fn test_add_memmap_entry ( ) {
499+ const MEMMAP_TYPE_RESERVED : u32 = 2 ;
500+
501+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
502+
503+ let expected_memmap = vec ! [
504+ hvm_memmap_table_entry {
505+ addr: 0x0 ,
506+ size: 0x1000 ,
507+ type_: MEMMAP_TYPE_RAM ,
508+ ..Default :: default ( )
509+ } ,
510+ hvm_memmap_table_entry {
511+ addr: 0x10000 ,
512+ size: 0xa000 ,
513+ type_: MEMMAP_TYPE_RESERVED ,
514+ ..Default :: default ( )
515+ } ,
516+ ] ;
517+
518+ add_memmap_entry ( & mut memmap, 0 , 0x1000 , MEMMAP_TYPE_RAM ) . unwrap ( ) ;
519+ add_memmap_entry ( & mut memmap, 0x10000 , 0xa000 , MEMMAP_TYPE_RESERVED ) . unwrap ( ) ;
520+
521+ assert_eq ! ( format!( "{:?}" , memmap) , format!( "{:?}" , expected_memmap) ) ;
522+ }
286523}
0 commit comments