Skip to content

Commit 77fe920

Browse files
committed
add support of the generic interrupt controler
- increase version number - revise README.md - add option to print the vCPU state
1 parent d9055f3 commit 77fe920

File tree

6 files changed

+467
-59
lines changed

6 files changed

+467
-59
lines changed

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "xhypervisor"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
authors = ["Stefan Lankes", "Saurav Sachidanand"]
55
edition = "2021"
66

@@ -13,6 +13,11 @@ keywords = ["hypervisor", "virtualization", "osx", "x86", "aarch64"]
1313

1414
build = "build.rs"
1515

16+
[features]
17+
default = []
18+
macos_15_0_0 = ["macos_13_0_0"]
19+
macos_13_0_0 = []
20+
1621
[dependencies]
1722
libc = "0.2"
1823
thiserror = "2.0"

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,28 @@ It binds to the [Hypervisor](https://developer.apple.com/documentation/hyperviso
1414

1515
To use this library, you need
1616

17-
* OS X Yosemite (10.10), or newer
17+
* macOS 10.10, or newer
1818

19-
* A Intel processor with the VT-x feature or an Apple Silicon processor with virtualization support. To verify this, run and expect the
19+
* An Intel processor with the VT-x feature or an Apple Silicon processor with virtualization support. To verify this, run and expect the
2020
following in your Terminal:
2121
```shell
2222
$ sysctl kern.hv_support
2323
kern.hv_support: 1
2424
```
2525

2626
## Status
27-
- **WARNING:** The Apple Silicon support is in an early state
2827
- [x] Accessing x86 registers
2928
- [x] Accessing aarch64 registers
30-
- [x] x86: Accessing model-specific registers (MSRs)
3129
- [x] Mapping guest physical memory segments into guest physical address space
32-
- [x] Virtual CPUs
30+
- [x] x86: Accessing fields of Virtual Machine Control Structures (VMCS)
31+
- [x] Virtual x86 CPUs
3332
- [x] Executing and interrupting
3433
- [x] Force flushing cached state
3534
- [x] Invalidating translation lookaside buffer (TLB)
3635
- [x] Accessing floating point (FP) and SIMD state
3736
- [x] Obtaining cumulative execution time
3837
- [x] Synchronizing guest timestamp-counters (TSC)
39-
- [x] x86: Accessing fields of Virtual Machine Control Structures (VMCS)
38+
- [x] Accessing model-specific registers (MSRs)
39+
- [x] Virtual aarch64 CPUs
40+
- [x] Executing and interrupting
41+
- [x] GICv3 support (requires macOS 10.15, or newer)

src/aarch64/ffi.rs

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ use core::ffi::c_void;
55
/// vCPU configuration.
66
pub type hv_vcpu_config_t = *mut c_void;
77

8+
/// A generic interrupt controller (GIC) configuration’s reference type.
9+
pub type hv_gic_config_t = *mut c_void;
10+
11+
/// Type of an ARM GIC distributor register
12+
pub type hv_gic_redistributor_reg_t = u32;
13+
814
/// VM configuration.
915
pub type hv_vm_config_t = *mut c_void;
1016

@@ -669,13 +675,13 @@ pub const HV_DENIED: hv_return_t = 0xfae94007;
669675
pub const HV_UNSUPPORTED: hv_return_t = 0xfae9400f;
670676

671677
/// Read memory permission.
672-
pub const HV_MEMORY_READ: hv_memory_flags_t = 1 << 0;
678+
pub const HV_MEMORY_READ: hv_memory_flags_t = 1u64 << 0;
673679

674680
/// Write memory permission.
675-
pub const HV_MEMORY_WRITE: hv_memory_flags_t = 1 << 1;
681+
pub const HV_MEMORY_WRITE: hv_memory_flags_t = 1u64 << 1;
676682

677683
/// Execute memory permission.
678-
pub const HV_MEMORY_EXEC: hv_memory_flags_t = 1 << 2;
684+
pub const HV_MEMORY_EXEC: hv_memory_flags_t = 1u64 << 2;
679685

680686
/// The value that identifies feature register ID_AA64DFR0_EL1.
681687
pub const HV_FEATURE_REG_ID_AA64DFR0_EL1: hv_feature_reg_t = 0;
@@ -714,6 +720,8 @@ pub const HV_FEATURE_REG_CLIDR_EL1: hv_feature_reg_t = 10;
714720
pub const HV_FEATURE_REG_DCZID_EL0: hv_feature_reg_t = 11;
715721

716722
extern "C" {
723+
/// Release OS objects
724+
pub fn os_release(obj: *const c_void);
717725

718726
// VM APIs
719727

@@ -723,6 +731,66 @@ extern "C" {
723731
/// Destroys the VM instance associated with the current process.
724732
pub fn hv_vm_destroy() -> hv_return_t;
725733

734+
/// Creates a generic interrupt controller (GIC) v3 device for a VM configuration.
735+
#[cfg(feature = "macos_15_0_0")]
736+
pub fn hv_gic_create(gic_config: hv_gic_config_t) -> hv_return_t;
737+
738+
/// Creates a generic interrupt controller (GIC) configuration object.
739+
#[cfg(feature = "macos_15_0_0")]
740+
pub fn hv_gic_config_create() -> hv_gic_config_t;
741+
742+
/// Resets the generic interrupt controller (GIC) device.
743+
#[cfg(feature = "macos_15_0_0")]
744+
pub fn hv_gic_reset() -> hv_return_t;
745+
746+
/// Sets the generic interrupt controller (GIC) distributor region’s base address.
747+
#[cfg(feature = "macos_15_0_0")]
748+
pub fn hv_gic_config_set_distributor_base(
749+
config: hv_gic_config_t,
750+
ipa: hv_ipa_t,
751+
) -> hv_return_t;
752+
753+
/// Sets the generic interrupt controller (GIC) redistributor region base address.
754+
#[cfg(feature = "macos_15_0_0")]
755+
pub fn hv_gic_config_set_redistributor_base(
756+
config: hv_gic_config_t,
757+
ipa: hv_ipa_t,
758+
) -> hv_return_t;
759+
760+
/// Sets the generic interrupt controllers message signaled interrupts (MSIs) region base address.
761+
#[cfg(feature = "macos_15_0_0")]
762+
pub fn hv_gic_config_set_msi_region_base(
763+
config: hv_gic_config_t,
764+
msi_region_base_address: hv_ipa_t,
765+
) -> hv_return_t;
766+
767+
/// Sets the range of message signaled interrupts (MSIs) the generic interrupt controller supports.
768+
#[cfg(feature = "macos_15_0_0")]
769+
pub fn hv_gic_config_set_msi_interrupt_range(
770+
config: hv_gic_config_t,
771+
msi_intid_base: u32,
772+
msi_intid_count: u32,
773+
) -> hv_return_t;
774+
775+
/// Gets the redistributor base guest physical address for the given vCPU.
776+
#[cfg(feature = "macos_15_0_0")]
777+
pub fn hv_gic_get_redistributor_base(cpu: hv_vcpu_t, ipa: hv_ipa_t) -> hv_return_t;
778+
779+
/// Gets the range of shared peripheral interrupts (SPIs) the generic interrupt controller supports.
780+
#[cfg(feature = "macos_15_0_0")]
781+
pub fn hv_gic_get_spi_interrupt_range(
782+
spi_intid_base: *mut u32,
783+
spi_intid_count: *mut u32,
784+
) -> hv_return_t;
785+
786+
/// Read a generic interrupt controller (GIC) redistributor register.
787+
#[cfg(feature = "macos_15_0_0")]
788+
pub fn hv_gic_get_redistributor_reg(
789+
vcpu: hv_vcpu_t,
790+
reg: hv_gic_redistributor_reg_t,
791+
value: *mut u64,
792+
) -> hv_return_t;
793+
726794
/// Maps a region in the virtual address space of the current process into the guest physical address space of the VM.
727795
pub fn hv_vm_map(
728796
address: *mut c_void,

src/aarch64/mod.rs

Lines changed: 160 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,52 @@ pub fn create_vm() -> Result<(), Error> {
1010
match_error_code(unsafe { hv_vm_create(null_mut()) })
1111
}
1212

13+
#[cfg(feature = "macos_15_0_0")]
14+
/// Generic interrupt controller (GIC)
15+
pub struct Gic(hv_gic_config_t);
16+
17+
#[cfg(feature = "macos_15_0_0")]
18+
impl Gic {
19+
/// Creates a generic interrupt controller (GIC)
20+
pub fn new(gicd_addr: u64, gicr_base: u64, msi_base: u64) -> Result<Self, Error> {
21+
let config = unsafe { hv_gic_config_create() };
22+
23+
let mut intid_base: u32 = 0;
24+
let mut intid_count: u32 = 0;
25+
match_error_code(unsafe {
26+
hv_gic_get_spi_interrupt_range(&mut intid_base as *mut _, &mut intid_count as *mut _)
27+
})?;
28+
29+
match_error_code(unsafe { hv_gic_config_set_distributor_base(config, gicd_addr) })?;
30+
match_error_code(unsafe { hv_gic_config_set_redistributor_base(config, gicr_base) })?;
31+
match_error_code(unsafe { hv_gic_config_set_msi_region_base(config, msi_base) })?;
32+
match_error_code(unsafe {
33+
hv_gic_config_set_msi_interrupt_range(config, intid_base, intid_count)
34+
})?;
35+
match_error_code(unsafe { hv_gic_create(config) })?;
36+
37+
Ok(Self(config))
38+
}
39+
40+
/// Resets the generic interrupt controller (GIC) device.
41+
pub fn reset(&self) -> Result<(), Error> {
42+
match_error_code(unsafe { hv_gic_reset() })?;
43+
44+
Ok(())
45+
}
46+
}
47+
48+
#[cfg(feature = "macos_15_0_0")]
49+
impl Drop for Gic {
50+
fn drop(&mut self) {
51+
unsafe {
52+
os_release(self.0);
53+
}
54+
}
55+
}
56+
1357
/// Maps a region in the virtual address space of the current task into the guest physical
14-
/// address space of the virutal machine
58+
/// address space of the virtual machine
1559
pub fn map_mem(mem: &[u8], ipa: u64, mem_perm: MemPerm) -> Result<(), Error> {
1660
match_error_code(unsafe {
1761
hv_vm_map(
@@ -75,8 +119,11 @@ impl From<hv_vcpu_exit_t> for VirtualCpuExitReason {
75119

76120
/// Virtual CPU
77121
pub struct VirtualCpu {
122+
/// Virtual CPU Id
123+
id: u32,
124+
78125
/// Virtual CPU handle
79-
id: hv_vcpu_t,
126+
vcpu_handle: hv_vcpu_t,
80127

81128
/// VirtualCPU exit informations.
82129
vcpu_exit: *const hv_vcpu_exit_t,
@@ -700,23 +747,35 @@ impl From<SystemRegister> for hv_sys_reg_t {
700747
}
701748

702749
impl VirtualCpu {
703-
pub fn new() -> Result<VirtualCpu, Error> {
750+
/// Creates a VirtualCpu instance for the current thread
751+
///
752+
/// `id` represents the internal numbering of the processor.
753+
pub fn new(id: u32) -> Result<VirtualCpu, Error> {
704754
let handle: hv_vcpu_config_t = core::ptr::null_mut();
705755
let mut vcpu_handle: hv_vcpu_t = 0;
706756
let mut vcpu_exit: *const hv_vcpu_exit_t = core::ptr::null_mut();
707757

708758
match_error_code(unsafe { hv_vcpu_create(&mut vcpu_handle, &mut vcpu_exit, &handle) })?;
709759

710-
Ok(VirtualCpu {
711-
id: vcpu_handle,
712-
vcpu_exit: vcpu_exit,
713-
})
760+
let vcpu = VirtualCpu {
761+
id,
762+
vcpu_handle,
763+
vcpu_exit,
764+
};
765+
766+
vcpu.write_system_register(SystemRegister::MPIDR_EL1, (id & 0xff).into())?;
767+
768+
Ok(vcpu)
714769
}
715770

716-
pub fn get_id(&self) -> hv_vcpu_t {
771+
pub fn get_id(&self) -> u32 {
717772
self.id
718773
}
719774

775+
pub fn get_handle(&self) -> hv_vcpu_t {
776+
self.vcpu_handle
777+
}
778+
720779
pub fn exit_reason(&self) -> VirtualCpuExitReason {
721780
VirtualCpuExitReason::from(unsafe { *self.vcpu_exit })
722781
}
@@ -727,30 +786,119 @@ impl VirtualCpu {
727786
let mut value: u64 = 0;
728787

729788
match_error_code(unsafe {
730-
hv_vcpu_get_reg(self.id, hv_reg_t::from(reg), &mut value as *mut u64)
789+
hv_vcpu_get_reg(
790+
self.vcpu_handle,
791+
hv_reg_t::from(reg),
792+
&mut value as *mut u64,
793+
)
794+
})?;
795+
796+
Ok(value)
797+
}
798+
799+
/// Read a generic interrupt controller (GIC) redistributor register.
800+
#[cfg(feature = "macos_15_0_0")]
801+
pub fn read_redistributor_register(&self, offset: u32) -> Result<u64, Error> {
802+
let mut value: u64 = 0;
803+
804+
match_error_code(unsafe {
805+
hv_gic_get_redistributor_reg(self.vcpu_handle, offset, &mut value as *mut u64)
731806
})?;
732807

733808
Ok(value)
734809
}
735810

736811
/// Sets the value of an architectural x86 register of the VirtualCpu
737812
pub fn write_register(&self, reg: Register, value: u64) -> Result<(), Error> {
738-
match_error_code(unsafe { hv_vcpu_set_reg(self.id, hv_reg_t::from(reg), value) })
813+
match_error_code(unsafe { hv_vcpu_set_reg(self.vcpu_handle, hv_reg_t::from(reg), value) })
739814
}
740815

741816
/// Gets a system register value.
742817
pub fn read_system_register(&self, reg: SystemRegister) -> Result<u64, Error> {
743818
let mut value: u64 = 0;
744819

745820
match_error_code(unsafe {
746-
hv_vcpu_get_sys_reg(self.id, hv_sys_reg_t::from(reg), &mut value as *mut u64)
821+
hv_vcpu_get_sys_reg(
822+
self.vcpu_handle,
823+
hv_sys_reg_t::from(reg),
824+
&mut value as *mut u64,
825+
)
747826
})?;
748827

749828
Ok(value)
750829
}
751830

752831
/// Gets a system register value.
753832
pub fn write_system_register(&self, reg: SystemRegister, value: u64) -> Result<(), Error> {
754-
match_error_code(unsafe { hv_vcpu_set_sys_reg(self.id, hv_sys_reg_t::from(reg), value) })
833+
match_error_code(unsafe {
834+
hv_vcpu_set_sys_reg(self.vcpu_handle, hv_sys_reg_t::from(reg), value)
835+
})
836+
}
837+
}
838+
839+
impl core::fmt::Debug for VirtualCpu {
840+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
841+
let pc = self.read_register(Register::PC).unwrap();
842+
let cpsr = self.read_register(Register::CPSR).unwrap();
843+
let sp = self.read_system_register(SystemRegister::SP_EL1).unwrap();
844+
let sctlr = self
845+
.read_system_register(SystemRegister::SCTLR_EL1)
846+
.unwrap();
847+
let ttbr0 = self
848+
.read_system_register(SystemRegister::TTBR0_EL1)
849+
.unwrap();
850+
let lr = self.read_register(Register::LR).unwrap();
851+
let x0 = self.read_register(Register::X0).unwrap();
852+
let x1 = self.read_register(Register::X1).unwrap();
853+
let x2 = self.read_register(Register::X2).unwrap();
854+
let x3 = self.read_register(Register::X3).unwrap();
855+
let x4 = self.read_register(Register::X4).unwrap();
856+
let x5 = self.read_register(Register::X5).unwrap();
857+
let x6 = self.read_register(Register::X6).unwrap();
858+
let x7 = self.read_register(Register::X7).unwrap();
859+
let x8 = self.read_register(Register::X8).unwrap();
860+
let x9 = self.read_register(Register::X9).unwrap();
861+
let x10 = self.read_register(Register::X10).unwrap();
862+
let x11 = self.read_register(Register::X11).unwrap();
863+
let x12 = self.read_register(Register::X12).unwrap();
864+
let x13 = self.read_register(Register::X13).unwrap();
865+
let x14 = self.read_register(Register::X14).unwrap();
866+
let x15 = self.read_register(Register::X15).unwrap();
867+
let x16 = self.read_register(Register::X16).unwrap();
868+
let x17 = self.read_register(Register::X17).unwrap();
869+
let x18 = self.read_register(Register::X18).unwrap();
870+
let x19 = self.read_register(Register::X19).unwrap();
871+
let x20 = self.read_register(Register::X20).unwrap();
872+
let x21 = self.read_register(Register::X21).unwrap();
873+
let x22 = self.read_register(Register::X22).unwrap();
874+
let x23 = self.read_register(Register::X23).unwrap();
875+
let x24 = self.read_register(Register::X24).unwrap();
876+
let x25 = self.read_register(Register::X25).unwrap();
877+
let x26 = self.read_register(Register::X26).unwrap();
878+
let x27 = self.read_register(Register::X27).unwrap();
879+
let x28 = self.read_register(Register::X28).unwrap();
880+
let x29 = self.read_register(Register::X29).unwrap();
881+
let x30 = self.read_register(Register::X30).unwrap();
882+
883+
writeln!(f, "\nRegisters of CPU {}:", (*self).get_id())?;
884+
writeln!(
885+
f,
886+
"PC : {pc:016x} LR : {lr:016x} CPSR : {cpsr:016x}\n\
887+
SP : {sp:016x} SCTLR : {sctlr:016x} TTBR0 : {ttbr0:016x}",
888+
)?;
889+
writeln!(
890+
f,
891+
"x0 : {x0:016x} x1 : {x1:016x} x2 : {x2:016x}\n\
892+
x3 : {x3:016x} x4 : {x4:016x} x5 : {x5:016x}\n\
893+
x6 : {x6:016x} x7 : {x7:016x} x8 : {x8:016x}\n\
894+
x9 : {x9:016x} x10 : {x10:016x} x11 : {x11:016x}\n\
895+
x12 : {x12:016x} x13 : {x13:016x} x14 : {x14:016x}\n\
896+
x15 : {x15:016x} x16 : {x16:016x} x17 : {x17:016x}\n\
897+
x18 : {x18:016x} x19 : {x19:016x} x20 : {x20:016x}\n\
898+
x21 : {x21:016x} x22 : {x22:016x} x23 : {x23:016x}\n\
899+
x24 : {x24:016x} x25 : {x25:016x} x26 : {x26:016x}\n\
900+
x27 : {x27:016x} x28 : {x28:016x} x29 : {x29:016x}\n\
901+
x30 : {x30:016x}\n",
902+
)
755903
}
756904
}

0 commit comments

Comments
 (0)