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,14 @@ 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;
30+ use linux_loader:: loader:: elf:: start_info:: {
31+ hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
32+ } ;
2733
28- use crate :: arch:: InitrdConfig ;
34+ use crate :: arch:: { BootProtocol , InitrdConfig } ;
2935use crate :: device_manager:: resources:: ResourceAllocator ;
3036use crate :: utils:: u64_to_usize;
3137use crate :: vstate:: memory:: {
@@ -35,8 +41,10 @@ use crate::vstate::memory::{
3541// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
3642// Usable normal RAM
3743const E820_RAM : u32 = 1 ;
44+
3845// Reserved area that should be avoided during memory allocations
3946const E820_RESERVED : u32 = 2 ;
47+ const MEMMAP_TYPE_RAM : u32 = 1 ;
4048
4149/// Errors thrown while configuring x86_64 system.
4250#[ derive( Debug , PartialEq , Eq , thiserror:: Error , displaydoc:: Display ) ]
@@ -49,6 +57,12 @@ pub enum ConfigurationError {
4957 ZeroPageSetup ,
5058 /// Failed to compute initrd address.
5159 InitrdAddress ,
60+ /// Error writing module entry to guest memory.
61+ ModlistSetup ,
62+ /// Error writing memory map table to guest memory.
63+ MemmapTableSetup ,
64+ /// Error writing hvm_start_info to guest memory.
65+ StartInfoSetup ,
5266}
5367
5468const FIRST_ADDR_PAST_32BITS : u64 = 1 << 32 ;
@@ -110,13 +124,148 @@ pub fn initrd_load_addr(
110124/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
111125/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
112126/// * `num_cpus` - Number of virtual CPUs the guest will have.
127+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
113128pub fn configure_system (
114129 guest_mem : & GuestMemoryMmap ,
115130 resource_allocator : & mut ResourceAllocator ,
116131 cmdline_addr : GuestAddress ,
117132 cmdline_size : usize ,
118133 initrd : & Option < InitrdConfig > ,
119134 num_cpus : u8 ,
135+ boot_prot : BootProtocol ,
136+ ) -> Result < ( ) , ConfigurationError > {
137+ // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
138+ mptable:: setup_mptable ( guest_mem, resource_allocator, num_cpus)
139+ . 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+ #[ allow( clippy:: cast_possible_truncation) ] // the vec lenghts are single digit integers
218+ let mut start_info = hvm_start_info {
219+ magic : XEN_HVM_START_MAGIC_VALUE ,
220+ version : 1 ,
221+ cmdline_paddr : cmdline_addr. raw_value ( ) ,
222+ memmap_paddr : layout:: MEMMAP_START ,
223+ memmap_entries : memmap. len ( ) as u32 ,
224+ nr_modules : modules. len ( ) as u32 ,
225+ ..Default :: default ( )
226+ } ;
227+ if !modules. is_empty ( ) {
228+ start_info. modlist_paddr = layout:: MODLIST_START ;
229+ }
230+ let mut boot_params =
231+ BootParams :: new :: < hvm_start_info > ( & start_info, GuestAddress ( layout:: PVH_INFO_START ) ) ;
232+
233+ // Copy the vector with the memmap table to the MEMMAP_START address
234+ // which is already saved in the memmap_paddr field of hvm_start_info struct.
235+ boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap, GuestAddress ( layout:: MEMMAP_START ) ) ;
236+
237+ // Copy the vector with the modules list to the MODLIST_START address.
238+ // Note that we only set the modlist_paddr address if there is a nonzero
239+ // number of modules, but serializing an empty list is harmless.
240+ boot_params. set_modules :: < hvm_modlist_entry > ( & modules, GuestAddress ( layout:: MODLIST_START ) ) ;
241+
242+ // Write the hvm_start_info struct to guest memory.
243+ PvhBootConfigurator :: write_bootparams ( & boot_params, guest_mem)
244+ . map_err ( |_| ConfigurationError :: StartInfoSetup )
245+ }
246+
247+ fn add_memmap_entry (
248+ memmap : & mut Vec < hvm_memmap_table_entry > ,
249+ addr : u64 ,
250+ size : u64 ,
251+ mem_type : u32 ,
252+ ) -> Result < ( ) , ConfigurationError > {
253+ // Add the table entry to the vector
254+ memmap. push ( hvm_memmap_table_entry {
255+ addr,
256+ size,
257+ type_ : mem_type,
258+ reserved : 0 ,
259+ } ) ;
260+
261+ Ok ( ( ) )
262+ }
263+
264+ fn configure_64bit_boot (
265+ guest_mem : & GuestMemoryMmap ,
266+ cmdline_addr : GuestAddress ,
267+ cmdline_size : usize ,
268+ initrd : & Option < InitrdConfig > ,
120269) -> Result < ( ) , ConfigurationError > {
121270 const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
122271 const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -127,15 +276,12 @@ pub fn configure_system(
127276
128277 let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
129278
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) ?;
132-
133- // Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
134279 let mut params = boot_params {
135280 acpi_rsdp_addr : layout:: RSDP_ADDR ,
136281 ..Default :: default ( )
137282 } ;
138283
284+ // Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
139285 params. hdr . type_of_loader = KERNEL_LOADER_OTHER ;
140286 params. hdr . boot_flag = KERNEL_BOOT_FLAG_MAGIC ;
141287 params. hdr . header = KERNEL_HDR_MAGIC ;
@@ -245,8 +391,15 @@ mod tests {
245391 let no_vcpus = 4 ;
246392 let gm = single_region_mem ( 0x10000 ) ;
247393 let mut resource_allocator = ResourceAllocator :: new ( ) . unwrap ( ) ;
248- let config_err =
249- configure_system ( & gm, & mut resource_allocator, GuestAddress ( 0 ) , 0 , & None , 1 ) ;
394+ let config_err = configure_system (
395+ & gm,
396+ & mut resource_allocator,
397+ GuestAddress ( 0 ) ,
398+ 0 ,
399+ & None ,
400+ 1 ,
401+ BootProtocol :: LinuxBoot ,
402+ ) ;
250403 assert_eq ! (
251404 config_err. unwrap_err( ) ,
252405 super :: ConfigurationError :: MpTableSetup ( mptable:: MptableError :: NotEnoughMemory )
@@ -263,6 +416,17 @@ mod tests {
263416 0 ,
264417 & None ,
265418 no_vcpus,
419+ BootProtocol :: LinuxBoot ,
420+ )
421+ . unwrap ( ) ;
422+ configure_system (
423+ & gm,
424+ & mut resource_allocator,
425+ GuestAddress ( 0 ) ,
426+ 0 ,
427+ & None ,
428+ no_vcpus,
429+ BootProtocol :: PvhBoot ,
266430 )
267431 . unwrap ( ) ;
268432
@@ -277,6 +441,17 @@ mod tests {
277441 0 ,
278442 & None ,
279443 no_vcpus,
444+ BootProtocol :: LinuxBoot ,
445+ )
446+ . unwrap ( ) ;
447+ configure_system (
448+ & gm,
449+ & mut resource_allocator,
450+ GuestAddress ( 0 ) ,
451+ 0 ,
452+ & None ,
453+ no_vcpus,
454+ BootProtocol :: PvhBoot ,
280455 )
281456 . unwrap ( ) ;
282457
@@ -291,6 +466,17 @@ mod tests {
291466 0 ,
292467 & None ,
293468 no_vcpus,
469+ BootProtocol :: LinuxBoot ,
470+ )
471+ . unwrap ( ) ;
472+ configure_system (
473+ & gm,
474+ & mut resource_allocator,
475+ GuestAddress ( 0 ) ,
476+ 0 ,
477+ & None ,
478+ no_vcpus,
479+ BootProtocol :: PvhBoot ,
294480 )
295481 . unwrap ( ) ;
296482 }
@@ -334,4 +520,31 @@ mod tests {
334520 )
335521 . is_err( ) ) ;
336522 }
523+
524+ #[ test]
525+ fn test_add_memmap_entry ( ) {
526+ const MEMMAP_TYPE_RESERVED : u32 = 2 ;
527+
528+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
529+
530+ let expected_memmap = vec ! [
531+ hvm_memmap_table_entry {
532+ addr: 0x0 ,
533+ size: 0x1000 ,
534+ type_: MEMMAP_TYPE_RAM ,
535+ ..Default :: default ( )
536+ } ,
537+ hvm_memmap_table_entry {
538+ addr: 0x10000 ,
539+ size: 0xa000 ,
540+ type_: MEMMAP_TYPE_RESERVED ,
541+ ..Default :: default ( )
542+ } ,
543+ ] ;
544+
545+ add_memmap_entry ( & mut memmap, 0 , 0x1000 , MEMMAP_TYPE_RAM ) . unwrap ( ) ;
546+ add_memmap_entry ( & mut memmap, 0x10000 , 0xa000 , MEMMAP_TYPE_RESERVED ) . unwrap ( ) ;
547+
548+ assert_eq ! ( format!( "{:?}" , memmap) , format!( "{:?}" , expected_memmap) ) ;
549+ }
337550}
0 commit comments