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//
@@ -22,10 +24,15 @@ pub mod regs;
2224pub mod gen;
2325
2426use linux_loader:: configurator:: linux:: LinuxBootConfigurator ;
27+ use linux_loader:: configurator:: pvh:: PvhBootConfigurator ;
2528use linux_loader:: configurator:: { BootConfigurator , BootParams } ;
2629use linux_loader:: loader:: bootparam:: boot_params;
2730
28- use crate :: arch:: InitrdConfig ;
31+ use linux_loader:: loader:: elf:: start_info:: {
32+ hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
33+ } ;
34+
35+ use crate :: arch:: { BootProtocol , InitrdConfig } ;
2936use crate :: device_manager:: resources:: ResourceAllocator ;
3037use crate :: utils:: u64_to_usize;
3138use crate :: vstate:: memory:: {
@@ -35,8 +42,10 @@ use crate::vstate::memory::{
3542// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
3643// Usable normal RAM
3744const E820_RAM : u32 = 1 ;
45+
3846// Reserved area that should be avoided during memory allocations
3947const E820_RESERVED : u32 = 2 ;
48+ const MEMMAP_TYPE_RAM : u32 = 1 ;
4049
4150/// Errors thrown while configuring x86_64 system.
4251#[ derive( Debug , PartialEq , Eq , thiserror:: Error , displaydoc:: Display ) ]
@@ -49,6 +58,12 @@ pub enum ConfigurationError {
4958 ZeroPageSetup ,
5059 /// Failed to compute initrd address.
5160 InitrdAddress ,
61+ /// Error writing module entry to guest memory.
62+ ModlistSetup ,
63+ /// Error writing memory map table to guest memory.
64+ MemmapTableSetup ,
65+ /// Error writing hvm_start_info to guest memory.
66+ StartInfoSetup ,
5267}
5368
5469const FIRST_ADDR_PAST_32BITS : u64 = 1 << 32 ;
@@ -110,13 +125,146 @@ pub fn initrd_load_addr(
110125/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
111126/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
112127/// * `num_cpus` - Number of virtual CPUs the guest will have.
128+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
113129pub fn configure_system (
114130 guest_mem : & GuestMemoryMmap ,
115131 resource_allocator : & mut ResourceAllocator ,
116132 cmdline_addr : GuestAddress ,
117133 cmdline_size : usize ,
118134 initrd : & Option < InitrdConfig > ,
119135 num_cpus : u8 ,
136+ boot_prot : BootProtocol ,
137+ ) -> Result < ( ) , ConfigurationError > {
138+ // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
139+ mptable:: setup_mptable ( guest_mem, resource_allocator, num_cpus) . map_err ( ConfigurationError :: MpTableSetup ) ?;
140+
141+ match boot_prot {
142+ BootProtocol :: PvhBoot => {
143+ configure_pvh ( guest_mem, cmdline_addr, initrd) ?;
144+ }
145+ BootProtocol :: LinuxBoot => {
146+ configure_64bit_boot ( guest_mem, cmdline_addr, cmdline_size, initrd) ?;
147+ }
148+ }
149+
150+ Ok ( ( ) )
151+ }
152+
153+ fn configure_pvh (
154+ guest_mem : & GuestMemoryMmap ,
155+ cmdline_addr : GuestAddress ,
156+ initrd : & Option < InitrdConfig > ,
157+ ) -> Result < ( ) , ConfigurationError > {
158+ const XEN_HVM_START_MAGIC_VALUE : u32 = 0x336e_c578 ;
159+ let first_addr_past_32bits = GuestAddress ( FIRST_ADDR_PAST_32BITS ) ;
160+ let end_32bit_gap_start = GuestAddress ( MMIO_MEM_START ) ;
161+ let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
162+
163+ // Vector to hold modules (currently either empty or holding initrd).
164+ let mut modules: Vec < hvm_modlist_entry > = Vec :: new ( ) ;
165+ if let Some ( initrd_config) = initrd {
166+ // The initrd has been written to guest memory already, here we just need to
167+ // create the module structure that describes it.
168+ modules. push ( hvm_modlist_entry {
169+ paddr : initrd_config. address . raw_value ( ) ,
170+ size : initrd_config. size as u64 ,
171+ ..Default :: default ( )
172+ } ) ;
173+ }
174+
175+ // Vector to hold the memory maps which needs to be written to guest memory
176+ // at MEMMAP_START after all of the mappings are recorded.
177+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
178+
179+ // Create the memory map entries.
180+ add_memmap_entry ( & mut memmap, 0 , layout:: SYSTEM_MEM_START , MEMMAP_TYPE_RAM ) ?;
181+ add_memmap_entry (
182+ & mut memmap,
183+ layout:: SYSTEM_MEM_START ,
184+ layout:: SYSTEM_MEM_SIZE ,
185+ E820_RESERVED ,
186+ ) ?;
187+ let last_addr = guest_mem. last_addr ( ) ;
188+ if last_addr < end_32bit_gap_start {
189+ add_memmap_entry (
190+ & mut memmap,
191+ himem_start. raw_value ( ) ,
192+ last_addr. unchecked_offset_from ( himem_start) + 1 ,
193+ MEMMAP_TYPE_RAM ,
194+ ) ?;
195+ } else {
196+ add_memmap_entry (
197+ & mut memmap,
198+ himem_start. raw_value ( ) ,
199+ end_32bit_gap_start. unchecked_offset_from ( himem_start) ,
200+ MEMMAP_TYPE_RAM ,
201+ ) ?;
202+
203+ if last_addr > first_addr_past_32bits {
204+ add_memmap_entry (
205+ & mut memmap,
206+ first_addr_past_32bits. raw_value ( ) ,
207+ last_addr. unchecked_offset_from ( first_addr_past_32bits) + 1 ,
208+ MEMMAP_TYPE_RAM ,
209+ ) ?;
210+ }
211+ }
212+
213+ // Construct the hvm_start_info structure and serialize it into
214+ // boot_params. This will be stored at PVH_INFO_START address, and %rbx
215+ // will be initialized to contain PVH_INFO_START prior to starting the
216+ // guest, as required by the PVH ABI.
217+ let mut start_info = hvm_start_info {
218+ magic : XEN_HVM_START_MAGIC_VALUE ,
219+ version : 1 ,
220+ cmdline_paddr : cmdline_addr. raw_value ( ) ,
221+ memmap_paddr : layout:: MEMMAP_START ,
222+ memmap_entries : memmap. len ( ) as u32 ,
223+ nr_modules : modules. len ( ) as u32 ,
224+ ..Default :: default ( )
225+ } ;
226+ if !modules. is_empty ( ) {
227+ start_info. modlist_paddr = layout:: MODLIST_START ;
228+ }
229+ let mut boot_params =
230+ BootParams :: new :: < hvm_start_info > ( & start_info, GuestAddress ( layout:: PVH_INFO_START ) ) ;
231+
232+ // Copy the vector with the memmap table to the MEMMAP_START address
233+ // which is already saved in the memmap_paddr field of hvm_start_info struct.
234+ boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap, GuestAddress ( layout:: MEMMAP_START ) ) ;
235+
236+ // Copy the vector with the modules list to the MODLIST_START address.
237+ // Note that we only set the modlist_paddr address if there is a nonzero
238+ // number of modules, but serializing an empty list is harmless.
239+ boot_params. set_modules :: < hvm_modlist_entry > ( & modules, GuestAddress ( layout:: MODLIST_START ) ) ;
240+
241+ // Write the hvm_start_info struct to guest memory.
242+ PvhBootConfigurator :: write_bootparams ( & boot_params, guest_mem)
243+ . map_err ( |_| ConfigurationError :: StartInfoSetup )
244+ }
245+
246+ fn add_memmap_entry (
247+ memmap : & mut Vec < hvm_memmap_table_entry > ,
248+ addr : u64 ,
249+ size : u64 ,
250+ mem_type : u32 ,
251+ ) -> Result < ( ) , ConfigurationError > {
252+ // Add the table entry to the vector
253+ memmap. push ( hvm_memmap_table_entry {
254+ addr,
255+ size,
256+ type_ : mem_type,
257+ reserved : 0 ,
258+ } ) ;
259+
260+ Ok ( ( ) )
261+ }
262+
263+ fn configure_64bit_boot (
264+ guest_mem : & GuestMemoryMmap ,
265+ cmdline_addr : GuestAddress ,
266+ cmdline_size : usize ,
267+ initrd : & Option < InitrdConfig > ,
120268) -> Result < ( ) , ConfigurationError > {
121269 const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
122270 const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -127,15 +275,10 @@ pub fn configure_system(
127275
128276 let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
129277
130- // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
131- mptable:: setup_mptable ( guest_mem, resource_allocator, num_cpus) ?;
278+ let mut params = boot_params:: default ( ) ;
132279
133280 // Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
134- let mut params = boot_params {
135- acpi_rsdp_addr : layout:: RSDP_ADDR ,
136- ..Default :: default ( )
137- } ;
138-
281+ params. acpi_rsdp_addr = layout:: RSDP_ADDR ;
139282 params. hdr . type_of_loader = KERNEL_LOADER_OTHER ;
140283 params. hdr . boot_flag = KERNEL_BOOT_FLAG_MAGIC ;
141284 params. hdr . header = KERNEL_HDR_MAGIC ;
@@ -246,7 +389,7 @@ mod tests {
246389 let gm = single_region_mem ( 0x10000 ) ;
247390 let mut resource_allocator = ResourceAllocator :: new ( ) . unwrap ( ) ;
248391 let config_err =
249- configure_system ( & gm, & mut resource_allocator, GuestAddress ( 0 ) , 0 , & None , 1 ) ;
392+ configure_system ( & gm, & mut resource_allocator, GuestAddress ( 0 ) , 0 , & None , 1 , BootProtocol :: LinuxBoot ) ;
250393 assert_eq ! (
251394 config_err. unwrap_err( ) ,
252395 super :: ConfigurationError :: MpTableSetup ( mptable:: MptableError :: NotEnoughMemory )
@@ -263,6 +406,17 @@ mod tests {
263406 0 ,
264407 & None ,
265408 no_vcpus,
409+ BootProtocol :: LinuxBoot ,
410+ )
411+ . unwrap ( ) ;
412+ configure_system (
413+ & gm,
414+ & mut resource_allocator,
415+ GuestAddress ( 0 ) ,
416+ 0 ,
417+ & None ,
418+ no_vcpus,
419+ BootProtocol :: PvhBoot ,
266420 )
267421 . unwrap ( ) ;
268422
@@ -277,6 +431,17 @@ mod tests {
277431 0 ,
278432 & None ,
279433 no_vcpus,
434+ BootProtocol :: LinuxBoot ,
435+ )
436+ . unwrap ( ) ;
437+ configure_system (
438+ & gm,
439+ & mut resource_allocator,
440+ GuestAddress ( 0 ) ,
441+ 0 ,
442+ & None ,
443+ no_vcpus,
444+ BootProtocol :: PvhBoot ,
280445 )
281446 . unwrap ( ) ;
282447
@@ -291,6 +456,17 @@ mod tests {
291456 0 ,
292457 & None ,
293458 no_vcpus,
459+ BootProtocol :: LinuxBoot ,
460+ )
461+ . unwrap ( ) ;
462+ configure_system (
463+ & gm,
464+ & mut resource_allocator,
465+ GuestAddress ( 0 ) ,
466+ 0 ,
467+ & None ,
468+ no_vcpus,
469+ BootProtocol :: PvhBoot ,
294470 )
295471 . unwrap ( ) ;
296472 }
@@ -334,4 +510,31 @@ mod tests {
334510 )
335511 . is_err( ) ) ;
336512 }
513+
514+ #[ test]
515+ fn test_add_memmap_entry ( ) {
516+ const MEMMAP_TYPE_RESERVED : u32 = 2 ;
517+
518+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
519+
520+ let expected_memmap = vec ! [
521+ hvm_memmap_table_entry {
522+ addr: 0x0 ,
523+ size: 0x1000 ,
524+ type_: MEMMAP_TYPE_RAM ,
525+ ..Default :: default ( )
526+ } ,
527+ hvm_memmap_table_entry {
528+ addr: 0x10000 ,
529+ size: 0xa000 ,
530+ type_: MEMMAP_TYPE_RESERVED ,
531+ ..Default :: default ( )
532+ } ,
533+ ] ;
534+
535+ add_memmap_entry ( & mut memmap, 0 , 0x1000 , MEMMAP_TYPE_RAM ) . unwrap ( ) ;
536+ add_memmap_entry ( & mut memmap, 0x10000 , 0xa000 , MEMMAP_TYPE_RESERVED ) . unwrap ( ) ;
537+
538+ assert_eq ! ( format!( "{:?}" , memmap) , format!( "{:?}" , expected_memmap) ) ;
539+ }
337540}
0 commit comments