From 92a004c914123722a4154f96ed3a64314ba6ea55 Mon Sep 17 00:00:00 2001
From: hky1999 <976929993@qq.com>
Date: Sat, 24 Jan 2026 17:46:58 +0800
Subject: [PATCH 1/6] [feat] refactor according to rules and prepare to release
---
.github/workflows/ci.yml | 106 ++++++++++-------
.github/workflows/release-check.yml | 72 ++++++++++++
CHANGELOG.md | 38 ++++++
Cargo.toml | 30 +++--
README.md | 5 +-
axvisor_api_proc/Cargo.toml | 11 +-
axvisor_api_proc/src/lib.rs | 138 ++++++++++++++++++++--
src/arch.rs | 109 ++++++++++++++++-
src/host.rs | 51 +++++++-
src/memory.rs | 160 ++++++++++++++++++++++++-
src/time.rs | 174 +++++++++++++++++++++++++---
src/vmm.rs | 168 +++++++++++++++++++++++++--
12 files changed, 964 insertions(+), 98 deletions(-)
create mode 100644 .github/workflows/release-check.yml
create mode 100644 CHANGELOG.md
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5d66dd5..37dcbf7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,57 +1,83 @@
name: CI
-on: [push, pull_request]
+on:
+ push:
+ branches: [main, dev, master]
+ pull_request:
+ branches: [main, dev, master]
+
+env:
+ CARGO_TERM_COLOR: always
+ RUST_BACKTRACE: 1
jobs:
- ci:
+ # Quick checks: format and clippy
+ quick-check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2025-05-20
+ components: rustfmt, clippy
+ - name: Check rust version
+ run: rustc --version --verbose
+ - name: Check code format
+ run: cargo fmt --all -- --check
+ - name: Clippy (x86_64-unknown-linux-gnu)
+ run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default
+
+ # Build and test for multiple targets
+ build-and-test:
runs-on: ubuntu-latest
+ needs: quick-check
strategy:
fail-fast: false
matrix:
rust-toolchain: [nightly-2025-05-20, nightly]
- targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
+ targets:
+ - x86_64-unknown-linux-gnu
+ - x86_64-unknown-none
+ - riscv64gc-unknown-none-elf
+ - aarch64-unknown-none-softfloat
steps:
- - uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@nightly
- with:
- toolchain: ${{ matrix.rust-toolchain }}
- components: rust-src, clippy, rustfmt
- targets: ${{ matrix.targets }}
- - name: Check rust version
- run: rustc --version --verbose
- - name: Check code format
- run: cargo fmt --all -- --check
- - name: Clippy
- run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
- - name: Build
- run: cargo build --target ${{ matrix.targets }} --all-features
- - name: Unit test
- if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
- run: cargo test --target ${{ matrix.targets }} -- --nocapture
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: ${{ matrix.rust-toolchain }}
+ components: rust-src, clippy
+ targets: ${{ matrix.targets }}
+ - name: Check rust version
+ run: rustc --version --verbose
+ - name: Clippy
+ run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
+ - name: Build
+ run: cargo build --target ${{ matrix.targets }} --all-features
+ - name: Unit test
+ if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
+ run: cargo test --target ${{ matrix.targets }} -- --nocapture
+ # Documentation build and deploy
doc:
runs-on: ubuntu-latest
- strategy:
- fail-fast: false
+ needs: quick-check
permissions:
contents: write
env:
- default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
- RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
+ RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs"
steps:
- - uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@nightly
- with:
- toolchain: nightly-2025-05-20
- - name: Build docs
- continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
- run: |
- cargo doc --no-deps --all-features
- printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
- - name: Deploy to Github Pages
- if: ${{ github.ref == env.default-branch }}
- uses: JamesIves/github-pages-deploy-action@v4
- with:
- single-commit: true
- branch: gh-pages
- folder: target/doc
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2025-05-20
+ - name: Build docs
+ run: |
+ cargo doc --no-deps --all-features
+ printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
+ - name: Deploy to Github Pages
+ if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
+ uses: JamesIves/github-pages-deploy-action@v4
+ with:
+ single-commit: true
+ branch: gh-pages
+ folder: target/doc
diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml
new file mode 100644
index 0000000..8d406e2
--- /dev/null
+++ b/.github/workflows/release-check.yml
@@ -0,0 +1,72 @@
+name: Release Check
+
+on:
+ push:
+ tags:
+ - "v*"
+
+env:
+ CARGO_TERM_COLOR: always
+ RUST_BACKTRACE: 1
+ RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs"
+
+jobs:
+ release-check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2025-05-20
+ components: rust-src, clippy, rustfmt
+ targets: x86_64-unknown-linux-gnu,x86_64-unknown-none,riscv64gc-unknown-none-elf,aarch64-unknown-none-softfloat
+
+ - name: Check rust version
+ run: rustc --version --verbose
+
+ # Verify version consistency between git tag and Cargo.toml
+ - name: Verify version consistency
+ run: |
+ TAG_VERSION=${GITHUB_REF#refs/tags/v}
+ CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version')
+ if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
+ echo "Error: Git tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)"
+ exit 1
+ fi
+ echo "Version check passed: $TAG_VERSION"
+
+ # Format check
+ - name: Check code format
+ run: cargo fmt --all -- --check
+
+ # Clippy check
+ - name: Clippy
+ run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default
+
+ # Build for all supported targets
+ - name: Build (x86_64-unknown-linux-gnu)
+ run: cargo build --target x86_64-unknown-linux-gnu --all-features
+ - name: Build (x86_64-unknown-none)
+ run: cargo build --target x86_64-unknown-none --all-features
+ - name: Build (riscv64gc-unknown-none-elf)
+ run: cargo build --target riscv64gc-unknown-none-elf --all-features
+ - name: Build (aarch64-unknown-none-softfloat)
+ run: cargo build --target aarch64-unknown-none-softfloat --all-features
+
+ # Run tests
+ - name: Unit test
+ run: cargo test --all-features -- --nocapture
+
+ # Doc test
+ - name: Doc test
+ run: cargo test --doc --all-features
+
+ # Build documentation
+ - name: Build docs
+ run: cargo doc --no-deps --all-features
+
+ # Dry run publish check
+ - name: Publish check (axvisor_api_proc)
+ run: cargo publish --dry-run -p axvisor_api_proc
+ - name: Publish check (axvisor_api)
+ run: cargo publish --dry-run -p axvisor_api
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..c6e8c00
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,38 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [0.2.0] - 2026-01-24
+
+### Added
+
+- Added `api_def` and `api_impl` procedural macros for defining and implementing APIs.
+- Added `arch` module with architecture-specific APIs for AArch64 GIC operations.
+- Added `host` module with host system APIs.
+- Added `memory` module with memory allocation and address translation APIs.
+- Added `time` module with time and timer APIs.
+- Added `vmm` module with virtual machine management APIs.
+- Added `PhysFrame` type alias for automatic frame deallocation.
+- Added comprehensive documentation and examples in crate-level docs.
+- Added docs.rs configuration for multi-target documentation.
+
+### Changed
+
+- Improved Cargo.toml with complete metadata fields.
+- Enhanced CI configuration with documentation checks.
+
+## [0.1.0] - Initial Release
+
+### Added
+
+- Initial implementation of the axvisor_api crate.
+- Basic API definition and implementation framework using `crate_interface`.
+
+[Unreleased]: https://github.com/arceos-hypervisor/axvisor_api/compare/v0.2.0...HEAD
+[0.2.0]: https://github.com/arceos-hypervisor/axvisor_api/compare/v0.1.0...v0.2.0
+[0.1.0]: https://github.com/arceos-hypervisor/axvisor_api/releases/tag/v0.1.0
diff --git a/Cargo.toml b/Cargo.toml
index b21e2f6..d7cd7d4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,6 @@
[workspace]
resolver = "2"
-members = [
- "axvisor_api_proc",
-]
+members = ["axvisor_api_proc"]
[workspace.package]
authors = ["Su Mingxian "]
@@ -12,9 +10,11 @@ repository = "https://github.com/arceos-hypervisor/axvisor_api"
[package]
name = "axvisor_api"
-description = "Basic API for components of the Hypervisor on ArceOS"
version = "0.2.0"
-keywords = ["axvisor", "api", "embedded"]
+description = "Basic API for components of the Hypervisor on ArceOS"
+documentation = "https://docs.rs/axvisor_api"
+readme = "README.md"
+keywords = ["axvisor", "api", "embedded", "hypervisor"]
categories = ["embedded", "no-std"]
authors.workspace = true
edition.workspace = true
@@ -22,8 +22,24 @@ license.workspace = true
repository.workspace = true
[dependencies]
-axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0"}
+# === 核心组件 ===
+# 过程宏定义
+axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0" }
+# === 第三方库 ===
+# 地址空间抽象
+axaddrspace = "0.1.0"
+# 接口定义工具
crate_interface = "0.2"
+# 物理/虚拟地址类型
memory_addr = "0.4"
-axaddrspace = "0.1.0"
+
+[package.metadata.docs.rs]
+all-features = true
+default-target = "x86_64-unknown-linux-gnu"
+targets = [
+ "x86_64-unknown-linux-gnu",
+ "x86_64-unknown-none",
+ "riscv64gc-unknown-none-elf",
+ "aarch64-unknown-none-softfloat",
+]
diff --git a/README.md b/README.md
index 05303f1..a3494b4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,9 @@
# axvisor\_api (Experimental Next-Generation Axvisor API)
+
[](https://github.com/arceos-hypervisor/axvisor_api/actions/workflows/ci.yml)
-[]
+[](https://crates.io/crates/axvisor_api)
+[](https://docs.rs/axvisor_api)
+[](LICENSE.Apache2)
\> [中文README](README.zh-cn.md) <
diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml
index f080e76..5ab1ff9 100644
--- a/axvisor_api_proc/Cargo.toml
+++ b/axvisor_api_proc/Cargo.toml
@@ -2,6 +2,8 @@
name = "axvisor_api_proc"
version = "0.2.0"
description = "Procedural macros for the `axvisor_api` crate"
+documentation = "https://docs.rs/axvisor_api_proc"
+readme = "README.md"
keywords = ["axvisor", "api", "embedded", "macros"]
categories = ["development-tools::procedural-macro-helpers"]
authors.workspace = true
@@ -9,12 +11,15 @@ edition.workspace = true
license.workspace = true
repository.workspace = true
-
[lib]
proc-macro = true
[dependencies]
-syn = { version = "2.0", features = ["full"] }
-quote = "1.0"
+# 过程宏辅助工具
proc-macro2 = "1.0"
+# crate 名称查找
proc-macro-crate = "3.1.0"
+# 代码生成
+quote = "1.0"
+# Rust 语法解析
+syn = { version = "2.0", features = ["full"] }
diff --git a/axvisor_api_proc/src/lib.rs b/axvisor_api_proc/src/lib.rs
index b8f1c90..3b1a875 100644
--- a/axvisor_api_proc/src/lib.rs
+++ b/axvisor_api_proc/src/lib.rs
@@ -1,3 +1,45 @@
+//! Procedural macros for the `axvisor_api` crate.
+//!
+//! This crate provides the procedural macros used to define and implement
+//! AxVisor API interfaces. These macros are built on top of the
+//! `crate_interface` crate and provide a convenient way to create
+//! link-time-resolved API interfaces.
+//!
+//! # Macros
+//!
+//! - [`api_def`] - Define an API interface trait.
+//! - [`api_impl`] - Implement an API interface.
+//!
+//! # Usage
+//!
+//! This crate is re-exported by `axvisor_api` and should not be used directly.
+//! Instead, use the macros through `axvisor_api`:
+//!
+//! ```rust,ignore
+//! use axvisor_api::{api_def, api_impl};
+//!
+//! #[api_def]
+//! pub trait MyApiIf {
+//! fn my_function() -> u32;
+//! }
+//!
+//! struct MyApiImpl;
+//!
+//! #[api_impl]
+//! impl MyApiIf for MyApiImpl {
+//! fn my_function() -> u32 {
+//! 42
+//! }
+//! }
+//! ```
+//!
+//! # How It Works
+//!
+//! The macros use `crate_interface` under the hood, which leverages Rust's
+//! link-time symbol resolution to connect API definitions with their
+//! implementations. This allows for a cleaner API without explicit generic
+//! parameters.
+
use proc_macro::TokenStream as TokenStream1;
use proc_macro_crate::{FoundCrate, crate_name};
use proc_macro2::{Span, TokenStream};
@@ -5,6 +47,11 @@ use quote::{quote, quote_spanned};
use syn::{Ident, spanned::Spanned};
/// Find the path to the `axvisor_api` crate.
+///
+/// This function determines the correct path to use when referring to
+/// `axvisor_api` from within the generated code, handling both the case
+/// where we're inside the `axvisor_api` crate itself and when we're in
+/// an external crate.
fn axvisor_api_crate() -> TokenStream {
match crate_name("axvisor_api") {
Ok(FoundCrate::Itself) => quote! { crate },
@@ -16,29 +63,68 @@ fn axvisor_api_crate() -> TokenStream {
}
}
-/// The namespace used for AxVisor APIs when calling `crate_interface` macros.
+/// Get the namespace identifier used for AxVisor APIs.
+///
+/// All AxVisor APIs share a common namespace to avoid conflicts with other
+/// uses of `crate_interface`.
fn axvisor_api_namespace() -> Ident {
const AXVISOR_API_NS: &str = "AxVisorApi";
Ident::new(AXVISOR_API_NS, Span::call_site())
}
+/// Macro to assert that an attribute has no arguments.
macro_rules! assert_empty_attr {
($attr:expr) => {
if !$attr.is_empty() {
return (quote_spanned! {
TokenStream::from($attr).span() => compile_error!("This attribute does not accept any arguments")
- }).into();
+ })
+ .into();
}
};
}
/// Define an AxVisor API interface.
///
-/// This macro is applied to a trait definition. It generates the necessary
-/// boilerplate code to register the trait as an AxVisor API interface. No
-/// arguments are accepted.
+/// This attribute macro is applied to a trait definition to register it as
+/// an AxVisor API interface. It generates caller functions for each method
+/// in the trait, allowing the API to be called as regular functions.
+///
+/// # Usage
+///
+/// ```rust,ignore
+/// use axvisor_api::api_def;
+///
+/// #[api_def]
+/// pub trait MyApiIf {
+/// /// Get a value.
+/// fn get_value() -> u32;
+///
+/// /// Set a value.
+/// fn set_value(value: u32);
+/// }
+///
+/// // After the macro expansion, you can call:
+/// // my_module::get_value()
+/// // my_module::set_value(42)
+/// ```
+///
+/// # Generated Code
+///
+/// The macro generates:
+/// 1. The original trait definition with `crate_interface::def_interface`
+/// attribute.
+/// 2. Free-standing caller functions for each trait method at the same
+/// module level.
+///
+/// # Attributes
+///
+/// This macro does not accept any arguments.
///
-/// This macro uses `crate_interface::def_interface` internally.
+/// # Implementation
+///
+/// This macro uses `crate_interface::def_interface` internally with the
+/// `gen_caller` option to generate the caller functions.
#[proc_macro_attribute]
pub fn api_def(attr: TokenStream1, input: TokenStream1) -> TokenStream1 {
assert_empty_attr!(attr);
@@ -56,10 +142,42 @@ pub fn api_def(attr: TokenStream1, input: TokenStream1) -> TokenStream1 {
/// Implement an AxVisor API interface.
///
-/// This macro is applied to an `impl` block that implements a trait previously
-/// defined with `api_def`. It generates the necessary boilerplate code to
-/// register the implementation as an AxVisor API implementation. No arguments
-/// are accepted.
+/// This attribute macro is applied to an `impl` block that implements a
+/// trait previously defined with [`api_def`]. It registers the implementation
+/// so that calls to the API functions are resolved to this implementation
+/// at link time.
+///
+/// # Usage
+///
+/// ```rust,ignore
+/// use axvisor_api::{api_def, api_impl};
+///
+/// #[api_def]
+/// pub trait MyApiIf {
+/// fn get_value() -> u32;
+/// }
+///
+/// struct MyApiImpl;
+///
+/// #[api_impl]
+/// impl MyApiIf for MyApiImpl {
+/// fn get_value() -> u32 {
+/// 42
+/// }
+/// }
+/// ```
+///
+/// # Requirements
+///
+/// - The implemented trait must have been defined with [`api_def`].
+/// - The implementing type should be an empty struct (marker type).
+/// - Only one implementation per API trait is allowed in the final binary.
+///
+/// # Attributes
+///
+/// This macro does not accept any arguments.
+///
+/// # Implementation
///
/// This macro uses `crate_interface::impl_interface` internally.
#[proc_macro_attribute]
diff --git a/src/arch.rs b/src/arch.rs
index c11b030..4332142 100644
--- a/src/arch.rs
+++ b/src/arch.rs
@@ -1,25 +1,122 @@
-//! Architecture-specific APIs.
+//! Architecture-specific APIs for the AxVisor hypervisor.
+//!
+//! This module provides APIs that are specific to certain CPU architectures.
+//! Currently, it mainly contains AArch64 GIC (Generic Interrupt Controller)
+//! related operations.
+//!
+//! # Supported Architectures
+//!
+//! - **AArch64**: GIC distributor and redistributor operations for virtual
+//! interrupt injection and GIC initialization.
+//!
+//! # Usage
+//!
+//! The API functions in this module are conditionally compiled based on the
+//! target architecture. On non-AArch64 platforms, this module is essentially
+//! empty.
+//!
+//! # Implementation
+//!
+//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute
+//! macro on an impl block:
+//!
+//! ```rust,ignore
+//! struct ArchIfImpl;
+//!
+//! #[axvisor_api::api_impl]
+//! impl axvisor_api::arch::ArchIf for ArchIfImpl {
+//! // Implement the required functions...
+//! }
+//! ```
use super::{memory::PhysAddr, vmm::InterruptVector};
/// The API trait for architecture-specific functionalities.
+///
+/// This trait defines the interface for architecture-specific operations
+/// required by the hypervisor. Implementations should be provided by the
+/// host system or HAL layer.
#[crate::api_def]
pub trait ArchIf {
- /// Inject a virtual interrupt to the current virtual CPU.
+ /// Inject a virtual interrupt to the current virtual CPU using hardware
+ /// virtualization support.
+ ///
+ /// This function uses the GIC virtualization interface to directly inject
+ /// an interrupt into the guest without causing a VM exit.
+ ///
+ /// # Arguments
+ ///
+ /// * `vector` - The interrupt vector number to inject.
+ ///
+ /// # Platform Support
+ ///
+ /// This function is only available on AArch64 platforms with GICv2/v3
+ /// virtualization extensions.
#[cfg(target_arch = "aarch64")]
fn hardware_inject_virtual_interrupt(vector: InterruptVector);
- /// Get the TYPER register of the GIC distributor. Used in virtual GIC initialization.
+ /// Read the TYPER (Type Register) of the GIC distributor.
+ ///
+ /// The TYPER register provides information about the GIC implementation,
+ /// including the maximum number of SPIs supported and the number of
+ /// implemented CPU interfaces.
+ ///
+ /// # Returns
+ ///
+ /// The 32-bit value of the GICD_TYPER register.
+ ///
+ /// # Platform Support
+ ///
+ /// This function is only available on AArch64 platforms.
#[cfg(target_arch = "aarch64")]
fn read_vgicd_typer() -> u32;
- /// Get the IIDR register of the GIC distributor. Used in virtual GIC initialization.
+
+ /// Read the IIDR (Implementer Identification Register) of the GIC
+ /// distributor.
+ ///
+ /// The IIDR register provides identification information about the GIC
+ /// implementation, including the implementer, revision, and variant.
+ ///
+ /// # Returns
+ ///
+ /// The 32-bit value of the GICD_IIDR register.
+ ///
+ /// # Platform Support
+ ///
+ /// This function is only available on AArch64 platforms.
#[cfg(target_arch = "aarch64")]
fn read_vgicd_iidr() -> u32;
- /// Get the base address of the GIC distributor in the host system.
+ /// Get the base physical address of the GIC distributor in the host system.
+ ///
+ /// The GIC distributor is responsible for interrupt prioritization and
+ /// distribution to CPU interfaces.
+ ///
+ /// # Returns
+ ///
+ /// The physical address of the GICD (GIC Distributor) registers.
+ ///
+ /// # Platform Support
+ ///
+ /// This function is only available on AArch64 platforms.
#[cfg(target_arch = "aarch64")]
fn get_host_gicd_base() -> PhysAddr;
- /// Get the base address of the GIC redistributor in the host system.
+
+ /// Get the base physical address of the GIC redistributor in the host
+ /// system.
+ ///
+ /// The GIC redistributor (GICv3+) handles per-PE (Processing Element)
+ /// interrupt configuration and provides the LPI (Locality-specific
+ /// Peripheral Interrupt) configuration interface.
+ ///
+ /// # Returns
+ ///
+ /// The physical address of the GICR (GIC Redistributor) registers.
+ ///
+ /// # Platform Support
+ ///
+ /// This function is only available on AArch64 platforms with GICv3 or
+ /// later.
#[cfg(target_arch = "aarch64")]
fn get_host_gicr_base() -> PhysAddr;
}
diff --git a/src/host.rs b/src/host.rs
index 5d27eab..cee440c 100644
--- a/src/host.rs
+++ b/src/host.rs
@@ -1,8 +1,55 @@
-//! Host system related APIs.
+//! Host system related APIs for the AxVisor hypervisor.
+//!
+//! This module provides APIs for querying information about the host system
+//! on which the hypervisor is running.
+//!
+//! # Overview
+//!
+//! The host system APIs provide essential information about the underlying
+//! hardware that the hypervisor needs to manage virtual machines effectively.
+//!
+//! # Available APIs
+//!
+//! - [`get_host_cpu_num`] - Get the total number of CPUs in the host system.
+//!
+//! # Implementation
+//!
+//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute
+//! macro on an impl block:
+//!
+//! ```rust,ignore
+//! struct HostIfImpl;
+//!
+//! #[axvisor_api::api_impl]
+//! impl axvisor_api::host::HostIf for HostIfImpl {
+//! fn get_host_cpu_num() -> usize {
+//! // Return the number of CPUs from your platform
+//! 4
+//! }
+//! }
+//! ```
/// The API trait for host system functionalities.
+///
+/// This trait defines the interface for querying host system information.
+/// Implementations should be provided by the host system or HAL layer.
#[crate::api_def]
pub trait HostIf {
- /// Get the total number of cpus in the host system.
+ /// Get the total number of CPUs (logical processors) in the host system.
+ ///
+ /// This function returns the number of CPUs available to the hypervisor,
+ /// which is typically the same as the number of physical or logical
+ /// processors in the system.
+ ///
+ /// # Returns
+ ///
+ /// The number of CPUs in the host system.
+ ///
+ /// # Example
+ ///
+ /// ```rust,ignore
+ /// let cpu_count = axvisor_api::host::get_host_cpu_num();
+ /// println!("Host has {} CPUs", cpu_count);
+ /// ```
fn get_host_cpu_num() -> usize;
}
diff --git a/src/memory.rs b/src/memory.rs
index cc19682..9d74bf3 100644
--- a/src/memory.rs
+++ b/src/memory.rs
@@ -1,26 +1,160 @@
-//! Memory allocation and address translation APIs.
+//! Memory allocation and address translation APIs for the AxVisor hypervisor.
+//!
+//! This module provides APIs for physical memory management, including frame
+//! allocation/deallocation and physical-virtual address translation.
+//!
+//! # Overview
+//!
+//! The memory APIs are fundamental to the hypervisor's operation, enabling:
+//! - Physical frame allocation for guest memory
+//! - Contiguous frame allocation for DMA and other hardware requirements
+//! - Address translation between physical and virtual addresses
+//!
+//! # Re-exports
+//!
+//! This module re-exports [`PhysAddr`] and [`VirtAddr`] from the `memory_addr`
+//! crate for convenience.
+//!
+//! # Types
+//!
+//! - [`PhysFrame`] - A physical frame that is automatically deallocated when
+//! dropped.
+//!
+//! # Implementation
+//!
+//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute
+//! macro on an impl block:
+//!
+//! ```rust,ignore
+//! struct MemoryIfImpl;
+//!
+//! #[axvisor_api::api_impl]
+//! impl axvisor_api::memory::MemoryIf for MemoryIfImpl {
+//! fn alloc_frame() -> Option {
+//! // Allocate a physical frame from your allocator
+//! }
+//! // ... implement other functions
+//! }
+//! ```
pub use memory_addr::{PhysAddr, VirtAddr};
/// The API trait for memory allocation and address translation functionalities.
+///
+/// This trait defines the core memory management interface required by the
+/// hypervisor. Implementations should be provided by the host system or HAL
+/// layer.
#[crate::api_def]
pub trait MemoryIf {
- /// Allocate a frame.
+ /// Allocate a single physical frame (4KB page).
+ ///
+ /// # Returns
+ ///
+ /// - `Some(PhysAddr)` - The physical address of the allocated frame.
+ /// - `None` - If allocation fails (e.g., out of memory).
+ ///
+ /// # Example
+ ///
+ /// ```rust,ignore
+ /// use axvisor_api::memory::{alloc_frame, dealloc_frame};
+ ///
+ /// if let Some(frame) = alloc_frame() {
+ /// // Use the frame...
+ /// dealloc_frame(frame);
+ /// }
+ /// ```
fn alloc_frame() -> Option;
- /// Allocate a number of contiguous frames, with a specified alignment.
+
+ /// Allocate a number of contiguous physical frames with a specified
+ /// alignment.
+ ///
+ /// This function is useful for allocating memory for DMA buffers or other
+ /// hardware that requires contiguous physical memory.
+ ///
+ /// # Arguments
+ ///
+ /// * `num_frames` - The number of contiguous frames to allocate.
+ /// * `frame_align_pow2` - The alignment requirement as a power of 2
+ /// (e.g., 0 for 4KB alignment, 1 for 8KB alignment).
+ ///
+ /// # Returns
+ ///
+ /// - `Some(PhysAddr)` - The physical address of the first allocated frame.
+ /// - `None` - If allocation fails.
fn alloc_contiguous_frames(num_frames: usize, frame_align_pow2: usize) -> Option;
- /// Deallocate a frame allocated previously by [`alloc_frame`].
+
+ /// Deallocate a frame previously allocated by [`alloc_frame`].
+ ///
+ /// # Arguments
+ ///
+ /// * `addr` - The physical address of the frame to deallocate.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that:
+ /// - The address was previously returned by [`alloc_frame`].
+ /// - The frame has not been deallocated yet.
+ /// - No references to the frame's memory exist after deallocation.
fn dealloc_frame(addr: PhysAddr);
- /// Deallocate a number of contiguous frames allocated previously by
+
+ /// Deallocate contiguous frames previously allocated by
/// [`alloc_contiguous_frames`].
+ ///
+ /// # Arguments
+ ///
+ /// * `first_addr` - The physical address of the first frame.
+ /// * `num_frames` - The number of frames to deallocate.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that:
+ /// - The address and count match a previous [`alloc_contiguous_frames`]
+ /// call.
+ /// - The frames have not been deallocated yet.
+ /// - No references to the frames' memory exist after deallocation.
fn dealloc_contiguous_frames(first_addr: PhysAddr, num_frames: usize);
+
/// Convert a physical address to a virtual address.
+ ///
+ /// This function performs the physical-to-virtual address translation
+ /// based on the host's memory mapping.
+ ///
+ /// # Arguments
+ ///
+ /// * `addr` - The physical address to convert.
+ ///
+ /// # Returns
+ ///
+ /// The corresponding virtual address.
+ ///
+ /// # Panics
+ ///
+ /// May panic if the physical address is not mapped.
fn phys_to_virt(addr: PhysAddr) -> VirtAddr;
+
/// Convert a virtual address to a physical address.
+ ///
+ /// This function performs the virtual-to-physical address translation
+ /// based on the host's memory mapping.
+ ///
+ /// # Arguments
+ ///
+ /// * `addr` - The virtual address to convert.
+ ///
+ /// # Returns
+ ///
+ /// The corresponding physical address.
+ ///
+ /// # Panics
+ ///
+ /// May panic if the virtual address is not mapped.
fn virt_to_phys(addr: VirtAddr) -> PhysAddr;
}
/// [`AxMmHal`](axaddrspace::AxMmHal) implementation by axvisor_api.
+///
+/// This struct provides an implementation of the `AxMmHal` trait from the
+/// `axaddrspace` crate, delegating to the axvisor_api memory functions.
#[doc(hidden)]
pub struct AxMmHalApiImpl;
@@ -43,4 +177,20 @@ impl axaddrspace::AxMmHal for AxMmHalApiImpl {
}
/// A physical frame which will be automatically deallocated when dropped.
+///
+/// This type alias provides a convenient RAII wrapper around physical frame
+/// allocation. When a `PhysFrame` is dropped, it automatically deallocates
+/// the underlying physical memory.
+///
+/// # Example
+///
+/// ```rust,ignore
+/// use axvisor_api::memory::PhysFrame;
+///
+/// fn allocate_guest_memory() -> Option {
+/// PhysFrame::alloc()
+/// }
+///
+/// // The frame will be automatically deallocated when it goes out of scope
+/// ```
pub type PhysFrame = axaddrspace::PhysFrame;
diff --git a/src/time.rs b/src/time.rs
index 1e8129f..65a40ee 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -1,53 +1,201 @@
-//! Time and timer APIs.
+//! Time and timer APIs for the AxVisor hypervisor.
+//!
+//! This module provides APIs for time measurement and timer management,
+//! which are essential for implementing virtual timers and time-related
+//! virtualization features.
+//!
+//! # Overview
+//!
+//! The time APIs provide:
+//! - Current time and tick count queries
+//! - Conversion between ticks, nanoseconds, and duration
+//! - Timer registration and cancellation
+//!
+//! # Types
+//!
+//! - [`TimeValue`] - A time value represented as [`Duration`].
+//! - [`Nanos`] - Nanoseconds count (u64).
+//! - [`Ticks`] - Tick count (u64).
+//! - [`CancelToken`] - Token used to cancel a registered timer.
+//!
+//! # Helper Functions
+//!
+//! In addition to the core API trait, this module provides helper functions:
+//! - [`current_time_nanos`] - Get the current time in nanoseconds.
+//! - [`current_time`] - Get the current time as a [`TimeValue`].
+//! - [`ticks_to_time`] - Convert ticks to [`TimeValue`].
+//! - [`time_to_ticks`] - Convert [`TimeValue`] to ticks.
+//!
+//! # Implementation
+//!
+//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute
+//! macro on an impl block:
+//!
+//! ```rust,ignore
+//! struct TimeIfImpl;
+//!
+//! #[axvisor_api::api_impl]
+//! impl axvisor_api::time::TimeIf for TimeIfImpl {
+//! fn current_ticks() -> Ticks {
+//! // Read the hardware timer counter
+//! }
+//! // ... implement other functions
+//! }
+//! ```
extern crate alloc;
use alloc::boxed::Box;
use core::time::Duration;
-/// Time value.
+/// Time value type.
+///
+/// Represents a point in time or a duration as a [`Duration`].
pub type TimeValue = Duration;
-/// Nanoseconds count.
+
+/// Nanoseconds count type.
+///
+/// Used for high-precision time measurements in nanoseconds.
pub type Nanos = u64;
-/// Tick count.
+
+/// Tick count type.
+///
+/// Represents the raw hardware timer counter value.
pub type Ticks = u64;
-/// Cancel token, used to cancel a scheduled timer event.
+
+/// Cancel token type for timer cancellation.
+///
+/// This token is returned when registering a timer and can be used to cancel
+/// the timer before it fires.
pub type CancelToken = usize;
/// The API trait for time and timer functionalities.
+///
+/// This trait defines the core time management interface required by the
+/// hypervisor. Implementations should be provided by the host system or HAL
+/// layer.
#[crate::api_def]
pub trait TimeIf {
- /// Get the current tick count.
+ /// Get the current tick count from the hardware timer.
+ ///
+ /// The tick count is a monotonically increasing counter that can be
+ /// converted to time using [`ticks_to_nanos`].
+ ///
+ /// # Returns
+ ///
+ /// The current hardware timer counter value.
fn current_ticks() -> Ticks;
- /// Convert ticks to nanoseconds.
+
+ /// Convert a tick count to nanoseconds.
+ ///
+ /// # Arguments
+ ///
+ /// * `ticks` - The tick count to convert.
+ ///
+ /// # Returns
+ ///
+ /// The equivalent time in nanoseconds.
fn ticks_to_nanos(ticks: Ticks) -> Nanos;
- /// Convert nanoseconds to ticks.
+
+ /// Convert nanoseconds to a tick count.
+ ///
+ /// # Arguments
+ ///
+ /// * `nanos` - The nanoseconds to convert.
+ ///
+ /// # Returns
+ ///
+ /// The equivalent tick count.
fn nanos_to_ticks(nanos: Nanos) -> Ticks;
- /// Register a timer.
+
+ /// Register a timer that will fire at the specified deadline.
+ ///
+ /// When the deadline is reached, the callback function will be called
+ /// with the actual time at which it was invoked.
+ ///
+ /// # Arguments
+ ///
+ /// * `deadline` - The time at which the timer should fire.
+ /// * `callback` - The function to call when the timer fires. It receives
+ /// the actual time as an argument.
+ ///
+ /// # Returns
+ ///
+ /// A [`CancelToken`] that can be used to cancel the timer with
+ /// [`cancel_timer`].
+ ///
+ /// # Example
+ ///
+ /// ```rust,ignore
+ /// use axvisor_api::time::{register_timer, current_time, TimeValue};
+ /// use core::time::Duration;
+ ///
+ /// let deadline = current_time() + Duration::from_millis(100);
+ /// let token = register_timer(deadline, Box::new(|actual_time| {
+ /// println!("Timer fired at {:?}", actual_time);
+ /// }));
+ /// ```
fn register_timer(
deadline: TimeValue,
callback: Box,
) -> CancelToken;
- /// Cancel a timer.
+
+ /// Cancel a previously registered timer.
+ ///
+ /// If the timer has already fired, this function has no effect.
+ ///
+ /// # Arguments
+ ///
+ /// * `token` - The cancel token returned by [`register_timer`].
fn cancel_timer(token: CancelToken);
}
/// Get the current time in nanoseconds.
+///
+/// This is a convenience function that combines [`current_ticks`] and
+/// [`ticks_to_nanos`].
+///
+/// # Returns
+///
+/// The current time in nanoseconds since an unspecified epoch.
pub fn current_time_nanos() -> Nanos {
ticks_to_nanos(current_ticks())
}
-/// Get the current time.
+/// Get the current time as a [`TimeValue`].
+///
+/// This is a convenience function that returns the current time as a
+/// [`Duration`].
+///
+/// # Returns
+///
+/// The current time as a [`TimeValue`] (Duration).
pub fn current_time() -> TimeValue {
Duration::from_nanos(current_time_nanos())
}
-/// Convert ticks to time.
+/// Convert ticks to a [`TimeValue`].
+///
+/// # Arguments
+///
+/// * `ticks` - The tick count to convert.
+///
+/// # Returns
+///
+/// The equivalent time as a [`TimeValue`] (Duration).
pub fn ticks_to_time(ticks: Ticks) -> TimeValue {
Duration::from_nanos(ticks_to_nanos(ticks))
}
-/// Convert time to ticks.
+/// Convert a [`TimeValue`] to ticks.
+///
+/// # Arguments
+///
+/// * `time` - The time value to convert.
+///
+/// # Returns
+///
+/// The equivalent tick count.
pub fn time_to_ticks(time: TimeValue) -> Ticks {
nanos_to_ticks(time.as_nanos() as Nanos)
}
diff --git a/src/vmm.rs b/src/vmm.rs
index 5d3ee25..621b063 100644
--- a/src/vmm.rs
+++ b/src/vmm.rs
@@ -1,37 +1,183 @@
-//! Virtual machine management APIs.
+//! Virtual machine management APIs for the AxVisor hypervisor.
+//!
+//! This module provides APIs for managing virtual machines (VMs) and virtual
+//! CPUs (vCPUs), including querying VM/vCPU information and interrupt
+//! injection.
+//!
+//! # Overview
+//!
+//! The VMM (Virtual Machine Monitor) APIs enable:
+//! - Querying the current VM and vCPU context
+//! - Getting information about VMs and their vCPUs
+//! - Injecting interrupts into virtual CPUs
+//! - Timer expiration notifications
+//!
+//! # Types
+//!
+//! - [`VMId`] - Virtual machine identifier.
+//! - [`VCpuId`] - Virtual CPU identifier.
+//! - [`InterruptVector`] - Interrupt vector number.
+//!
+//! # Helper Functions
+//!
+//! In addition to the core API trait, this module provides helper functions:
+//! - [`current_vm_vcpu_num`] - Get the vCPU count of the current VM.
+//! - [`current_vm_active_vcpus`] - Get the active vCPU mask of the current VM.
+//!
+//! # Implementation
+//!
+//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute
+//! macro on an impl block:
+//!
+//! ```rust,ignore
+//! struct VmmIfImpl;
+//!
+//! #[axvisor_api::api_impl]
+//! impl axvisor_api::vmm::VmmIf for VmmIfImpl {
+//! fn current_vm_id() -> VMId {
+//! // Return the current VM's ID
+//! }
+//! // ... implement other functions
+//! }
+//! ```
-/// Virtual machine ID.
+/// Virtual machine identifier type.
+///
+/// Each virtual machine is assigned a unique identifier that can be used
+/// to reference it in API calls.
pub type VMId = usize;
-/// Virtual CPU ID.
+
+/// Virtual CPU identifier type.
+///
+/// Each vCPU within a VM is assigned a unique identifier (0-indexed).
pub type VCpuId = usize;
-/// Interrupt vector.
+
+/// Interrupt vector type.
+///
+/// Represents the interrupt vector number to be injected into a guest.
pub type InterruptVector = u8;
/// The API trait for virtual machine management functionalities.
+///
+/// This trait defines the core VM management interface required by the
+/// hypervisor components. Implementations should be provided by the VMM
+/// layer.
#[crate::api_def]
pub trait VmmIf {
- /// Get the ID of the current virtual machine.
+ /// Get the identifier of the current virtual machine.
+ ///
+ /// This function returns the VM ID of the VM that the calling context
+ /// belongs to.
+ ///
+ /// # Returns
+ ///
+ /// The current VM's identifier.
fn current_vm_id() -> VMId;
- /// Get the ID of the current virtual CPU.
+
+ /// Get the identifier of the current virtual CPU.
+ ///
+ /// This function returns the vCPU ID within the current VM context.
+ ///
+ /// # Returns
+ ///
+ /// The current vCPU's identifier (0-indexed within the VM).
fn current_vcpu_id() -> VCpuId;
+
/// Get the number of virtual CPUs in a virtual machine.
+ ///
+ /// # Arguments
+ ///
+ /// * `vm_id` - The identifier of the virtual machine to query.
+ ///
+ /// # Returns
+ ///
+ /// - `Some(count)` - The number of vCPUs in the specified VM.
+ /// - `None` - If the VM ID is invalid.
fn vcpu_num(vm_id: VMId) -> Option;
- /// Get the mask of active virtual CPUs in a virtual machine.
+
+ /// Get the bitmask of active virtual CPUs in a virtual machine.
+ ///
+ /// Each bit in the returned value represents a vCPU, where bit N is set
+ /// if vCPU N is active (online and running).
+ ///
+ /// # Arguments
+ ///
+ /// * `vm_id` - The identifier of the virtual machine to query.
+ ///
+ /// # Returns
+ ///
+ /// - `Some(mask)` - The active vCPU bitmask for the specified VM.
+ /// - `None` - If the VM ID is invalid.
fn active_vcpus(vm_id: VMId) -> Option;
- /// Inject an interrupt to a virtual CPU.
+
+ /// Inject an interrupt into a specific virtual CPU.
+ ///
+ /// This function queues an interrupt to be delivered to the specified
+ /// vCPU when it is next scheduled.
+ ///
+ /// # Arguments
+ ///
+ /// * `vm_id` - The identifier of the target virtual machine.
+ /// * `vcpu_id` - The identifier of the target virtual CPU.
+ /// * `vector` - The interrupt vector to inject.
+ ///
+ /// # Example
+ ///
+ /// ```rust,ignore
+ /// use axvisor_api::vmm::{inject_interrupt, current_vm_id};
+ ///
+ /// // Inject timer interrupt (vector 0x20) to vCPU 0 of the current VM
+ /// inject_interrupt(current_vm_id(), 0, 0x20);
+ /// ```
fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector);
- /// Notify that a virtual CPU timer has expired.
+
+ /// Notify that a virtual CPU's timer has expired.
+ ///
+ /// This function is called when a vCPU's virtual timer expires and needs
+ /// to be handled.
+ ///
+ /// # Arguments
+ ///
+ /// * `vm_id` - The identifier of the virtual machine.
+ /// * `vcpu_id` - The identifier of the virtual CPU whose timer expired.
+ ///
+ /// # Note
///
- /// TODO: determine whether we can skip this function.
+ /// This API may be revised in future versions as the timer virtualization
+ /// design evolves.
fn notify_vcpu_timer_expired(vm_id: VMId, vcpu_id: VCpuId);
}
/// Get the number of virtual CPUs in the current virtual machine.
+///
+/// This is a convenience function that combines [`current_vm_id`] and
+/// [`vcpu_num`].
+///
+/// # Returns
+///
+/// The number of vCPUs in the current VM.
+///
+/// # Panics
+///
+/// Panics if called outside of a valid VM context (when [`current_vm_id`]
+/// returns an invalid ID).
pub fn current_vm_vcpu_num() -> usize {
vcpu_num(current_vm_id()).unwrap()
}
-/// Get the mask of active virtual CPUs in the current virtual machine.
+/// Get the bitmask of active virtual CPUs in the current virtual machine.
+///
+/// This is a convenience function that combines [`current_vm_id`] and
+/// [`active_vcpus`].
+///
+/// # Returns
+///
+/// The active vCPU bitmask for the current VM.
+///
+/// # Panics
+///
+/// Panics if called outside of a valid VM context (when [`current_vm_id`]
+/// returns an invalid ID).
pub fn current_vm_active_vcpus() -> usize {
active_vcpus(current_vm_id()).unwrap()
}
From 1753883f79ed3cb82f59a0b51945f4ce6b75b0a7 Mon Sep 17 00:00:00 2001
From: Su Mingxian
Date: Wed, 28 Jan 2026 17:17:34 +0800
Subject: [PATCH 2/6] Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Cargo.toml b/Cargo.toml
index d7cd7d4..4d72443 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,7 +29,7 @@ axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0" }
# === 第三方库 ===
# 地址空间抽象
axaddrspace = "0.1.0"
-# 接口定义工具
+# Interface definition tools
crate_interface = "0.2"
# 物理/虚拟地址类型
memory_addr = "0.4"
From 91346b827b5e109464df2118cd3792f3527f4ca2 Mon Sep 17 00:00:00 2001
From: Su Mingxian
Date: Wed, 28 Jan 2026 17:17:45 +0800
Subject: [PATCH 3/6] Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
Cargo.toml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 4d72443..a7ae5fb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,16 +22,16 @@ license.workspace = true
repository.workspace = true
[dependencies]
-# === 核心组件 ===
-# 过程宏定义
+# === Core Components ===
+# Procedural macro definitions
axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0" }
-# === 第三方库 ===
-# 地址空间抽象
+# === Third-party Libraries ===
+# Address space abstraction
axaddrspace = "0.1.0"
# Interface definition tools
crate_interface = "0.2"
-# 物理/虚拟地址类型
+# Physical/virtual address types
memory_addr = "0.4"
[package.metadata.docs.rs]
From 73ee99877e37f40dd1f601dbd2056d0b4a463fbf Mon Sep 17 00:00:00 2001
From: Su Mingxian
Date: Wed, 28 Jan 2026 17:18:07 +0800
Subject: [PATCH 4/6] Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
axvisor_api_proc/Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml
index 5ab1ff9..670dab4 100644
--- a/axvisor_api_proc/Cargo.toml
+++ b/axvisor_api_proc/Cargo.toml
@@ -17,7 +17,7 @@ proc-macro = true
[dependencies]
# 过程宏辅助工具
proc-macro2 = "1.0"
-# crate 名称查找
+# Crate name lookup
proc-macro-crate = "3.1.0"
# 代码生成
quote = "1.0"
From 27563e7ff079f7a54255e2fb0d690d74c0dcffe1 Mon Sep 17 00:00:00 2001
From: Su Mingxian
Date: Wed, 28 Jan 2026 17:18:15 +0800
Subject: [PATCH 5/6] Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
axvisor_api_proc/Cargo.toml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml
index 670dab4..2d05c2b 100644
--- a/axvisor_api_proc/Cargo.toml
+++ b/axvisor_api_proc/Cargo.toml
@@ -15,11 +15,11 @@ repository.workspace = true
proc-macro = true
[dependencies]
-# 过程宏辅助工具
+# Procedural macro helper utilities
proc-macro2 = "1.0"
-# Crate name lookup
+# Crate name resolution
proc-macro-crate = "3.1.0"
-# 代码生成
+# Code generation
quote = "1.0"
-# Rust 语法解析
+# Rust syntax parsing
syn = { version = "2.0", features = ["full"] }
From 65ae95fa61a727fd731ac51c8f2503a0dddb19a8 Mon Sep 17 00:00:00 2001
From: hky1999 <976929993@qq.com>
Date: Wed, 28 Jan 2026 22:18:37 +0800
Subject: [PATCH 6/6] [feat] modify github workflows
---
.github/workflows/check.yml | 45 +++++++
.github/workflows/ci.yml | 83 -------------
.github/workflows/deploy.yml | 68 +++++++++++
.github/workflows/release-check.yml | 72 ------------
.github/workflows/release.yml | 174 ++++++++++++++++++++++++++++
.github/workflows/test.yml | 23 ++++
axvisor_api_proc/Cargo.toml | 2 +-
7 files changed, 311 insertions(+), 156 deletions(-)
create mode 100644 .github/workflows/check.yml
delete mode 100644 .github/workflows/ci.yml
create mode 100644 .github/workflows/deploy.yml
delete mode 100644 .github/workflows/release-check.yml
create mode 100644 .github/workflows/release.yml
create mode 100644 .github/workflows/test.yml
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
new file mode 100644
index 0000000..a6ee3b0
--- /dev/null
+++ b/.github/workflows/check.yml
@@ -0,0 +1,45 @@
+name: Quality Checks
+
+on:
+ workflow_call:
+
+jobs:
+ check:
+ name: Quality Checks
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ target:
+ - x86_64-unknown-linux-gnu
+ - x86_64-unknown-none
+ - riscv64gc-unknown-none-elf
+ - aarch64-unknown-none-softfloat
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install Rust toolchain
+ uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2025-05-20
+ components: rust-src, clippy, rustfmt
+ targets: ${{ matrix.target }}
+
+ - name: Check rust version
+ run: rustc --version --verbose
+
+ - name: Check code format
+ run: cargo fmt --all -- --check
+
+ - name: Build
+ run: cargo build --target ${{ matrix.target }} --all-features
+
+ - name: Run clippy
+ run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings -A clippy::new_without_default
+
+ - name: Build documentation
+ env:
+ RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
+ run: cargo doc --no-deps --target ${{ matrix.target }} --all-features
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 37dcbf7..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,83 +0,0 @@
-name: CI
-
-on:
- push:
- branches: [main, dev, master]
- pull_request:
- branches: [main, dev, master]
-
-env:
- CARGO_TERM_COLOR: always
- RUST_BACKTRACE: 1
-
-jobs:
- # Quick checks: format and clippy
- quick-check:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@nightly
- with:
- toolchain: nightly-2025-05-20
- components: rustfmt, clippy
- - name: Check rust version
- run: rustc --version --verbose
- - name: Check code format
- run: cargo fmt --all -- --check
- - name: Clippy (x86_64-unknown-linux-gnu)
- run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default
-
- # Build and test for multiple targets
- build-and-test:
- runs-on: ubuntu-latest
- needs: quick-check
- strategy:
- fail-fast: false
- matrix:
- rust-toolchain: [nightly-2025-05-20, nightly]
- targets:
- - x86_64-unknown-linux-gnu
- - x86_64-unknown-none
- - riscv64gc-unknown-none-elf
- - aarch64-unknown-none-softfloat
- steps:
- - uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@nightly
- with:
- toolchain: ${{ matrix.rust-toolchain }}
- components: rust-src, clippy
- targets: ${{ matrix.targets }}
- - name: Check rust version
- run: rustc --version --verbose
- - name: Clippy
- run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
- - name: Build
- run: cargo build --target ${{ matrix.targets }} --all-features
- - name: Unit test
- if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
- run: cargo test --target ${{ matrix.targets }} -- --nocapture
-
- # Documentation build and deploy
- doc:
- runs-on: ubuntu-latest
- needs: quick-check
- permissions:
- contents: write
- env:
- RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs"
- steps:
- - uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@nightly
- with:
- toolchain: nightly-2025-05-20
- - name: Build docs
- run: |
- cargo doc --no-deps --all-features
- printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
- - name: Deploy to Github Pages
- if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
- uses: JamesIves/github-pages-deploy-action@v4
- with:
- single-commit: true
- branch: gh-pages
- folder: target/doc
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..d438641
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,68 @@
+name: Deploy
+
+on:
+ push:
+ branches:
+ - '**'
+ tags-ignore:
+ - 'v*'
+ - 'v*-pre.*'
+ pull_request:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: 'pages'
+ cancel-in-progress: false
+
+env:
+ CARGO_TERM_COLOR: always
+ RUST_BACKTRACE: 1
+
+jobs:
+ quality-check:
+ uses: ./.github/workflows/check.yml
+
+ test:
+ uses: ./.github/workflows/test.yml
+
+ build-doc:
+ name: Build documentation
+ runs-on: ubuntu-latest
+ needs: quality-check
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install Rust toolchain
+ uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2025-05-20
+
+ - name: Build docs
+ env:
+ RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
+ run: |
+ cargo doc --no-deps --all-features
+ printf '' > target/doc/index.html
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: target/doc
+
+ deploy-doc:
+ name: Deploy to GitHub Pages
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ needs: build-doc
+ if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml
deleted file mode 100644
index 8d406e2..0000000
--- a/.github/workflows/release-check.yml
+++ /dev/null
@@ -1,72 +0,0 @@
-name: Release Check
-
-on:
- push:
- tags:
- - "v*"
-
-env:
- CARGO_TERM_COLOR: always
- RUST_BACKTRACE: 1
- RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs"
-
-jobs:
- release-check:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@nightly
- with:
- toolchain: nightly-2025-05-20
- components: rust-src, clippy, rustfmt
- targets: x86_64-unknown-linux-gnu,x86_64-unknown-none,riscv64gc-unknown-none-elf,aarch64-unknown-none-softfloat
-
- - name: Check rust version
- run: rustc --version --verbose
-
- # Verify version consistency between git tag and Cargo.toml
- - name: Verify version consistency
- run: |
- TAG_VERSION=${GITHUB_REF#refs/tags/v}
- CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version')
- if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
- echo "Error: Git tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)"
- exit 1
- fi
- echo "Version check passed: $TAG_VERSION"
-
- # Format check
- - name: Check code format
- run: cargo fmt --all -- --check
-
- # Clippy check
- - name: Clippy
- run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default
-
- # Build for all supported targets
- - name: Build (x86_64-unknown-linux-gnu)
- run: cargo build --target x86_64-unknown-linux-gnu --all-features
- - name: Build (x86_64-unknown-none)
- run: cargo build --target x86_64-unknown-none --all-features
- - name: Build (riscv64gc-unknown-none-elf)
- run: cargo build --target riscv64gc-unknown-none-elf --all-features
- - name: Build (aarch64-unknown-none-softfloat)
- run: cargo build --target aarch64-unknown-none-softfloat --all-features
-
- # Run tests
- - name: Unit test
- run: cargo test --all-features -- --nocapture
-
- # Doc test
- - name: Doc test
- run: cargo test --doc --all-features
-
- # Build documentation
- - name: Build docs
- run: cargo doc --no-deps --all-features
-
- # Dry run publish check
- - name: Publish check (axvisor_api_proc)
- run: cargo publish --dry-run -p axvisor_api_proc
- - name: Publish check (axvisor_api)
- run: cargo publish --dry-run -p axvisor_api
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..c92d84f
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,174 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*.*.*'
+ - 'v*.*.*-pre.*'
+
+permissions:
+ contents: write
+
+env:
+ CARGO_TERM_COLOR: always
+ RUST_BACKTRACE: 1
+ RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
+
+jobs:
+ check:
+ uses: ./.github/workflows/check.yml
+
+ test:
+ uses: ./.github/workflows/test.yml
+ needs: check
+
+ create-release:
+ name: Create GitHub Release
+ runs-on: ubuntu-latest
+ needs: check
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Validate tag and branch (HEAD-based)
+ shell: bash
+ run: |
+ set -e
+
+ TAG="${{ github.ref_name }}"
+ TAG_COMMIT=$(git rev-list -n 1 "$TAG")
+
+ git fetch origin main dev
+
+ MAIN_HEAD=$(git rev-parse origin/main)
+ DEV_HEAD=$(git rev-parse origin/dev)
+
+ echo "Tag: $TAG"
+ echo "Tag commit: $TAG_COMMIT"
+ echo "main HEAD: $MAIN_HEAD"
+ echo "dev HEAD: $DEV_HEAD"
+
+ if [[ "$TAG" == *-pre.* ]]; then
+ if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then
+ echo "❌ prerelease tag must be created from dev HEAD"
+ exit 1
+ fi
+ echo "✅ prerelease tag validated on dev"
+ else
+ if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then
+ echo "❌ stable release tag must be created from main HEAD"
+ exit 1
+ fi
+ echo "✅ stable release tag validated on main"
+ fi
+
+ - name: Verify version consistency
+ run: |
+ # Extract version from git tag (remove 'v' prefix)
+ TAG_VERSION="${{ github.ref_name }}"
+ TAG_VERSION="${TAG_VERSION#v}"
+ # Extract version from Cargo.toml using cargo metadata
+ CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version')
+ echo "Git tag version: $TAG_VERSION"
+ echo "Cargo.toml version: $CARGO_VERSION"
+ if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
+ echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)"
+ exit 1
+ fi
+ echo "Version check passed!"
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ draft: false
+ prerelease: ${{ contains(github.ref_name, '-pre.') }}
+ body: |
+ ## ${{ github.ref_name }}
+
+ - [Documentation](https://docs.rs/axvisor_api)
+ - [crates.io](https://crates.io/crates/axvisor_api)
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ publish-crates:
+ name: Publish to crates.io
+ runs-on: ubuntu-latest
+ needs: check
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Validate tag and branch (HEAD-based)
+ shell: bash
+ run: |
+ set -e
+
+ TAG="${{ github.ref_name }}"
+ TAG_COMMIT=$(git rev-list -n 1 "$TAG")
+
+ git fetch origin main dev
+
+ MAIN_HEAD=$(git rev-parse origin/main)
+ DEV_HEAD=$(git rev-parse origin/dev)
+
+ echo "Tag: $TAG"
+ echo "Tag commit: $TAG_COMMIT"
+ echo "main HEAD: $MAIN_HEAD"
+ echo "dev HEAD: $DEV_HEAD"
+
+ if [[ "$TAG" == *-pre.* ]]; then
+ if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then
+ echo "❌ prerelease tag must be created from dev HEAD"
+ exit 1
+ fi
+ echo "✅ prerelease tag validated on dev"
+ else
+ if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then
+ echo "❌ stable release tag must be created from main HEAD"
+ exit 1
+ fi
+ echo "✅ stable release tag validated on main"
+ fi
+
+ - name: Verify version consistency
+ run: |
+ # Extract version from git tag (remove 'v' prefix)
+ TAG_VERSION="${{ github.ref_name }}"
+ TAG_VERSION="${TAG_VERSION#v}"
+ # Extract version from Cargo.toml using cargo metadata
+ CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version')
+ echo "Git tag version: $TAG_VERSION"
+ echo "Cargo.toml version: $CARGO_VERSION"
+ if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
+ echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)"
+ exit 1
+ fi
+ echo "Version check passed!"
+
+ - name: Install Rust toolchain
+ uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2025-05-20
+
+ # Workspace crates must be published in dependency order
+ - name: Dry run publish (axvisor_api_proc)
+ run: cargo publish --dry-run -p axvisor_api_proc
+
+ - name: Dry run publish (axvisor_api)
+ run: cargo publish --dry-run -p axvisor_api
+
+ - name: Publish axvisor_api_proc to crates.io
+ run: cargo publish -p axvisor_api_proc --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
+
+ # Wait for crates.io to index the proc-macro crate
+ - name: Wait for crates.io indexing
+ run: sleep 30
+
+ - name: Publish axvisor_api to crates.io
+ run: cargo publish -p axvisor_api --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..d8abfaf
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,23 @@
+name: Test
+
+on:
+ workflow_call:
+
+jobs:
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install Rust toolchain
+ uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2025-05-20
+
+ - name: Run tests
+ run: cargo test --all-features -- --nocapture
+
+ - name: Run doc tests
+ run: cargo test --doc --all-features
diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml
index 5ab1ff9..e84f3d9 100644
--- a/axvisor_api_proc/Cargo.toml
+++ b/axvisor_api_proc/Cargo.toml
@@ -22,4 +22,4 @@ proc-macro-crate = "3.1.0"
# 代码生成
quote = "1.0"
# Rust 语法解析
-syn = { version = "2.0", features = ["full"] }
+syn = { version = "2.0", features = ["full"] }
\ No newline at end of file