@@ -16,7 +16,7 @@ use std::arch::asm;
16
16
17
17
use std:: convert:: TryInto ;
18
18
use std:: fmt:: { Display , Formatter } ;
19
- use std:: sync:: Arc ;
19
+ use std:: sync:: { Arc , LazyLock } ;
20
20
use std:: time:: Duration ;
21
21
22
22
#[ cfg( all( target_arch = "aarch64" , target_os = "macos" ) ) ]
@@ -37,11 +37,54 @@ const TMR_CTL_IMASK: u64 = 1 << 1;
37
37
const TMR_CTL_ISTATUS : u64 = 1 << 2 ;
38
38
39
39
const PSR_MODE_EL1H : u64 = 0x0000_0005 ;
40
+ const PSR_MODE_EL2H : u64 = 0x0000_0009 ;
40
41
const PSR_F_BIT : u64 = 0x0000_0040 ;
41
42
const PSR_I_BIT : u64 = 0x0000_0080 ;
42
43
const PSR_A_BIT : u64 = 0x0000_0100 ;
43
44
const PSR_D_BIT : u64 = 0x0000_0200 ;
44
- const PSTATE_FAULT_BITS_64 : u64 = PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT ;
45
+ const PSTATE_EL1_FAULT_BITS_64 : u64 = PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT ;
46
+ const PSTATE_EL2_FAULT_BITS_64 : u64 = PSR_MODE_EL2H | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT ;
47
+
48
+ const HCR_TLOR : u64 = 1 << 35 ;
49
+ const HCR_RW : u64 = 1 << 31 ;
50
+ const HCR_TSW : u64 = 1 << 22 ;
51
+ const HCR_TACR : u64 = 1 << 21 ;
52
+ const HCR_TIDCP : u64 = 1 << 20 ;
53
+ const HCR_TSC : u64 = 1 << 19 ;
54
+ const HCR_TID3 : u64 = 1 << 18 ;
55
+ const HCR_TWE : u64 = 1 << 14 ;
56
+ const HCR_TWI : u64 = 1 << 13 ;
57
+ const HCR_BSU_IS : u64 = 1 << 10 ;
58
+ const HCR_FB : u64 = 1 << 9 ;
59
+ const HCR_AMO : u64 = 1 << 5 ;
60
+ const HCR_IMO : u64 = 1 << 4 ;
61
+ const HCR_FMO : u64 = 1 << 3 ;
62
+ const HCR_PTW : u64 = 1 << 2 ;
63
+ const HCR_SWIO : u64 = 1 << 1 ;
64
+ const HCR_VM : u64 = 1 << 0 ;
65
+ // Use the same bits as KVM uses in vcpu reset.
66
+ const HCR_EL2_BITS : u64 = HCR_TSC
67
+ | HCR_TSW
68
+ | HCR_TWE
69
+ | HCR_TWI
70
+ | HCR_VM
71
+ | HCR_BSU_IS
72
+ | HCR_FB
73
+ | HCR_TACR
74
+ | HCR_AMO
75
+ | HCR_SWIO
76
+ | HCR_TIDCP
77
+ | HCR_RW
78
+ | HCR_TLOR
79
+ | HCR_FMO
80
+ | HCR_IMO
81
+ | HCR_PTW
82
+ | HCR_TID3 ;
83
+
84
+ const CNTHCTL_EL0VCTEN : u64 = 1 << 1 ;
85
+ const CNTHCTL_EL0PCTEN : u64 = 1 << 0 ;
86
+ // Trap accesses to both virtual and physical counter registers.
87
+ const CNTHCTL_EL2_BITS : u64 = CNTHCTL_EL0VCTEN | CNTHCTL_EL0PCTEN ;
45
88
46
89
const EC_WFX_TRAP : u64 = 0x1 ;
47
90
const EC_AA64_HVC : u64 = 0x16 ;
@@ -53,9 +96,11 @@ const EC_AA64_BKPT: u64 = 0x3c;
53
96
54
97
#[ derive( Debug ) ]
55
98
pub enum Error {
99
+ EnableEL2 ,
56
100
FindSymbol ( libloading:: Error ) ,
57
101
MemoryMap ,
58
102
MemoryUnmap ,
103
+ NestedCheck ,
59
104
VcpuCreate ,
60
105
VcpuInitialRegisters ,
61
106
VcpuReadRegister ,
@@ -74,9 +119,14 @@ impl Display for Error {
74
119
use self :: Error :: * ;
75
120
76
121
match self {
122
+ EnableEL2 => write ! ( f, "Error enabling EL2 mode in HVF" ) ,
77
123
FindSymbol ( ref err) => write ! ( f, "Couldn't find symbol in HVF library: {}" , err) ,
78
124
MemoryMap => write ! ( f, "Error registering memory region in HVF" ) ,
79
125
MemoryUnmap => write ! ( f, "Error unregistering memory region in HVF" ) ,
126
+ NestedCheck => write ! (
127
+ f,
128
+ "Nested virtualization was requested but it's not support in this system"
129
+ ) ,
80
130
VcpuCreate => write ! ( f, "Error creating HVF vCPU instance" ) ,
81
131
VcpuInitialRegisters => write ! ( f, "Error setting up initial HVF vCPU registers" ) ,
82
132
VcpuReadRegister => write ! ( f, "Error reading HVF vCPU register" ) ,
@@ -156,11 +206,56 @@ pub fn vcpu_set_vtimer_mask(vcpuid: u64, masked: bool) -> Result<(), Error> {
156
206
}
157
207
}
158
208
209
+ pub struct HvfNestedBindings {
210
+ hv_vm_config_get_el2_supported :
211
+ libloading:: Symbol < ' static , unsafe extern "C" fn ( * mut bool ) -> hv_return_t > ,
212
+ hv_vm_config_set_el2_enabled :
213
+ libloading:: Symbol < ' static , unsafe extern "C" fn ( hv_vm_config_t , * mut bool ) -> hv_return_t > ,
214
+ }
215
+
159
216
pub struct HvfVm { }
160
217
218
+ static HVF : LazyLock < libloading:: Library > = LazyLock :: new ( || unsafe {
219
+ libloading:: Library :: new (
220
+ "/System/Library/Frameworks/Hypervisor.framework/Versions/A/Hypervisor" ,
221
+ )
222
+ . unwrap ( )
223
+ } ) ;
224
+
161
225
impl HvfVm {
162
- pub fn new ( ) -> Result < Self , Error > {
163
- let ret = unsafe { hv_vm_create ( std:: ptr:: null_mut ( ) ) } ;
226
+ pub fn new ( nested_enabled : bool ) -> Result < Self , Error > {
227
+ let config = unsafe { hv_vm_config_create ( ) } ;
228
+
229
+ if nested_enabled {
230
+ let bindings = unsafe {
231
+ HvfNestedBindings {
232
+ hv_vm_config_get_el2_supported : HVF
233
+ . get ( b"hv_vm_config_get_el2_supported" )
234
+ . map_err ( Error :: FindSymbol ) ?,
235
+ hv_vm_config_set_el2_enabled : HVF
236
+ . get ( b"hv_vm_config_set_el2_enabled" )
237
+ . map_err ( Error :: FindSymbol ) ?,
238
+ }
239
+ } ;
240
+
241
+ let mut el2_supported: bool = false ;
242
+ let ret = unsafe { ( bindings. hv_vm_config_get_el2_supported ) ( & mut el2_supported) } ;
243
+ if ret != HV_SUCCESS {
244
+ return Err ( Error :: NestedCheck ) ;
245
+ }
246
+
247
+ if !el2_supported {
248
+ return Err ( Error :: NestedCheck ) ;
249
+ }
250
+
251
+ let mut el2_enabled = true ;
252
+ let ret = unsafe { ( bindings. hv_vm_config_set_el2_enabled ) ( config, & mut el2_enabled) } ;
253
+ if ret != HV_SUCCESS {
254
+ return Err ( Error :: EnableEL2 ) ;
255
+ }
256
+ }
257
+
258
+ let ret = unsafe { hv_vm_create ( config) } ;
164
259
165
260
if ret != HV_SUCCESS {
166
261
Err ( Error :: VmCreate )
@@ -232,10 +327,11 @@ pub struct HvfVcpu<'a> {
232
327
pending_mmio_read : Option < MmioRead > ,
233
328
pending_advance_pc : bool ,
234
329
vtimer_masked : bool ,
330
+ nested_enabled : bool ,
235
331
}
236
332
237
333
impl HvfVcpu < ' _ > {
238
- pub fn new ( mpidr : u64 ) -> Result < Self , Error > {
334
+ pub fn new ( mpidr : u64 , nested_enabled : bool ) -> Result < Self , Error > {
239
335
let mut vcpuid: hv_vcpu_t = 0 ;
240
336
let vcpu_exit_ptr: * mut hv_vcpu_exit_t = std:: ptr:: null_mut ( ) ;
241
337
@@ -276,14 +372,43 @@ impl HvfVcpu<'_> {
276
372
pending_mmio_read : None ,
277
373
pending_advance_pc : false ,
278
374
vtimer_masked : false ,
375
+ nested_enabled,
279
376
} )
280
377
}
281
378
282
379
pub fn set_initial_state ( & self , entry_addr : u64 , fdt_addr : u64 ) -> Result < ( ) , Error > {
283
- let ret =
284
- unsafe { hv_vcpu_set_reg ( self . vcpuid , hv_reg_t_HV_REG_CPSR, PSTATE_FAULT_BITS_64 ) } ;
285
- if ret != HV_SUCCESS {
286
- return Err ( Error :: VcpuInitialRegisters ) ;
380
+ if self . nested_enabled {
381
+ let ret = unsafe {
382
+ hv_vcpu_set_reg ( self . vcpuid , hv_reg_t_HV_REG_CPSR, PSTATE_EL2_FAULT_BITS_64 )
383
+ } ;
384
+ if ret != HV_SUCCESS {
385
+ return Err ( Error :: VcpuInitialRegisters ) ;
386
+ }
387
+
388
+ let ret = unsafe {
389
+ hv_vcpu_set_sys_reg ( self . vcpuid , hv_sys_reg_t_HV_SYS_REG_HCR_EL2, HCR_EL2_BITS )
390
+ } ;
391
+ if ret != HV_SUCCESS {
392
+ return Err ( Error :: VcpuInitialRegisters ) ;
393
+ }
394
+
395
+ let ret = unsafe {
396
+ hv_vcpu_set_sys_reg (
397
+ self . vcpuid ,
398
+ hv_sys_reg_t_HV_SYS_REG_CNTHCTL_EL2,
399
+ CNTHCTL_EL2_BITS ,
400
+ )
401
+ } ;
402
+ if ret != HV_SUCCESS {
403
+ return Err ( Error :: VcpuInitialRegisters ) ;
404
+ }
405
+ } else {
406
+ let ret = unsafe {
407
+ hv_vcpu_set_reg ( self . vcpuid , hv_reg_t_HV_REG_CPSR, PSTATE_EL1_FAULT_BITS_64 )
408
+ } ;
409
+ if ret != HV_SUCCESS {
410
+ return Err ( Error :: VcpuInitialRegisters ) ;
411
+ }
287
412
}
288
413
289
414
let ret = unsafe { hv_vcpu_set_reg ( self . vcpuid , hv_reg_t_HV_REG_PC, entry_addr) } ;
0 commit comments