Skip to content

Commit e58d57c

Browse files
aarkegzZR233szybuhenxihuanbullhh
authored
Refactor dependencies and enhance AxVM lifecycle management, remove hal, bump to v0.3.0 (#54)
* refactor: deps git to crates-io * fix: feature * refactor: support guest dyn entry * refactor: add Clone trait to VMImageConfig * fix: update arm_vcpu branch to 'next' * add fdt support and add some AxVMConfig impl * fix fmt bug * fix bug: when no phys_cpu_ids panic * add function map_reserved_memory_region (#28) * add function map_reserved_memory_region * add passthrough address (#29) * add pass_through address --------- Co-authored-by: szy <songzhyo@qq.com> * add VMStatus enum and integrate into AxVM lifecycle management (#31) * feat: update axerrno to 0.2 (#33) * feat: update axerrno to 0.2 * fix: clippy * update toolchain * update dependencies * remove `AxVCpuHal` * remove type parameter on `AxVMInnerMut` * remove all type parameters * formatted * Update riscv_vcpu dependency source (#39) * fix(riscv): Fix RISC-V vCPU initialization and I/O handling * chore: Update riscv_vcpu dependency source * chore: specify axdevice branch and fix code formatting * docs: add missing documentation comments * Use local path for merged components (axdevice, axvmconfig) * update dependencies * fixes after code merging * formatted * fix clippy * bump version to v0.3.0 * remove `AxVMHal` --------- Co-authored-by: 周睿 <zrufo747@outlook.com> Co-authored-by: szy <songzhyo@qq.com> Co-authored-by: bhxh <32200913+buhenxihuan@users.noreply.github.com> Co-authored-by: szy <673586548@qq.com> Co-authored-by: TQ <128586861+YanLien@users.noreply.github.com> Co-authored-by: 朝倉水希 <asakuramizu111@gmail.com>
1 parent c5e7af4 commit e58d57c

File tree

5 files changed

+102
-96
lines changed

5 files changed

+102
-96
lines changed

Cargo.toml

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "axvm"
33
authors = ["aarkegz <aarkegz@gmail.com>"]
4-
version = "0.2.2"
4+
version = "0.3.0"
55
edition = "2024"
66
categories = ["virtualization", "no-std"]
77
description = "Virtual Machine resource management crate for ArceOS's hypervisor variant."
@@ -12,12 +12,12 @@ license = "Apache-2.0"
1212
[features]
1313
default = ["vmx"]
1414
vmx = []
15-
4-level-ept = ["axaddrspace/4-level-ept"] # TODO: Realize 4-level-ept on x86_64 and riscv64.
15+
4-level-ept = [] # TODO: Realize 4-level-ept on x86_64 and riscv64.
1616

1717
[dependencies]
1818
log = "0.4"
1919
cfg-if = "1.0"
20-
spin = "0.9"
20+
spin = "0.10"
2121

2222
# System independent crates provided by ArceOS.
2323
axerrno = "0.2"
@@ -29,18 +29,19 @@ page_table_multiarch = "0.6"
2929
percpu = { version = "0.2.0", features = ["arm-el2"] }
3030

3131
# System dependent modules provided by ArceOS-Hypervisor.
32-
axvcpu = "0.2"
33-
axaddrspace = "0.1.4"
34-
axdevice = "0.2.1"
35-
axdevice_base = "0.2.1"
36-
axvmconfig = { version = "0.2", default-features = false }
32+
axvcpu = "0.3"
33+
axaddrspace = "0.3"
34+
axvisor_api = "0.3"
35+
axdevice = "0.2.2"
36+
axdevice_base = "0.2.2"
37+
axvmconfig = { version = "0.2.2", default-features = false }
3738

3839
[target.'cfg(target_arch = "x86_64")'.dependencies]
39-
x86_vcpu = "0.2.1"
40+
x86_vcpu = "0.3"
4041

4142
[target.'cfg(target_arch = "riscv64")'.dependencies]
42-
riscv_vcpu = "0.2.1"
43+
riscv_vcpu = "0.3"
4344

4445
[target.'cfg(target_arch = "aarch64")'.dependencies]
45-
arm_vcpu = "0.2.1"
46-
arm_vgic = { version = "0.2.1", features = ["vgicv3"] }
46+
arm_vcpu = "0.3"
47+
arm_vgic = { version = "0.2.2", features = ["vgicv3"] }

rust-toolchain.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[toolchain]
2+
profile = "minimal"
3+
channel = "nightly-2025-05-20"
4+
components = ["rust-src", "llvm-tools", "rustfmt", "clippy"]
5+
targets = [
6+
"x86_64-unknown-none",
7+
"riscv64gc-unknown-none-elf",
8+
"aarch64-unknown-none-softfloat",
9+
"loongarch64-unknown-none-softfloat",
10+
]

src/hal.rs

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,41 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use axaddrspace::{HostPhysAddr, HostVirtAddr};
16-
use axerrno::AxResult;
17-
18-
/// The interfaces which the underlying software (kernel or hypervisor) must implement.
19-
pub trait AxVMHal: Sized {
20-
/// The low-level **OS-dependent** helpers that must be provided for physical address management.
21-
type PagingHandler: page_table_multiarch::PagingHandler;
22-
23-
/// Converts a virtual address to the corresponding physical address.
24-
fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr;
25-
26-
/// Current time in nanoseconds.
27-
fn current_time_nanos() -> u64;
28-
29-
/// Current VM ID.
30-
fn current_vm_id() -> usize;
31-
32-
/// Current Virtual CPU ID.
33-
fn current_vcpu_id() -> usize;
34-
35-
/// Current Physical CPU ID.
36-
fn current_pcpu_id() -> usize;
37-
38-
/// Get the Physical CPU ID where the specified VCPU of the current VM resides.
39-
///
40-
/// Returns an error if the VCPU is not found.
41-
fn vcpu_resides_on(vm_id: usize, vcpu_id: usize) -> AxResult<usize>;
42-
43-
/// Inject an IRQ to the specified VCPU.
44-
///
45-
/// This method should find the physical CPU where the specified VCPU resides and inject the IRQ
46-
/// to it on that physical CPU with [`axvcpu::AxVCpu::inject_interrupt`].
47-
///
48-
/// Returns an error if the VCPU is not found.
49-
fn inject_irq_to_vcpu(vm_id: usize, vcpu_id: usize, irq: usize) -> AxResult;
15+
use memory_addr::{PAGE_SIZE_4K, PhysAddr, VirtAddr};
16+
use page_table_multiarch::PagingHandler;
17+
18+
pub struct PagingHandlerImpl;
19+
20+
impl PagingHandler for PagingHandlerImpl {
21+
fn alloc_frames(num: usize, align: usize) -> Option<PhysAddr> {
22+
let align_frames = if align.is_multiple_of(PAGE_SIZE_4K) {
23+
align / PAGE_SIZE_4K
24+
} else {
25+
panic!("align must be multiple of PAGE_SIZE_4K")
26+
};
27+
28+
let align_frames_pow2 = if align_frames.is_power_of_two() {
29+
align_frames.trailing_zeros()
30+
} else {
31+
panic!("align must be a power of 2")
32+
};
33+
34+
axvisor_api::memory::alloc_contiguous_frames(num, align_frames_pow2 as _)
35+
}
36+
37+
fn dealloc_frames(paddr: PhysAddr, num: usize) {
38+
axvisor_api::memory::dealloc_contiguous_frames(paddr, num);
39+
}
40+
41+
fn alloc_frame() -> Option<PhysAddr> {
42+
axvisor_api::memory::alloc_frame()
43+
}
44+
45+
fn dealloc_frame(paddr: PhysAddr) {
46+
axvisor_api::memory::dealloc_frame(paddr)
47+
}
48+
49+
fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
50+
axvisor_api::memory::phys_to_virt(paddr)
51+
}
5052
}

src/lib.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,10 @@ mod vm;
3333

3434
pub mod config;
3535

36-
pub use hal::AxVMHal;
37-
pub use vm::AxVCpuRef;
38-
pub use vm::AxVM;
39-
pub use vm::AxVMRef;
40-
pub use vm::VMMemoryRegion;
41-
pub use vm::VMStatus;
36+
pub use vm::{AxVCpuRef, AxVM, AxVMRef, VMMemoryRegion, VMStatus};
4237

4338
/// The architecture-independent per-CPU type.
44-
pub type AxVMPerCpu<U> = axvcpu::AxPerCpu<vcpu::AxVMArchPerCpuImpl<U>>;
39+
pub type AxVMPerCpu = axvcpu::AxPerCpu<vcpu::AxVMArchPerCpuImpl>;
4540

4641
/// Whether the hardware has virtualization support.
4742
pub fn has_hardware_support() -> bool {

src/vm.rs

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,46 +18,46 @@ use alloc::sync::Arc;
1818
use alloc::vec::Vec;
1919
use axaddrspace::HostVirtAddr;
2020
use axerrno::{AxError, AxResult, ax_err, ax_err_type};
21+
use axvisor_api::vmm::InterruptVector;
2122
use core::alloc::Layout;
2223
use core::fmt;
2324
use memory_addr::{align_down_4k, align_up_4k};
2425
use spin::{Mutex, Once};
2526

2627
use axaddrspace::{AddrSpace, GuestPhysAddr, HostPhysAddr, MappingFlags, device::AccessWidth};
2728
use axdevice::{AxVmDeviceConfig, AxVmDevices};
28-
use axvcpu::{AxVCpu, AxVCpuExitReason, AxVCpuHal};
29+
use axvcpu::{AxVCpu, AxVCpuExitReason};
2930
use cpumask::CpuMask;
3031

3132
use crate::config::{AxVMConfig, PhysCpuList};
33+
use crate::hal::PagingHandlerImpl;
34+
use crate::has_hardware_support;
3235
use crate::vcpu::AxArchVCpuImpl;
33-
use crate::{AxVMHal, has_hardware_support};
3436

35-
#[cfg(target_arch = "riscv64")]
37+
#[cfg(not(target_arch = "x86_64"))]
3638
use crate::vcpu::AxVCpuCreateConfig;
39+
3740
#[cfg(target_arch = "aarch64")]
38-
use crate::vcpu::{AxVCpuCreateConfig, get_sysreg_device};
41+
use crate::vcpu::get_sysreg_device;
3942

4043
const VM_ASPACE_BASE: usize = 0x0;
4144
const VM_ASPACE_SIZE: usize = 0x7fff_ffff_f000;
4245

4346
/// A vCPU with architecture-independent interface.
44-
#[allow(type_alias_bounds)]
45-
type VCpu<U: AxVCpuHal> = AxVCpu<AxArchVCpuImpl<U>>;
47+
type VCpu = AxVCpu<AxArchVCpuImpl>;
4648
/// A reference to a vCPU.
47-
#[allow(type_alias_bounds)]
48-
pub type AxVCpuRef<U: AxVCpuHal> = Arc<VCpu<U>>;
49+
pub type AxVCpuRef = Arc<VCpu>;
4950
/// A reference to a VM.
50-
#[allow(type_alias_bounds)]
51-
pub type AxVMRef<H: AxVMHal, U: AxVCpuHal> = Arc<AxVM<H, U>>; // we know the bound is not enforced here, we keep it for clarity
51+
pub type AxVMRef = Arc<AxVM>;
5252

53-
struct AxVMInnerConst<U: AxVCpuHal> {
53+
struct AxVMInnerConst {
5454
phys_cpu_ls: PhysCpuList,
55-
vcpu_list: Box<[AxVCpuRef<U>]>,
55+
vcpu_list: Box<[AxVCpuRef]>,
5656
devices: AxVmDevices,
5757
}
5858

59-
unsafe impl<U: AxVCpuHal> Send for AxVMInnerConst<U> {}
60-
unsafe impl<U: AxVCpuHal> Sync for AxVMInnerConst<U> {}
59+
unsafe impl Send for AxVMInnerConst {}
60+
unsafe impl Sync for AxVMInnerConst {}
6161

6262
/// Represents a memory region in a virtual machine.
6363
#[derive(Debug, Clone)]
@@ -84,13 +84,12 @@ impl VMMemoryRegion {
8484
}
8585
}
8686

87-
struct AxVMInnerMut<H: AxVMHal> {
87+
struct AxVMInnerMut {
8888
// Todo: use more efficient lock.
89-
address_space: AddrSpace<H::PagingHandler>,
89+
address_space: AddrSpace<PagingHandlerImpl>,
9090
memory_regions: Vec<VMMemoryRegion>,
9191
config: AxVMConfig,
9292
vm_status: VMStatus,
93-
_marker: core::marker::PhantomData<H>,
9493
}
9594

9695
/// VM status enumeration representing the lifecycle states of a virtual machine
@@ -145,19 +144,20 @@ impl fmt::Display for VMStatus {
145144
const TEMP_MAX_VCPU_NUM: usize = 64;
146145

147146
/// A Virtual Machine.
148-
pub struct AxVM<H: AxVMHal, U: AxVCpuHal> {
147+
pub struct AxVM {
149148
id: usize,
150-
inner_const: Once<AxVMInnerConst<U>>,
151-
inner_mut: Mutex<AxVMInnerMut<H>>,
149+
inner_const: Once<AxVMInnerConst>,
150+
inner_mut: Mutex<AxVMInnerMut>,
152151
}
153152

154-
impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
153+
impl AxVM {
155154
/// Creates a new VM with the given configuration.
156155
/// Returns an error if the configuration is invalid.
157156
/// The VM is not started until `boot` is called.
158-
pub fn new(config: AxVMConfig) -> AxResult<AxVMRef<H, U>> {
157+
pub fn new(config: AxVMConfig) -> AxResult<AxVMRef> {
159158
let address_space =
160-
AddrSpace::new_empty(GuestPhysAddr::from(VM_ASPACE_BASE), VM_ASPACE_SIZE)?;
159+
// TODO: read level from config
160+
AddrSpace::new_empty(4, GuestPhysAddr::from(VM_ASPACE_BASE), VM_ASPACE_SIZE)?;
161161

162162
let result = Arc::new(Self {
163163
id: config.id(),
@@ -167,7 +167,6 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
167167
config,
168168
memory_regions: Vec::new(),
169169
vm_status: VMStatus::Loading,
170-
_marker: core::marker::PhantomData,
171170
}),
172171
});
173172

@@ -189,7 +188,7 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
189188
let dtb_addr = inner_mut.config.image_config().dtb_load_gpa;
190189
let vcpu_id_pcpu_sets = inner_mut.config.phys_cpu_ls.get_vcpu_affinities_pcpu_ids();
191190

192-
info!("dtb_load_gpa: {:?}", dtb_addr);
191+
info!("dtb_load_gpa: {dtb_addr:?}");
193192
debug!("id: {}, VCpuIdPCpuSets: {vcpu_id_pcpu_sets:#x?}", self.id());
194193

195194
let mut vcpu_list = Vec::with_capacity(vcpu_id_pcpu_sets.len());
@@ -205,6 +204,10 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
205204
dtb_addr: dtb_addr.unwrap_or_default().as_usize(),
206205
};
207206

207+
// FIXME: VCpu is neither `Send` nor `Sync` by design, check whether
208+
// 1. we should make it `Send` and `Sync`, or
209+
// 2. we can guarantee that no cross-thread access is performed
210+
#[allow(clippy::arc_with_non_send_sync)]
208211
vcpu_list.push(Arc::new(VCpu::new(
209212
self.id(),
210213
vcpu_id,
@@ -278,14 +281,10 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
278281
)?;
279282
}
280283

281-
#[cfg(target_arch = "aarch64")]
284+
#[cfg_attr(not(target_arch = "aarch64"), expect(unused_mut))]
282285
let mut devices = axdevice::AxVmDevices::new(AxVmDeviceConfig {
283286
emu_configs: inner_mut.config.emu_devices().to_vec(),
284287
});
285-
#[cfg(not(target_arch = "aarch64"))]
286-
let devices = axdevice::AxVmDevices::new(AxVmDeviceConfig {
287-
emu_configs: inner_mut.config.emu_devices().to_vec(),
288-
});
289288

290289
#[cfg(target_arch = "aarch64")]
291290
{
@@ -346,6 +345,9 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
346345
passthrough_timer: passthrough,
347346
}
348347
};
348+
#[cfg(not(target_arch = "aarch64"))]
349+
#[allow(clippy::let_unit_value)]
350+
let setup_config = <AxArchVCpuImpl as axvcpu::AxArchVCpu>::SetupConfig::default();
349351

350352
let entry = if vcpu.id() == 0 {
351353
inner_mut.config.bsp_entry()
@@ -358,10 +360,7 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
358360
vcpu.setup(
359361
entry,
360362
inner_mut.address_space.page_table_root(),
361-
#[cfg(target_arch = "aarch64")]
362363
setup_config,
363-
#[cfg(not(target_arch = "aarch64"))]
364-
(),
365364
)?;
366365
}
367366
info!("VM setup: id={}", self.id());
@@ -383,7 +382,7 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
383382
/// Retrieves the vCPU corresponding to the given vcpu_id for the VM.
384383
/// Returns None if the vCPU does not exist.
385384
#[inline]
386-
pub fn vcpu(&self, vcpu_id: usize) -> Option<AxVCpuRef<U>> {
385+
pub fn vcpu(&self, vcpu_id: usize) -> Option<AxVCpuRef> {
387386
self.vcpu_list().get(vcpu_id).cloned()
388387
}
389388

@@ -393,15 +392,15 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
393392
self.inner_const().vcpu_list.len()
394393
}
395394

396-
fn inner_const(&self) -> &AxVMInnerConst<U> {
395+
fn inner_const(&self) -> &AxVMInnerConst {
397396
self.inner_const
398397
.get()
399398
.expect("VM inner_const not initialized")
400399
}
401400

402401
/// Returns a reference to the list of vCPUs corresponding to the VM.
403402
#[inline]
404-
pub fn vcpu_list(&self) -> &[AxVCpuRef<U>] {
403+
pub fn vcpu_list(&self) -> &[AxVCpuRef] {
405404
&self.inner_const().vcpu_list
406405
}
407406

@@ -594,13 +593,12 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
594593
// It is not supported to inject interrupt to a vcpu in another VM yet.
595594
//
596595
// It may be supported in the future, as a essential feature for cross-VM communication.
597-
if H::current_vm_id() != self.id() {
596+
let current_running_vm = axvisor_api::vmm::current_vm_id();
597+
if current_running_vm != vm_id {
598598
panic!("Injecting interrupt to a vcpu in another VM is not supported");
599599
}
600600

601-
for target_vcpu in &targets {
602-
H::inject_irq_to_vcpu(vm_id, target_vcpu, irq)?;
603-
}
601+
axvisor_api::vmm::inject_interrupt_to_cpus(vm_id, targets, irq as InterruptVector);
604602

605603
Ok(())
606604
}
@@ -757,7 +755,7 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
757755
let s = unsafe { core::slice::from_raw_parts_mut(hva, layout.size()) };
758756
let hva = HostVirtAddr::from_mut_ptr_of(hva);
759757

760-
let hpa = H::virt_to_phys(hva);
758+
let hpa = axvisor_api::memory::virt_to_phys(hva);
761759

762760
let gpa = gpa.unwrap_or_else(|| hpa.as_usize().into());
763761

@@ -923,7 +921,7 @@ impl<H: AxVMHal, U: AxVCpuHal> AxVM<H, U> {
923921
}
924922
}
925923

926-
impl<H: AxVMHal, U: AxVCpuHal> Drop for AxVM<H, U> {
924+
impl Drop for AxVM {
927925
fn drop(&mut self) {
928926
info!("Dropping VM[{}]", self.id());
929927

0 commit comments

Comments
 (0)