1
+ // Copyright © 2020, Oracle and/or its affiliates.
2
+ //
1
3
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
4
// SPDX-License-Identifier: Apache-2.0
3
5
//
@@ -17,14 +19,20 @@ pub mod msr;
17
19
pub mod regs;
18
20
19
21
use linux_loader:: configurator:: linux:: LinuxBootConfigurator ;
22
+ use linux_loader:: configurator:: pvh:: PvhBootConfigurator ;
20
23
use linux_loader:: configurator:: { BootConfigurator , BootParams } ;
21
24
use 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
+ } ;
22
28
use vm_memory:: { Address , GuestAddress , GuestMemory , GuestMemoryMmap , GuestMemoryRegion } ;
23
29
30
+ use super :: BootProtocol ;
24
31
use crate :: InitrdConfig ;
25
32
26
33
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
27
34
const E820_RAM : u32 = 1 ;
35
+ const MEMMAP_TYPE_RAM : u32 = 1 ;
28
36
29
37
/// Errors thrown while configuring x86_64 system.
30
38
#[ derive( Debug , PartialEq , Eq , derive_more:: From ) ]
@@ -37,6 +45,12 @@ pub enum Error {
37
45
ZeroPageSetup ,
38
46
/// Failed to compute initrd address.
39
47
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 ,
40
54
}
41
55
42
56
// 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
97
111
/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
98
112
/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
99
113
/// * `num_cpus` - Number of virtual CPUs the guest will have.
114
+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
100
115
pub fn configure_system (
101
116
guest_mem : & GuestMemoryMmap ,
102
117
cmdline_addr : GuestAddress ,
103
118
cmdline_size : usize ,
104
119
initrd : & Option < InitrdConfig > ,
105
120
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 > ,
106
247
) -> super :: Result < ( ) > {
107
248
const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
108
249
const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -113,9 +254,6 @@ pub fn configure_system(
113
254
114
255
let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
115
256
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
-
119
257
let mut params = boot_params:: default ( ) ;
120
258
121
259
params. hdr . type_of_loader = KERNEL_LOADER_OTHER ;
@@ -218,7 +356,8 @@ mod tests {
218
356
let gm =
219
357
vm_memory:: test_utils:: create_anon_guest_memory ( & [ ( GuestAddress ( 0 ) , 0x10000 ) ] , false )
220
358
. 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 ) ;
222
361
assert ! ( config_err. is_err( ) ) ;
223
362
assert_eq ! (
224
363
config_err. unwrap_err( ) ,
@@ -229,19 +368,70 @@ mod tests {
229
368
let mem_size = 128 << 20 ;
230
369
let arch_mem_regions = arch_memory_regions ( mem_size) ;
231
370
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 ( ) ;
233
389
234
390
// Now assigning some memory that is equal to the start of the 32bit memory hole.
235
391
let mem_size = 3328 << 20 ;
236
392
let arch_mem_regions = arch_memory_regions ( mem_size) ;
237
393
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 ( ) ;
239
412
240
413
// Now assigning some memory that falls after the 32bit memory hole.
241
414
let mem_size = 3330 << 20 ;
242
415
let arch_mem_regions = arch_memory_regions ( mem_size) ;
243
416
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 ( ) ;
245
435
}
246
436
247
437
#[ test]
@@ -283,4 +473,31 @@ mod tests {
283
473
)
284
474
. is_err( ) ) ;
285
475
}
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
+ }
286
503
}
0 commit comments