Skip to content

Commit 21f2ee9

Browse files
authored
Merge pull request #20 from numpy1314/master
Refactor the ci.yml and add the mock test
2 parents 75d9db2 + f9b9fca commit 21f2ee9

File tree

8 files changed

+297
-57
lines changed

8 files changed

+297
-57
lines changed

.github/workflows/check.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Check
2+
3+
on:
4+
workflow_call:
5+
push:
6+
branches: [ master ]
7+
pull_request:
8+
branches: [ master ]
9+
10+
jobs:
11+
check:
12+
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Install Rust
21+
uses: dtolnay/rust-toolchain@nightly
22+
with:
23+
components: rust-src, clippy, rustfmt
24+
targets: ${{ matrix.targets }}
25+
26+
- name: Check Formatting
27+
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
28+
run: cargo fmt --all -- --check
29+
30+
- name: Clippy
31+
run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
32+
33+
- name: Build
34+
run: cargo build --target ${{ matrix.targets }} --all-features

.github/workflows/ci.yml

Lines changed: 0 additions & 55 deletions
This file was deleted.

.github/workflows/deploy.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Deploy Docs
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
deploy:
12+
runs-on: ubuntu-latest
13+
env:
14+
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- name: Install Rust
19+
uses: dtolnay/rust-toolchain@nightly
20+
with:
21+
components: rust-src
22+
23+
- name: Build Documentation
24+
run: cargo doc --no-deps --all-features
25+
26+
- name: Create index.html
27+
run: |
28+
PKG_NAME=$(cargo tree | head -1 | cut -d' ' -f1)
29+
echo "<meta http-equiv=\"refresh\" content=\"0;url=${PKG_NAME}/index.html\">" > target/doc/index.html
30+
31+
- name: Deploy to GitHub Pages
32+
uses: peaceiris/actions-gh-pages@v3
33+
with:
34+
github_token: ${{ secrets.GITHUB_TOKEN }}
35+
publish_dir: ./target/doc

.github/workflows/release.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*.*.*'
7+
- 'v*.*.*-pre.*'
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
check:
15+
if: github.repository_owner == 'arceos-hypervisor'
16+
name: Check
17+
uses: ./.github/workflows/check.yml
18+
19+
test:
20+
if: github.repository_owner == 'arceos-hypervisor'
21+
name: Test
22+
uses: ./.github/workflows/test.yml
23+
needs: check
24+
25+
publish-crates:
26+
if: github.repository_owner == 'arceos-hypervisor'
27+
name: Publish to crates.io
28+
runs-on: ubuntu-latest
29+
needs: test
30+
steps:
31+
- name: Checkout code
32+
uses: actions/checkout@v4
33+
with:
34+
fetch-depth: 0
35+
36+
- name: Validate tag and branch (HEAD-based)
37+
shell: bash
38+
run: |
39+
set -e
40+
41+
TAG="${{ github.ref_name }}"
42+
TAG_COMMIT=$(git rev-list -n 1 "$TAG")
43+
44+
git fetch origin master
45+
46+
MASTER_HEAD=$(git rev-parse origin/master)
47+
48+
echo "Tag: $TAG"
49+
echo "Tag commit: $TAG_COMMIT"
50+
echo "Master HEAD: $MASTER_HEAD"
51+
52+
if [ "$TAG_COMMIT" != "$MASTER_HEAD" ]; then
53+
echo "❌ release tag must be created from master HEAD"
54+
exit 1
55+
fi
56+
echo "✅ tag validated on master"
57+
58+
- name: Install Rust
59+
uses: dtolnay/rust-toolchain@nightly
60+
with:
61+
components: rust-src
62+
63+
- name: Publish to crates.io
64+
run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
65+
66+
create-release:
67+
if: github.repository_owner == 'arceos-hypervisor'
68+
name: Create GitHub Release
69+
runs-on: ubuntu-latest
70+
needs: publish-crates
71+
steps:
72+
- name: Checkout code
73+
uses: actions/checkout@v4
74+
with:
75+
fetch-depth: 0
76+
77+
- name: Create GitHub Release
78+
uses: softprops/action-gh-release@v2
79+
with:
80+
draft: false
81+
prerelease: ${{ contains(github.ref_name, '-pre.') }}
82+
generate_release_notes: true
83+
body: |
84+
## ${{ github.ref_name }}
85+
86+
- Documentation: https://docs.rs/axdevice
87+
- crates.io: https://crates.io/crates/axdevice

.github/workflows/test.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Test
2+
3+
on:
4+
workflow_call:
5+
push:
6+
branches: [ master ]
7+
pull_request:
8+
branches: [ master ]
9+
10+
jobs:
11+
test:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Install Rust
17+
uses: dtolnay/rust-toolchain@nightly
18+
with:
19+
components: rust-src
20+
21+
- name: Run Tests
22+
run: cargo test --target x86_64-unknown-linux-gnu -- --nocapture

Cargo.toml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
[package]
22
name = "axdevice"
33
version = "0.1.0"
4+
description = "A reusable, OS-agnostic device abstraction layer designed for virtual machines."
5+
homepage = "https://github.com/arceos-hypervisor/axdevice"
6+
repository = "https://github.com/arceos-hypervisor/axdevice"
7+
keywords = ["hypervisor"]
8+
license = "MIT OR Apache-2.0"
9+
authors = ["aarkegz <aarkegz@gmail.com>"]
10+
documentation = "https://docs.rs/axdevice"
11+
categories = ["virtualization"]
412
edition = "2024"
13+
readme = "README.md"
514

615
[features]
716

@@ -17,7 +26,14 @@ memory_addr = "0.4"
1726
axvmconfig = { version = "0.1", default-features = false }
1827
axaddrspace = "0.1"
1928
axdevice_base = "0.1"
20-
range-alloc = { git = "https://github.com/arceos-hypervisor/range-alloc.git" }
29+
range-alloc-arceos = "0.1.4-pre.1"
2130

2231
[target.'cfg(target_arch = "aarch64")'.dependencies]
2332
arm_vgic = { version = "0.1", features = ["vgicv3"] }
33+
34+
[dev-dependencies]
35+
axdevice_base = "0.1"
36+
axaddrspace = "0.1"
37+
memory_addr = "0.4"
38+
axerrno = "0.1"
39+
axvmconfig = "0.1"

src/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use alloc::sync::Arc;
22
use alloc::vec::Vec;
33
use core::ops::Range;
44

5-
use range_alloc::RangeAllocator;
5+
use range_alloc_arceos::RangeAllocator;
66
use spin::Mutex;
77

88
use axaddrspace::{

tests/test.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use axaddrspace::device::AccessWidth;
2+
use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange};
3+
use axdevice::{AxVmDeviceConfig, AxVmDevices};
4+
use axdevice_base::BaseDeviceOps;
5+
use axerrno::AxResult;
6+
use axvmconfig::EmulatedDeviceType;
7+
use std::sync::{Arc, Mutex};
8+
9+
struct MockMmioDevice {
10+
name: String,
11+
range: GuestPhysAddrRange,
12+
last_write: Mutex<Option<(usize, usize)>>,
13+
}
14+
15+
impl MockMmioDevice {
16+
fn new(name: &str, base: usize, len: usize) -> Self {
17+
let start = GuestPhysAddr::from(base);
18+
let end = GuestPhysAddr::from(base + len);
19+
20+
Self {
21+
name: String::from(name),
22+
range: GuestPhysAddrRange::new(start, end),
23+
last_write: Mutex::new(None),
24+
}
25+
}
26+
27+
fn get_last_write(&self) -> Option<(usize, usize)> {
28+
*self.last_write.lock().unwrap()
29+
}
30+
}
31+
32+
impl BaseDeviceOps<GuestPhysAddrRange> for MockMmioDevice {
33+
fn address_range(&self) -> GuestPhysAddrRange {
34+
self.range
35+
}
36+
37+
fn emu_type(&self) -> EmulatedDeviceType {
38+
EmulatedDeviceType::IVCChannel
39+
}
40+
41+
fn handle_read(&self, _addr: GuestPhysAddr, _width: AccessWidth) -> AxResult<usize> {
42+
Ok(0xDEAD_BEEF)
43+
}
44+
45+
fn handle_write(&self, addr: GuestPhysAddr, _width: AccessWidth, val: usize) -> AxResult {
46+
println!(
47+
"[Test] Device {} write: addr={:?}, val={:#x}",
48+
self.name, addr, val
49+
);
50+
51+
let offset = addr.as_usize() - self.range.start.as_usize();
52+
*self.last_write.lock().unwrap() = Some((offset, val));
53+
Ok(())
54+
}
55+
}
56+
57+
#[test]
58+
fn test_mmio_dispatch_functionality() {
59+
let config = AxVmDeviceConfig::new(vec![]);
60+
let mut devices = AxVmDevices::new(config);
61+
62+
let base_addr = 0x1000_0000;
63+
let dev_size = 0x1000;
64+
let mock_dev = Arc::new(MockMmioDevice::new("TestDev", base_addr, dev_size));
65+
66+
devices.add_mmio_dev(mock_dev.clone());
67+
68+
let write_offset = 0x40;
69+
let target_addr = GuestPhysAddr::from(base_addr + write_offset);
70+
let write_val = 0x1234_5678;
71+
72+
let width = AccessWidth::try_from(4).unwrap();
73+
74+
devices
75+
.handle_mmio_write(target_addr, width, write_val)
76+
.expect("MMIO write failed");
77+
78+
let last = mock_dev.get_last_write();
79+
assert!(last.is_some(), "Device did not receive write command");
80+
let (off, val) = last.unwrap();
81+
assert_eq!(off, write_offset, "Write offset mismatch");
82+
assert_eq!(val, write_val, "Write value mismatch");
83+
84+
let read_result = devices
85+
.handle_mmio_read(target_addr, width)
86+
.expect("MMIO read failed");
87+
88+
assert_eq!(read_result, 0xDEAD_BEEF, "Read value mismatch");
89+
}
90+
91+
#[test]
92+
#[should_panic(expected = "emu_device not found")]
93+
fn test_mmio_panic_on_missing_device() {
94+
let config = AxVmDeviceConfig::new(vec![]);
95+
let devices = AxVmDevices::new(config);
96+
97+
let invalid_addr = GuestPhysAddr::from(0x9999_9999);
98+
let width = AccessWidth::try_from(4).unwrap();
99+
100+
let _ = devices.handle_mmio_read(invalid_addr, width);
101+
}

0 commit comments

Comments
 (0)