Skip to content

Commit 0d7b97a

Browse files
committed
Safe Rust API
Signed-off-by: Maksym Pavlenko <pavlenko.maksym@gmail.com>
1 parent 6723e19 commit 0d7b97a

File tree

9 files changed

+182
-195
lines changed

9 files changed

+182
-195
lines changed

README.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
Build virtualization solutions on top of a lightweight hypervisor using Rust:
1212
- Full Hypervisor Framework support.
1313
- Supports Apple Silicon.
14-
- Rusty API.
14+
- Safe Rust API.
1515

1616
This repository contains the following crates:
1717
| Name | Description | Links |
@@ -56,21 +56,20 @@ otherwise `bindgen` may fail to find Hypervisor headers.
5656
Here is basic "Hello world" example on Apple Silicon:
5757
```rust
5858
// Init VM
59-
hv::Vm::create_vm(ptr::null_mut())?;
59+
let vm = Arc::new(hv::Vm::new(std::ptr::null_mut())?);
6060

6161
// Initialize guest memory
62-
hv::Vm::map(load_addr, GUEST_ADDR as _, MEM_SIZE as _, hv::Memory::READ)?;
62+
vm.map(load_addr, GUEST_ADDR, MEM_SIZE, hv::Memory::READ)?;
6363

6464
// Create VCPU
65-
let cpu = hv::Vm::create_cpu()?;
65+
let cpu = vm.create_cpu()?;
6666

6767
// Set regs
68-
cpu.set_reg(Reg::PC, GUEST_ADDR)?;
69-
cpu.set_reg(Reg::X1, GUEST_RESULT_ADDR)?;
68+
cpu.set_reg(Reg::PC, GUEST_ADDR)?
69+
cpu.set_reg(Reg::X1, GUEST_RESULT_ADDR)?
7070

71-
// Run CPU
7271
loop {
73-
cpu.run()?;
72+
cpu.run().expect("Failed to run CPU");
7473

7574
let info = cpu.exit_info();
7675
println!("{:?}", info);

hv/README.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
Build virtualization solutions on top of a lightweight hypervisor using Rust:
1111
- Full Hypervisor Framework support.
1212
- Supports Apple Silicon.
13-
- Rusty API.
13+
- Safe Rust API.
1414

1515
Please also see the [repository](https://github.com/mxpv/hv) for latest changes and updates.
1616

@@ -43,28 +43,27 @@ otherwise `bindgen` may fail to find Hypervisor headers.
4343

4444
## Usage
4545

46-
This crate uses [hv-sys]()
46+
This crate uses [hv-sys](https://crates.io/crates/hv-sys)
4747

4848
## Example
4949

5050
Here is basic "Hello world" example on Apple Silicon:
5151
```rust
5252
// Init VM
53-
hv::Vm::create_vm(ptr::null_mut())?;
53+
let vm = Arc::new(hv::Vm::new(std::ptr::null_mut())?);
5454

5555
// Initialize guest memory
56-
hv::Vm::map(load_addr, GUEST_ADDR as _, MEM_SIZE as _, hv::Memory::READ)?;
56+
vm.map(load_addr, GUEST_ADDR, MEM_SIZE, hv::Memory::READ)?;
5757

5858
// Create VCPU
59-
let cpu = hv::Vm::create_cpu()?;
59+
let cpu = vm.create_cpu()?;
6060

6161
// Set regs
62-
cpu.set_reg(Reg::PC, GUEST_ADDR)?;
63-
cpu.set_reg(Reg::X1, GUEST_RESULT_ADDR)?;
62+
cpu.set_reg(Reg::PC, GUEST_ADDR)?
63+
cpu.set_reg(Reg::X1, GUEST_RESULT_ADDR)?
6464

65-
// Run CPU
6665
loop {
67-
cpu.run()?;
66+
cpu.run().expect("Failed to run CPU");
6867

6968
let info = cpu.exit_info();
7069
println!("{:?}", info);

hv/examples/as.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const GUEST_RESULT_ADDR: usize = GUEST_ADDR + RESULT_OFFSET;
2828
#[cfg(target_arch = "aarch64")]
2929
use hv::arm64::{Reg, VcpuExt};
3030

31+
use std::sync::Arc;
32+
3133
#[cfg(target_arch = "aarch64")]
3234
fn main() -> Result<(), hv::Error> {
3335
let load_addr = unsafe {
@@ -50,19 +52,18 @@ fn main() -> Result<(), hv::Error> {
5052
}
5153

5254
// Init VM
53-
hv::Vm::create_vm(std::ptr::null_mut()).expect("Failed to create VM");
55+
let vm = Arc::new(hv::Vm::new(std::ptr::null_mut())?);
5456

5557
// Initialize guest memory
56-
hv::Vm::map(
58+
vm.map(
5759
load_addr,
5860
GUEST_ADDR as _,
5961
MEM_SIZE as _,
6062
hv::Memory::READ | hv::Memory::WRITE | hv::Memory::EXEC,
61-
)
62-
.expect("Failed to map guest memory");
63+
)?;
6364

6465
// Create VCPU
65-
let cpu = hv::Vm::create_cpu().expect("Failed to create CPU");
66+
let cpu = vm.create_cpu()?;
6667

6768
// Set regs
6869
cpu.set_reg(Reg::PC, GUEST_ADDR as _)
@@ -86,10 +87,6 @@ fn main() -> Result<(), hv::Error> {
8687
println!("Result: {}", result);
8788
assert_eq!(result, 3);
8889

89-
drop(cpu);
90-
91-
hv::Vm::destroy()?;
92-
9390
Ok(())
9491
}
9592

hv/examples/caps.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@
22
fn main() -> Result<(), hv::Error> {
33
use hv::x86::{Capability, VmExt, VmOptions};
44

5-
hv::Vm::create_vm(VmOptions::default())?;
5+
let vm = hv::Vm::new(VmOptions::default())?;
66

7-
println!("Max vCPUs: {}", hv::Vm::capability(Capability::VcpuMax)?);
7+
println!("Max vCPUs: {}", vm.capability(Capability::VcpuMax)?);
88

99
println!(
1010
"Available address spaces: {}",
11-
hv::Vm::capability(Capability::AddrSpaceMax)?
11+
vm.capability(Capability::AddrSpaceMax)?
1212
);
1313

14-
hv::Vm::destroy()?;
15-
1614
Ok(())
1715
}
1816

hv/src/arm64/mod.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,19 @@ impl VcpuExt for Vcpu {
7777
/// Returns the current value of a vCPU register.
7878
fn get_reg(&self, reg: regs::Reg) -> Result<u64, Error> {
7979
let mut out = 0_u64;
80-
call!(sys::hv_vcpu_get_reg(self.cpu, reg as _, &mut out))?;
80+
call!(sys::hv_vcpu_get_reg(self.id, reg as _, &mut out))?;
8181
Ok(out)
8282
}
8383

8484
/// Sets the value of a vCPU register.
8585
fn set_reg(&self, reg: regs::Reg, value: u64) -> Result<(), Error> {
86-
call!(sys::hv_vcpu_set_reg(self.cpu, reg as _, value))
86+
call!(sys::hv_vcpu_set_reg(self.id, reg as _, value))
8787
}
8888

8989
/// Returns the current value of a vCPU SIMD & FP register.
9090
fn get_simd_fp_reg(&self, reg: regs::SimdFpReg) -> Result<regs::SimdFpUchar16, Error> {
9191
let mut out = 0_u128;
92-
call!(sys::hv_vcpu_get_simd_fp_reg(self.cpu, reg as _, &mut out))?;
92+
call!(sys::hv_vcpu_get_simd_fp_reg(self.id, reg as _, &mut out))?;
9393
Ok(out)
9494
}
9595

@@ -99,35 +99,35 @@ impl VcpuExt for Vcpu {
9999
reg: regs::SimdFpReg,
100100
value: regs::SimdFpUchar16,
101101
) -> Result<(), Error> {
102-
call!(sys::hv_vcpu_set_simd_fp_reg(self.cpu, reg as _, value))?;
102+
call!(sys::hv_vcpu_set_simd_fp_reg(self.id, reg as _, value))?;
103103
Ok(())
104104
}
105105

106106
/// Returns the current value of a vCPU system register.
107107
fn get_sys_reg(&self, reg: regs::SysReg) -> Result<u64, Error> {
108108
let mut out = 0_u64;
109-
call!(sys::hv_vcpu_get_sys_reg(self.cpu, reg as _, &mut out))?;
109+
call!(sys::hv_vcpu_get_sys_reg(self.id, reg as _, &mut out))?;
110110
Ok(out)
111111
}
112112

113113
/// Sets the value of a vCPU system register.
114114
fn set_sys_reg(&self, reg: regs::SysReg, value: u64) -> Result<(), Error> {
115-
call!(sys::hv_vcpu_set_sys_reg(self.cpu, reg as _, value))
115+
call!(sys::hv_vcpu_set_sys_reg(self.id, reg as _, value))
116116
}
117117

118118
/// Gets pending interrupts for a vcpu.
119119
fn pending_interrupt(&self, ty: InterruptType) -> Result<bool, Error> {
120120
let mut out = false;
121121
call!(sys::hv_vcpu_get_pending_interrupt(
122-
self.cpu, ty as u32, &mut out
122+
self.id, ty as u32, &mut out
123123
))?;
124124
Ok(out)
125125
}
126126

127127
/// Sets pending interrupts for a vcpu.
128128
fn set_pending_interrupt(&self, ty: InterruptType, mut pending: bool) -> Result<(), Error> {
129129
call!(sys::hv_vcpu_get_pending_interrupt(
130-
self.cpu,
130+
self.id,
131131
ty as u32,
132132
&mut pending
133133
))
@@ -136,49 +136,49 @@ impl VcpuExt for Vcpu {
136136
/// Get whether debug exceptions in the guest are trapped to the host.
137137
fn trap_debug_exceptions(&self) -> Result<bool, Error> {
138138
let mut out = false;
139-
call!(sys::hv_vcpu_get_trap_debug_exceptions(self.cpu, &mut out))?;
139+
call!(sys::hv_vcpu_get_trap_debug_exceptions(self.id, &mut out))?;
140140
Ok(out)
141141
}
142142

143143
/// Set whether debug exceptions in the guest are trapped to the host.
144144
fn set_trap_debug_exceptions(&self, enable: bool) -> Result<(), Error> {
145-
call!(sys::hv_vcpu_set_trap_debug_exceptions(self.cpu, enable))
145+
call!(sys::hv_vcpu_set_trap_debug_exceptions(self.id, enable))
146146
}
147147

148148
/// Get whether debug register accesses in the guest are trapped to the host.
149149
fn trap_debug_reg_accesses(&self) -> Result<bool, Error> {
150150
let mut out = false;
151-
call!(sys::hv_vcpu_get_trap_debug_reg_accesses(self.cpu, &mut out))?;
151+
call!(sys::hv_vcpu_get_trap_debug_reg_accesses(self.id, &mut out))?;
152152
Ok(out)
153153
}
154154

155155
/// Set whether debug register accesses in the guest are trapped to the host.
156156
fn set_trap_debug_reg_accesses(&self, enable: bool) -> Result<(), Error> {
157-
call!(sys::hv_vcpu_set_trap_debug_reg_accesses(self.cpu, enable))
157+
call!(sys::hv_vcpu_set_trap_debug_reg_accesses(self.id, enable))
158158
}
159159

160160
/// Gets the VTimer mask.
161161
fn vtimer_mask(&self) -> Result<bool, Error> {
162162
let mut out = false;
163-
call!(sys::hv_vcpu_get_vtimer_mask(self.cpu, &mut out))?;
163+
call!(sys::hv_vcpu_get_vtimer_mask(self.id, &mut out))?;
164164
Ok(out)
165165
}
166166

167167
/// Sets the VTimer mask.
168168
fn set_vtimer_mask(&self, vtimer_is_masked: bool) -> Result<(), Error> {
169-
call!(sys::hv_vcpu_set_vtimer_mask(self.cpu, vtimer_is_masked))
169+
call!(sys::hv_vcpu_set_vtimer_mask(self.id, vtimer_is_masked))
170170
}
171171

172172
/// Gets the VTimer offset.
173173
fn vtimer_offset(&self) -> Result<u64, Error> {
174174
let mut out = 0_u64;
175-
call!(sys::hv_vcpu_get_vtimer_offset(self.cpu, &mut out))?;
175+
call!(sys::hv_vcpu_get_vtimer_offset(self.id, &mut out))?;
176176
Ok(out)
177177
}
178178

179179
/// Sets the VTimer offset.
180180
fn set_vtimer_offset(&self, vtimer_offset: u64) -> Result<(), Error> {
181-
call!(sys::hv_vcpu_set_vtimer_offset(self.cpu, vtimer_offset))
181+
call!(sys::hv_vcpu_set_vtimer_offset(self.id, vtimer_offset))
182182
}
183183

184184
/// Returns the underlying `hv_vcpu_exit_t` structure.

hv/src/vcpu.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
use crate::{call, sys, Error};
1+
use crate::{call, sys, Error, Vm};
2+
use std::sync::Arc;
3+
4+
/// The type that describes a vCPU ID on Intel.
5+
#[cfg(target_arch = "x86_64")]
6+
pub type Id = sys::hv_vcpuid_t;
7+
8+
/// The type that describes a vCPU ID on Apple Silicon.
9+
#[cfg(target_arch = "aarch64")]
10+
pub type Id = sys::hv_vcpu_t;
211

312
/// Represents a single virtual CPU.
413
///
514
/// [Vcpu] object is not thread safe, all calls must be performed from
615
/// the owning thread.
716
pub struct Vcpu {
8-
#[cfg(target_arch = "x86_64")]
9-
pub(crate) cpu: sys::hv_vcpuid_t,
10-
#[cfg(target_arch = "aarch64")]
11-
pub(crate) cpu: sys::hv_vcpu_t,
17+
#[allow(dead_code)] // VM instance must outlive CPU in order to deallocate things properly.
18+
vm: Arc<Vm>,
19+
pub(crate) id: Id,
1220
#[cfg(target_arch = "aarch64")]
1321
/// The pointer to the vCPU exit information.
1422
/// The function `hv_vcpu_run` updates this structure on return.
@@ -18,24 +26,24 @@ pub struct Vcpu {
1826

1927
impl Vcpu {
2028
/// Creates a vCPU instance for the current thread.
21-
pub(crate) fn new() -> Result<Vcpu, Error> {
29+
pub(crate) fn new(vm: Arc<Vm>) -> Result<Vcpu, Error> {
2230
#[cfg(target_arch = "x86_64")]
2331
{
24-
let mut cpu = 0;
25-
call!(sys::hv_vcpu_create(&mut cpu, 0))?;
26-
Ok(Vcpu { cpu })
32+
let mut id = 0;
33+
call!(sys::hv_vcpu_create(&mut id, 0))?;
34+
Ok(Vcpu { vm, id })
2735
}
2836

2937
#[cfg(target_arch = "aarch64")]
3038
{
31-
let mut cpu = 0;
39+
let mut id = 0;
3240
let mut exit = std::ptr::null_mut();
3341
call!(sys::hv_vcpu_create(
34-
&mut cpu,
42+
&mut id,
3543
&mut exit,
3644
std::ptr::null_mut()
3745
))?;
38-
Ok(Vcpu { cpu, exit })
46+
Ok(Vcpu { vm, id, exit })
3947
}
4048
}
4149

@@ -54,20 +62,26 @@ impl Vcpu {
5462
///
5563
/// [1]: https://developer.apple.com/documentation/hypervisor/1441231-hv_vcpu_run
5664
pub fn run(&self) -> Result<(), Error> {
57-
call!(sys::hv_vcpu_run(self.cpu))
65+
call!(sys::hv_vcpu_run(self.id))
5866
}
5967

6068
/// Returns the cumulative execution time of a vCPU in nanoseconds.
6169
pub fn exec_time(&self) -> Result<u64, Error> {
6270
let mut out = 0_u64;
63-
call!(sys::hv_vcpu_get_exec_time(self.cpu, &mut out))?;
71+
call!(sys::hv_vcpu_get_exec_time(self.id, &mut out))?;
6472
Ok(out)
6573
}
74+
75+
/// Returns the underlying vCPU ID.
76+
#[inline]
77+
pub fn id(&self) -> Id {
78+
self.id
79+
}
6680
}
6781

82+
/// Destroys the vCPU instance associated with the current thread.
6883
impl Drop for Vcpu {
69-
/// Destroys the vCPU instance associated with the current thread.
7084
fn drop(&mut self) {
71-
let _ = call!(sys::hv_vcpu_destroy(self.cpu));
85+
call!(sys::hv_vcpu_destroy(self.id)).unwrap()
7286
}
7387
}

0 commit comments

Comments
 (0)