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.
@@ -89,20 +91,30 @@ pub struct SetupRegistersError(utils::errno::Error);
89
91
/// # Errors
90
92
///
91
93
/// When [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_regs`] errors.
92
- pub fn setup_regs ( vcpu : & VcpuFd , boot_ip : u64 ) -> Result < ( ) , SetupRegistersError > {
93
- let regs: kvm_regs = kvm_regs {
94
- rflags : 0x0000_0000_0000_0002u64 ,
95
- rip : boot_ip,
96
- // Frame pointer. It gets a snapshot of the stack pointer (rsp) so that when adjustments are
97
- // made to rsp (i.e. reserving space for local variables or pushing values on to the stack),
98
- // local variables and function parameters are still accessible from a constant offset from
99
- // rbp.
100
- rsp : super :: layout:: BOOT_STACK_POINTER ,
101
- // Starting stack pointer.
102
- rbp : super :: layout:: BOOT_STACK_POINTER ,
103
- // Must point to zero page address per Linux ABI. This is x86_64 specific.
104
- rsi : super :: layout:: ZERO_PAGE_START ,
105
- ..Default :: default ( )
94
+ pub fn setup_regs ( vcpu : & VcpuFd , entry_point : EntryPoint ) -> Result < ( ) , SetupRegistersError > {
95
+ let regs: kvm_regs = match entry_point. protocol {
96
+ BootProtocol :: PvhBoot => kvm_regs {
97
+ // Configure regs as required by PVH boot protocol.
98
+ rflags : 0x0000_0000_0000_0002u64 ,
99
+ rbx : super :: layout:: PVH_INFO_START ,
100
+ rip : entry_point. entry_addr . raw_value ( ) ,
101
+ ..Default :: default ( )
102
+ } ,
103
+ BootProtocol :: LinuxBoot => kvm_regs {
104
+ // Configure regs as required by Linux 64-bit boot protocol.
105
+ rflags : 0x0000_0000_0000_0002u64 ,
106
+ rip : entry_point. entry_addr . raw_value ( ) ,
107
+ // Frame pointer. It gets a snapshot of the stack pointer (rsp) so that when adjustments
108
+ // are made to rsp (i.e. reserving space for local variables or pushing
109
+ // values on to the stack), local variables and function parameters are
110
+ // still accessible from a constant offset from rbp.
111
+ rsp : super :: layout:: BOOT_STACK_POINTER ,
112
+ // Starting stack pointer.
113
+ rbp : super :: layout:: BOOT_STACK_POINTER ,
114
+ // Must point to zero page address per Linux ABI. This is x86_64 specific.
115
+ rsi : super :: layout:: ZERO_PAGE_START ,
116
+ ..Default :: default ( )
117
+ } ,
106
118
} ;
107
119
108
120
vcpu. set_regs ( & regs) . map_err ( SetupRegistersError )
@@ -131,6 +143,7 @@ pub enum SetupSpecialRegistersError {
131
143
///
132
144
/// * `mem` - The memory that will be passed to the guest.
133
145
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
146
+ /// * `boot_prot` - The boot protocol being used.
134
147
///
135
148
/// # Errors
136
149
///
@@ -139,14 +152,21 @@ pub enum SetupSpecialRegistersError {
139
152
/// - [`configure_segments_and_sregs`] errors.
140
153
/// - [`setup_page_tables`] errors
141
154
/// - [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_sregs`] errors.
142
- pub fn setup_sregs ( mem : & GuestMemoryMmap , vcpu : & VcpuFd ) -> Result < ( ) , SetupSpecialRegistersError > {
155
+ pub fn setup_sregs (
156
+ mem : & GuestMemoryMmap ,
157
+ vcpu : & VcpuFd ,
158
+ boot_prot : BootProtocol ,
159
+ ) -> Result < ( ) , SetupSpecialRegistersError > {
143
160
let mut sregs: kvm_sregs = vcpu
144
161
. get_sregs ( )
145
162
. map_err ( SetupSpecialRegistersError :: GetSpecialRegisters ) ?;
146
163
147
- configure_segments_and_sregs ( mem, & mut sregs)
164
+ configure_segments_and_sregs ( mem, & mut sregs, boot_prot )
148
165
. map_err ( SetupSpecialRegistersError :: ConfigureSegmentsAndSpecialRegisters ) ?;
149
- setup_page_tables ( mem, & mut sregs) . map_err ( SetupSpecialRegistersError :: SetupPageTables ) ?; // TODO(dgreid) - Can this be done once per system instead?
166
+ if let BootProtocol :: LinuxBoot = boot_prot {
167
+ setup_page_tables ( mem, & mut sregs) . map_err ( SetupSpecialRegistersError :: SetupPageTables ) ?;
168
+ // TODO(dgreid) - Can this be done once per system instead?
169
+ }
150
170
151
171
vcpu. set_sregs ( & sregs)
152
172
. map_err ( SetupSpecialRegistersError :: SetSpecialRegisters )
@@ -161,6 +181,7 @@ const EFER_LMA: u64 = 0x400;
161
181
const EFER_LME : u64 = 0x100 ;
162
182
163
183
const X86_CR0_PE : u64 = 0x1 ;
184
+ const X86_CR0_ET : u64 = 0x10 ;
164
185
const X86_CR0_PG : u64 = 0x8000_0000 ;
165
186
const X86_CR4_PAE : u64 = 0x20 ;
166
187
@@ -187,13 +208,28 @@ fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<(), RegsErro
187
208
fn configure_segments_and_sregs (
188
209
mem : & GuestMemoryMmap ,
189
210
sregs : & mut kvm_sregs ,
211
+ boot_prot : BootProtocol ,
190
212
) -> Result < ( ) , RegsError > {
191
- let gdt_table: [ u64 ; BOOT_GDT_MAX ] = [
192
- gdt_entry ( 0 , 0 , 0 ) , // NULL
193
- gdt_entry ( 0xa09b , 0 , 0xfffff ) , // CODE
194
- gdt_entry ( 0xc093 , 0 , 0xfffff ) , // DATA
195
- gdt_entry ( 0x808b , 0 , 0xfffff ) , // TSS
196
- ] ;
213
+ let gdt_table: [ u64 ; BOOT_GDT_MAX ] = match boot_prot {
214
+ BootProtocol :: PvhBoot => {
215
+ // Configure GDT entries as specified by PVH boot protocol
216
+ [
217
+ gdt_entry ( 0 , 0 , 0 ) , // NULL
218
+ gdt_entry ( 0xc09b , 0 , 0xffff_ffff ) , // CODE
219
+ gdt_entry ( 0xc093 , 0 , 0xffff_ffff ) , // DATA
220
+ gdt_entry ( 0x008b , 0 , 0x67 ) , // TSS
221
+ ]
222
+ }
223
+ BootProtocol :: LinuxBoot => {
224
+ // Configure GDT entries as specified by Linux 64bit boot protocol
225
+ [
226
+ gdt_entry ( 0 , 0 , 0 ) , // NULL
227
+ gdt_entry ( 0xa09b , 0 , 0xfffff ) , // CODE
228
+ gdt_entry ( 0xc093 , 0 , 0xfffff ) , // DATA
229
+ gdt_entry ( 0x808b , 0 , 0xfffff ) , // TSS
230
+ ]
231
+ }
232
+ } ;
197
233
198
234
let code_seg = kvm_segment_from_gdt ( gdt_table[ 1 ] , 1 ) ;
199
235
let data_seg = kvm_segment_from_gdt ( gdt_table[ 2 ] , 2 ) ;
@@ -216,9 +252,17 @@ fn configure_segments_and_sregs(
216
252
sregs. ss = data_seg;
217
253
sregs. tr = tss_seg;
218
254
219
- // 64-bit protected mode
220
- sregs. cr0 |= X86_CR0_PE ;
221
- sregs. efer |= EFER_LME | EFER_LMA ;
255
+ match boot_prot {
256
+ BootProtocol :: PvhBoot => {
257
+ sregs. cr0 = X86_CR0_PE | X86_CR0_ET ;
258
+ sregs. cr4 = 0 ;
259
+ }
260
+ BootProtocol :: LinuxBoot => {
261
+ // 64-bit protected mode
262
+ sregs. cr0 |= X86_CR0_PE ;
263
+ sregs. efer |= EFER_LME | EFER_LMA ;
264
+ }
265
+ }
222
266
223
267
Ok ( ( ) )
224
268
}
@@ -279,24 +323,45 @@ mod tests {
279
323
gm. read_obj ( read_addr) . unwrap ( )
280
324
}
281
325
282
- fn validate_segments_and_sregs ( gm : & GuestMemoryMmap , sregs : & kvm_sregs ) {
326
+ fn validate_segments_and_sregs (
327
+ gm : & GuestMemoryMmap ,
328
+ sregs : & kvm_sregs ,
329
+ boot_prot : BootProtocol ,
330
+ ) {
331
+ if let BootProtocol :: LinuxBoot = boot_prot {
332
+ assert_eq ! ( 0xaf_9b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 8 ) ) ;
333
+ assert_eq ! ( 0xcf_9300_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 16 ) ) ;
334
+ assert_eq ! ( 0x8f_8b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 24 ) ) ;
335
+
336
+ assert_eq ! ( 0xffff_ffff , sregs. tr. limit) ;
337
+
338
+ assert ! ( sregs. cr0 & X86_CR0_PE != 0 ) ;
339
+ assert ! ( sregs. efer & EFER_LME != 0 && sregs. efer & EFER_LMA != 0 ) ;
340
+ } else {
341
+ // Validate values that are specific to PVH boot protocol
342
+ assert_eq ! ( 0xcf_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 ! ( 0x00_8b00_0000_0067 , read_u64( gm, BOOT_GDT_OFFSET + 24 ) ) ;
345
+
346
+ assert_eq ! ( 0x67 , sregs. tr. limit) ;
347
+ assert_eq ! ( 0 , sregs. tr. g) ;
348
+
349
+ assert ! ( sregs. cr0 & X86_CR0_PE != 0 && sregs. cr0 & X86_CR0_ET != 0 ) ;
350
+ assert_eq ! ( 0 , sregs. cr4) ;
351
+ }
352
+
353
+ // Common settings for both PVH and Linux boot protocol
283
354
assert_eq ! ( 0x0 , read_u64( gm, BOOT_GDT_OFFSET ) ) ;
284
- assert_eq ! ( 0xaf_9b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 8 ) ) ;
285
- assert_eq ! ( 0xcf_9300_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 16 ) ) ;
286
- assert_eq ! ( 0x8f_8b00_0000_ffff , read_u64( gm, BOOT_GDT_OFFSET + 24 ) ) ;
287
355
assert_eq ! ( 0x0 , read_u64( gm, BOOT_IDT_OFFSET ) ) ;
288
356
289
357
assert_eq ! ( 0 , sregs. cs. base) ;
290
- assert_eq ! ( 0xfffff , sregs. ds. limit) ;
358
+ assert_eq ! ( 0xffff_ffff , sregs. ds. limit) ;
291
359
assert_eq ! ( 0x10 , sregs. es. selector) ;
292
360
assert_eq ! ( 1 , sregs. fs. present) ;
293
361
assert_eq ! ( 1 , sregs. gs. g) ;
294
362
assert_eq ! ( 0 , sregs. ss. avl) ;
295
363
assert_eq ! ( 0 , sregs. tr. base) ;
296
- assert_eq ! ( 0xfffff , sregs. tr. limit) ;
297
364
assert_eq ! ( 0 , sregs. tr. avl) ;
298
- assert ! ( sregs. cr0 & X86_CR0_PE != 0 ) ;
299
- assert ! ( sregs. efer & EFER_LME != 0 && sregs. efer & EFER_LMA != 0 ) ;
300
365
}
301
366
302
367
fn validate_page_tables ( gm : & GuestMemoryMmap , sregs : & kvm_sregs ) {
@@ -348,7 +413,12 @@ mod tests {
348
413
..Default :: default ( )
349
414
} ;
350
415
351
- setup_regs ( & vcpu, expected_regs. rip ) . unwrap ( ) ;
416
+ let entry_point: EntryPoint = EntryPoint {
417
+ entry_addr : GuestAddress ( expected_regs. rip ) ,
418
+ protocol : BootProtocol :: LinuxBoot ,
419
+ } ;
420
+
421
+ setup_regs ( & vcpu, entry_point) . unwrap ( ) ;
352
422
353
423
let actual_regs: kvm_regs = vcpu. get_regs ( ) . unwrap ( ) ;
354
424
assert_eq ! ( actual_regs, expected_regs) ;
@@ -361,16 +431,22 @@ mod tests {
361
431
let vcpu = vm. create_vcpu ( 0 ) . unwrap ( ) ;
362
432
let gm = create_guest_mem ( None ) ;
363
433
364
- assert ! ( vcpu. set_sregs( & Default :: default ( ) ) . is_ok( ) ) ;
365
- setup_sregs ( & gm, & vcpu) . unwrap ( ) ;
366
-
367
- let mut sregs: kvm_sregs = vcpu. get_sregs ( ) . unwrap ( ) ;
368
- // for AMD KVM_GET_SREGS returns g = 0 for each kvm_segment.
369
- // We set it to 1, otherwise the test will fail.
370
- sregs. gs . g = 1 ;
371
-
372
- validate_segments_and_sregs ( & gm, & sregs) ;
373
- validate_page_tables ( & gm, & sregs) ;
434
+ [ BootProtocol :: LinuxBoot , BootProtocol :: PvhBoot ]
435
+ . iter ( )
436
+ . for_each ( |boot_prot| {
437
+ assert ! ( vcpu. set_sregs( & Default :: default ( ) ) . is_ok( ) ) ;
438
+ setup_sregs ( & gm, & vcpu, * boot_prot) . unwrap ( ) ;
439
+
440
+ let mut sregs: kvm_sregs = vcpu. get_sregs ( ) . unwrap ( ) ;
441
+ // for AMD KVM_GET_SREGS returns g = 0 for each kvm_segment.
442
+ // We set it to 1, otherwise the test will fail.
443
+ sregs. gs . g = 1 ;
444
+
445
+ validate_segments_and_sregs ( & gm, & sregs, * boot_prot) ;
446
+ if let BootProtocol :: LinuxBoot = * boot_prot {
447
+ validate_page_tables ( & gm, & sregs) ;
448
+ }
449
+ } ) ;
374
450
}
375
451
376
452
#[ test]
@@ -415,9 +491,13 @@ mod tests {
415
491
fn test_configure_segments_and_sregs ( ) {
416
492
let mut sregs: kvm_sregs = Default :: default ( ) ;
417
493
let gm = create_guest_mem ( None ) ;
418
- configure_segments_and_sregs ( & gm, & mut sregs) . unwrap ( ) ;
494
+ configure_segments_and_sregs ( & gm, & mut sregs, BootProtocol :: LinuxBoot ) . unwrap ( ) ;
495
+
496
+ validate_segments_and_sregs ( & gm, & sregs, BootProtocol :: LinuxBoot ) ;
497
+
498
+ configure_segments_and_sregs ( & gm, & mut sregs, BootProtocol :: PvhBoot ) . unwrap ( ) ;
419
499
420
- validate_segments_and_sregs ( & gm, & sregs) ;
500
+ validate_segments_and_sregs ( & gm, & sregs, BootProtocol :: PvhBoot ) ;
421
501
}
422
502
423
503
#[ test]
0 commit comments