Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Build

on: [ pull_request, merge_group ]
on: [ pull_request, merge_group ]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }}
Expand Down Expand Up @@ -40,14 +40,14 @@ jobs:
cargo build --verbose --all-features --target aarch64-unknown-uefi
cargo build --verbose --all-features --target i686-unknown-uefi
cargo build --verbose --all-features --target riscv64gc-unknown-none-elf
cargo build --verbose --all-features --target thumbv7em-none-eabihf
cargo build --verbose --all-features --target x86_64-unknown-uefi
- name: Build (no_std, aarch64)
run: cargo build --verbose --all-features --target thumbv7em-none-eabihf
- name: Run tests
run: cargo test --verbose --all-features

integration_test:
runs-on: ubuntu-latest
name: Integration Test
steps:
- uses: actions/checkout@v6
- name: Setup Rust toolchain
Expand All @@ -58,18 +58,26 @@ jobs:
with:
key: "integration-test"
cache-directories:
./test/target
- name: Install QEMU
./integration-test/x86/target
./integration-test/riscv/target
- name: Install integration-test dependencies
run: |
sudo apt-get update
sudo apt-get install -y qemu-system-i386
sudo apt-get install -y \
make \
qemu-system-misc \
qemu-system-x86

- name: Verify QEMU installation
run: qemu-system-i386 --version
run: |
qemu-system-i386 --version
qemu-system-riscv64 --version
- name: Build
run: cd test && make
working-directory: integration-test
run: make
- name: Run
run: cd test && make run
working-directory: integration-test
run: make run

miri:
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

[files]
extend-exclude = [
"test/link.ld"
"integration-test/riscv/link.ld",
"integration-test/x86/link.ld",
]

[default]
Expand All @@ -14,4 +15,3 @@ extend-ignore-identifiers-re = [
THR = "THR"

[default.extend-identifiers]

8 changes: 8 additions & 0 deletions integration-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
target
x86/target
riscv/target

serial.chv.pio.txt
serial.qemu.pio.txt
cloud-hypervisor.stderr.txt
serial.qemu.mmio-riscv.txt
33 changes: 33 additions & 0 deletions integration-test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.PHONY: default
default: build

.PHONY: build
build:
$(MAKE) -C x86 build
$(MAKE) -C riscv build

.PHONY: build_x86
build_x86:
$(MAKE) -C x86 build

.PHONY: build_riscv
build_riscv:
$(MAKE) -C riscv build

.PHONY: run
run:
$(MAKE) -C x86 run
$(MAKE) -C riscv run

.PHONY: run_x86
run_x86:
$(MAKE) -C x86 run

.PHONY: run_riscv
run_riscv:
$(MAKE) -C riscv run

.PHONY: clean
clean:
$(MAKE) -C x86 clean
$(MAKE) -C riscv clean
26 changes: 26 additions & 0 deletions integration-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Integration Tests

This directory contains architecture-specific integration tests for
`uart_16550`.

## Layout

- `x86/`: boots a tiny x86 guest and verifies 16550 access through
port I/O.
- `riscv/`: boots a tiny RISC-V guest on QEMU `virt` and verifies
16550 access through MMIO.

## Usage

- `make`: build all integration test guests.
- `make run`: run all integration tests.
- `make run_x86`: run the x86 tests only.
- `make run_riscv`: run the RISC-V MMIO test only.
- `make clean`: clean both test builds.

## Notes

- The x86 test covers QEMU directly and tries Cloud Hypervisor when
the host supports it.
- The RISC-V test uses QEMU's `virt` machine because it exposes an
MMIO `ns16550a` UART at `0x1000_0000`.
30 changes: 30 additions & 0 deletions integration-test/riscv/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions integration-test/riscv/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "kernel-riscv-mmio"
version = "0.1.0"
edition = "2024"
publish = false

[profile.release]
codegen-units = 1
lto = true
opt-level = "s"

[dependencies]
qemu-exit = { version = "3.0.2", default-features = false }
uart_16550 = { path = "../../" }
32 changes: 32 additions & 0 deletions integration-test/riscv/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.PHONY: default
default: build

.PHONY: build
build:
cargo build --offline --release \
--target riscv64gc-unknown-none-elf \
-Z build-std=core,alloc,compiler_builtins \
-Z build-std-features=compiler-builtins-mem

.PHONY: run
run: | build
qemu-system-riscv64 \
-bios none \
-display none \
-device loader,file=target/riscv64gc-unknown-none-elf/release/kernel-riscv-mmio,cpu-num=0 \
-m 64M \
-machine virt \
-monitor none \
-no-reboot \
-nodefaults \
-serial file:serial.qemu.mmio-riscv.txt; \
rc=$$? ; \
if [ $$rc -ne 0 ]; then \
echo "QEMU RISC-V MMIO Test Run Failed. rc=$$rc" >&2 ; \
exit 1 ; \
fi
grep "hello from serial via riscv mmio" ./serial.qemu.mmio-riscv.txt

.PHONY: clean
clean:
cargo clean
File renamed without changes.
48 changes: 48 additions & 0 deletions integration-test/riscv/link.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
ENTRY(start)

PHDRS
{
kernel_rx PT_LOAD FLAGS(5);
kernel_rw PT_LOAD FLAGS(6);
kernel_ro PT_LOAD FLAGS(4);
}

SECTIONS
{
/*
* QEMU's RISC-V virt board maps RAM starting at 0x8000_0000.
* Load the guest image slightly above that base.
*/
.text 0x80200000 : AT(0x80200000) ALIGN(4K)
{
*(.text.start)
*(.text .text.*)
} : kernel_rx

.rodata :
{
*(.rodata .rodata.*)
} : kernel_ro

.data :
{
*(.data .data.*)
} : kernel_rw

.bss :
{
*(COMMON)
*(.bss .bss.*)
} : kernel_rw

/DISCARD/ :
{
*(.comment .comment.*)
*(.dynamic)
*(.eh_frame*)
*(.got .got.*)
*(.note*)
*(.plt .plt.*)
*(.rela .rela.*)
}
}
62 changes: 62 additions & 0 deletions integration-test/riscv/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#![no_main]
#![no_std]

core::arch::global_asm!(include_str!("start.S"));

use core::alloc::{GlobalAlloc, Layout};
use core::fmt::Write;
use core::panic::PanicInfo;
use core::ptr::NonNull;
use qemu_exit::QEMUExit;
use uart_16550::{Config, Uart16550Tty};

struct DummyGlobalAlloc;

#[global_allocator]
static GLOBAL_ALLOCATOR: DummyGlobalAlloc = DummyGlobalAlloc;

unsafe impl GlobalAlloc for DummyGlobalAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
panic!("unsupported! layout={layout:?}");
}

unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
panic!("unsupported! ptr={ptr:?}, layout={layout:?}");
}
}

const UART_BASE: usize = 0x1000_0000;
const UART_STRIDE: u8 = 1;
const SIFIVE_TEST_BASE: u64 = 0x0010_0000;

#[unsafe(no_mangle)]
extern "C" fn rust_entry() -> ! {
main()
}

fn exit_qemu(success: bool) -> ! {
let exit = qemu_exit::RISCV64::new(SIFIVE_TEST_BASE);
if success {
exit.exit_success()
} else {
exit.exit_failure()
}
}

fn main() -> ! {
let uart_base = NonNull::new(UART_BASE as *mut u8).expect("valid MMIO base");

unsafe {
let mut uart = Uart16550Tty::new_mmio(uart_base, UART_STRIDE, Config::default())
.expect("MMIO UART should initialize");
uart.write_str("hello from serial via riscv mmio")
.expect("serial write should succeed");
}

exit_qemu(true)
}

#[panic_handler]
fn panic_handler(_info: &PanicInfo) -> ! {
exit_qemu(false);
}
16 changes: 16 additions & 0 deletions integration-test/riscv/src/start.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.section .text.start

.globl start
start:
la sp, stack_end
call rust_entry

1:
wfi
j 1b

.section .bss.stack, "aw", @nobits
.align 16
stack_begin:
.zero 16384
stack_end:
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
# rustc 1.96 - nightly needed for the build-std feature
channel = "nightly-2026-03-17"
profile = "minimal"
targets = [ "x86_64-unknown-linux-gnu" ]
targets = [
"x86_64-unknown-linux-gnu",
"riscv64gc-unknown-none-elf",
]
components = [
"cargo",
"clippy",
Expand Down
2 changes: 1 addition & 1 deletion test/Cargo.lock → integration-test/x86/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions test/Cargo.toml → integration-test/x86/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "kernel"
name = "kernel-x86"
version = "0.1.0"
edition = "2024"
publish = false
Expand All @@ -12,5 +12,5 @@ opt-level = "s"
[dependencies]
anyhow = { version = "1.0", default-features = false }
log = { version = "0.4", default-features = false }
uart_16550 = { path = "../." }
qemu-exit = { version = "3.0.2", default-features = false }
qemu-exit = { version = "3.0.2", default-features = false }
uart_16550 = { path = "../../" }
Loading
Loading