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
//
@@ -22,10 +24,15 @@ pub mod regs;
22
24
pub mod gen;
23
25
24
26
use linux_loader:: configurator:: linux:: LinuxBootConfigurator ;
27
+ use linux_loader:: configurator:: pvh:: PvhBootConfigurator ;
25
28
use linux_loader:: configurator:: { BootConfigurator , BootParams } ;
26
29
use linux_loader:: loader:: bootparam:: boot_params;
27
30
28
- use crate :: arch:: InitrdConfig ;
31
+ use linux_loader:: loader:: elf:: start_info:: {
32
+ hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
33
+ } ;
34
+
35
+ use crate :: arch:: { BootProtocol , InitrdConfig } ;
29
36
use crate :: device_manager:: resources:: ResourceAllocator ;
30
37
use crate :: utils:: u64_to_usize;
31
38
use crate :: vstate:: memory:: {
@@ -35,8 +42,10 @@ use crate::vstate::memory::{
35
42
// Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31
36
43
// Usable normal RAM
37
44
const E820_RAM : u32 = 1 ;
45
+
38
46
// Reserved area that should be avoided during memory allocations
39
47
const E820_RESERVED : u32 = 2 ;
48
+ const MEMMAP_TYPE_RAM : u32 = 1 ;
40
49
41
50
/// Errors thrown while configuring x86_64 system.
42
51
#[ derive( Debug , PartialEq , Eq , thiserror:: Error , displaydoc:: Display ) ]
@@ -49,6 +58,12 @@ pub enum ConfigurationError {
49
58
ZeroPageSetup ,
50
59
/// Failed to compute initrd address.
51
60
InitrdAddress ,
61
+ /// Error writing module entry to guest memory.
62
+ ModlistSetup ,
63
+ /// Error writing memory map table to guest memory.
64
+ MemmapTableSetup ,
65
+ /// Error writing hvm_start_info to guest memory.
66
+ StartInfoSetup ,
52
67
}
53
68
54
69
const FIRST_ADDR_PAST_32BITS : u64 = 1 << 32 ;
@@ -110,13 +125,146 @@ pub fn initrd_load_addr(
110
125
/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
111
126
/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
112
127
/// * `num_cpus` - Number of virtual CPUs the guest will have.
128
+ /// * `boot_prot` - Boot protocol that will be used to boot the guest.
113
129
pub fn configure_system (
114
130
guest_mem : & GuestMemoryMmap ,
115
131
resource_allocator : & mut ResourceAllocator ,
116
132
cmdline_addr : GuestAddress ,
117
133
cmdline_size : usize ,
118
134
initrd : & Option < InitrdConfig > ,
119
135
num_cpus : u8 ,
136
+ boot_prot : BootProtocol ,
137
+ ) -> Result < ( ) , ConfigurationError > {
138
+ // Note that this puts the mptable at the last 1k of Linux's 640k base RAM
139
+ mptable:: setup_mptable ( guest_mem, resource_allocator, num_cpus) . 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
+ let mut start_info = hvm_start_info {
218
+ magic : XEN_HVM_START_MAGIC_VALUE ,
219
+ version : 1 ,
220
+ cmdline_paddr : cmdline_addr. raw_value ( ) ,
221
+ memmap_paddr : layout:: MEMMAP_START ,
222
+ memmap_entries : memmap. len ( ) as u32 ,
223
+ nr_modules : modules. len ( ) as u32 ,
224
+ ..Default :: default ( )
225
+ } ;
226
+ if !modules. is_empty ( ) {
227
+ start_info. modlist_paddr = layout:: MODLIST_START ;
228
+ }
229
+ let mut boot_params =
230
+ BootParams :: new :: < hvm_start_info > ( & start_info, GuestAddress ( layout:: PVH_INFO_START ) ) ;
231
+
232
+ // Copy the vector with the memmap table to the MEMMAP_START address
233
+ // which is already saved in the memmap_paddr field of hvm_start_info struct.
234
+ boot_params. set_sections :: < hvm_memmap_table_entry > ( & memmap, GuestAddress ( layout:: MEMMAP_START ) ) ;
235
+
236
+ // Copy the vector with the modules list to the MODLIST_START address.
237
+ // Note that we only set the modlist_paddr address if there is a nonzero
238
+ // number of modules, but serializing an empty list is harmless.
239
+ boot_params. set_modules :: < hvm_modlist_entry > ( & modules, GuestAddress ( layout:: MODLIST_START ) ) ;
240
+
241
+ // Write the hvm_start_info struct to guest memory.
242
+ PvhBootConfigurator :: write_bootparams ( & boot_params, guest_mem)
243
+ . map_err ( |_| ConfigurationError :: StartInfoSetup )
244
+ }
245
+
246
+ fn add_memmap_entry (
247
+ memmap : & mut Vec < hvm_memmap_table_entry > ,
248
+ addr : u64 ,
249
+ size : u64 ,
250
+ mem_type : u32 ,
251
+ ) -> Result < ( ) , ConfigurationError > {
252
+ // Add the table entry to the vector
253
+ memmap. push ( hvm_memmap_table_entry {
254
+ addr,
255
+ size,
256
+ type_ : mem_type,
257
+ reserved : 0 ,
258
+ } ) ;
259
+
260
+ Ok ( ( ) )
261
+ }
262
+
263
+ fn configure_64bit_boot (
264
+ guest_mem : & GuestMemoryMmap ,
265
+ cmdline_addr : GuestAddress ,
266
+ cmdline_size : usize ,
267
+ initrd : & Option < InitrdConfig > ,
120
268
) -> Result < ( ) , ConfigurationError > {
121
269
const KERNEL_BOOT_FLAG_MAGIC : u16 = 0xaa55 ;
122
270
const KERNEL_HDR_MAGIC : u32 = 0x5372_6448 ;
@@ -127,15 +275,10 @@ pub fn configure_system(
127
275
128
276
let himem_start = GuestAddress ( layout:: HIMEM_START ) ;
129
277
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) ?;
278
+ let mut params = boot_params:: default ( ) ;
132
279
133
280
// Set the location of RSDP in Boot Parameters to help the guest kernel find it faster.
134
- let mut params = boot_params {
135
- acpi_rsdp_addr : layout:: RSDP_ADDR ,
136
- ..Default :: default ( )
137
- } ;
138
-
281
+ params. acpi_rsdp_addr = layout:: RSDP_ADDR ;
139
282
params. hdr . type_of_loader = KERNEL_LOADER_OTHER ;
140
283
params. hdr . boot_flag = KERNEL_BOOT_FLAG_MAGIC ;
141
284
params. hdr . header = KERNEL_HDR_MAGIC ;
@@ -246,7 +389,7 @@ mod tests {
246
389
let gm = single_region_mem ( 0x10000 ) ;
247
390
let mut resource_allocator = ResourceAllocator :: new ( ) . unwrap ( ) ;
248
391
let config_err =
249
- configure_system ( & gm, & mut resource_allocator, GuestAddress ( 0 ) , 0 , & None , 1 ) ;
392
+ configure_system ( & gm, & mut resource_allocator, GuestAddress ( 0 ) , 0 , & None , 1 , BootProtocol :: LinuxBoot ) ;
250
393
assert_eq ! (
251
394
config_err. unwrap_err( ) ,
252
395
super :: ConfigurationError :: MpTableSetup ( mptable:: MptableError :: NotEnoughMemory )
@@ -263,6 +406,17 @@ mod tests {
263
406
0 ,
264
407
& None ,
265
408
no_vcpus,
409
+ BootProtocol :: LinuxBoot ,
410
+ )
411
+ . unwrap ( ) ;
412
+ configure_system (
413
+ & gm,
414
+ & mut resource_allocator,
415
+ GuestAddress ( 0 ) ,
416
+ 0 ,
417
+ & None ,
418
+ no_vcpus,
419
+ BootProtocol :: PvhBoot ,
266
420
)
267
421
. unwrap ( ) ;
268
422
@@ -277,6 +431,17 @@ mod tests {
277
431
0 ,
278
432
& None ,
279
433
no_vcpus,
434
+ BootProtocol :: LinuxBoot ,
435
+ )
436
+ . unwrap ( ) ;
437
+ configure_system (
438
+ & gm,
439
+ & mut resource_allocator,
440
+ GuestAddress ( 0 ) ,
441
+ 0 ,
442
+ & None ,
443
+ no_vcpus,
444
+ BootProtocol :: PvhBoot ,
280
445
)
281
446
. unwrap ( ) ;
282
447
@@ -291,6 +456,17 @@ mod tests {
291
456
0 ,
292
457
& None ,
293
458
no_vcpus,
459
+ BootProtocol :: LinuxBoot ,
460
+ )
461
+ . unwrap ( ) ;
462
+ configure_system (
463
+ & gm,
464
+ & mut resource_allocator,
465
+ GuestAddress ( 0 ) ,
466
+ 0 ,
467
+ & None ,
468
+ no_vcpus,
469
+ BootProtocol :: PvhBoot ,
294
470
)
295
471
. unwrap ( ) ;
296
472
}
@@ -334,4 +510,31 @@ mod tests {
334
510
)
335
511
. is_err( ) ) ;
336
512
}
513
+
514
+ #[ test]
515
+ fn test_add_memmap_entry ( ) {
516
+ const MEMMAP_TYPE_RESERVED : u32 = 2 ;
517
+
518
+ let mut memmap: Vec < hvm_memmap_table_entry > = Vec :: new ( ) ;
519
+
520
+ let expected_memmap = vec ! [
521
+ hvm_memmap_table_entry {
522
+ addr: 0x0 ,
523
+ size: 0x1000 ,
524
+ type_: MEMMAP_TYPE_RAM ,
525
+ ..Default :: default ( )
526
+ } ,
527
+ hvm_memmap_table_entry {
528
+ addr: 0x10000 ,
529
+ size: 0xa000 ,
530
+ type_: MEMMAP_TYPE_RESERVED ,
531
+ ..Default :: default ( )
532
+ } ,
533
+ ] ;
534
+
535
+ add_memmap_entry ( & mut memmap, 0 , 0x1000 , MEMMAP_TYPE_RAM ) . unwrap ( ) ;
536
+ add_memmap_entry ( & mut memmap, 0x10000 , 0xa000 , MEMMAP_TYPE_RESERVED ) . unwrap ( ) ;
537
+
538
+ assert_eq ! ( format!( "{:?}" , memmap) , format!( "{:?}" , expected_memmap) ) ;
539
+ }
337
540
}
0 commit comments