From 7362aa5b54a7ed6f81b6eb0332372fa2c9fc840f Mon Sep 17 00:00:00 2001 From: YanLien Date: Thu, 19 Mar 2026 13:56:46 +0800 Subject: [PATCH 1/2] refactor(ci): migrate workflows to axci shared framework and move tests to integration --- .github/config.json | 7 + .github/workflows/check.yml | 67 +----- .github/workflows/deploy.yml | 107 +--------- .github/workflows/push.yml | 145 +------------ .github/workflows/release.yml | 159 ++------------- .github/workflows/test.yml | 43 +--- .gitignore | 8 + README.md | 226 ++++++++++++--------- README_CN.md | 179 ++++++++++++++++ scripts/check.sh | 35 ++++ scripts/test.sh | 34 ++++ src/address_space/mod.rs | 325 ----------------------------- src/frame.rs | 89 -------- src/lib.rs | 3 - src/memory_accessor.rs | 264 ------------------------ src/npt/arch/x86_64.rs | 17 +- tests/address_space.rs | 337 +++++++++++++++++++++++++++++++ tests/frame.rs | 104 ++++++++++ tests/memory_accessor.rs | 269 ++++++++++++++++++++++++ {src => tests}/test_utils/mod.rs | 38 ++-- 20 files changed, 1174 insertions(+), 1282 deletions(-) create mode 100644 README_CN.md create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh create mode 100644 tests/address_space.rs create mode 100644 tests/frame.rs create mode 100644 tests/memory_accessor.rs rename {src => tests}/test_utils/mod.rs (85%) diff --git a/.github/config.json b/.github/config.json index f347e10..2a12971 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1,10 +1,17 @@ { + "component": { + "name": "axaddrspace", + "crate_name": "axaddrspace" + }, "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 882ba93..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,105 +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: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: target/doc - 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 ff78c42..22d7b02 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,11 @@ /.vscode .DS_Store 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 c626dd6..3fc8398 100644 --- a/README.md +++ b/README.md @@ -1,143 +1,181 @@ -# axaddrspace +

axaddrspace

-**ArceOS-Hypervisor guest VM address space management module** +

ArceOS-Hypervisor guest VM address space management module

-[![CI](https://github.com/arceos-hypervisor/axaddrspace/actions/workflows/ci.yml/badge.svg)](https://github.com/arceos-hypervisor/axaddrspace/actions/workflows/ci.yml) -[![Crates.io](https://img.shields.io/crates/v/axaddrspace)](https://crates.io/crates/axaddrspace) -[![License](https://img.shields.io/badge/license-Apache%202.0%20OR%20MIT-blue)](LICENSE) +
-## Overview +[![Crates.io](https://img.shields.io/crates/v/axaddrspace.svg)](https://crates.io/crates/axaddrspace) +[![Docs.rs](https://docs.rs/axaddrspace/badge.svg)](https://docs.rs/axaddrspace) +[![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/axaddrspace/blob/main/LICENSE) -`axaddrspace` is a core component of the [ArceOS-Hypervisor](https://github.com/arceos-hypervisor/) project that provides guest virtual machine address space management capabilities. The crate implements nested page tables and address translation for hypervisor environments, supporting multiple architectures including x86_64, AArch64, and RISC-V. +
-## Features +English | [中文](README_CN.md) -- **Multi-architecture support**: x86_64 (VMX EPT), AArch64 (Stage 2 page tables), and RISC-V nested page tables -- **Flexible memory mapping backends**: - - **Linear mapping**: For contiguous physical memory regions with known addresses - - **Allocation mapping**: Dynamic allocation with optional lazy loading support -- **Nested page fault handling**: Comprehensive page fault management for guest VMs -- **Hardware abstraction layer**: Clean interface for memory management operations -- **No-std compatible**: Designed for bare-metal hypervisor environments +# Introduction -## Architecture Support +`axaddrspace` is the guest address space management crate for the +[ArceOS-Hypervisor](https://github.com/arceos-hypervisor/) project. It provides +nested page table management, guest physical address translation, memory +mapping backends, and nested page fault handling for hypervisor environments. -### x86_64 -- VMX Extended Page Tables (EPT) -- Memory type configuration (WriteBack, Uncached, etc.) -- Execute permissions for user-mode addresses +This crate supports multiple architectures: -### AArch64 -- VMSAv8-64 Stage 2 translation tables -- Configurable MAIR_EL2 memory attributes -- EL2 privilege level support +- **x86_64** - VMX Extended Page Tables (EPT) +- **AArch64** - Stage-2 page tables +- **RISC-V** - Nested page tables based on the hypervisor extension -### RISC-V -- Nested page table implementation -- Hypervisor fence instructions (`hfence.vvma`) -- Sv39 metadata support +Key capabilities include: -## Core Components +- **`AddrSpace`** - address space creation, mapping, unmapping, and translation +- **`AxMmHal`** - hardware abstraction trait for frame allocation and address conversion +- **Linear mapping backend** - map known contiguous host physical memory ranges +- **Allocation mapping backend** - allocate frames eagerly or lazily on page faults +- **Guest memory helpers** - translate guest addresses to accessible host buffers -### Address Space Management -The `AddrSpace` struct provides: -- Virtual address range management -- Page table root address tracking -- Memory area organization -- Address translation services +Supports `#![no_std]` and is intended for bare-metal hypervisor and kernel use. -### Memory Mapping Backends -Two types of mapping backends are supported: +## Quick Start -1. **Linear Backend**: Direct mapping with constant offset between virtual and physical addresses -2. **Allocation Backend**: Dynamic memory allocation with optional population strategies +### Requirements -### Nested Page Tables -Architecture-specific nested page table implementations: -- **x86_64**: `ExtendedPageTable` with EPT entries -- **AArch64**: Stage 2 page tables with descriptor attributes -- **RISC-V**: Sv39-based nested page tables +- Rust nightly toolchain +- Rust components: `rust-src`, `clippy`, `rustfmt` -## Usage +```bash +# Install rustup (if not installed) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -Add this to your `Cargo.toml`: - -```toml -[dependencies] -axaddrspace = "0.1.0" +# Install nightly toolchain and components +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly ``` -### Basic Example +### Run Check and Test -```rust -use axaddrspace::{AddrSpace, MappingFlags, GuestPhysAddr}; -use page_table_multiarch::PagingHandler; +```bash +# 1. Clone the repository +git clone https://github.com/arceos-hypervisor/axaddrspace.git +cd axaddrspace + +# 2. Code check +./scripts/check.sh + +# 3. Run tests +./scripts/test.sh -// Create a new address space -let mut addr_space = AddrSpace::::new_empty( - GuestPhysAddr::from(0x1000_0000), - 0x1000_0000, // 256MB -)?; - -// Create a linear mapping -addr_space.map_linear( - GuestPhysAddr::from(0x1000_0000), // Guest virtual address - PhysAddr::from(0x8000_0000), // Host physical address - 0x10_0000, // 1MB - MappingFlags::READ | MappingFlags::WRITE, -)?; - -// Handle a nested page fault -let fault_handled = addr_space.handle_page_fault( - GuestPhysAddr::from(0x1000_1000), - MappingFlags::READ, -); +# 4. Run a specific integration test target directly +cargo test --test address_space ``` -### Hardware Abstraction Layer +The helper scripts download the shared `axci` test/check framework on first run. + +## Integration + +### Installation + +Add to your `Cargo.toml`: + +```toml +[dependencies] +axaddrspace = "0.3.0" +``` -Implement the `AxMmHal` trait for your platform: +### Example ```rust -use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; +use axaddrspace::{AddrSpace, AxMmHal, GuestPhysAddr, HostPhysAddr, HostVirtAddr, MappingFlags}; +use memory_addr::{PhysAddr, VirtAddr}; +use page_table_multiarch::PagingHandler; struct MyHal; impl AxMmHal for MyHal { fn alloc_frame() -> Option { - // Your frame allocation implementation + unimplemented!() } - fn dealloc_frame(paddr: HostPhysAddr) { - // Your frame deallocation implementation + fn dealloc_frame(_paddr: HostPhysAddr) { + unimplemented!() } - fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { - // Your physical to virtual address conversion + fn phys_to_virt(_paddr: HostPhysAddr) -> HostVirtAddr { + unimplemented!() } - fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { - // Your virtual to physical address conversion + fn virt_to_phys(_vaddr: HostVirtAddr) -> HostPhysAddr { + unimplemented!() } } + +impl PagingHandler for MyHal { + fn alloc_frame() -> Option { + ::alloc_frame() + } + + fn dealloc_frame(paddr: PhysAddr) { + ::dealloc_frame(paddr) + } + + fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { + ::phys_to_virt(paddr) + } +} + +fn example() -> axerrno::AxResult<()> { + let base = GuestPhysAddr::from_usize(0x1000_0000); + let mut addr_space = AddrSpace::::new_empty(4, base, 0x20_0000)?; + + addr_space.map_linear( + base, + PhysAddr::from_usize(0x8000_0000), + 0x10_0000, + MappingFlags::READ | MappingFlags::WRITE, + )?; + + addr_space.map_alloc( + base + 0x10_0000, + 0x2000, + MappingFlags::READ | MappingFlags::WRITE, + false, + )?; + + let fault_handled = addr_space.handle_page_fault( + base + 0x10_0000, + MappingFlags::READ, + ); + assert!(fault_handled); + + let host_paddr = addr_space.translate(base).unwrap(); + assert_eq!(host_paddr, PhysAddr::from_usize(0x8000_0000)); + + Ok(()) +} ``` -## Configuration +### Features + +- `arm-el2`: enable AArch64 EL2 support +- `default`: includes `arm-el2` -### Feature Flags +### Documentation -- `arm-el2`: Enable AArch64 EL2 support (default) -- `default`: Includes `arm-el2` feature +Generate and view API documentation: -## Contributing +```bash +cargo doc --no-deps --open +``` -Contributions are welcome! Please feel free to submit a Pull Request. +Online documentation: [docs.rs/axaddrspace](https://docs.rs/axaddrspace) -## Repository +# Contributing -- [GitHub Repository](https://github.com/arceos-hypervisor/axaddrspace) -- [ArceOS-Hypervisor Project](https://github.com/arceos-hypervisor/) +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 -Axaddrspace 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 new file mode 100644 index 0000000..91fcf28 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,179 @@ +

axaddrspace

+ +

ArceOS-Hypervisor 客户机虚拟机地址空间管理模块

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/axaddrspace.svg)](https://crates.io/crates/axaddrspace) +[![Docs.rs](https://docs.rs/axaddrspace/badge.svg)](https://docs.rs/axaddrspace) +[![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/axaddrspace/blob/main/LICENSE) + +
+ +[English](README.md) | 中文 + +# 简介 + +`axaddrspace` 是 [ArceOS-Hypervisor](https://github.com/arceos-hypervisor/) +项目中的客户机地址空间管理 crate,提供嵌套页表管理、客户机物理地址转换、内存映射后端以及嵌套页错误处理能力,面向 Hypervisor 场景使用。 + +该 crate 支持多种体系结构: + +- **x86_64** - VMX Extended Page Tables(EPT) +- **AArch64** - Stage-2 页表 +- **RISC-V** - 基于 Hypervisor 扩展的嵌套页表 + +核心能力包括: + +- **`AddrSpace`** - 地址空间创建、映射、解除映射与地址转换 +- **`AxMmHal`** - 用于页帧分配与地址转换的硬件抽象 trait +- **线性映射后端** - 映射已知的连续宿主物理内存区域 +- **分配映射后端** - 支持预分配或缺页时惰性分配页帧 +- **客户机内存辅助接口** - 将客户机地址转换为宿主可访问缓冲区 + +该库支持 `#![no_std]`,适用于裸机 Hypervisor 和内核环境。 + +## 快速开始 + +### 环境要求 + +- 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 +``` + +### 运行检查和测试 + +```bash +# 1. 克隆仓库 +git clone https://github.com/arceos-hypervisor/axaddrspace.git +cd axaddrspace + +# 2. 代码检查 +./scripts/check.sh + +# 3. 运行测试 +./scripts/test.sh + +# 4. 直接运行指定集成测试目标 +cargo test --test address_space +``` + +辅助脚本会在首次运行时自动下载共享的 `axci` 检查/测试框架。 + +## 集成方式 + +### 安装 + +在 `Cargo.toml` 中添加: + +```toml +[dependencies] +axaddrspace = "0.3.0" +``` + +### 示例 + +```rust +use axaddrspace::{AddrSpace, AxMmHal, GuestPhysAddr, HostPhysAddr, HostVirtAddr, MappingFlags}; +use memory_addr::{PhysAddr, VirtAddr}; +use page_table_multiarch::PagingHandler; + +struct MyHal; + +impl AxMmHal for MyHal { + fn alloc_frame() -> Option { + unimplemented!() + } + + fn dealloc_frame(_paddr: HostPhysAddr) { + unimplemented!() + } + + fn phys_to_virt(_paddr: HostPhysAddr) -> HostVirtAddr { + unimplemented!() + } + + fn virt_to_phys(_vaddr: HostVirtAddr) -> HostPhysAddr { + unimplemented!() + } +} + +impl PagingHandler for MyHal { + fn alloc_frame() -> Option { + ::alloc_frame() + } + + fn dealloc_frame(paddr: PhysAddr) { + ::dealloc_frame(paddr) + } + + fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { + ::phys_to_virt(paddr) + } +} + +fn example() -> axerrno::AxResult<()> { + let base = GuestPhysAddr::from_usize(0x1000_0000); + let mut addr_space = AddrSpace::::new_empty(4, base, 0x20_0000)?; + + addr_space.map_linear( + base, + PhysAddr::from_usize(0x8000_0000), + 0x10_0000, + MappingFlags::READ | MappingFlags::WRITE, + )?; + + addr_space.map_alloc( + base + 0x10_0000, + 0x2000, + MappingFlags::READ | MappingFlags::WRITE, + false, + )?; + + let fault_handled = addr_space.handle_page_fault( + base + 0x10_0000, + MappingFlags::READ, + ); + assert!(fault_handled); + + let host_paddr = addr_space.translate(base).unwrap(); + assert_eq!(host_paddr, PhysAddr::from_usize(0x8000_0000)); + + Ok(()) +} +``` + +### 特性 + +- `arm-el2`:启用 AArch64 EL2 支持 +- `default`:默认包含 `arm-el2` + +### 文档 + +生成并查看 API 文档: + +```bash +cargo doc --no-deps --open +``` + +在线文档:[docs.rs/axaddrspace](https://docs.rs/axaddrspace) + +# 贡献 + +1. Fork 仓库并创建分支 +2. 本地运行检查:`./scripts/check.sh` +3. 本地运行测试:`./scripts/test.sh` +4. 提交 PR 并通过 CI 检查 + +# 许可证 + +本项目采用 Apache License 2.0。详见 [LICENSE](LICENSE)。 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 "$@" diff --git a/src/address_space/mod.rs b/src/address_space/mod.rs index 8cf6ebd..a566481 100644 --- a/src/address_space/mod.rs +++ b/src/address_space/mod.rs @@ -275,328 +275,3 @@ impl Drop for AddrSpace { self.clear(); } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_utils::{ - ALLOC_COUNT, BASE_PADDR, DEALLOC_COUNT, MEMORY_LEN, MockHal, mock_hal_test, - test_dealloc_count, - }; - use axin::axin; - use core::sync::atomic::Ordering; - - /// Generate an address space for the test - fn setup_test_addr_space() -> (AddrSpace, GuestPhysAddr, usize) { - const BASE: GuestPhysAddr = GuestPhysAddr::from_usize(0x10000); - const SIZE: usize = 0x10000; - let addr_space = AddrSpace::::new_empty(4, BASE, SIZE).unwrap(); - (addr_space, BASE, SIZE) - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] - /// Check whether an address_space can be created correctly. - /// When creating a new address_space, a frame will be allocated for the page table, - /// thus triggering an alloc_frame operation. - fn test_addrspace_creation() { - let (addr_space, base, size) = setup_test_addr_space(); - assert_eq!(addr_space.base(), base); - assert_eq!(addr_space.size(), size); - assert_eq!(addr_space.end(), base + size); - assert_eq!(ALLOC_COUNT.load(Ordering::SeqCst), 1); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_contains_range() { - let (addr_space, base, size) = setup_test_addr_space(); - - // Within range - assert!(addr_space.contains_range(base, 0x1000)); - assert!(addr_space.contains_range(base + 0x1000, 0x2000)); - assert!(addr_space.contains_range(base, size)); - - // Out of range - assert!(!addr_space.contains_range(base - 0x1000, 0x1000)); - assert!(!addr_space.contains_range(base + size, 0x1000)); - assert!(!addr_space.contains_range(base, size + 0x1000)); - - // Partially out of range - assert!(!addr_space.contains_range(base + 0x3000, 0xf000)); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_map_linear() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x18000); - let paddr = PhysAddr::from_usize(0x10000); - let map_linear_size = 0x8000; // 32KB - let flags = MappingFlags::READ | MappingFlags::WRITE; - - addr_space - .map_linear(vaddr, paddr, map_linear_size, flags) - .unwrap(); - - assert_eq!(addr_space.translate(vaddr).unwrap(), paddr); - assert_eq!( - addr_space.translate(vaddr + 0x1000).unwrap(), - paddr + 0x1000 - ); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_map_alloc_populate() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x10000); - let map_alloc_size = 0x2000; // 8KB - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Frame count before allocation: 1 root page table - let initial_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - assert_eq!(initial_allocs, 1); - - // Allocate physical frames immediately - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify additional frames were allocated - let final_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - assert!(final_allocs > initial_allocs); - - // Verify mappings exist and addresses are valid - let paddr1 = addr_space.translate(vaddr).unwrap(); - let paddr2 = addr_space.translate(vaddr + 0x1000).unwrap(); - - // Verify physical addresses are within valid range - assert!(paddr1.as_usize() >= BASE_PADDR && paddr1.as_usize() < BASE_PADDR + MEMORY_LEN); - assert!(paddr2.as_usize() >= BASE_PADDR && paddr2.as_usize() < BASE_PADDR + MEMORY_LEN); - - // Verify two pages have different physical addresses - assert_ne!(paddr1, paddr2); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_map_alloc_lazy() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x13000); - let map_alloc_size = 0x1000; - let flags = MappingFlags::READ | MappingFlags::WRITE; - - let initial_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - - // Lazy allocation - don't allocate physical frames immediately - addr_space - .map_alloc(vaddr, map_alloc_size, flags, false) - .unwrap(); - - // Frame count should only increase for page table structure, not data pages - let after_map_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - assert!(after_map_allocs >= initial_allocs); // May have allocated intermediate page tables - assert!(addr_space.translate(vaddr).is_none()); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_page_fault_handling() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x14000); - let map_alloc_size = 0x1000; - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Create lazy allocation mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, false) - .unwrap(); - - let before_pf_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - - // Simulate page fault - let handled = addr_space.handle_page_fault(vaddr, MappingFlags::READ); - - // Page fault should be handled - assert!(handled); - - // Should have allocated physical frames - let after_pf_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - assert!(after_pf_allocs > before_pf_allocs); - - // Translation should succeed now - let paddr = addr_space.translate(vaddr); - assert!(paddr.is_some()); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_unmap() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x15000); - let map_alloc_size = 0x2000; - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Create mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify mapping exists - assert!(addr_space.translate(vaddr).is_some()); - assert!(addr_space.translate(vaddr + 0x1000).is_some()); - - let before_unmap_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); - - // Unmap - addr_space.unmap(vaddr, map_alloc_size).unwrap(); - - // Verify mapping is removed - assert!(addr_space.translate(vaddr).is_none()); - assert!(addr_space.translate(vaddr + 0x1000).is_none()); - - // Verify frames were deallocated - let after_unmap_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); - assert!(after_unmap_deallocs > before_unmap_deallocs); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_clear() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr1 = GuestPhysAddr::from_usize(0x16000); - let vaddr2 = GuestPhysAddr::from_usize(0x17000); - let flags = MappingFlags::READ | MappingFlags::WRITE; - let map_alloc_size = 0x1000; - - // Create multiple mappings - addr_space - .map_alloc(vaddr1, map_alloc_size, flags, true) - .unwrap(); - addr_space - .map_alloc(vaddr2, map_alloc_size, flags, true) - .unwrap(); - - // Verify mappings exist - assert!(addr_space.translate(vaddr1).is_some()); - assert!(addr_space.translate(vaddr2).is_some()); - - let before_clear_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); - - // Clear all mappings - addr_space.clear(); - - // Verify all mappings are removed - assert!(addr_space.translate(vaddr1).is_none()); - assert!(addr_space.translate(vaddr2).is_none()); - - // Verify frames were deallocated - let after_clear_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); - assert!(after_clear_deallocs > before_clear_deallocs); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_translate() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x18000); - let map_alloc_size = 0x1000; - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Create mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify translation succeeds - let paddr = addr_space.translate(vaddr).expect("Translation failed"); - assert!(paddr.as_usize() >= BASE_PADDR); - assert!(paddr.as_usize() < BASE_PADDR + MEMORY_LEN); - - // Verify unmapped address translation fails - let unmapped_vaddr = GuestPhysAddr::from_usize(0x19000); - assert!(addr_space.translate(unmapped_vaddr).is_none()); - - // Verify out-of-range address translation fails - let out_of_range = GuestPhysAddr::from_usize(0x30000); - assert!(addr_space.translate(out_of_range).is_none()); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_translated_byte_buffer() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x19000); - let map_alloc_size = 0x2000; // 8KB - let flags = MappingFlags::READ | MappingFlags::WRITE; - let buffer_size = 0x1100; - - // Create mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify byte buffer can be obtained - let mut buffer = addr_space - .translated_byte_buffer(vaddr, buffer_size) - .expect("Failed to get byte buffer"); - - // Verify data write and read - // Fill with values ranging from 0 to 0x100 - for buffer_segment in buffer.iter_mut() { - for (i, byte) in buffer_segment.iter_mut().enumerate() { - *byte = (i % 0x100) as u8; - } - } - - // Verify data read correctness - for buffer_segment in buffer.iter_mut() { - for (i, byte) in buffer_segment.iter_mut().enumerate() { - assert_eq!(*byte, (i % 0x100) as u8); - } - } - - // Verify exceeding area size returns None - assert!( - addr_space - .translated_byte_buffer(vaddr, map_alloc_size + 0x1000) - .is_none() - ); - - // Verify unmapped address returns None - let unmapped_vaddr = GuestPhysAddr::from_usize(0x1D000); - assert!( - addr_space - .translated_byte_buffer(unmapped_vaddr, 0x100) - .is_none() - ); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_translate_and_get_limit() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x1A000); - let map_alloc_size = 0x3000; // 12KB - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Create mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify translation and area size retrieval - let (paddr, area_size) = addr_space.translate_and_get_limit(vaddr).unwrap(); - assert!(paddr.as_usize() >= BASE_PADDR && paddr.as_usize() < BASE_PADDR + MEMORY_LEN); - assert_eq!(area_size, map_alloc_size); - - // Verify unmapped address returns None - let unmapped_vaddr = GuestPhysAddr::from_usize(0x1E000); - assert!(addr_space.translate_and_get_limit(unmapped_vaddr).is_none()); - - // Verify out-of-range address returns None - let out_of_range = GuestPhysAddr::from_usize(0x30000); - assert!(addr_space.translate_and_get_limit(out_of_range).is_none()); - } -} diff --git a/src/frame.rs b/src/frame.rs index 891f7fd..b130264 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -86,92 +86,3 @@ impl Drop for PhysFrame { } } } - -#[cfg(test)] -mod test { - use super::*; - use crate::test_utils::{BASE_PADDR, MockHal, mock_hal_test, test_dealloc_count}; - use alloc::vec::Vec; - use assert_matches::assert_matches; - use axin::axin; - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] - fn test_alloc_dealloc_cycle() { - let frame = PhysFrame::::alloc() - .unwrap_or_else(|e| panic!("Failed to allocate frame: {:?}", e)); - assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); - // frame is dropped here, dealloc_frame should be called - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] - fn test_alloc_zero() { - let frame = PhysFrame::::alloc_zero() - .unwrap_or_else(|e| panic!("Failed to allocate zero frame: {:?}", e)); - assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); - let ptr = frame.as_mut_ptr(); - let page = unsafe { &*(ptr as *const [u8; PAGE_SIZE]) }; - assert!(page.iter().all(|&x| x == 0)); - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] - fn test_fill_operation() { - let mut frame = PhysFrame::::alloc() - .unwrap_or_else(|e| panic!("Failed to allocate frame: {:?}", e)); - assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); - frame.fill(0xAA); - let ptr = frame.as_mut_ptr(); - let page = unsafe { &*(ptr as *const [u8; PAGE_SIZE]) }; - assert!(page.iter().all(|&x| x == 0xAA)); - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(5)))] - fn test_fill_multiple_frames() { - const NUM_FRAMES: usize = 5; - - let mut frames = Vec::new(); - let mut patterns = Vec::new(); - - for i in 0..NUM_FRAMES { - let mut frame = PhysFrame::::alloc().unwrap(); - let pattern = (0xA0 + i) as u8; - frame.fill(pattern); - frames.push(frame); - patterns.push(pattern); - } - - for i in 0..NUM_FRAMES { - let actual_page = unsafe { &*(frames[i].as_mut_ptr() as *mut [u8; PAGE_SIZE]) }; - let expected_page = &[patterns[i]; PAGE_SIZE]; - - assert_eq!( - actual_page, expected_page, - "Frame verification failed for frame index {i}: Expected pattern 0x{:02x}", - patterns[i] - ); - } - } - - #[test] - #[should_panic(expected = "uninitialized PhysFrame")] - fn test_uninit_access() { - // This test verifies that accessing an uninitialized PhysFrame (created with `unsafe { uninit() }`) - // leads to a panic when trying to retrieve its physical address. - let frame = unsafe { PhysFrame::::uninit() }; - frame.start_paddr(); // This should panic - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(0)))] - fn test_alloc_no_memory() { - // Configure MockHal to simulate an allocation failure. - MockHal::set_alloc_fail(true); - let result = PhysFrame::::alloc(); - // Assert that allocation failed and verify the specific error type. - assert_matches!(result, Err(axerrno::AxError::NoMemory)); - MockHal::set_alloc_fail(false); // Reset for other tests - } -} diff --git a/src/lib.rs b/src/lib.rs index e91f573..b4989cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,3 @@ fn mapping_err_to_ax_err(err: MappingError) -> AxError { MappingError::BadState => AxError::BadState, } } - -#[cfg(test)] -pub(crate) mod test_utils; diff --git a/src/memory_accessor.rs b/src/memory_accessor.rs index a0855a2..7da85b5 100644 --- a/src/memory_accessor.rs +++ b/src/memory_accessor.rs @@ -197,267 +197,3 @@ pub trait GuestMemoryAccessor { self.write_obj(guest_addr, val) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_utils::{BASE_PADDR, mock_hal_test}; - use axin::axin; - use memory_addr::PhysAddr; - - /// Mock implementation of GuestMemoryAccessor for testing - struct MockTranslator { - base_addr: PhysAddr, - memory_size: usize, - } - - impl MockTranslator { - pub fn new(base_addr: PhysAddr, memory_size: usize) -> Self { - Self { - base_addr, - memory_size, - } - } - } - - impl GuestMemoryAccessor for MockTranslator { - fn translate_and_get_limit(&self, guest_addr: GuestPhysAddr) -> Option<(PhysAddr, usize)> { - // Simple mapping: guest address directly maps to mock memory region - let offset = guest_addr.as_usize(); - if offset < self.memory_size { - // Convert physical address to virtual address for actual memory access - let phys_addr = - PhysAddr::from_usize(BASE_PADDR + self.base_addr.as_usize() + offset); - let virt_addr = crate::test_utils::MockHal::mock_phys_to_virt(phys_addr); - let accessible_size = self.memory_size - offset; - Some((PhysAddr::from_usize(virt_addr.as_usize()), accessible_size)) - } else { - None - } - } - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_basic_read_write_operations() { - let translator = - MockTranslator::new(PhysAddr::from_usize(0), crate::test_utils::MEMORY_LEN); - - // Test u32 read/write operations - let test_addr = GuestPhysAddr::from_usize(0x100); - let test_value: u32 = 0x12345678; - - // Write a u32 value - translator - .write_obj(test_addr, test_value) - .expect("Failed to write u32 value"); - - // Read back the u32 value - let read_value: u32 = translator - .read_obj(test_addr) - .expect("Failed to read u32 value"); - - assert_eq!( - read_value, test_value, - "Read value should match written value" - ); - - // Test buffer read/write operations - let buffer_addr = GuestPhysAddr::from_usize(0x200); - let test_buffer = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; - - // Write buffer - translator - .write_buffer(buffer_addr, &test_buffer) - .expect("Failed to write buffer"); - - // Read buffer back - let mut read_buffer = [0u8; 8]; - translator - .read_buffer(buffer_addr, &mut read_buffer) - .expect("Failed to read buffer"); - - assert_eq!( - read_buffer, test_buffer, - "Read buffer should match written buffer" - ); - - // Test error handling with invalid address - let invalid_addr = GuestPhysAddr::from_usize(crate::test_utils::MEMORY_LEN + 0x1000); - let result: AxResult = translator.read_obj(invalid_addr); - assert!(result.is_err(), "Reading from invalid address should fail"); - - let result = translator.write_obj(invalid_addr, 42u32); - assert!(result.is_err(), "Writing to invalid address should fail"); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_two_vm_isolation() { - // Create two different translators to simulate two different VMs - let vm1_translator = - MockTranslator::new(PhysAddr::from_usize(0), crate::test_utils::MEMORY_LEN / 2); // Offset for VM1 - let vm2_translator = MockTranslator::new( - PhysAddr::from_usize(crate::test_utils::MEMORY_LEN / 2), - crate::test_utils::MEMORY_LEN, - ); // Offset for VM2 - - // Both VMs write to the same guest address but different host memory regions - let guest_addr = GuestPhysAddr::from_usize(0x100); - let vm1_data: u64 = 0xDEADBEEFCAFEBABE; - let vm2_data: u64 = 0x1234567890ABCDEF; - - // VM1 writes its data - vm1_translator - .write_obj(guest_addr, vm1_data) - .expect("VM1 failed to write data"); - - // VM2 writes its data - vm2_translator - .write_obj(guest_addr, vm2_data) - .expect("VM2 failed to write data"); - - // Both VMs read back their own data - should be isolated - let vm1_read: u64 = vm1_translator - .read_obj(guest_addr) - .expect("VM1 failed to read data"); - let vm2_read: u64 = vm2_translator - .read_obj(guest_addr) - .expect("VM2 failed to read data"); - - // Verify isolation: each VM should read its own data - assert_eq!(vm1_read, vm1_data, "VM1 should read its own data"); - assert_eq!(vm2_read, vm2_data, "VM2 should read its own data"); - assert_ne!( - vm1_read, vm2_read, - "VM1 and VM2 should have different data (isolation)" - ); - - // Test buffer operations with different patterns - let buffer_addr = GuestPhysAddr::from_usize(0x200); - let vm1_buffer = [0xAA; 16]; // Pattern for VM1 - let vm2_buffer = [0x55; 16]; // Pattern for VM2 - - // Both VMs write their patterns - vm1_translator - .write_buffer(buffer_addr, &vm1_buffer) - .expect("VM1 failed to write buffer"); - vm2_translator - .write_buffer(buffer_addr, &vm2_buffer) - .expect("VM2 failed to write buffer"); - - // Read back and verify isolation - let mut vm1_read_buffer = [0u8; 16]; - let mut vm2_read_buffer = [0u8; 16]; - - vm1_translator - .read_buffer(buffer_addr, &mut vm1_read_buffer) - .expect("VM1 failed to read buffer"); - vm2_translator - .read_buffer(buffer_addr, &mut vm2_read_buffer) - .expect("VM2 failed to read buffer"); - - assert_eq!( - vm1_read_buffer, vm1_buffer, - "VM1 should read its own buffer pattern" - ); - assert_eq!( - vm2_read_buffer, vm2_buffer, - "VM2 should read its own buffer pattern" - ); - assert_ne!( - vm1_read_buffer, vm2_read_buffer, - "VM buffers should be isolated" - ); - - // Test that VM1 cannot access VM2's address space (beyond its limit) - let vm2_only_addr = GuestPhysAddr::from_usize(crate::test_utils::MEMORY_LEN / 2 + 0x100); - let result: AxResult = vm1_translator.read_obj(vm2_only_addr); - assert!( - result.is_err(), - "VM1 should not be able to access VM2's exclusive address space" - ); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_cross_page_access() { - let translator = - MockTranslator::new(PhysAddr::from_usize(0), crate::test_utils::MEMORY_LEN); - - // Test cross-region buffer operations - // Place buffer near a region boundary to test multi-region access - let cross_region_addr = GuestPhysAddr::from_usize(4096 - 8); // 8 bytes before 4K boundary - let test_data = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, - ]; // 16 bytes - - // Write cross-region data - translator - .write_buffer(cross_region_addr, &test_data) - .expect("Failed to write cross-region buffer"); - - // Read cross-region data back - let mut read_data = [0u8; 16]; - translator - .read_buffer(cross_region_addr, &mut read_data) - .expect("Failed to read cross-region buffer"); - - assert_eq!( - read_data, test_data, - "Cross-region read should match written data" - ); - - // Test individual byte access across region boundary - for (i, &expected_byte) in test_data.iter().enumerate() { - let byte_addr = GuestPhysAddr::from_usize(cross_region_addr.as_usize() + i); - let read_byte: u8 = translator - .read_obj(byte_addr) - .expect("Failed to read individual byte"); - assert_eq!( - read_byte, expected_byte, - "Byte at offset {} should match", - i - ); - } - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_region_boundary_edge_cases() { - let translator = - MockTranslator::new(PhysAddr::from_usize(0), crate::test_utils::MEMORY_LEN); - - let boundary_addr = GuestPhysAddr::from_usize(4096); - let boundary_data = [0xAB, 0xCD, 0xEF, 0x12]; - - translator - .write_buffer(boundary_addr, &boundary_data) - .expect("Failed to write at boundary"); - - let mut read_boundary = [0u8; 4]; - translator - .read_buffer(boundary_addr, &mut read_boundary) - .expect("Failed to read at boundary"); - - assert_eq!(read_boundary, boundary_data, "Boundary data should match"); - - // Test zero-size buffer (should not fail) - let empty_buffer: &[u8] = &[]; - translator - .write_buffer(boundary_addr, empty_buffer) - .expect("Empty buffer write should succeed"); - - let mut empty_read: &mut [u8] = &mut []; - translator - .read_buffer(boundary_addr, &mut empty_read) - .expect("Empty buffer read should succeed"); - - // Test single byte at boundary (should work fine) - let single_byte = [0x42]; - translator - .write_buffer(boundary_addr, &single_byte) - .expect("Single byte write should succeed"); - } -} diff --git a/src/npt/arch/x86_64.rs b/src/npt/arch/x86_64.rs index 65b6261..82a952c 100644 --- a/src/npt/arch/x86_64.rs +++ b/src/npt/arch/x86_64.rs @@ -188,15 +188,18 @@ impl PagingMetaData for ExtendedPageTableMetadata { type VirtAddr = GuestPhysAddr; - // Under the x86 architecture, the flush_tlb operation will invoke the ring0 instruction, - // causing the test to trigger a SIGSEGV exception. + // Under the x86 architecture, flushing the TLB requires privileged + // instructions. Hosted binaries such as integration tests run in ring 3, + // so issue TLB invalidations only for bare-metal targets. #[allow(unused_variables)] fn flush_tlb(vaddr: Option) { - #[cfg(not(test))] - if let Some(vaddr) = vaddr { - unsafe { x86::tlb::flush(vaddr.into()) } - } else { - unsafe { x86::tlb::flush_all() } + #[cfg(target_os = "none")] + { + if let Some(vaddr) = vaddr { + unsafe { x86::tlb::flush(vaddr.into()) } + } else { + unsafe { x86::tlb::flush_all() } + } } } } diff --git a/tests/address_space.rs b/tests/address_space.rs new file mode 100644 index 0000000..e78285c --- /dev/null +++ b/tests/address_space.rs @@ -0,0 +1,337 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod test_utils; + +use axaddrspace::{AddrSpace, GuestPhysAddr, MappingFlags}; +use axin::axin; +use core::sync::atomic::Ordering; +use memory_addr::PhysAddr; +use test_utils::{ + ALLOC_COUNT, BASE_PADDR, DEALLOC_COUNT, MEMORY_LEN, MockHal, mock_hal_test, test_dealloc_count, +}; + +/// Generate an address space for the test +fn setup_test_addr_space() -> (AddrSpace, GuestPhysAddr, usize) { + const BASE: GuestPhysAddr = GuestPhysAddr::from_usize(0x10000); + const SIZE: usize = 0x10000; + let addr_space = AddrSpace::::new_empty(4, BASE, SIZE).unwrap(); + (addr_space, BASE, SIZE) +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] +/// Check whether an address_space can be created correctly. +/// When creating a new address_space, a frame will be allocated for the page table, +/// thus triggering an alloc_frame operation. +fn test_addrspace_creation() { + let (addr_space, base, size) = setup_test_addr_space(); + assert_eq!(addr_space.base(), base); + assert_eq!(addr_space.size(), size); + assert_eq!(addr_space.end(), base + size); + assert_eq!(ALLOC_COUNT.load(Ordering::SeqCst), 1); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_contains_range() { + let (addr_space, base, size) = setup_test_addr_space(); + + // Within range + assert!(addr_space.contains_range(base, 0x1000)); + assert!(addr_space.contains_range(base + 0x1000, 0x2000)); + assert!(addr_space.contains_range(base, size)); + + // Out of range + assert!(!addr_space.contains_range(base - 0x1000, 0x1000)); + assert!(!addr_space.contains_range(base + size, 0x1000)); + assert!(!addr_space.contains_range(base, size + 0x1000)); + + // Partially out of range + assert!(!addr_space.contains_range(base + 0x3000, 0xf000)); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_map_linear() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x18000); + let paddr = PhysAddr::from_usize(0x10000); + let map_linear_size = 0x8000; // 32KB + let flags = MappingFlags::READ | MappingFlags::WRITE; + + addr_space + .map_linear(vaddr, paddr, map_linear_size, flags) + .unwrap(); + + assert_eq!(addr_space.translate(vaddr).unwrap(), paddr); + assert_eq!( + addr_space.translate(vaddr + 0x1000).unwrap(), + paddr + 0x1000 + ); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_map_alloc_populate() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x10000); + let map_alloc_size = 0x2000; // 8KB + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Frame count before allocation: 1 root page table + let initial_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + assert_eq!(initial_allocs, 1); + + // Allocate physical frames immediately + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify additional frames were allocated + let final_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + assert!(final_allocs > initial_allocs); + + // Verify mappings exist and addresses are valid + let paddr1 = addr_space.translate(vaddr).unwrap(); + let paddr2 = addr_space.translate(vaddr + 0x1000).unwrap(); + + // Verify physical addresses are within valid range + assert!(paddr1.as_usize() >= BASE_PADDR && paddr1.as_usize() < BASE_PADDR + MEMORY_LEN); + assert!(paddr2.as_usize() >= BASE_PADDR && paddr2.as_usize() < BASE_PADDR + MEMORY_LEN); + + // Verify two pages have different physical addresses + assert_ne!(paddr1, paddr2); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_map_alloc_lazy() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x13000); + let map_alloc_size = 0x1000; + let flags = MappingFlags::READ | MappingFlags::WRITE; + + let initial_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + + // Lazy allocation - don't allocate physical frames immediately + addr_space + .map_alloc(vaddr, map_alloc_size, flags, false) + .unwrap(); + + // Frame count should only increase for page table structure, not data pages + let after_map_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + assert!(after_map_allocs >= initial_allocs); // May have allocated intermediate page tables + assert!(addr_space.translate(vaddr).is_none()); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_page_fault_handling() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x14000); + let map_alloc_size = 0x1000; + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Create lazy allocation mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, false) + .unwrap(); + + let before_pf_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + + // Simulate page fault + let handled = addr_space.handle_page_fault(vaddr, MappingFlags::READ); + + // Page fault should be handled + assert!(handled); + + // Should have allocated physical frames + let after_pf_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + assert!(after_pf_allocs > before_pf_allocs); + + // Translation should succeed now + let paddr = addr_space.translate(vaddr); + assert!(paddr.is_some()); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_unmap() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x15000); + let map_alloc_size = 0x2000; + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Create mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify mapping exists + assert!(addr_space.translate(vaddr).is_some()); + assert!(addr_space.translate(vaddr + 0x1000).is_some()); + + let before_unmap_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); + + // Unmap + addr_space.unmap(vaddr, map_alloc_size).unwrap(); + + // Verify mapping is removed + assert!(addr_space.translate(vaddr).is_none()); + assert!(addr_space.translate(vaddr + 0x1000).is_none()); + + // Verify frames were deallocated + let after_unmap_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); + assert!(after_unmap_deallocs > before_unmap_deallocs); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_clear() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr1 = GuestPhysAddr::from_usize(0x16000); + let vaddr2 = GuestPhysAddr::from_usize(0x17000); + let flags = MappingFlags::READ | MappingFlags::WRITE; + let map_alloc_size = 0x1000; + + // Create multiple mappings + addr_space + .map_alloc(vaddr1, map_alloc_size, flags, true) + .unwrap(); + addr_space + .map_alloc(vaddr2, map_alloc_size, flags, true) + .unwrap(); + + // Verify mappings exist + assert!(addr_space.translate(vaddr1).is_some()); + assert!(addr_space.translate(vaddr2).is_some()); + + let before_clear_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); + + // Clear all mappings + addr_space.clear(); + + // Verify all mappings are removed + assert!(addr_space.translate(vaddr1).is_none()); + assert!(addr_space.translate(vaddr2).is_none()); + + // Verify frames were deallocated + let after_clear_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); + assert!(after_clear_deallocs > before_clear_deallocs); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_translate() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x18000); + let map_alloc_size = 0x1000; + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Create mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify translation succeeds + let paddr = addr_space.translate(vaddr).expect("Translation failed"); + assert!(paddr.as_usize() >= BASE_PADDR); + assert!(paddr.as_usize() < BASE_PADDR + MEMORY_LEN); + + // Verify unmapped address translation fails + let unmapped_vaddr = GuestPhysAddr::from_usize(0x19000); + assert!(addr_space.translate(unmapped_vaddr).is_none()); + + // Verify out-of-range address translation fails + let out_of_range = GuestPhysAddr::from_usize(0x30000); + assert!(addr_space.translate(out_of_range).is_none()); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_translated_byte_buffer() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x19000); + let map_alloc_size = 0x2000; // 8KB + let flags = MappingFlags::READ | MappingFlags::WRITE; + let buffer_size = 0x1100; + + // Create mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify byte buffer can be obtained + let mut buffer = addr_space + .translated_byte_buffer(vaddr, buffer_size) + .expect("Failed to get byte buffer"); + + // Verify data write and read + // Fill with values ranging from 0 to 0x100 + for buffer_segment in buffer.iter_mut() { + for (i, byte) in buffer_segment.iter_mut().enumerate() { + *byte = (i % 0x100) as u8; + } + } + + // Verify data read correctness + for buffer_segment in buffer.iter_mut() { + for (i, byte) in buffer_segment.iter_mut().enumerate() { + assert_eq!(*byte, (i % 0x100) as u8); + } + } + + // Verify exceeding area size returns None + assert!( + addr_space + .translated_byte_buffer(vaddr, map_alloc_size + 0x1000) + .is_none() + ); + + // Verify unmapped address returns None + let unmapped_vaddr = GuestPhysAddr::from_usize(0x1D000); + assert!( + addr_space + .translated_byte_buffer(unmapped_vaddr, 0x100) + .is_none() + ); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_translate_and_get_limit() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x1A000); + let map_alloc_size = 0x3000; // 12KB + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Create mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify translation and area size retrieval + let (paddr, area_size) = addr_space.translate_and_get_limit(vaddr).unwrap(); + assert!(paddr.as_usize() >= BASE_PADDR && paddr.as_usize() < BASE_PADDR + MEMORY_LEN); + assert_eq!(area_size, map_alloc_size); + + // Verify unmapped address returns None + let unmapped_vaddr = GuestPhysAddr::from_usize(0x1E000); + assert!(addr_space.translate_and_get_limit(unmapped_vaddr).is_none()); + + // Verify out-of-range address returns None + let out_of_range = GuestPhysAddr::from_usize(0x30000); + assert!(addr_space.translate_and_get_limit(out_of_range).is_none()); +} diff --git a/tests/frame.rs b/tests/frame.rs new file mode 100644 index 0000000..eab6bd2 --- /dev/null +++ b/tests/frame.rs @@ -0,0 +1,104 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate alloc; + +mod test_utils; + +use alloc::vec::Vec; +use assert_matches::assert_matches; +use axaddrspace::PhysFrame; +use axin::axin; +use memory_addr::PAGE_SIZE_4K as PAGE_SIZE; +use test_utils::{BASE_PADDR, MockHal, mock_hal_test, test_dealloc_count}; + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] +fn test_alloc_dealloc_cycle() { + let frame = PhysFrame::::alloc() + .unwrap_or_else(|e| panic!("Failed to allocate frame: {:?}", e)); + assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); + // frame is dropped here, dealloc_frame should be called +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] +fn test_alloc_zero() { + let frame = PhysFrame::::alloc_zero() + .unwrap_or_else(|e| panic!("Failed to allocate zero frame: {:?}", e)); + assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); + let ptr = frame.as_mut_ptr(); + let page = unsafe { &*(ptr as *const [u8; PAGE_SIZE]) }; + assert!(page.iter().all(|&x| x == 0)); +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] +fn test_fill_operation() { + let mut frame = PhysFrame::::alloc() + .unwrap_or_else(|e| panic!("Failed to allocate frame: {:?}", e)); + assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); + frame.fill(0xAA); + let ptr = frame.as_mut_ptr(); + let page = unsafe { &*(ptr as *const [u8; PAGE_SIZE]) }; + assert!(page.iter().all(|&x| x == 0xAA)); +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(5)))] +fn test_fill_multiple_frames() { + const NUM_FRAMES: usize = 5; + + let mut frames = Vec::new(); + let mut patterns = Vec::new(); + + for i in 0..NUM_FRAMES { + let mut frame = PhysFrame::::alloc().unwrap(); + let pattern = (0xA0 + i) as u8; + frame.fill(pattern); + frames.push(frame); + patterns.push(pattern); + } + + for i in 0..NUM_FRAMES { + let actual_page = unsafe { &*(frames[i].as_mut_ptr() as *mut [u8; PAGE_SIZE]) }; + let expected_page = &[patterns[i]; PAGE_SIZE]; + + assert_eq!( + actual_page, expected_page, + "Frame verification failed for frame index {i}: Expected pattern 0x{:02x}", + patterns[i] + ); + } +} + +#[test] +#[should_panic(expected = "uninitialized PhysFrame")] +fn test_uninit_access() { + // This test verifies that accessing an uninitialized PhysFrame (created with `unsafe { uninit() }`) + // leads to a panic when trying to retrieve its physical address. + let frame = unsafe { PhysFrame::::uninit() }; + frame.start_paddr(); // This should panic +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(0)))] +fn test_alloc_no_memory() { + // Configure MockHal to simulate an allocation failure. + MockHal::set_alloc_fail(true); + let result = PhysFrame::::alloc(); + // Assert that allocation failed and verify the specific error type. + assert_matches!(result, Err(axerrno::AxError::NoMemory)); + MockHal::set_alloc_fail(false); // Reset for other tests +} diff --git a/tests/memory_accessor.rs b/tests/memory_accessor.rs new file mode 100644 index 0000000..95f4dc4 --- /dev/null +++ b/tests/memory_accessor.rs @@ -0,0 +1,269 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod test_utils; + +use axaddrspace::{GuestMemoryAccessor, GuestPhysAddr}; +use axerrno::AxResult; +use axin::axin; +use memory_addr::PhysAddr; +use test_utils::{BASE_PADDR, MEMORY_LEN, MockHal, mock_hal_test}; + +/// Mock implementation of GuestMemoryAccessor for testing +struct MockTranslator { + base_addr: PhysAddr, + memory_size: usize, +} + +impl MockTranslator { + pub fn new(base_addr: PhysAddr, memory_size: usize) -> Self { + Self { + base_addr, + memory_size, + } + } +} + +impl GuestMemoryAccessor for MockTranslator { + fn translate_and_get_limit(&self, guest_addr: GuestPhysAddr) -> Option<(PhysAddr, usize)> { + // Simple mapping: guest address directly maps to mock memory region + let offset = guest_addr.as_usize(); + if offset < self.memory_size { + // Convert physical address to virtual address for actual memory access + let phys_addr = PhysAddr::from_usize(BASE_PADDR + self.base_addr.as_usize() + offset); + let virt_addr = MockHal::mock_phys_to_virt(phys_addr); + let accessible_size = self.memory_size - offset; + Some((PhysAddr::from_usize(virt_addr.as_usize()), accessible_size)) + } else { + None + } + } +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_basic_read_write_operations() { + let translator = MockTranslator::new(PhysAddr::from_usize(0), MEMORY_LEN); + + // Test u32 read/write operations + let test_addr = GuestPhysAddr::from_usize(0x100); + let test_value: u32 = 0x12345678; + + // Write a u32 value + translator + .write_obj(test_addr, test_value) + .expect("Failed to write u32 value"); + + // Read back the u32 value + let read_value: u32 = translator + .read_obj(test_addr) + .expect("Failed to read u32 value"); + + assert_eq!( + read_value, test_value, + "Read value should match written value" + ); + + // Test buffer read/write operations + let buffer_addr = GuestPhysAddr::from_usize(0x200); + let test_buffer = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; + + // Write buffer + translator + .write_buffer(buffer_addr, &test_buffer) + .expect("Failed to write buffer"); + + // Read buffer back + let mut read_buffer = [0u8; 8]; + translator + .read_buffer(buffer_addr, &mut read_buffer) + .expect("Failed to read buffer"); + + assert_eq!( + read_buffer, test_buffer, + "Read buffer should match written buffer" + ); + + // Test error handling with invalid address + let invalid_addr = GuestPhysAddr::from_usize(MEMORY_LEN + 0x1000); + let result: AxResult = translator.read_obj(invalid_addr); + assert!(result.is_err(), "Reading from invalid address should fail"); + + let result = translator.write_obj(invalid_addr, 42u32); + assert!(result.is_err(), "Writing to invalid address should fail"); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_two_vm_isolation() { + // Create two different translators to simulate two different VMs + let vm1_translator = MockTranslator::new(PhysAddr::from_usize(0), MEMORY_LEN / 2); // Offset for VM1 + let vm2_translator = MockTranslator::new(PhysAddr::from_usize(MEMORY_LEN / 2), MEMORY_LEN); // Offset for VM2 + + // Both VMs write to the same guest address but different host memory regions + let guest_addr = GuestPhysAddr::from_usize(0x100); + let vm1_data: u64 = 0xDEADBEEFCAFEBABE; + let vm2_data: u64 = 0x1234567890ABCDEF; + + // VM1 writes its data + vm1_translator + .write_obj(guest_addr, vm1_data) + .expect("VM1 failed to write data"); + + // VM2 writes its data + vm2_translator + .write_obj(guest_addr, vm2_data) + .expect("VM2 failed to write data"); + + // Both VMs read back their own data - should be isolated + let vm1_read: u64 = vm1_translator + .read_obj(guest_addr) + .expect("VM1 failed to read data"); + let vm2_read: u64 = vm2_translator + .read_obj(guest_addr) + .expect("VM2 failed to read data"); + + // Verify isolation: each VM should read its own data + assert_eq!(vm1_read, vm1_data, "VM1 should read its own data"); + assert_eq!(vm2_read, vm2_data, "VM2 should read its own data"); + assert_ne!( + vm1_read, vm2_read, + "VM1 and VM2 should have different data (isolation)" + ); + + // Test buffer operations with different patterns + let buffer_addr = GuestPhysAddr::from_usize(0x200); + let vm1_buffer = [0xAA; 16]; // Pattern for VM1 + let vm2_buffer = [0x55; 16]; // Pattern for VM2 + + // Both VMs write their patterns + vm1_translator + .write_buffer(buffer_addr, &vm1_buffer) + .expect("VM1 failed to write buffer"); + vm2_translator + .write_buffer(buffer_addr, &vm2_buffer) + .expect("VM2 failed to write buffer"); + + // Read back and verify isolation + let mut vm1_read_buffer = [0u8; 16]; + let mut vm2_read_buffer = [0u8; 16]; + + vm1_translator + .read_buffer(buffer_addr, &mut vm1_read_buffer) + .expect("VM1 failed to read buffer"); + vm2_translator + .read_buffer(buffer_addr, &mut vm2_read_buffer) + .expect("VM2 failed to read buffer"); + + assert_eq!( + vm1_read_buffer, vm1_buffer, + "VM1 should read its own buffer pattern" + ); + assert_eq!( + vm2_read_buffer, vm2_buffer, + "VM2 should read its own buffer pattern" + ); + assert_ne!( + vm1_read_buffer, vm2_read_buffer, + "VM buffers should be isolated" + ); + + // Test that VM1 cannot access VM2's address space (beyond its limit) + let vm2_only_addr = GuestPhysAddr::from_usize(MEMORY_LEN / 2 + 0x100); + let result: AxResult = vm1_translator.read_obj(vm2_only_addr); + assert!( + result.is_err(), + "VM1 should not be able to access VM2's exclusive address space" + ); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_cross_page_access() { + let translator = MockTranslator::new(PhysAddr::from_usize(0), MEMORY_LEN); + + // Test cross-region buffer operations + // Place buffer near a region boundary to test multi-region access + let cross_region_addr = GuestPhysAddr::from_usize(4096 - 8); // 8 bytes before 4K boundary + let test_data = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, + ]; // 16 bytes + + // Write cross-region data + translator + .write_buffer(cross_region_addr, &test_data) + .expect("Failed to write cross-region buffer"); + + // Read cross-region data back + let mut read_data = [0u8; 16]; + translator + .read_buffer(cross_region_addr, &mut read_data) + .expect("Failed to read cross-region buffer"); + + assert_eq!( + read_data, test_data, + "Cross-region read should match written data" + ); + + // Test individual byte access across region boundary + for (i, &expected_byte) in test_data.iter().enumerate() { + let byte_addr = GuestPhysAddr::from_usize(cross_region_addr.as_usize() + i); + let read_byte: u8 = translator + .read_obj(byte_addr) + .expect("Failed to read individual byte"); + assert_eq!( + read_byte, expected_byte, + "Byte at offset {} should match", + i + ); + } +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_region_boundary_edge_cases() { + let translator = MockTranslator::new(PhysAddr::from_usize(0), MEMORY_LEN); + + let boundary_addr = GuestPhysAddr::from_usize(4096); + let boundary_data = [0xAB, 0xCD, 0xEF, 0x12]; + + translator + .write_buffer(boundary_addr, &boundary_data) + .expect("Failed to write at boundary"); + + let mut read_boundary = [0u8; 4]; + translator + .read_buffer(boundary_addr, &mut read_boundary) + .expect("Failed to read at boundary"); + + assert_eq!(read_boundary, boundary_data, "Boundary data should match"); + + // Test zero-size buffer (should not fail) + let empty_buffer: &[u8] = &[]; + translator + .write_buffer(boundary_addr, empty_buffer) + .expect("Empty buffer write should succeed"); + + let mut empty_read: &mut [u8] = &mut []; + translator + .read_buffer(boundary_addr, &mut empty_read) + .expect("Empty buffer read should succeed"); + + // Test single byte at boundary (should work fine) + let single_byte = [0x42]; + translator + .write_buffer(boundary_addr, &single_byte) + .expect("Single byte write should succeed"); +} diff --git a/src/test_utils/mod.rs b/tests/test_utils/mod.rs similarity index 85% rename from src/test_utils/mod.rs rename to tests/test_utils/mod.rs index e2d6a47..cc0a17b 100644 --- a/src/test_utils/mod.rs +++ b/tests/test_utils/mod.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{AxMmHal, HostPhysAddr, HostVirtAddr}; +use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use lazy_static::lazy_static; use memory_addr::{PhysAddr, VirtAddr}; @@ -23,17 +23,17 @@ use memory_addr::PAGE_SIZE_4K as PAGE_SIZE; /// The starting physical address for the simulated memory region in tests. /// This offset is used to map simulated physical addresses to the `MEMORY` array's virtual address space. -pub(crate) const BASE_PADDR: usize = 0x1000; +pub const BASE_PADDR: usize = 0x1000; /// Static variables to simulate global state of a memory allocator in tests. -pub(crate) static NEXT_PADDR: AtomicUsize = AtomicUsize::new(BASE_PADDR); +pub static NEXT_PADDR: AtomicUsize = AtomicUsize::new(BASE_PADDR); /// Total length of the simulated physical memory block for testing, in bytes. -pub(crate) const MEMORY_LEN: usize = 0x10000; // 64KB for testing +pub const MEMORY_LEN: usize = 0x10000; // 64KB for testing // Use #[repr(align(4096))] to ensure 4KB alignment #[repr(align(4096))] -pub(crate) struct AlignedMemory([u8; MEMORY_LEN]); +pub struct AlignedMemory([u8; MEMORY_LEN]); impl Default for AlignedMemory { fn default() -> Self { @@ -43,21 +43,21 @@ impl Default for AlignedMemory { lazy_static! { /// Simulates the actual physical memory block used for allocation. - pub(crate) static ref MEMORY: Mutex = Mutex::new(AlignedMemory::default()); + pub static ref MEMORY: Mutex = Mutex::new(AlignedMemory::default()); /// Global mutex to enforce serial execution for tests that modify shared state. /// This ensures test isolation and prevents race conditions between tests. - pub(crate) static ref TEST_MUTEX: Mutex<()> = Mutex::new(()); + pub static ref TEST_MUTEX: Mutex<()> = Mutex::new(()); } /// Counter to track the number of allocations. (Added from Chen Hong's code) -pub(crate) static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0); +pub static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0); /// Counter to track the number of deallocations. -pub(crate) static DEALLOC_COUNT: AtomicUsize = AtomicUsize::new(0); +pub static DEALLOC_COUNT: AtomicUsize = AtomicUsize::new(0); /// Flag to simulate memory allocation failures for testing error handling. -pub(crate) static ALLOC_SHOULD_FAIL: AtomicBool = AtomicBool::new(false); +pub static ALLOC_SHOULD_FAIL: AtomicBool = AtomicBool::new(false); #[derive(Debug)] /// A mock implementation of AxMmHal for testing purposes. @@ -65,7 +65,7 @@ pub(crate) static ALLOC_SHOULD_FAIL: AtomicBool = AtomicBool::new(false); /// /// The `Debug` trait is derived because `assert_matches!` on `Result, _>` /// requires `PhysFrame` (the `T` type) to implement `Debug` for diagnostic output on assertion failure. -pub(crate) struct MockHal {} +pub struct MockHal {} impl AxMmHal for MockHal { fn alloc_frame() -> Option { @@ -121,7 +121,7 @@ impl PagingHandler for MockHal { } /// A utility decorator for test functions that require the MockHal state to be reset before execution. -pub(crate) fn mock_hal_test(test_fn: F) -> R +pub fn mock_hal_test(test_fn: F) -> R where F: FnOnce() -> R, { @@ -131,7 +131,7 @@ where } /// A utility function to verify the number of deallocations performed by the MockHal. -pub(crate) fn test_dealloc_count(expected: usize) { +pub fn test_dealloc_count(expected: usize) { let actual_dealloc_count = DEALLOC_COUNT.load(Ordering::SeqCst); assert_eq!( actual_dealloc_count, expected, @@ -141,7 +141,7 @@ pub(crate) fn test_dealloc_count(expected: usize) { impl MockHal { /// Simulates the allocation of a single physical frame. - pub(crate) fn mock_alloc_frame() -> Option { + pub fn mock_alloc_frame() -> Option { // Use a static mutable variable to control alloc_should_fail state if ALLOC_SHOULD_FAIL.load(Ordering::SeqCst) { return None; @@ -156,14 +156,14 @@ impl MockHal { } /// Simulates the deallocation of a single physical frame. - pub(crate) fn mock_dealloc_frame(_paddr: PhysAddr) { + pub fn mock_dealloc_frame(_paddr: PhysAddr) { DEALLOC_COUNT.fetch_add(1, Ordering::SeqCst); } /// In this test mock, the "virtual address" is simply a direct pointer /// to the corresponding location within the `MEMORY` array. /// It simulates a physical-to-virtual memory mapping for test purposes. - pub(crate) fn mock_phys_to_virt(paddr: PhysAddr) -> VirtAddr { + pub fn mock_phys_to_virt(paddr: PhysAddr) -> VirtAddr { let paddr_usize = paddr.as_usize(); assert!( paddr_usize >= BASE_PADDR && paddr_usize < BASE_PADDR + MEMORY_LEN, @@ -175,7 +175,7 @@ impl MockHal { } /// Maps a virtual address (within the test process) back to a simulated physical address. - pub(crate) fn mock_virt_to_phys(vaddr: VirtAddr) -> PhysAddr { + pub fn mock_virt_to_phys(vaddr: VirtAddr) -> PhysAddr { let base_virt = MEMORY.lock().0.as_ptr() as usize; let vaddr_usize = vaddr.as_usize(); assert!( @@ -188,13 +188,13 @@ impl MockHal { } /// Helper function to control the simulated allocation failure. - pub(crate) fn set_alloc_fail(fail: bool) { + pub fn set_alloc_fail(fail: bool) { ALLOC_SHOULD_FAIL.store(fail, Ordering::SeqCst); } /// Resets all static state of the MockHal to its initial, clean state. /// This is crucial for ensuring test isolation between individual test functions. - pub(crate) fn reset_state() { + pub fn reset_state() { NEXT_PADDR.store(BASE_PADDR, Ordering::SeqCst); ALLOC_SHOULD_FAIL.store(false, Ordering::SeqCst); ALLOC_COUNT.store(0, Ordering::SeqCst); From dfb981882ff55c4325f2bb2fe42a3dfbd021d34c Mon Sep 17 00:00:00 2001 From: YanLien Date: Thu, 19 Mar 2026 16:20:13 +0800 Subject: [PATCH 2/2] chore(scripts): update comments to axaddrspace --- 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..1e4092c 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# aarch64_sysreg 代码检查脚本 +# axaddrspace 代码检查脚本 # 下载并调用 axci 仓库中的检查脚本 # diff --git a/scripts/test.sh b/scripts/test.sh index 751cb0e..0ac99ec 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# aarch64_sysreg 测试脚本 +# axaddrspace 测试脚本 # 下载并调用 axci 仓库中的测试框架 #