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
//
@@ -19,14 +21,20 @@ pub mod msr;
19
21
pub mod regs;
20
22
21
23
use linux_loader:: configurator:: linux:: LinuxBootConfigurator ;
24
+ use linux_loader:: configurator:: pvh:: PvhBootConfigurator ;
22
25
use linux_loader:: configurator:: { BootConfigurator , BootParams } ;
23
26
use 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
+ } ;
24
30
use utils:: vm_memory:: { Address , GuestAddress , GuestMemory , GuestMemoryMmap , GuestMemoryRegion } ;
25
31
32
+ use super :: BootProtocol ;
26
33
use crate :: arch:: InitrdConfig ;
27
34
28
35
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
29
36
const E820_RAM : u32 = 1 ;
37
+ const MEMMAP_TYPE_RAM : u32 = 1 ;
30
38
31
39
/// Errors thrown while configuring x86_64 system.
32
40
#[ derive( Debug , PartialEq , Eq , derive_more:: From ) ]
@@ -39,6 +47,12 @@ pub enum ConfigurationError {
39
47
ZeroPageSetup ,
40
48
/// Failed to compute initrd address.
41
49
InitrdAddress ,
50
+ /// Error writing module entry to guest memory.
51
+ ModlistSetup ,
52
+ /// Error writing memory map table to guest memory.
53
+ MemmapTableSetup ,
54
+ /// Error writing hvm_start_info to guest memory.
55
+ StartInfoSetup ,
42
56
}
43
57
44
58
// Where BIOS/VGA magic would live on a real PC.
@@ -102,12 +116,139 @@ pub fn initrd_load_addr(
102
116
/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
103
117
/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
104
118
/// * `num_cpus` - Number of virtual CPUs the guest will have.
119
+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
105
120
pub fn configure_system (
106
121
guest_mem : & GuestMemoryMmap ,
107
122
cmdline_addr : GuestAddress ,
108
123
cmdline_size : usize ,
109
124
initrd : & Option < InitrdConfig > ,
110
125
num_cpus : u8 ,
126
+ boot_prot : BootProtocol ,
127
+ ) -> Result < ( ) , ConfigurationError > {
128
+ // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
129
+ mptable:: setup_mptable ( guest_mem, num_cpus) . map_err ( ConfigurationError :: MpTableSetup ) ?;
130
+
131
+ match boot_prot {
132
+ BootProtocol :: PvhBoot => {
133
+ configure_pvh ( guest_mem, cmdline_addr, initrd) ?;
134
+ }
135
+ BootProtocol :: LinuxBoot => {
136
+ configure_64bit_boot ( guest_mem, cmdline_addr, cmdline_size, initrd) ?;
137
+ }
138
+ }
139
+
140
+ Ok ( ( ) )
141
+ }
142
+
143
+ fn configure_pvh (
144
+ guest_mem : & GuestMemoryMmap ,
145
+ cmdline_addr : GuestAddress ,
146
+ initrd : & Option < InitrdConfig > ,
147
+ ) -> Result < ( ) , ConfigurationError > {
148
+ const XEN_HVM_START_MAGIC_VALUE : u32 = 0x336e_c578 ;
149
+ let first_addr_past_32bits = GuestAddress ( FIRST_ADDR_PAST_32BITS ) ;
150
+ let end_32bit_gap_start = GuestAddress ( MMIO_MEM_START ) ;
151
+ let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
152
+
153
+ // Vector to hold modules (currently either empty or holding initrd).
154
+ let mut modules: Vec < hvm_modlist_entry > = Vec :: new ( ) ;
155
+ if let Some ( initrd_config) = initrd {
156
+ // The initrd has been written to guest memory already, here we just need to
157
+ // create the module structure that describes it.
158
+ modules. push ( hvm_modlist_entry {
159
+ paddr : initrd_config. address . raw_value ( ) ,
160
+ size : initrd_config. size as u64 ,
161
+ ..Default :: default ( )
162
+ } ) ;
163
+ }
164
+
165
+ // Vector to hold the memory maps which needs to be written to guest memory
166
+ // at MEMMAP_START after all of the mappings are recorded.
167
+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
168
+
169
+ // Create the memory map entries.
170
+ add_memmap_entry ( & mut memmap, 0 , EBDA_START , MEMMAP_TYPE_RAM ) ?;
171
+ let last_addr = guest_mem. last_addr ( ) ;
172
+ if last_addr < end_32bit_gap_start {
173
+ add_memmap_entry (
174
+ & mut memmap,
175
+ himem_start. raw_value ( ) ,
176
+ last_addr. unchecked_offset_from ( himem_start) + 1 ,
177
+ MEMMAP_TYPE_RAM ,
178
+ ) ?;
179
+ } else {
180
+ add_memmap_entry (
181
+ & mut memmap,
182
+ himem_start. raw_value ( ) ,
183
+ end_32bit_gap_start. unchecked_offset_from ( himem_start) ,
184
+ MEMMAP_TYPE_RAM ,
185
+ ) ?;
186
+
187
+ if last_addr > first_addr_past_32bits {
188
+ add_memmap_entry (
189
+ & mut memmap,
190
+ first_addr_past_32bits. raw_value ( ) ,
191
+ last_addr. unchecked_offset_from ( first_addr_past_32bits) + 1 ,
192
+ MEMMAP_TYPE_RAM ,
193
+ ) ?;
194
+ }
195
+ }
196
+
197
+ // Construct the hvm_start_info structure and serialize it into
198
+ // boot_params. This will be stored at PVH_INFO_START address, and %rbx
199
+ // will be initialized to contain PVH_INFO_START prior to starting the
200
+ // guest, as required by the PVH ABI.
201
+ let mut start_info = hvm_start_info {
202
+ magic : XEN_HVM_START_MAGIC_VALUE ,
203
+ version : 1 ,
204
+ cmdline_paddr : cmdline_addr. raw_value ( ) ,
205
+ memmap_paddr : layout:: MEMMAP_START ,
206
+ memmap_entries : memmap. len ( ) as u32 ,
207
+ nr_modules : modules. len ( ) as u32 ,
208
+ ..Default :: default ( )
209
+ } ;
210
+ if !modules. is_empty ( ) {
211
+ start_info. modlist_paddr = layout:: MODLIST_START ;
212
+ }
213
+ let mut boot_params =
214
+ BootParams :: new :: < hvm_start_info > ( & start_info, GuestAddress ( layout:: PVH_INFO_START ) ) ;
215
+
216
+ // Copy the vector with the memmap table to the MEMMAP_START address
217
+ // which is already saved in the memmap_paddr field of hvm_start_info struct.
218
+ boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap, GuestAddress ( layout:: MEMMAP_START ) ) ;
219
+
220
+ // Copy the vector with the modules list to the MODLIST_START address.
221
+ // Note that we only set the modlist_paddr address if there is a nonzero
222
+ // number of modules, but serializing an empty list is harmless.
223
+ boot_params. set_modules :: < hvm_modlist_entry > ( & modules, GuestAddress ( layout:: MODLIST_START ) ) ;
224
+
225
+ // Write the hvm_start_info struct to guest memory.
226
+ PvhBootConfigurator :: write_bootparams ( & boot_params, guest_mem)
227
+ . map_err ( |_| ConfigurationError :: StartInfoSetup )
228
+ }
229
+
230
+ fn add_memmap_entry (
231
+ memmap : & mut Vec < hvm_memmap_table_entry > ,
232
+ addr : u64 ,
233
+ size : u64 ,
234
+ mem_type : u32 ,
235
+ ) -> Result < ( ) , ConfigurationError > {
236
+ // Add the table entry to the vector
237
+ memmap. push ( hvm_memmap_table_entry {
238
+ addr,
239
+ size,
240
+ type_ : mem_type,
241
+ reserved : 0 ,
242
+ } ) ;
243
+
244
+ Ok ( ( ) )
245
+ }
246
+
247
+ fn configure_64bit_boot (
248
+ guest_mem : & GuestMemoryMmap ,
249
+ cmdline_addr : GuestAddress ,
250
+ cmdline_size : usize ,
251
+ initrd : & Option < InitrdConfig > ,
111
252
) -> Result < ( ) , ConfigurationError > {
112
253
const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
113
254
const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -118,9 +259,6 @@ pub fn configure_system(
118
259
119
260
let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
120
261
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
-
124
262
let mut params = boot_params:: default ( ) ;
125
263
126
264
params. hdr . type_of_loader = KERNEL_LOADER_OTHER ;
@@ -225,7 +363,8 @@ mod tests {
225
363
false ,
226
364
)
227
365
. unwrap ( ) ;
228
- let config_err = configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , 1 ) ;
366
+ let config_err =
367
+ configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , 1 , BootProtocol :: LinuxBoot ) ;
229
368
assert ! ( config_err. is_err( ) ) ;
230
369
assert_eq ! (
231
370
config_err. unwrap_err( ) ,
@@ -237,21 +376,72 @@ mod tests {
237
376
let arch_mem_regions = arch_memory_regions ( mem_size) ;
238
377
let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
239
378
. unwrap ( ) ;
240
- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
379
+ configure_system (
380
+ & gm,
381
+ GuestAddress ( 0 ) ,
382
+ 0 ,
383
+ & None ,
384
+ no_vcpus,
385
+ BootProtocol :: LinuxBoot ,
386
+ )
387
+ . unwrap ( ) ;
388
+ configure_system (
389
+ & gm,
390
+ GuestAddress ( 0 ) ,
391
+ 0 ,
392
+ & None ,
393
+ no_vcpus,
394
+ BootProtocol :: PvhBoot ,
395
+ )
396
+ . unwrap ( ) ;
241
397
242
398
// Now assigning some memory that is equal to the start of the 32bit memory hole.
243
399
let mem_size = 3328 << 20 ;
244
400
let arch_mem_regions = arch_memory_regions ( mem_size) ;
245
401
let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
246
402
. unwrap ( ) ;
247
- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
403
+ configure_system (
404
+ & gm,
405
+ GuestAddress ( 0 ) ,
406
+ 0 ,
407
+ & None ,
408
+ no_vcpus,
409
+ BootProtocol :: LinuxBoot ,
410
+ )
411
+ . unwrap ( ) ;
412
+ configure_system (
413
+ & gm,
414
+ GuestAddress ( 0 ) ,
415
+ 0 ,
416
+ & None ,
417
+ no_vcpus,
418
+ BootProtocol :: PvhBoot ,
419
+ )
420
+ . unwrap ( ) ;
248
421
249
422
// Now assigning some memory that falls after the 32bit memory hole.
250
423
let mem_size = 3330 << 20 ;
251
424
let arch_mem_regions = arch_memory_regions ( mem_size) ;
252
425
let gm = utils:: vm_memory:: test_utils:: create_anon_guest_memory ( & arch_mem_regions, false )
253
426
. unwrap ( ) ;
254
- configure_system ( & gm, GuestAddress ( 0 ) , 0 , & None , no_vcpus) . unwrap ( ) ;
427
+ configure_system (
428
+ & gm,
429
+ GuestAddress ( 0 ) ,
430
+ 0 ,
431
+ & None ,
432
+ no_vcpus,
433
+ BootProtocol :: LinuxBoot ,
434
+ )
435
+ . unwrap ( ) ;
436
+ configure_system (
437
+ & gm,
438
+ GuestAddress ( 0 ) ,
439
+ 0 ,
440
+ & None ,
441
+ no_vcpus,
442
+ BootProtocol :: PvhBoot ,
443
+ )
444
+ . unwrap ( ) ;
255
445
}
256
446
257
447
#[ test]
@@ -293,4 +483,31 @@ mod tests {
293
483
)
294
484
. is_err( ) ) ;
295
485
}
486
+
487
+ #[ test]
488
+ fn test_add_memmap_entry ( ) {
489
+ const MEMMAP_TYPE_RESERVED : u32 = 2 ;
490
+
491
+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
492
+
493
+ let expected_memmap = vec ! [
494
+ hvm_memmap_table_entry {
495
+ addr: 0x0 ,
496
+ size: 0x1000 ,
497
+ type_: MEMMAP_TYPE_RAM ,
498
+ ..Default :: default ( )
499
+ } ,
500
+ hvm_memmap_table_entry {
501
+ addr: 0x10000 ,
502
+ size: 0xa000 ,
503
+ type_: MEMMAP_TYPE_RESERVED ,
504
+ ..Default :: default ( )
505
+ } ,
506
+ ] ;
507
+
508
+ add_memmap_entry ( & mut memmap, 0 , 0x1000 , MEMMAP_TYPE_RAM ) . unwrap ( ) ;
509
+ add_memmap_entry ( & mut memmap, 0x10000 , 0xa000 , MEMMAP_TYPE_RESERVED ) . unwrap ( ) ;
510
+
511
+ assert_eq ! ( format!( "{:?}" , memmap) , format!( "{:?}" , expected_memmap) ) ;
512
+ }
296
513
}
0 commit comments