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