From c1d3c2dd7dce1275fab90b61472ec8dc9a0ece86 Mon Sep 17 00:00:00 2001 From: YanLien Date: Thu, 19 Mar 2026 14:10:46 +0800 Subject: [PATCH 1/2] [Refactor]: Migrate CI workflows to shared axci and update project docs --- .github/config.json | 7 ++ .github/workflows/check.yml | 67 ++--------- .github/workflows/deploy.yml | 124 ++------------------- .github/workflows/push.yml | 145 ++---------------------- .github/workflows/release.yml | 159 +++----------------------- .github/workflows/test.yml | 43 +------ .gitignore | 8 ++ README.md | 202 ++++++++++++++++++++++----------- README_CN.md | 203 +++++++++++++++++++++++----------- rust-toolchain.toml | 10 ++ scripts/check.sh | 35 ++++++ scripts/test.sh | 34 ++++++ 12 files changed, 411 insertions(+), 626 deletions(-) create mode 100644 rust-toolchain.toml create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh diff --git a/.github/config.json b/.github/config.json index f347e10..de46bed 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1,10 +1,17 @@ { + "component": { + "name": "axdevice", + "crate_name": "axdevice" + }, "targets": [ "aarch64-unknown-none-softfloat", "x86_64-unknown-linux-gnu", "x86_64-unknown-none", "riscv64gc-unknown-none-elf" ], + "unit_test_targets": [ + "x86_64-unknown-linux-gnu" + ], "rust_components": [ "rust-src", "clippy", diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 330fa15..018bbbe 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,66 +1,15 @@ -name: Quality Checks +# Quality Check Workflow +# References shared workflow from axci + +name: Check on: push: - branches: - - '**' - tags-ignore: - - '**' + branches: ['**'] + tags-ignore: ['**'] pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - 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 - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fda9a32..37b00c0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,3 +1,6 @@ +# Deploy Workflow +# References shared workflow from axci + name: Deploy on: @@ -5,122 +8,9 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+' -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - 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!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - test: - uses: ./.github/workflows/test.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: [verify-tag, check, test] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - # Build documentation - cargo doc --no-deps --all-features - - # Auto-detect documentation directory - # Check if doc exists in target/doc or target/*/doc - if [ -d "target/doc" ]; then - DOC_DIR="target/doc" - else - # Find doc directory under target/*/doc pattern - DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) - if [ -z "$DOC_DIR" ]; then - echo "Error: Could not find documentation directory" - exit 1 - fi - fi - - echo "Documentation found in: $DOC_DIR" - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" - echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ${{ env.DOC_DIR }} - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main + with: + verify_branch: true + verify_version: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a18de4a..3ff391a 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,147 +1,16 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - name: Notify Parent Repository -# 当有新的推送时触发 on: push: branches: - main - - master + - zcs workflow_dispatch: jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY + notify-parent: + name: Notify Parent Repository + # 调用 axci 仓库的可复用工作流 + uses: arceos-hypervisor/axci/.github/workflows/push.yml@main + secrets: + PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b4..20f1863 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,7 @@ +# Release Workflow +# References shared workflow from axci +# check + test must pass before release + name: Release on: @@ -6,155 +10,18 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' -permissions: - contents: write - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - 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!" - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main test: - uses: ./.github/workflows/test.yml - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + needs: [check, test] + uses: arceos-hypervisor/axci/.github/workflows/release.yml@main + with: + verify_branch: true + verify_version: true + secrets: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293..6a58ef8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,6 @@ +# Integration Test Workflow +# References shared workflow from axci + name: Test on: @@ -7,44 +10,8 @@ on: tags-ignore: - '**' pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - test: - name: Test - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - # - name: Run tests - # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - # - name: Run doc tests - # run: cargo test --target ${{ matrix.target }} --doc - - name: Run tests - run: echo "Tests are skipped!" + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main diff --git a/.gitignore b/.gitignore index 55c0d45..39ee779 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,11 @@ rusty-tags.vi # We ignore Cargo.lock because `axvcpu` is just a library Cargo.lock + +# Test results (generated by shared test framework) +/test-results/ +/test_repos/ +*.log + +# Downloaded test framework +/scripts/.axci/ diff --git a/README.md b/README.md index 12ebcf7..6a3ef63 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,154 @@ -# axdevice - -**axdevice** is a reusable, OS-agnostic device abstraction layer designed for virtual machines. It allows dynamic device configuration and MMIO emulation in `no_std` environments, making it suitable for hypervisors or operating systems targeting RISC-V or AArch64. - -## ✨ Highlights - -- 📦 **Componentized**: Designed as a modular crate to be integrated into any OS or hypervisor. -- 🧩 **Flexible device abstraction**: Supports dynamic device registration and MMIO handling. -- 🛠️ **No `std` required**: Uses `alloc` and `core` only, suitable for bare-metal development. -- 🧵 **Thread-safe**: Devices are stored using `Arc`, ready for multicore use. -- 🧱 **Easily extensible**: Just plug in device types via `axdevice_base::BaseDeviceOps`. - -## 📦 Structure - -- `config.rs`: Defines `AxVmDeviceConfig`, a wrapper for device configuration input. -- `device.rs`: Defines `AxVmDevices`, manages and dispatches MMIO to registered devices. - -## 📐 Dependency Graph - -```text - +-------------------+ - | axvmconfig | <- defines EmulatedDeviceConfig - +-------------------+ - | - v -+------------------+ uses +-----------------------+ -| axdevice +-------------->+ axdevice_base::trait | -| (this crate) | +-----------------------+ -+------------------+ ^ - | | - v | -+------------------+ | -| axaddrspace | -- GuestPhysAddr ----+ -+------------------+ +

axdevice

+ +

OS-Agnostic Virtual Device Abstraction Layer

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/axdevice.svg)](https://crates.io/crates/axdevice) +[![Docs.rs](https://docs.rs/axdevice/badge.svg)](https://docs.rs/axdevice) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/axdevice/blob/main/LICENSE) + +
+ +English | [中文](README_CN.md) + +# Introduction + +`axdevice` is a reusable, OS-agnostic device abstraction layer for virtual machines. It provides unified management for emulated devices and dispatches guest accesses to MMIO, system-register, and port-based devices in `#![no_std]` environments. + +This crate currently exports two core types: + +- **`AxVmDeviceConfig`** - Wraps a list of `EmulatedDeviceConfig` items used to initialize VM devices +- **`AxVmDevices`** - Manages device collections, dispatches device access requests, and allocates IVC channels + +The crate is suitable for hypervisors and low-level OS components targeting AArch64 or RISC-V. + +## Quick Start + +### Requirements + +- Rust nightly toolchain +- Rust components: rust-src, clippy, rustfmt + +```bash +# Install rustup (if not installed) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# Install nightly toolchain and components +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly ``` -## 🔁 Usage Flow - -```text -[1] Load VM device config (Vec) - ↓ -[2] Create AxVmDeviceConfig - ↓ -[3] Pass into AxVmDevices::new() - ↓ -[4] MMIO access triggers handle_mmio_{read,write} - ↓ -[5] Device selected by GuestPhysAddr - ↓ -[6] Forwarded to BaseDeviceOps::handle_{read,write}() +### Run Check and Test + +```bash +# 1. Enter the repository +cd axdevice + +# 2. Code check (format + clippy + build) +./scripts/check.sh + +# 3. Run tests +./scripts/test.sh ``` -## 🚀 Example +## Integration + +### Installation + +Add to your `Cargo.toml`: + +```toml +[dependencies] +axdevice = "0.2.2" +``` + +### Example ```rust +use std::sync::{Arc, Mutex}; + +use axaddrspace::device::AccessWidth; +use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange}; use axdevice::{AxVmDeviceConfig, AxVmDevices}; +use axdevice_base::BaseDeviceOps; +use axerrno::AxResult; +use axvmconfig::EmulatedDeviceType; + +struct MockMmioDevice { + range: GuestPhysAddrRange, + last_write: Mutex>, +} + +impl MockMmioDevice { + fn new(base: usize, size: usize) -> Self { + Self { + range: GuestPhysAddrRange::new( + GuestPhysAddr::from(base), + GuestPhysAddr::from(base + size), + ), + last_write: Mutex::new(None), + } + } +} + +impl BaseDeviceOps for MockMmioDevice { + fn address_range(&self) -> GuestPhysAddrRange { + self.range + } + + fn emu_type(&self) -> EmulatedDeviceType { + EmulatedDeviceType::IVCChannel + } + + fn handle_read(&self, _addr: GuestPhysAddr, _width: AccessWidth) -> AxResult { + Ok(0xDEAD_BEEF) + } + + fn handle_write(&self, addr: GuestPhysAddr, _width: AccessWidth, val: usize) -> AxResult { + let offset = addr.as_usize() - self.range.start.as_usize(); + assert_eq!(offset, 0x40); + *self.last_write.lock().unwrap() = Some(val); + Ok(()) + } +} + +fn main() { + let config = AxVmDeviceConfig::new(vec![]); + let mut devices = AxVmDevices::new(config); + + let mock = Arc::new(MockMmioDevice::new(0x1000_0000, 0x1000)); + devices.add_mmio_dev(mock.clone()); + + let width = AccessWidth::try_from(4).unwrap(); + let addr = GuestPhysAddr::from(0x1000_0040); + + devices.handle_mmio_write(addr, width, 0x1234_5678).unwrap(); + let value = devices.handle_mmio_read(addr, width).unwrap(); + + assert_eq!(value, 0xDEAD_BEEF); + assert_eq!(*mock.last_write.lock().unwrap(), Some(0x1234_5678)); +} +``` -// Step 1: Load configuration (e.g. from .toml or hypervisor setup) -let config = AxVmDeviceConfig::new(vec![/* EmulatedDeviceConfig */]); +### Documentation -// Step 2: Initialize devices -let devices = AxVmDevices::new(config); +Generate and view API documentation: -// Step 3: Emulate MMIO access -let _ = devices.handle_mmio_read(0x1000_0000, 4); -devices.handle_mmio_write(0x1000_0000, 4, 0xdead_beef); +```bash +cargo doc --no-deps --open ``` -## 📦 Dependencies +Online documentation: [docs.rs/axdevice](https://docs.rs/axdevice) + +# Contributing -- [`axvmconfig`](https://github.com/arceos-hypervisor/axvmconfig.git) -- [`axaddrspace`](https://github.com/arceos-hypervisor/axaddrspace.git) -- [`axdevice_base`](https://github.com/arceos-hypervisor/axdevice_crates.git) -- `log`, `alloc`, `cfg-if`, `axerrno` +1. Fork the repository and create a branch +2. Run local check: `./scripts/check.sh` +3. Run local tests: `./scripts/test.sh` +4. Submit PR and pass CI checks -## License +# License -Axdevice is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. +Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details. diff --git a/README_CN.md b/README_CN.md index df25881..9b804b1 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,81 +1,154 @@ -# axdevice - -**axdevice** 是一个可复用、与操作系统无关的设备抽象层,专为虚拟机设计,支持在 `no_std` 环境中进行设备配置与 MMIO 模拟。适用于开发 hypervisor 或嵌入式操作系统。 - -## ✨ 特性亮点 - -- 📦 **模块化设计**:适用于任意操作系统或虚拟化平台的组件库。 -- 🧩 **灵活设备抽象**:通过配置动态加载和注册设备。 -- 🛠️ **无标准库依赖**:适配裸机、EL2 等场景,仅依赖 `core` 与 `alloc`。 -- 🧵 **线程安全**:所有设备均用 `Arc` 管理,支持多核并发。 -- 🧱 **便于扩展**:接入自定义设备只需实现 `BaseDeviceOps` trait。 - -## 📦 模块结构 - -- `config.rs`: 定义 `AxVmDeviceConfig`,用于初始化设备配置。 -- `device.rs`: 定义 `AxVmDevices`,管理设备并处理 MMIO 读写。 - -## 📐 依赖图 - -```text - +-------------------+ - | axvmconfig | <- 提供 EmulatedDeviceConfig - +-------------------+ - | - v -+------------------+ uses +-----------------------+ -| axdevice +-------------->+ axdevice_base::trait | -| (当前模块) | +-----------------------+ -+------------------+ ^ - | | - v | -+------------------+ | -| axaddrspace | -- GuestPhysAddr ----+ -+------------------+ +

axdevice

+ +

面向虚拟机的操作系统无关设备抽象层

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/axdevice.svg)](https://crates.io/crates/axdevice) +[![Docs.rs](https://docs.rs/axdevice/badge.svg)](https://docs.rs/axdevice) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/axdevice/blob/main/LICENSE) + +
+ +[English](README.md) | 中文 + +# Introduction + +`axdevice` 是一个可复用、与操作系统无关的虚拟机设备抽象层。它在 `#![no_std]` 环境中为模拟设备提供统一管理能力,并将访存请求分发到 MMIO、系统寄存器和端口类设备。 + +该 crate 当前导出两个核心类型: + +- **`AxVmDeviceConfig`** - 封装用于初始化虚拟机设备的 `EmulatedDeviceConfig` 列表 +- **`AxVmDevices`** - 管理设备集合、分发设备访问请求,并提供 IVC 通道分配能力 + +该 crate 适用于面向 AArch64 或 RISC-V 的 hypervisor 及底层操作系统组件。 + +## Quick Start + +### Requirements + +- Rust nightly 工具链 +- Rust 组件:rust-src、clippy、rustfmt + +```bash +# 安装 rustup(如果尚未安装) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 安装 nightly 工具链与所需组件 +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly ``` -## 🔁 使用流程 - -```text -[1] 加载设备配置 Vec - ↓ -[2] 构造 AxVmDeviceConfig - ↓ -[3] AxVmDevices::new() 初始化所有设备 - ↓ -[4] guest发起 MMIO 访问 - ↓ -[5] 匹配设备地址范围 - ↓ -[6] 调用设备 trait 接口 handle_read / handle_write +### Run Check and Test + +```bash +# 1. 进入仓库目录 +cd axdevice + +# 2. 代码检查(格式化 + clippy + 构建) +./scripts/check.sh + +# 3. 运行测试 +./scripts/test.sh ``` -## 🚀 示例代码 +## Integration + +### Installation + +将以下依赖加入 `Cargo.toml`: + +```toml +[dependencies] +axdevice = "0.2.2" +``` + +### Example ```rust +use std::sync::{Arc, Mutex}; + +use axaddrspace::device::AccessWidth; +use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange}; use axdevice::{AxVmDeviceConfig, AxVmDevices}; +use axdevice_base::BaseDeviceOps; +use axerrno::AxResult; +use axvmconfig::EmulatedDeviceType; + +struct MockMmioDevice { + range: GuestPhysAddrRange, + last_write: Mutex>, +} + +impl MockMmioDevice { + fn new(base: usize, size: usize) -> Self { + Self { + range: GuestPhysAddrRange::new( + GuestPhysAddr::from(base), + GuestPhysAddr::from(base + size), + ), + last_write: Mutex::new(None), + } + } +} + +impl BaseDeviceOps for MockMmioDevice { + fn address_range(&self) -> GuestPhysAddrRange { + self.range + } + + fn emu_type(&self) -> EmulatedDeviceType { + EmulatedDeviceType::IVCChannel + } + + fn handle_read(&self, _addr: GuestPhysAddr, _width: AccessWidth) -> AxResult { + Ok(0xDEAD_BEEF) + } + + fn handle_write(&self, addr: GuestPhysAddr, _width: AccessWidth, val: usize) -> AxResult { + let offset = addr.as_usize() - self.range.start.as_usize(); + assert_eq!(offset, 0x40); + *self.last_write.lock().unwrap() = Some(val); + Ok(()) + } +} + +fn main() { + let config = AxVmDeviceConfig::new(vec![]); + let mut devices = AxVmDevices::new(config); + + let mock = Arc::new(MockMmioDevice::new(0x1000_0000, 0x1000)); + devices.add_mmio_dev(mock.clone()); + + let width = AccessWidth::try_from(4).unwrap(); + let addr = GuestPhysAddr::from(0x1000_0040); + + devices.handle_mmio_write(addr, width, 0x1234_5678).unwrap(); + let value = devices.handle_mmio_read(addr, width).unwrap(); + + assert_eq!(value, 0xDEAD_BEEF); + assert_eq!(*mock.last_write.lock().unwrap(), Some(0x1234_5678)); +} +``` -let config = AxVmDeviceConfig::new(vec![/* EmulatedDeviceConfig */]); +### Documentation -let devices = AxVmDevices::new(config); +生成并查看 API 文档: -let _ = devices.handle_mmio_read(0x1000_0000, 4); -devices.handle_mmio_write(0x1000_0000, 4, 0xdead_beef); +```bash +cargo doc --no-deps --open ``` -## 🔧 依赖组件 - -- [`axvmconfig`](https://github.com/arceos-hypervisor/axvmconfig.git) -- [`axaddrspace`](https://github.com/arceos-hypervisor/axaddrspace.git) -- [`axdevice_base`](https://github.com/arceos-hypervisor/axdevice_crates.git) +在线文档: [docs.rs/axdevice](https://docs.rs/axdevice) -其他依赖: +# Contributing -- `log` -- `alloc` -- `cfg-if` -- `axerrno` +1. Fork 仓库并创建分支 +2. 本地运行检查:`./scripts/check.sh` +3. 本地运行测试:`./scripts/test.sh` +4. 提交 PR 并通过 CI 检查 -## License +# License -Axdevice 采用 Apache License 2.0 开源协议。详见 [LICENSE](./LICENSE) 文件。 +本项目基于 Apache License 2.0 许可证发布。详见 [LICENSE](LICENSE)。 diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..8e03d91 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,10 @@ +[toolchain] +channel = "nightly-2025-05-20" +components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] +profile = "minimal" +targets = [ + "aarch64-unknown-none-softfloat", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-none", + "riscv64gc-unknown-none-elf" +] diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 0000000..8c95f07 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# aarch64_sysreg 代码检查脚本 +# 下载并调用 axci 仓库中的检查脚本 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPONENT_NAME="$(basename "$COMPONENT_DIR")" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行检查 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..751cb0e --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# aarch64_sysreg 测试脚本 +# 下载并调用 axci 仓库中的测试框架 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行测试,自动指定当前组件 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" From 8e60d3f9afab9805099305304f48ef8689c89b81 Mon Sep 17 00:00:00 2001 From: YanLien Date: Thu, 19 Mar 2026 16:20:44 +0800 Subject: [PATCH 2/2] chore: update script comments to reference axdevice --- scripts/check.sh | 2 +- scripts/test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/check.sh b/scripts/check.sh index 8c95f07..2b4c579 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# aarch64_sysreg 代码检查脚本 +# axdevice 代码检查脚本 # 下载并调用 axci 仓库中的检查脚本 # diff --git a/scripts/test.sh b/scripts/test.sh index 751cb0e..824f06a 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# aarch64_sysreg 测试脚本 +# axdevice 测试脚本 # 下载并调用 axci 仓库中的测试框架 #