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