diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index faf0e21..a76ba61 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 }} @@ -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 @@ -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 diff --git a/.typos.toml b/.typos.toml index d39b71d..b6ba42a 100644 --- a/.typos.toml +++ b/.typos.toml @@ -2,7 +2,8 @@ [files] extend-exclude = [ - "test/link.ld" + "integration-test/riscv/link.ld", + "integration-test/x86/link.ld", ] [default] @@ -14,4 +15,3 @@ extend-ignore-identifiers-re = [ THR = "THR" [default.extend-identifiers] - diff --git a/integration-test/.gitignore b/integration-test/.gitignore new file mode 100644 index 0000000..ab5faa7 --- /dev/null +++ b/integration-test/.gitignore @@ -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 diff --git a/integration-test/Makefile b/integration-test/Makefile new file mode 100644 index 0000000..01350bb --- /dev/null +++ b/integration-test/Makefile @@ -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 diff --git a/integration-test/README.md b/integration-test/README.md new file mode 100644 index 0000000..32c2ce7 --- /dev/null +++ b/integration-test/README.md @@ -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`. diff --git a/integration-test/riscv/Cargo.lock b/integration-test/riscv/Cargo.lock new file mode 100644 index 0000000..53e8525 --- /dev/null +++ b/integration-test/riscv/Cargo.lock @@ -0,0 +1,30 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "kernel-riscv-mmio" +version = "0.1.0" +dependencies = [ + "qemu-exit", + "uart_16550", +] + +[[package]] +name = "qemu-exit" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb0fd6580eeed0103c054e3fba2c2618ff476943762f28a645b63b8692b21c9" + +[[package]] +name = "uart_16550" +version = "0.5.0" +dependencies = [ + "bitflags", +] diff --git a/integration-test/riscv/Cargo.toml b/integration-test/riscv/Cargo.toml new file mode 100644 index 0000000..9b1347c --- /dev/null +++ b/integration-test/riscv/Cargo.toml @@ -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 = "../../" } diff --git a/integration-test/riscv/Makefile b/integration-test/riscv/Makefile new file mode 100644 index 0000000..58e9798 --- /dev/null +++ b/integration-test/riscv/Makefile @@ -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 diff --git a/test/build.rs b/integration-test/riscv/build.rs similarity index 100% rename from test/build.rs rename to integration-test/riscv/build.rs diff --git a/integration-test/riscv/link.ld b/integration-test/riscv/link.ld new file mode 100644 index 0000000..bd4610f --- /dev/null +++ b/integration-test/riscv/link.ld @@ -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.*) + } +} diff --git a/integration-test/riscv/src/main.rs b/integration-test/riscv/src/main.rs new file mode 100644 index 0000000..dfe6e5e --- /dev/null +++ b/integration-test/riscv/src/main.rs @@ -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); +} diff --git a/integration-test/riscv/src/start.S b/integration-test/riscv/src/start.S new file mode 100644 index 0000000..21d8c47 --- /dev/null +++ b/integration-test/riscv/src/start.S @@ -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: diff --git a/test/rust-toolchain.toml b/integration-test/rust-toolchain.toml similarity index 83% rename from test/rust-toolchain.toml rename to integration-test/rust-toolchain.toml index 8e66ce2..9e7e539 100644 --- a/test/rust-toolchain.toml +++ b/integration-test/rust-toolchain.toml @@ -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", diff --git a/test/Cargo.lock b/integration-test/x86/Cargo.lock similarity index 98% rename from test/Cargo.lock rename to integration-test/x86/Cargo.lock index 0c82e29..bf7a77f 100644 --- a/test/Cargo.lock +++ b/integration-test/x86/Cargo.lock @@ -15,7 +15,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] -name = "kernel" +name = "kernel-x86" version = "0.1.0" dependencies = [ "anyhow", diff --git a/test/Cargo.toml b/integration-test/x86/Cargo.toml similarity index 68% rename from test/Cargo.toml rename to integration-test/x86/Cargo.toml index c095881..42497ed 100644 --- a/test/Cargo.toml +++ b/integration-test/x86/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kernel" +name = "kernel-x86" version = "0.1.0" edition = "2024" publish = false @@ -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 = "../../" } diff --git a/integration-test/x86/Makefile b/integration-test/x86/Makefile new file mode 100644 index 0000000..aa1c62a --- /dev/null +++ b/integration-test/x86/Makefile @@ -0,0 +1,81 @@ +.PHONY: default +default: build + +.PHONY: build +build: + cargo build --offline --release \ + --target ./x86-unknown-none.json \ + -Z build-std=core,alloc,compiler_builtins \ + -Z build-std-features=compiler-builtins-mem \ + -Zjson-target-spec + @# Identical object, different ELF header. Required for Cloud Hypervisor PVH boot. + objcopy -O elf64-x86-64 target/x86-unknown-none/release/kernel-x86 target/x86-unknown-none/release/kernel-x86.elf64 + +.PHONY: run_qemu +run_qemu: | build + @# QEMU/TCG allows better debuggability over QEMU/KVM + qemu-system-i386 \ + -debugcon stdio \ + -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ + -display none \ + -kernel target/x86-unknown-none/release/kernel-x86 \ + -m 64M \ + -machine q35,accel=tcg \ + -monitor none \ + -no-reboot \ + -nodefaults \ + -serial file:serial.qemu.pio.txt; \ + rc=$$? ; \ + if [ $$rc -ne 73 ]; then \ + echo "QEMU Test Run Failed. rc=$$rc" >&2 ; \ + exit 1 ; \ + fi + grep "hello from serial via x86 port I/O" ./serial.qemu.pio.txt + +.PHONY: run_chv +run_chv: | build + cloud-hypervisor \ + --cpus boot=1 \ + --memory size=64M \ + --console off \ + --debug-console tty \ + --serial file=serial.chv.pio.txt \ + --kernel target/x86-unknown-none/release/kernel-x86.elf64; \ + rc=$$? ; \ + if [ $$rc -ne 0 ]; then \ + echo "Cloud Hypervisor Test Run Failed. rc=$$rc" >&2 ; \ + exit 1 ; \ + fi + grep "hello from serial via x86 port I/O" ./serial.chv.pio.txt + +.PHONY: run_chv_if_supported +run_chv_if_supported: | build + if ! command -v cloud-hypervisor >/dev/null 2>&1; then \ + echo "WARNING: Skipping Cloud Hypervisor test because cloud-hypervisor is not in PATH." >&2 ; \ + elif [ ! -c /dev/kvm ]; then \ + echo "WARNING: Skipping Cloud Hypervisor test because /dev/kvm is not available." >&2 ; \ + else \ + cloud-hypervisor \ + --cpus boot=1 \ + --memory size=64M \ + --console off \ + --debug-console tty \ + --serial file=serial.chv.pio.txt \ + --kernel target/x86-unknown-none/release/kernel-x86.elf64 \ + 2>cloud-hypervisor.stderr.txt; \ + rc=$$? ; \ + if [ $$rc -eq 0 ]; then \ + grep "hello from serial via x86 port I/O" ./serial.chv.pio.txt ; \ + else \ + echo "Cloud Hypervisor Test Run Failed. rc=$$rc" >&2 ; \ + cat ./cloud-hypervisor.stderr.txt >&2 ; \ + exit 1 ; \ + fi ; \ + fi + +.PHONY: run +run: run_qemu run_chv_if_supported + +.PHONY: clean +clean: + cargo clean diff --git a/integration-test/x86/build.rs b/integration-test/x86/build.rs new file mode 100644 index 0000000..c6fef93 --- /dev/null +++ b/integration-test/x86/build.rs @@ -0,0 +1,6 @@ +fn main() { + let linker_script = "link.ld"; + + println!("cargo:rerun-if-changed={linker_script}"); + println!("cargo:rustc-link-arg=-T{linker_script}"); +} diff --git a/test/link.ld b/integration-test/x86/link.ld similarity index 80% rename from test/link.ld rename to integration-test/x86/link.ld index 2b47bd6..4ff779b 100644 --- a/test/link.ld +++ b/integration-test/x86/link.ld @@ -3,9 +3,10 @@ ENTRY(start) PHDRS { - kernel_rx PT_LOAD FLAGS(5); - kernel_rw PT_LOAD FLAGS(6); - kernel_ro PT_LOAD FLAGS(4); + kernel_rx PT_LOAD FLAGS(5); + kernel_rw PT_LOAD FLAGS(6); + kernel_ro PT_LOAD FLAGS(4); + note PT_NOTE; } SECTIONS @@ -38,6 +39,10 @@ SECTIONS *(.bss .bss.*) } : kernel_rw + .note.xen_pvh : { + *(.note.xen_pvh) + } : note + /DISCARD/ : { *(.comment .comment.*) diff --git a/integration-test/x86/src/cpuid.rs b/integration-test/x86/src/cpuid.rs new file mode 100644 index 0000000..fa3fc6c --- /dev/null +++ b/integration-test/x86/src/cpuid.rs @@ -0,0 +1,97 @@ +//! Small CPUID helper for the two checks this project needs. + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +compile_error!("This module only supports x86/x86_64."); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct CpuidResult { + eax: u32, + ebx: u32, + ecx: u32, + edx: u32, +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct Cpuid; + +impl Cpuid { + #[inline] + pub const fn new() -> Self { + Self + } + + #[inline] + pub fn has_hypervisor_bit(&self) -> bool { + let res = self.cpuid(0x0000_0001, 0); + (res.ecx & (1 << 31)) != 0 + } + + #[inline] + pub fn cpu_brand_contains_qemu(&self) -> bool { + self.cpu_brand_bytes() + .map(|brand| contains_subslice(&brand, b"QEMU")) + .unwrap_or(false) + } + + #[inline] + fn cpuid(&self, leaf: u32, subleaf: u32) -> CpuidResult { + cpuid_count(leaf, subleaf) + } + + #[inline] + fn max_extended_leaf(&self) -> u32 { + self.cpuid(0x8000_0000, 0).eax + } + + #[inline] + fn cpu_brand_bytes(&self) -> Option<[u8; 48]> { + if self.max_extended_leaf() < 0x8000_0004 { + return None; + } + + let leaves = [ + self.cpuid(0x8000_0002, 0), + self.cpuid(0x8000_0003, 0), + self.cpuid(0x8000_0004, 0), + ]; + + let mut out = [0u8; 48]; + let mut i = 0; + + for leaf in leaves { + for reg in [leaf.eax, leaf.ebx, leaf.ecx, leaf.edx] { + out[i..i + 4].copy_from_slice(®.to_le_bytes()); + i += 4; + } + } + + Some(out) + } +} + +#[inline] +fn contains_subslice(haystack: &[u8], needle: &[u8]) -> bool { + if needle.is_empty() { + return true; + } + + haystack + .windows(needle.len()) + .any(|window| window == needle) +} + +#[inline] +fn cpuid_count(leaf: u32, subleaf: u32) -> CpuidResult { + #[cfg(target_arch = "x86")] + let r = core::arch::x86::__cpuid_count(leaf, subleaf); + + #[cfg(target_arch = "x86_64")] + let r = core::arch::x86_64::__cpuid_count(leaf, subleaf); + + CpuidResult { + eax: r.eax, + ebx: r.ebx, + ecx: r.ecx, + edx: r.edx, + } +} diff --git a/test/src/debugcon.rs b/integration-test/x86/src/debugcon.rs similarity index 88% rename from test/src/debugcon.rs rename to integration-test/x86/src/debugcon.rs index d21b14f..9e11319 100644 --- a/test/src/debugcon.rs +++ b/integration-test/x86/src/debugcon.rs @@ -8,7 +8,6 @@ static LOGGER: DebugconLogger = DebugconLogger; struct Debugcon; impl Debugcon { - /// I/O port of QEMUs debugcon device on x86. const IO_PORT: u16 = 0xe9; pub fn write_byte(byte: u8) { @@ -36,7 +35,6 @@ pub struct DebugconLogger; impl DebugconLogger { pub fn init() { - // Ignore, as we can't do anything about it here. let _ = log::set_logger(&LOGGER); log::set_max_level(LevelFilter::Trace); info!("Logger initialized!"); @@ -49,7 +47,6 @@ impl Log for DebugconLogger { } fn log(&self, record: &Record) { - // Ignore result as we can't do anything about it. let _ = writeln!( Debugcon, "[{:>5}: {}@{}]: {}", diff --git a/test/src/main.rs b/integration-test/x86/src/main.rs similarity index 71% rename from test/src/main.rs rename to integration-test/x86/src/main.rs index 97281cf..9a2339e 100644 --- a/test/src/main.rs +++ b/integration-test/x86/src/main.rs @@ -25,18 +25,22 @@ use log::error; use qemu_exit::QEMUExit; use uart_16550::{Config, Uart16550Tty}; +mod cpuid; mod debugcon; +mod pvh; + +fn runs_inside_qemu() -> bool { + let cpuid = cpuid::Cpuid::new(); + cpuid.has_hypervisor_bit() && cpuid.cpu_brand_contains_qemu() +} -/// Entry into the Rust code. #[unsafe(no_mangle)] extern "C" fn rust_entry() -> ! { main().expect("Should run kernel"); unreachable!(); } -/// Exits QEMU via the shutdown device on the i440fx board. fn exit_qemu(success: bool) -> ! { - // configured in Makefile let port = 0xf4; let exit = qemu_exit::X86::new(port, 73); if success { @@ -46,23 +50,38 @@ fn exit_qemu(success: bool) -> ! { } } -/// Executes the kernel's main logic. +fn exit_chv() -> ! { + unsafe { + core::arch::asm!( + "outw %ax, %dx", + in("ax") 0x34, + in("dx") 0x600, + options(att_syntax, noreturn) + ) + } +} + +fn exit_vmm(success: bool) -> ! { + if runs_inside_qemu() { + exit_qemu(success); + } else { + exit_chv(); + } +} + fn main() -> anyhow::Result<()> { debugcon::DebugconLogger::init(); - // SAFETY: we have exclusive access and the port is valid unsafe { let mut uart = Uart16550Tty::new_port(0x3f8, Config::default())?; uart.write_str("hello from serial via x86 port I/O")?; } - // TODO MMIO test? QEMU doesn't offer this (on x86). - - exit_qemu(true); + exit_vmm(true); } #[panic_handler] fn panic_handler(info: &PanicInfo) -> ! { error!("error: {}", info); - exit_qemu(false); + exit_vmm(false); } diff --git a/integration-test/x86/src/pvh.rs b/integration-test/x86/src/pvh.rs new file mode 100644 index 0000000..172501c --- /dev/null +++ b/integration-test/x86/src/pvh.rs @@ -0,0 +1,34 @@ +//! Xen PVH entry point. + +const XEN_ELFNOTE_PHYS32_ENTRY: u32 = 18; +type Name = [u8; 4]; +type CFunc = unsafe extern "C" fn(); + +#[repr(C, packed(4))] +struct ElfNote { + name_size: u32, + desc_size: u32, + kind: u32, + name: Name, + // Payload + desc: T, +} + +// The PVH Boot Protocol starts at the 32-bit entrypoint to our firmware. +unsafe extern "C" { + fn start(); +} + +/// Emits an ELF note into the binary which is a valid Xen PVH entry point. +/// +/// This is understood by some bootloaders or VMMs (e.g., Cloud Hypervisor) to +/// support direct kernel boot. +#[unsafe(link_section = ".note.xen_pvh")] +#[used] +static PVH_NOTE: ElfNote = ElfNote { + name_size: size_of::() as u32, + desc_size: size_of::() as u32, + kind: XEN_ELFNOTE_PHYS32_ENTRY, + name: *b"Xen\0", + desc: start, +}; diff --git a/test/src/start.S b/integration-test/x86/src/start.S similarity index 56% rename from test/src/start.S rename to integration-test/x86/src/start.S index ccff51b..cff845f 100644 --- a/test/src/start.S +++ b/integration-test/x86/src/start.S @@ -2,17 +2,12 @@ # # This is understood by LLVM. -# Symbol from Rust .extern rust_entry .code32 .section .multiboot_header, "a", @progbits -/* - * Multiboot v1 Header. - * Required so that we can be booted by QEMU via the "-kernel" parameter. - */ .align 8 .global multiboot_header multiboot_header: @@ -24,26 +19,21 @@ multiboot_header: .global start start: - # Set-up stack (stack grows downwards) mov $stack_end, %esp mov $stack_end, %ebp - # Pass some parameters to the kernel mov $0x42 , %eax mov $0x1337, %ebx mov $0xc0ffee, %ecx - # x86 (i386) calling convention: - # push arguments on stack in reverse order - push %ecx # 3rd parameter - push %ebx # 2nd parameter - push %eax # 1st parameter + push %ecx + push %ebx + push %eax call rust_entry ud2 .section .data -# 16-byte aligned 16k stack. .align 16 stack_begin: .zero 16384 diff --git a/test/x86-unknown-none.json b/integration-test/x86/x86-unknown-none.json similarity index 100% rename from test/x86-unknown-none.json rename to integration-test/x86/x86-unknown-none.json diff --git a/test/.gitignore b/test/.gitignore deleted file mode 100644 index f540534..0000000 --- a/test/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -target - -serial.mmio.txt -serial.pio.txt diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index 7ac9a24..0000000 --- a/test/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -.PHONY: default -default: build - - -.PHONY: build -build: - cargo build --release \ - --target ./x86-unknown-none.json \ - -Z build-std=core,alloc,compiler_builtins \ - -Z build-std-features=compiler-builtins-mem \ - -Zjson-target-spec - - -.PHONY: run -run: | build - @# QEMU/TCG allows better debuggability over QEMU/KVM - qemu-system-i386 \ - -debugcon stdio \ - -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ - -display none \ - -kernel target/x86-unknown-none/release/kernel \ - -m 64M \ - -machine q35,accel=tcg \ - -monitor none \ - -no-reboot \ - -nodefaults \ - -serial file:serial.pio.txt; \ - rc=$$? ; \ - if [ $$rc -ne 73 ]; then \ - echo "Test Run Failed" >&2 ; \ - exit 1 ; \ - fi - grep "hello from serial via x86 port I/O" ./serial.pio.txt - - -.PHONY: clean -clean: - cargo clean