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 531ddd1..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-name: CI
-
-on: [push, pull_request]
-
-jobs:
- ci:
- runs-on: ubuntu-latest
- 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]
- 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
-
- doc:
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- 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
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.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
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
-[](https://github.com/arceos-hypervisor/axdevice_crates/actions/workflows/ci.yml)
-[](https://github.com/arceos-hypervisor/axdevice_crates)
+[](https://github.com/arceos-hypervisor/axdevice_base/actions/workflows/ci.yml)
+[](https://crates.io/crates/axdevice_base)
+[](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)]