1
+ // Copyright © 2020, Oracle and/or its affiliates.
1
2
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
3
// SPDX-License-Identifier: Apache-2.0
3
4
//
@@ -11,6 +12,7 @@ use kvm_bindings::{kvm_fpu, kvm_regs, kvm_sregs};
11
12
use kvm_ioctls:: VcpuFd ;
12
13
use utils:: vm_memory:: { Address , Bytes , GuestAddress , GuestMemory , GuestMemoryMmap } ;
13
14
15
+ use super :: super :: { BootProtocol , EntryPoint } ;
14
16
use super :: gdt:: { gdt_entry, kvm_segment_from_gdt} ;
15
17
16
18
// Initial pagetables.
@@ -99,20 +101,30 @@ impl fmt::Display for SetupRegistersError {
99
101
/// # Errors
100
102
///
101
103
/// When [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_regs`] errors.
102
- pub fn setup_regs ( vcpu : & VcpuFd , boot_ip : u64 ) -> Result < ( ) , SetupRegistersError > {
103
- let regs: kvm_regs = kvm_regs {
104
- rflags : 0x0000_0000_0000_0002u64 ,
105
- rip : boot_ip,
106
- // Frame pointer. It gets a snapshot of the stack pointer (rsp) so that when adjustments are
107
- // made to rsp (i.e. reserving space for local variables or pushing values on to the stack),
108
- // local variables and function parameters are still accessible from a constant offset from
109
- // rbp.
110
- rsp : super :: layout:: BOOT_STACK_POINTER ,
111
- // Starting stack pointer.
112
- rbp : super :: layout:: BOOT_STACK_POINTER ,
113
- // Must point to zero page address per Linux ABI. This is x86_64 specific.
114
- rsi : super :: layout:: ZERO_PAGE_START ,
115
- ..Default :: default ( )
104
+ pub fn setup_regs ( vcpu : & VcpuFd , entry_point : EntryPoint ) -> Result < ( ) , SetupRegistersError > {
105
+ let regs: kvm_regs = match entry_point. protocol {
106
+ BootProtocol :: PvhBoot => kvm_regs {
107
+ // Configure regs as required by PVH boot protocol.
108
+ rflags : 0x0000_0000_0000_0002u64 ,
109
+ rbx : super :: layout:: PVH_INFO_START ,
110
+ rip : entry_point. entry_addr . raw_value ( ) ,
111
+ ..Default :: default ( )
112
+ } ,
113
+ BootProtocol :: LinuxBoot => kvm_regs {
114
+ // Configure regs as required by Linux 64-bit boot protocol.
115
+ rflags : 0x0000_0000_0000_0002u64 ,
116
+ rip : entry_point. entry_addr . raw_value ( ) ,
117
+ // Frame pointer. It gets a snapshot of the stack pointer (rsp) so that when adjustments
118
+ // are made to rsp (i.e. reserving space for local variables or pushing
119
+ // values on to the stack), local variables and function parameters are
120
+ // still accessible from a constant offset from rbp.
121
+ rsp : super :: layout:: BOOT_STACK_POINTER ,
122
+ // Starting stack pointer.
123
+ rbp : super :: layout:: BOOT_STACK_POINTER ,
124
+ // Must point to zero page address per Linux ABI. This is x86_64 specific.
125
+ rsi : super :: layout:: ZERO_PAGE_START ,
126
+ ..Default :: default ( )
127
+ } ,
116
128
} ;
117
129
118
130
vcpu. set_regs ( & regs) . map_err ( SetupRegistersError )
@@ -141,6 +153,7 @@ pub enum SetupSpecialRegistersError {
141
153
///
142
154
/// * `mem` - The memory that will be passed to the guest.
143
155
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
156
+ /// * `boot_prot` - The boot protocol being used.
144
157
///
145
158
/// # Errors
146
159
///
@@ -149,14 +162,21 @@ pub enum SetupSpecialRegistersError {
149
162
/// - [`configure_segments_and_sregs`] errors.
150
163
/// - [`setup_page_tables`] errors
151
164
/// - [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_sregs`] errors.
152
- pub fn setup_sregs ( mem : & GuestMemoryMmap , vcpu : & VcpuFd ) -> Result < ( ) , SetupSpecialRegistersError > {
165
+ pub fn setup_sregs (
166
+ mem : & GuestMemoryMmap ,
167
+ vcpu : & VcpuFd ,
168
+ boot_prot : BootProtocol ,
169
+ ) -> Result < ( ) , SetupSpecialRegistersError > {
153
170
let mut sregs: kvm_sregs = vcpu
154
171
. get_sregs ( )
155
172
. map_err ( SetupSpecialRegistersError :: GetSpecialRegisters ) ?;
156
173
157
- configure_segments_and_sregs ( mem, & mut sregs)
174
+ configure_segments_and_sregs ( mem, & mut sregs, boot_prot )
158
175
. map_err ( SetupSpecialRegistersError :: ConfigureSegmentsAndSpecialRegisters ) ?;
159
- setup_page_tables ( mem, & mut sregs) . map_err ( SetupSpecialRegistersError :: SetupPageTables ) ?; // TODO(dgreid) - Can this be done once per system instead?
176
+ if let BootProtocol :: LinuxBoot = boot_prot {
177
+ setup_page_tables ( mem, & mut sregs) . map_err ( SetupSpecialRegistersError :: SetupPageTables ) ?;
178
+ // TODO(dgreid) - Can this be done once per system instead?
179
+ }
160
180
161
181
vcpu. set_sregs ( & sregs)
162
182
. map_err ( SetupSpecialRegistersError :: SetSpecialRegisters )
@@ -171,6 +191,7 @@ const EFER_LMA: u64 = 0x400;
171
191
const EFER_LME : u64 = 0x100 ;
172
192
173
193
const X86_CR0_PE : u64 = 0x1 ;
194
+ const X86_CR0_ET : u64 = 0x10 ;
174
195
const X86_CR0_PG : u64 = 0x8000_0000 ;
175
196
const X86_CR4_PAE : u64 = 0x20 ;
176
197
@@ -197,13 +218,28 @@ fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<(), RegsErro
197
218
fn configure_segments_and_sregs (
198
219
mem : & GuestMemoryMmap ,
199
220
sregs : & mut kvm_sregs ,
221
+ boot_prot : BootProtocol ,
200
222
) -> Result < ( ) , RegsError > {
201
- let gdt_table: [ u64 ; BOOT_GDT_MAX ] = [
202
- gdt_entry ( 0 , 0 , 0 ) , // NULL
203
- gdt_entry ( 0xa09b , 0 , 0xfffff ) , // CODE
204
- gdt_entry ( 0xc093 , 0 , 0xfffff ) , // DATA
205
- gdt_entry ( 0x808b , 0 , 0xfffff ) , // TSS
206
- ] ;
223
+ let gdt_table: [ u64 ; BOOT_GDT_MAX ] = match boot_prot {
224
+ BootProtocol :: PvhBoot => {
225
+ // Configure GDT entries as specified by PVH boot protocol
226
+ [
227
+ gdt_entry ( 0 , 0 , 0 ) , // NULL
228
+ gdt_entry ( 0xc09b , 0 , 0xffff_ffff ) , // CODE
229
+ gdt_entry ( 0xc093 , 0 , 0xffff_ffff ) , // DATA
230
+ gdt_entry ( 0x008b , 0 , 0x67 ) , // TSS
231
+ ]
232
+ }
233
+ BootProtocol :: LinuxBoot => {
234
+ // Configure GDT entries as specified by Linux 64bit boot protocol
235
+ [
236
+ gdt_entry ( 0 , 0 , 0 ) , // NULL
237
+ gdt_entry ( 0xa09b , 0 , 0xfffff ) , // CODE
238
+ gdt_entry ( 0xc093 , 0 , 0xfffff ) , // DATA
239
+ gdt_entry ( 0x808b , 0 , 0xfffff ) , // TSS
240
+ ]
241
+ }
242
+ } ;
207
243
208
244
let code_seg = kvm_segment_from_gdt ( gdt_table[ 1 ] , 1 ) ;
209
245
let data_seg = kvm_segment_from_gdt ( gdt_table[ 2 ] , 2 ) ;
@@ -226,9 +262,17 @@ fn configure_segments_and_sregs(
226
262
sregs. ss = data_seg;
227
263
sregs. tr = tss_seg;
228
264
229
- // 64-bit protected mode
230
- sregs. cr0 |= X86_CR0_PE ;
231
- sregs. efer |= EFER_LME | EFER_LMA ;
265
+ match boot_prot {
266
+ BootProtocol :: PvhBoot => {
267
+ sregs. cr0 = X86_CR0_PE | X86_CR0_ET ;
268
+ sregs. cr4 = 0 ;
269
+ }
270
+ BootProtocol :: LinuxBoot => {
271
+ // 64-bit protected mode
272
+ sregs. cr0 |= X86_CR0_PE ;
273
+ sregs. efer |= EFER_LME | EFER_LMA ;
274
+ }
275
+ }
232
276
233
277
Ok ( ( ) )
234
278
}
@@ -289,24 +333,45 @@ mod tests {
289
333
gm. read_obj ( read_addr) . unwrap ( )
290
334
}
291
335
292
- fn validate_segments_and_sregs ( gm : & GuestMemoryMmap , sregs : & kvm_sregs ) {
336
+ fn validate_segments_and_sregs (
337
+ gm : & GuestMemoryMmap ,
338
+ sregs : & kvm_sregs ,
339
+ boot_prot : BootProtocol ,
340
+ ) {
341
+ if let BootProtocol :: LinuxBoot = boot_prot {
342
+ assert_eq ! ( 0xaf_9b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 8 ) ) ;
343
+ assert_eq ! ( 0xcf_9300_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 16 ) ) ;
344
+ assert_eq ! ( 0x8f_8b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 24 ) ) ;
345
+
346
+ assert_eq ! ( 0xffff_ffff , sregs. tr. limit) ;
347
+
348
+ assert ! ( sregs. cr0 & X86_CR0_PE != 0 ) ;
349
+ assert ! ( sregs. efer & EFER_LME != 0 && sregs. efer & EFER_LMA != 0 ) ;
350
+ } else {
351
+ // Validate values that are specific to PVH boot protocol
352
+ assert_eq ! ( 0xcf_9b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 8 ) ) ;
353
+ assert_eq ! ( 0xcf_9300_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 16 ) ) ;
354
+ assert_eq ! ( 0x00_8b00_0000_0067 , read_u64( gm, BOOT_GDT_OFFSET + 24 ) ) ;
355
+
356
+ assert_eq ! ( 0x67 , sregs. tr. limit) ;
357
+ assert_eq ! ( 0 , sregs. tr. g) ;
358
+
359
+ assert ! ( sregs. cr0 & X86_CR0_PE != 0 && sregs. cr0 & X86_CR0_ET != 0 ) ;
360
+ assert_eq ! ( 0 , sregs. cr4) ;
361
+ }
362
+
363
+ // Common settings for both PVH and Linux boot protocol
293
364
assert_eq ! ( 0x0 , read_u64( gm, BOOT_GDT_OFFSET ) ) ;
294
- assert_eq ! ( 0xaf_9b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 8 ) ) ;
295
- assert_eq ! ( 0xcf_9300_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 16 ) ) ;
296
- assert_eq ! ( 0x8f_8b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 24 ) ) ;
297
365
assert_eq ! ( 0x0 , read_u64( gm, BOOT_IDT_OFFSET ) ) ;
298
366
299
367
assert_eq ! ( 0 , sregs. cs. base) ;
300
- assert_eq ! ( 0xfffff , sregs. ds. limit) ;
368
+ assert_eq ! ( 0xffff_ffff , sregs. ds. limit) ;
301
369
assert_eq ! ( 0x10 , sregs. es. selector) ;
302
370
assert_eq ! ( 1 , sregs. fs. present) ;
303
371
assert_eq ! ( 1 , sregs. gs. g) ;
304
372
assert_eq ! ( 0 , sregs. ss. avl) ;
305
373
assert_eq ! ( 0 , sregs. tr. base) ;
306
- assert_eq ! ( 0xfffff , sregs. tr. limit) ;
307
374
assert_eq ! ( 0 , sregs. tr. avl) ;
308
- assert ! ( sregs. cr0 & X86_CR0_PE != 0 ) ;
309
- assert ! ( sregs. efer & EFER_LME != 0 && sregs. efer & EFER_LMA != 0 ) ;
310
375
}
311
376
312
377
fn validate_page_tables ( gm : & GuestMemoryMmap , sregs : & kvm_sregs ) {
@@ -358,7 +423,12 @@ mod tests {
358
423
..Default :: default ( )
359
424
} ;
360
425
361
- setup_regs ( & vcpu, expected_regs. rip ) . unwrap ( ) ;
426
+ let entry_point: EntryPoint = EntryPoint {
427
+ entry_addr : GuestAddress ( expected_regs. rip ) ,
428
+ protocol : BootProtocol :: LinuxBoot ,
429
+ } ;
430
+
431
+ setup_regs ( & vcpu, entry_point) . unwrap ( ) ;
362
432
363
433
let actual_regs: kvm_regs = vcpu. get_regs ( ) . unwrap ( ) ;
364
434
assert_eq ! ( actual_regs, expected_regs) ;
@@ -371,16 +441,22 @@ mod tests {
371
441
let vcpu = vm. create_vcpu ( 0 ) . unwrap ( ) ;
372
442
let gm = create_guest_mem ( None ) ;
373
443
374
- assert ! ( vcpu. set_sregs( & Default :: default ( ) ) . is_ok( ) ) ;
375
- setup_sregs ( & gm, & vcpu) . unwrap ( ) ;
376
-
377
- let mut sregs: kvm_sregs = vcpu. get_sregs ( ) . unwrap ( ) ;
378
- // for AMD KVM_GET_SREGS returns g = 0 for each kvm_segment.
379
- // We set it to 1, otherwise the test will fail.
380
- sregs. gs . g = 1 ;
381
-
382
- validate_segments_and_sregs ( & gm, & sregs) ;
383
- validate_page_tables ( & gm, & sregs) ;
444
+ [ BootProtocol :: LinuxBoot , BootProtocol :: PvhBoot ]
445
+ . iter ( )
446
+ . for_each ( |boot_prot| {
447
+ assert ! ( vcpu. set_sregs( & Default :: default ( ) ) . is_ok( ) ) ;
448
+ setup_sregs ( & gm, & vcpu, * boot_prot) . unwrap ( ) ;
449
+
450
+ let mut sregs: kvm_sregs = vcpu. get_sregs ( ) . unwrap ( ) ;
451
+ // for AMD KVM_GET_SREGS returns g = 0 for each kvm_segment.
452
+ // We set it to 1, otherwise the test will fail.
453
+ sregs. gs . g = 1 ;
454
+
455
+ validate_segments_and_sregs ( & gm, & sregs, * boot_prot) ;
456
+ if let BootProtocol :: LinuxBoot = * boot_prot {
457
+ validate_page_tables ( & gm, & sregs) ;
458
+ }
459
+ } ) ;
384
460
}
385
461
386
462
#[ test]
@@ -425,9 +501,13 @@ mod tests {
425
501
fn test_configure_segments_and_sregs ( ) {
426
502
let mut sregs: kvm_sregs = Default :: default ( ) ;
427
503
let gm = create_guest_mem ( None ) ;
428
- configure_segments_and_sregs ( & gm, & mut sregs) . unwrap ( ) ;
504
+ configure_segments_and_sregs ( & gm, & mut sregs, BootProtocol :: LinuxBoot ) . unwrap ( ) ;
505
+
506
+ validate_segments_and_sregs ( & gm, & sregs, BootProtocol :: LinuxBoot ) ;
507
+
508
+ configure_segments_and_sregs ( & gm, & mut sregs, BootProtocol :: PvhBoot ) . unwrap ( ) ;
429
509
430
- validate_segments_and_sregs ( & gm, & sregs) ;
510
+ validate_segments_and_sregs ( & gm, & sregs, BootProtocol :: PvhBoot ) ;
431
511
}
432
512
433
513
#[ test]
0 commit comments