From ef7912cb045833066030e7546b19327b23cef513 Mon Sep 17 00:00:00 2001 From: hky1999 <976929993@qq.com> Date: Sat, 24 Jan 2026 16:54:42 +0800 Subject: [PATCH 1/2] [feat] refactor according to rules and prepare to release --- .github/workflows/ci.yml | 96 ++++++---- .github/workflows/release-check.yml | 58 ++++++ CHANGELOG.md | 27 +++ Cargo.toml | 33 ++-- README.md | 107 +++++++++-- src/lib.rs | 277 ++++++++++++++++++++++++++-- 6 files changed, 519 insertions(+), 79 deletions(-) create mode 100644 .github/workflows/release-check.yml create mode 100644 CHANGELOG.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 531ddd1..6fdb266 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,55 +1,77 @@ name: CI -on: [push, pull_request] +on: + push: + branches: [main, dev] + pull_request: + branches: [main, dev] + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 jobs: - ci: + # Quick check job - runs format and lint checks + quick-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt, clippy + - name: Check rust version + run: rustc --version --verbose + - name: Check code format + run: cargo fmt --all -- --check + - name: Clippy (default target) + run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default + + # Build and test job - runs on multiple platforms + build-and-test: runs-on: ubuntu-latest + needs: quick-check strategy: fail-fast: false matrix: - rust-toolchain: [nightly] - targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-none + - riscv64gc-unknown-none-elf + - aarch64-unknown-none-softfloat steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: ${{ matrix.rust-toolchain }} - components: rust-src, clippy, rustfmt - targets: ${{ matrix.targets }} - - name: Check rust version - run: rustc --version --verbose - - name: Check code format - run: cargo fmt --all -- --check - - name: Clippy - run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - - name: Build - run: cargo build --target ${{ matrix.targets }} --all-features - - name: Unit test - if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.targets }} -- --nocapture + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rust-src, clippy + targets: ${{ matrix.target }} + - name: Clippy + run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings -A clippy::new_without_default + - name: Build + run: cargo build --target ${{ matrix.target }} --all-features + - name: Unit test + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture + # Documentation job doc: runs-on: ubuntu-latest - strategy: - fail-fast: false + needs: quick-check permissions: contents: write env: default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - - name: Build docs - continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - name: Deploy to Github Pages - if: ${{ github.ref == env.default-branch }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - single-commit: true - branch: gh-pages - folder: target/doc + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: Build docs + run: | + cargo doc --no-deps --all-features + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html + - name: Deploy to Github Pages + if: ${{ github.ref == env.default-branch }} + uses: JamesIves/github-pages-deploy-action@v4 + with: + single-commit: true + branch: gh-pages + folder: target/doc diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml new file mode 100644 index 0000000..43d1863 --- /dev/null +++ b/.github/workflows/release-check.yml @@ -0,0 +1,58 @@ +name: Release Check + +on: + push: + tags: + - 'v*' + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + release-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rust-src, clippy, rustfmt + + # Verify version consistency between git tag and Cargo.toml + - name: Verify version consistency + run: | + TAG_VERSION=${GITHUB_REF#refs/tags/v} + CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | 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!" + + # Format check + - name: Check code format + run: cargo fmt --all -- --check + + # Clippy check + - name: Clippy + run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default + + # Build check + - name: Build + run: cargo build --all-features + + # Unit tests + - name: Unit test + run: cargo test --all-features -- --nocapture + + # Documentation build + - name: Build docs + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: cargo doc --no-deps --all-features + + # Publish dry-run + - name: Publish dry-run + run: cargo publish --dry-run diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6677304 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.1.0] - 2026-01-24 + +### Added + +- Initial release of `axdevice_base` crate. +- `BaseDeviceOps` trait: Core interface for all emulated devices. +- `EmulatedDeviceConfig`: Configuration structure for device initialization. +- `EmuDeviceType`: Re-exported device type enumeration from `axvmconfig`. +- `map_device_of_type`: Helper function for runtime device type checking and casting. +- Trait aliases for common device types: + - `BaseMmioDeviceOps`: For MMIO (Memory-Mapped I/O) devices. + - `BaseSysRegDeviceOps`: For system register devices (ARM). + - `BasePortDeviceOps`: For port I/O devices (x86). +- Support for multiple architectures: x86_64, AArch64, RISC-V64. +- `no_std` compatible design. + +[Unreleased]: https://github.com/arceos-hypervisor/axdevice_base/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/arceos-hypervisor/axdevice_base/releases/tag/v0.1.0 diff --git a/Cargo.toml b/Cargo.toml index 7813f72..b16e1c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,29 @@ [package] name = "axdevice_base" -edition = "2024" version = "0.1.0" -description = "Basic traits and structures for emulated devices in ArceOS hypervisor." +edition = "2024" authors = ["Hui Xiao <1127751018@qq.com>"] -license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" # MulanPubL2 is not included in SPDX -categories = ["virtualization", "no-std"] -homepage = "https://github.com/arceos-hypervisor/axvisor" +description = "Basic traits and structures for emulated devices in ArceOS hypervisor." +documentation = "https://docs.rs/axdevice_base" +repository = "https://github.com/arceos-hypervisor/axdevice_base" +readme = "README.md" +license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" +keywords = ["arceos", "hypervisor", "virtualization", "device", "no-std"] +categories = ["no-std", "virtualization"] [dependencies] -cfg-if = "1.0" -serde = { version = "1.0.204", default-features = false, features = ["derive", "alloc"] } -# System independent crates provided by ArceOS. -axerrno = "0.1.0" -axaddrspace = "0.1.0" -axvmconfig = { version = "0.1.0", default-features = false } +# Serialization support +serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } + +# ArceOS core crates +axaddrspace = "0.1" +axerrno = "0.1" +axvmconfig = { version = "0.1", default-features = false } memory_addr = "0.4" + +[dev-dependencies] + +[package.metadata.docs.rs] +all-features = true +default-target = "x86_64-unknown-linux-gnu" +targets = ["x86_64-unknown-linux-gnu", "riscv64gc-unknown-none-elf", "aarch64-unknown-none-softfloat"] diff --git a/README.md b/README.md index 6f38b06..2a4e8fa 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ - # axdevice_base -[![CI](https://github.com/arceos-hypervisor/axdevice_crates/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/arceos-hypervisor/axdevice_crates/actions/workflows/ci.yml) -[![🚧 Work in Progress](https://img.shields.io/badge/Work_in_Progress-FFD700?style=plastic&logo=github)](https://github.com/arceos-hypervisor/axdevice_crates) +[![CI](https://github.com/arceos-hypervisor/axdevice_base/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/arceos-hypervisor/axdevice_base/actions/workflows/ci.yml) +[![Crates.io](https://img.shields.io/crates/v/axdevice_base)](https://crates.io/crates/axdevice_base) +[![Docs.rs](https://docs.rs/axdevice_base/badge.svg)](https://docs.rs/axdevice_base) Basic device abstraction library for [AxVisor](https://github.com/arceos-hypervisor/axvisor) virtual device subsystem, designed for `no_std` environments. @@ -12,27 +12,108 @@ Basic device abstraction library for [AxVisor](https://github.com/arceos-hypervi - `BaseDeviceOps` trait: Common interface that all virtual devices must implement. - `EmulatedDeviceConfig`: Device initialization and configuration structure. -- Device type enumeration `EmuDeviceType` (provided by `axvmconfig` crate). -- Trait aliases for various device types (MMIO, port, system register, etc.). +- Device type enumeration `EmuDeviceType` (re-exported from `axvmconfig` crate). +- Trait aliases for various device types: + - `BaseMmioDeviceOps`: For MMIO (Memory-Mapped I/O) devices. + - `BaseSysRegDeviceOps`: For system register devices (ARM). + - `BasePortDeviceOps`: For port I/O devices (x86). +- `map_device_of_type`: Helper function for runtime device type checking and casting. + +## Features + +- **`no_std` compatible**: Designed for bare-metal and hypervisor environments. +- **Multi-architecture support**: x86_64, AArch64, RISC-V64. +- **Type-safe addressing**: Different address range types for different device access methods. +- **Serialization support**: Device configuration can be serialized/deserialized via `serde`. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +axdevice_base = "0.1" +``` -## Usage Example +### Implementing a Custom Device ```rust,ignore -use axdevice_base::{BaseDeviceOps, EmulatedDeviceConfig, EmuDeviceType}; +use axdevice_base::{BaseDeviceOps, EmuDeviceType}; +use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange, device::AccessWidth}; +use axerrno::AxResult; + +struct MyUartDevice { + base_addr: usize, + // ... device state +} -// Implement a custom device -struct MyDevice { /* ... */ } +impl BaseDeviceOps for MyUartDevice { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::Dummy // Use appropriate device type + } -impl BaseDeviceOps for MyDevice { - // Implement trait methods ... + fn address_range(&self) -> GuestPhysAddrRange { + (self.base_addr..self.base_addr + 0x1000).try_into().unwrap() + } + + fn handle_read(&self, addr: GuestPhysAddr, width: AccessWidth) -> AxResult { + // Handle read operation from guest + Ok(0) + } + + fn handle_write(&self, addr: GuestPhysAddr, width: AccessWidth, val: usize) -> AxResult { + // Handle write operation from guest + Ok(()) + } } +``` -let config = EmulatedDeviceConfig::default(); +### Device Configuration + +```rust +use axdevice_base::EmulatedDeviceConfig; + +let config = EmulatedDeviceConfig { + name: "uart0".into(), + base_ipa: 0x0900_0000, + length: 0x1000, + irq_id: 33, + emu_type: 1, + cfg_list: vec![115200], // device-specific config (e.g., baud rate) +}; ``` +### Type Checking with `map_device_of_type` + +```rust,ignore +use axdevice_base::{BaseMmioDeviceOps, map_device_of_type}; +use alloc::sync::Arc; + +fn process_device(device: &Arc) { + // Try to access device-specific methods if it's a UartDevice + if let Some(baud_rate) = map_device_of_type(device, |uart: &MyUartDevice| { + uart.get_baud_rate() + }) { + println!("UART baud rate: {}", baud_rate); + } +} +``` + +## Supported Platforms + +| Architecture | MMIO Devices | Port I/O Devices | System Register Devices | +|--------------|--------------|------------------|-------------------------| +| x86_64 | ✓ | ✓ | - | +| AArch64 | ✓ | - | ✓ | +| RISC-V64 | ✓ | - | - | + +## Documentation + +For detailed API documentation, visit [docs.rs/axdevice_base](https://docs.rs/axdevice_base). + ## Contributing -Issues and PRs are welcome! Please follow the ArceOS-hypervisor project guidelines. +Issues and PRs are welcome! Please follow the [ArceOS-hypervisor project guidelines](https://github.com/arceos-hypervisor/axvisor). ## License diff --git a/src/lib.rs b/src/lib.rs index 0d45f3f..a963887 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,62 @@ //! Basic traits and structures for emulated devices in ArceOS hypervisor. //! -//! This crate contains: -//! - [`BaseDeviceOps`] trait: The trait that all emulated devices must implement. -//! - [`EmuDeviceType`] enum: Enumeration representing the type of emulator devices. -//! (Already moved to `axvmconfig` crate.) +//! This crate provides the foundational abstractions for implementing virtual devices +//! in the [AxVisor](https://github.com/arceos-hypervisor/axvisor) hypervisor. It is +//! designed for `no_std` environments and supports multiple architectures. +//! +//! # Overview +//! +//! The crate contains the following key components: +//! +//! - [`BaseDeviceOps`]: The core trait that all emulated devices must implement. +//! - [`EmuDeviceType`]: Enumeration representing the type of emulator devices +//! (re-exported from `axvmconfig` crate). //! - [`EmulatedDeviceConfig`]: Configuration structure for device initialization. +//! - Trait aliases for specific device types: +//! - [`BaseMmioDeviceOps`]: For MMIO (Memory-Mapped I/O) devices. +//! - [`BaseSysRegDeviceOps`]: For system register devices. +//! - [`BasePortDeviceOps`]: For port I/O devices. +//! +//! # Usage +//! +//! To implement a custom emulated device, you need to implement the [`BaseDeviceOps`] +//! trait with the appropriate address range type: +//! +//! ```rust,ignore +//! use axdevice_base::{BaseDeviceOps, EmuDeviceType}; +//! use axaddrspace::{GuestPhysAddrRange, device::AccessWidth}; +//! use axerrno::AxResult; +//! +//! struct MyDevice { +//! base_addr: usize, +//! size: usize, +//! } +//! +//! impl BaseDeviceOps for MyDevice { +//! fn emu_type(&self) -> EmuDeviceType { +//! EmuDeviceType::Dummy +//! } +//! +//! fn address_range(&self) -> GuestPhysAddrRange { +//! (self.base_addr..self.base_addr + self.size).try_into().unwrap() +//! } +//! +//! fn handle_read(&self, addr: GuestPhysAddr, width: AccessWidth) -> AxResult { +//! // Handle read operation +//! Ok(0) +//! } +//! +//! fn handle_write(&self, addr: GuestPhysAddr, width: AccessWidth, val: usize) -> AxResult { +//! // Handle write operation +//! Ok(()) +//! } +//! } +//! ``` +//! +//! # Feature Flags +//! +//! This crate currently has no optional feature flags. All functionality is available +//! by default. #![no_std] #![feature(trait_alias)] @@ -14,6 +66,7 @@ #![feature(trait_upcasting)] #![allow(incomplete_features)] #![feature(generic_const_exprs)] +#![warn(missing_docs)] extern crate alloc; @@ -29,36 +82,200 @@ use axerrno::AxResult; pub use axvmconfig::EmulatedDeviceType as EmuDeviceType; /// Represents the configuration of an emulated device for a virtual machine. +/// +/// This structure holds all the necessary information to initialize and configure +/// an emulated device, including its memory mapping, interrupt configuration, and +/// device-specific parameters. +/// +/// # Fields +/// +/// - `name`: A human-readable identifier for the device. +/// - `base_ipa`: The starting address in guest physical address space. +/// - `length`: The size of the device's address space in bytes. +/// - `irq_id`: The interrupt line number for device interrupts. +/// - `emu_type`: Numeric identifier for the device type. +/// - `cfg_list`: Device-specific configuration parameters. +/// +/// # Example +/// +/// ```rust +/// use axdevice_base::EmulatedDeviceConfig; +/// +/// let config = EmulatedDeviceConfig { +/// name: "uart0".into(), +/// base_ipa: 0x0900_0000, +/// length: 0x1000, +/// irq_id: 33, +/// emu_type: 1, +/// cfg_list: vec![115200], // baud rate +/// }; +/// ``` #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] pub struct EmulatedDeviceConfig { - /// The name of the device + /// The name of the device. + /// + /// This is a human-readable identifier used for logging, debugging, and + /// device tree generation. It should be unique within a virtual machine. pub name: String, + /// The base IPA (Intermediate Physical Address) of the device. + /// + /// This is the starting address in the guest's physical address space + /// where the device's registers are mapped. The guest OS will use this + /// address to access the device. pub base_ipa: usize, - /// The length of the device. + + /// The length of the device's address space in bytes. + /// + /// This defines the size of the memory region that the device occupies. + /// Any access within `[base_ipa, base_ipa + length)` will be routed to + /// this device. pub length: usize, + /// The IRQ (Interrupt Request) ID of the device. + /// + /// This is the interrupt line number that the device uses to signal + /// events to the guest. The value should correspond to a valid interrupt + /// ID in the virtual interrupt controller. pub irq_id: usize, + /// The type of emulated device. + /// + /// This numeric value identifies the device type and is used by the + /// device manager to instantiate the correct device implementation. + /// See [`EmuDeviceType`] for predefined device types. pub emu_type: usize, - /// The config_list of the device + + /// Device-specific configuration parameters. + /// + /// This is a list of configuration values whose meaning depends on the + /// specific device type. For example, a UART device might use this to + /// specify baud rate, while a virtio device might use it for queue sizes. pub cfg_list: Vec, } -/// [`BaseDeviceOps`] is the trait that all emulated devices must implement. +/// The core trait that all emulated devices must implement. +/// +/// This trait defines the common interface for all virtual devices in the hypervisor. +/// It provides methods for device identification, address range querying, and +/// handling read/write operations from the guest. +/// +/// # Type Parameters +/// +/// - `R`: The address range type that the device uses. This determines the +/// addressing scheme (MMIO, port I/O, system registers, etc.). +/// +/// # Implementation Notes +/// +/// - All implementations must also implement [`Any`] to support runtime type checking. +/// - The `handle_read` and `handle_write` methods are called by the hypervisor's +/// trap handler when the guest accesses the device's address range. +/// - Implementations should handle concurrent access appropriately if the device +/// can be accessed from multiple vCPUs. +/// +/// # Example +/// +/// See the crate-level documentation for a complete implementation example. pub trait BaseDeviceOps: Any { /// Returns the type of the emulated device. + /// + /// This is used by the device manager to identify the device type and + /// perform type-specific operations. fn emu_type(&self) -> EmuDeviceType; - /// Returns the address range of the emulated device. + + /// Returns the address range that this device occupies. + /// + /// The returned range is used by the hypervisor to route guest memory + /// accesses to the appropriate device handler. fn address_range(&self) -> R; + /// Handles a read operation on the emulated device. + /// + /// # Arguments + /// + /// - `addr`: The address within the device's range being read. + /// - `width`: The access width (byte, halfword, word, or doubleword). + /// + /// # Returns + /// + /// - `Ok(value)`: The value read from the device register. + /// - `Err(error)`: An error if the read operation failed. + /// + /// # Notes + /// + /// Implementations should respect the `width` parameter and only return + /// data of the appropriate size. The returned value should be zero-extended + /// if necessary. fn handle_read(&self, addr: R::Addr, width: AccessWidth) -> AxResult; + /// Handles a write operation on the emulated device. + /// + /// # Arguments + /// + /// - `addr`: The address within the device's range being written. + /// - `width`: The access width (byte, halfword, word, or doubleword). + /// - `val`: The value to write to the device register. + /// + /// # Returns + /// + /// - `Ok(())`: The write operation completed successfully. + /// - `Err(error)`: An error if the write operation failed. + /// + /// # Notes + /// + /// Implementations should only use the lower bits of `val` corresponding + /// to the specified `width`. fn handle_write(&self, addr: R::Addr, width: AccessWidth, val: usize) -> AxResult; } -/// Determines whether the given device is of type `T` and calls the provided function `f` with a -/// reference to the device if it is. +/// Attempts to downcast a device to a specific type and apply a function to it. +/// +/// This function is useful when you have a trait object (`Arc>`) +/// and need to access type-specific methods or data of the underlying concrete type. +/// +/// # Type Parameters +/// +/// - `T`: The concrete device type to downcast to. Must implement `BaseDeviceOps`. +/// - `R`: The address range type. +/// - `U`: The return type of the mapping function. +/// - `F`: The function to apply if the downcast succeeds. +/// +/// # Arguments +/// +/// - `device`: A reference to the device trait object. +/// - `f`: A function to call with a reference to the concrete device type. +/// +/// # Returns +/// +/// - `Some(result)`: If the device is of type `T`, returns the result of `f`. +/// - `None`: If the device is not of type `T`. +/// +/// # Example +/// +/// ```rust,ignore +/// use axdevice_base::{BaseDeviceOps, map_device_of_type}; +/// use alloc::sync::Arc; +/// +/// struct UartDevice { +/// baud_rate: u32, +/// } +/// +/// impl UartDevice { +/// fn get_baud_rate(&self) -> u32 { +/// self.baud_rate +/// } +/// } +/// +/// // ... implement BaseDeviceOps for UartDevice ... +/// +/// fn check_uart_config(device: &Arc) { +/// if let Some(baud_rate) = map_device_of_type(device, |uart: &UartDevice| { +/// uart.get_baud_rate() +/// }) { +/// println!("UART baud rate: {}", baud_rate); +/// } +/// } +/// ``` pub fn map_device_of_type, R: DeviceAddrRange, U, F: FnOnce(&T) -> U>( device: &Arc>, f: F, @@ -68,15 +285,39 @@ pub fn map_device_of_type, R: DeviceAddrRange, U, F: FnOnce( any_arc.downcast_ref::().map(f) } -// trait aliases are limited yet: https://github.com/rust-lang/rfcs/pull/3437 -/// [`BaseMmioDeviceOps`] is the trait that all emulated MMIO devices must implement. -/// It is a trait alias of [`BaseDeviceOps`] with [`GuestPhysAddrRange`] as the address range. +// Trait aliases are limited yet: https://github.com/rust-lang/rfcs/pull/3437 + +/// Trait alias for MMIO (Memory-Mapped I/O) device operations. +/// +/// This is a convenience alias for [`BaseDeviceOps`] with [`GuestPhysAddrRange`] +/// as the address range type. MMIO devices are the most common type of virtual +/// devices, where device registers are accessed through memory read/write operations. +/// +/// # Supported Architectures +/// +/// MMIO devices are supported on all architectures (x86_64, ARM, RISC-V). pub trait BaseMmioDeviceOps = BaseDeviceOps; -/// [`BaseSysRegDeviceOps`] is the trait that all emulated system register devices must implement. -/// It is a trait alias of [`BaseDeviceOps`] with [`SysRegAddrRange`] as the address range. + +/// Trait alias for system register device operations. +/// +/// This is a convenience alias for [`BaseDeviceOps`] with [`SysRegAddrRange`] +/// as the address range type. System register devices are typically used on +/// ARM architectures to emulate system registers accessed via MSR/MRS instructions. +/// +/// # Supported Architectures +/// +/// System register devices are primarily used on ARM/AArch64 architectures. pub trait BaseSysRegDeviceOps = BaseDeviceOps; -/// [`BasePortDeviceOps`] is the trait that all emulated port devices must implement. -/// It is a trait alias of [`BaseDeviceOps`] with [`PortRange`] as the address range. + +/// Trait alias for port I/O device operations. +/// +/// This is a convenience alias for [`BaseDeviceOps`] with [`PortRange`] +/// as the address range type. Port I/O devices are used on x86 architectures +/// where device registers are accessed via IN/OUT instructions. +/// +/// # Supported Architectures +/// +/// Port I/O devices are only used on x86/x86_64 architectures. pub trait BasePortDeviceOps = BaseDeviceOps; #[cfg(test)] From c307dbceb75f6223e5043d9b539dddbbbc6cb5f3 Mon Sep 17 00:00:00 2001 From: hky1999 <976929993@qq.com> Date: Wed, 28 Jan 2026 22:18:18 +0800 Subject: [PATCH 2/2] [feat] modify github workflows --- .github/workflows/check.yml | 44 ++++++++ .github/workflows/ci.yml | 77 -------------- .github/workflows/deploy.yml | 66 ++++++++++++ .github/workflows/release-check.yml | 58 ----------- .github/workflows/release.yml | 156 ++++++++++++++++++++++++++++ .github/workflows/test.yml | 21 ++++ 6 files changed, 287 insertions(+), 135 deletions(-) create mode 100644 .github/workflows/check.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/release-check.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..f410ba0 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,44 @@ +name: Quality Checks + +on: + workflow_call: + +jobs: + check: + name: Quality Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-none + - riscv64gc-unknown-none-elf + - aarch64-unknown-none-softfloat + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: rust-src, clippy, rustfmt + targets: ${{ matrix.target }} + + - name: Check rust version + run: rustc --version --verbose + + - name: Check code format + run: cargo fmt --all -- --check + + - name: Build + run: cargo build --target ${{ matrix.target }} --all-features + + - name: Run clippy + run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings -A clippy::new_without_default + + - name: Build documentation + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: cargo doc --no-deps --target ${{ matrix.target }} --all-features diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 6fdb266..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: CI - -on: - push: - branches: [main, dev] - pull_request: - branches: [main, dev] - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - -jobs: - # Quick check job - runs format and lint checks - quick-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - components: rustfmt, clippy - - name: Check rust version - run: rustc --version --verbose - - name: Check code format - run: cargo fmt --all -- --check - - name: Clippy (default target) - run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default - - # Build and test job - runs on multiple platforms - build-and-test: - runs-on: ubuntu-latest - needs: quick-check - strategy: - fail-fast: false - matrix: - target: - - x86_64-unknown-linux-gnu - - x86_64-unknown-none - - riscv64gc-unknown-none-elf - - aarch64-unknown-none-softfloat - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - components: rust-src, clippy - targets: ${{ matrix.target }} - - name: Clippy - run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings -A clippy::new_without_default - - name: Build - run: cargo build --target ${{ matrix.target }} --all-features - - name: Unit test - if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - # Documentation job - doc: - runs-on: ubuntu-latest - needs: quick-check - permissions: - contents: write - env: - default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - - name: Build docs - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - name: Deploy to Github Pages - if: ${{ github.ref == env.default-branch }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - single-commit: true - branch: gh-pages - folder: target/doc diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..bdcfbdb --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,66 @@ +name: Deploy + +on: + push: + branches: + - '**' + tags-ignore: + - 'v*' + - 'v*-pre.*' + pull_request: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + quality-check: + uses: ./.github/workflows/check.yml + + test: + uses: ./.github/workflows/test.yml + + build-doc: + name: Build documentation + runs-on: ubuntu-latest + needs: quality-check + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: Build docs + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: | + cargo doc --no-deps --all-features + printf '' > target/doc/index.html + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: target/doc + + deploy-doc: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build-doc + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml deleted file mode 100644 index 43d1863..0000000 --- a/.github/workflows/release-check.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Release Check - -on: - push: - tags: - - 'v*' - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - -jobs: - release-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - components: rust-src, clippy, rustfmt - - # Verify version consistency between git tag and Cargo.toml - - name: Verify version consistency - run: | - TAG_VERSION=${GITHUB_REF#refs/tags/v} - CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | 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!" - - # Format check - - name: Check code format - run: cargo fmt --all -- --check - - # Clippy check - - name: Clippy - run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default - - # Build check - - name: Build - run: cargo build --all-features - - # Unit tests - - name: Unit test - run: cargo test --all-features -- --nocapture - - # Documentation build - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --all-features - - # Publish dry-run - - name: Publish dry-run - run: cargo publish --dry-run diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a63ae37 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,156 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + - 'v*.*.*-pre.*' + +permissions: + contents: write + +jobs: + check: + uses: ./.github/workflows/check.yml + + test: + uses: ./.github/workflows/test.yml + needs: check + + create-release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag and branch (HEAD-based) + shell: bash + run: | + set -e + + TAG="${{ github.ref_name }}" + TAG_COMMIT=$(git rev-list -n 1 "$TAG") + + git fetch origin main dev + + MAIN_HEAD=$(git rev-parse origin/main) + DEV_HEAD=$(git rev-parse origin/dev) + + echo "Tag: $TAG" + echo "Tag commit: $TAG_COMMIT" + echo "main HEAD: $MAIN_HEAD" + echo "dev HEAD: $DEV_HEAD" + + if [[ "$TAG" == *-pre.* ]]; then + if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then + echo "❌ prerelease tag must be created from dev HEAD" + exit 1 + fi + echo "✅ prerelease tag validated on dev" + else + if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then + echo "❌ stable release tag must be created from main HEAD" + exit 1 + fi + echo "✅ stable release tag validated on main" + fi + + - name: Verify version consistency + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml + 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!" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + draft: false + prerelease: ${{ contains(github.ref_name, '-pre.') }} + body: | + ## ${{ github.ref_name }} + + - [Documentation](https://docs.rs/axdevice_base) + - [crates.io](https://crates.io/crates/axdevice_base) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-crates: + name: Publish to crates.io + runs-on: ubuntu-latest + needs: check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag and branch (HEAD-based) + shell: bash + run: | + set -e + + TAG="${{ github.ref_name }}" + TAG_COMMIT=$(git rev-list -n 1 "$TAG") + + git fetch origin main dev + + MAIN_HEAD=$(git rev-parse origin/main) + DEV_HEAD=$(git rev-parse origin/dev) + + echo "Tag: $TAG" + echo "Tag commit: $TAG_COMMIT" + echo "main HEAD: $MAIN_HEAD" + echo "dev HEAD: $DEV_HEAD" + + if [[ "$TAG" == *-pre.* ]]; then + if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then + echo "❌ prerelease tag must be created from dev HEAD" + exit 1 + fi + echo "✅ prerelease tag validated on dev" + else + if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then + echo "❌ stable release tag must be created from main HEAD" + exit 1 + fi + echo "✅ stable release tag validated on main" + fi + + - name: Verify version consistency + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml + 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!" + + - 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 }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..cd99d90 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,21 @@ +name: Test + +on: + workflow_call: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: Run tests + run: cargo test --all-features -- --nocapture + + - name: Run doc tests + run: cargo test --doc