From eed3fbda4b7bf80ba6b2f8dbfe462fbf6c373b58 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Fri, 14 Nov 2025 17:04:48 +0530 Subject: [PATCH 1/9] Add qemu CI --- .github/workflows/qemu.yaml | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/qemu.yaml diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml new file mode 100644 index 00000000..6182dfdb --- /dev/null +++ b/.github/workflows/qemu.yaml @@ -0,0 +1,54 @@ +on: + push: + branches: [ master ] + pull_request: + merge_group: + +name: QEMU tests + +jobs: + qemu-tests: + strategy: + matrix: + target: + - riscv32i-unknown-none-elf + - riscv32im-unknown-none-elf + - riscv32imc-unknown-none-elf + - riscv32imac-unknown-none-elf + - riscv64imac-unknown-none-elf + - riscv64gc-unknown-none-elf + example: + - empty + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Install QEMU + run: | + sudo apt-get update + sudo apt-get install -y qemu-system-misc + + - name: Build example for QEMU + run: | + RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --release + + - name: Run example on QEMU (riscv32) + if: startsWith(matrix.target, 'riscv32') + run: | + timeout 5 qemu-system-riscv32 -machine sifive_u -nographic -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || true + + - name: Run example on QEMU (riscv64) + if: startsWith(matrix.target, 'riscv64') + run: | + timeout 5 qemu-system-riscv64 -machine sifive_u -nographic -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || true + + qemu-tests-check: + needs: + - qemu-tests + runs-on: ubuntu-latest + if: always() + steps: + - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' From 3e41c1245e8fe9635e1e73ffb42b270f8dcbb6ad Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Fri, 14 Nov 2025 17:58:33 +0530 Subject: [PATCH 2/9] Improve QEMU CI --- .github/workflows/qemu.yaml | 69 ++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml index 6182dfdb..feb24429 100644 --- a/.github/workflows/qemu.yaml +++ b/.github/workflows/qemu.yaml @@ -10,20 +10,40 @@ jobs: qemu-tests: strategy: matrix: + toolchain: [ stable, nightly, 1.68.0 ] target: - riscv32i-unknown-none-elf - riscv32im-unknown-none-elf - riscv32imc-unknown-none-elf - riscv32imac-unknown-none-elf + - riscv32imafc-unknown-none-elf - riscv64imac-unknown-none-elf - riscv64gc-unknown-none-elf example: - empty + - multi_core + features: + - "" + - s-mode + - single-hart + - v-trap + - s-mode,single-hart,v-trap + include: + # Nightly is only for reference and allowed to fail + - toolchain: nightly + experimental: true + exclude: + - toolchain: 1.68.0 + target: riscv32im-unknown-none-elf + - toolchain: 1.68.0 + target: riscv32imafc-unknown-none-elf runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || false }} steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@master with: + toolchain: ${{ matrix.toolchain }} targets: ${{ matrix.target }} - name: Install QEMU @@ -33,17 +53,47 @@ jobs: - name: Build example for QEMU run: | - RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --release + FEATURES_ARG="" + if [ -n "${{ matrix.features }}" ]; then + FEATURES_ARG="--features=${{ matrix.features }}" + fi + RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --release ${FEATURES_ARG} - name: Run example on QEMU (riscv32) if: startsWith(matrix.target, 'riscv32') run: | - timeout 5 qemu-system-riscv32 -machine sifive_u -nographic -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || true + FEATURES_LABEL="" + if [ -n "${{ matrix.features }}" ]; then + FEATURES_LABEL=" (${{ matrix.features }})" + else + FEATURES_LABEL=" (no features)" + fi + timeout 30 qemu-system-riscv32 -machine sifive_u -nographic -serial mon:stdio -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || { + exitcode=$? + if [ $exitcode -eq 124 ]; then + echo "ERROR: QEMU timeout${FEATURES_LABEL}" + exit 1 + fi + exit $exitcode + } - name: Run example on QEMU (riscv64) if: startsWith(matrix.target, 'riscv64') run: | - timeout 5 qemu-system-riscv64 -machine sifive_u -nographic -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || true + FEATURES_LABEL="" + if [ -n "${{ matrix.features }}" ]; then + FEATURES_LABEL=" (${{ matrix.features }})" + else + FEATURES_LABEL=" (no features)" + fi + timeout 30 qemu-system-riscv64 -machine sifive_u -nographic -serial mon:stdio -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || { + exitcode=$? + if [ $exitcode -eq 124 ]; then + echo "ERROR: QEMU timeout${FEATURES_LABEL}" + exit 1 + fi + exit $exitcode + } qemu-tests-check: needs: @@ -51,4 +101,13 @@ jobs: runs-on: ubuntu-latest if: always() steps: - - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' + - run: | + RESULT="${{ needs.qemu-tests.result }}" + if [ "$RESULT" = "failure" ]; then + echo "QEMU tests failed" + exit 1 + elif [ "$RESULT" = "skipped" ]; then + echo "QEMU tests skipped" + exit 1 + fi + echo "QEMU tests passed" From 509434678b223d4b8a8a80f76fb9eb95caffc77e Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Fri, 14 Nov 2025 18:10:45 +0530 Subject: [PATCH 3/9] Fix QEMU CI --- .github/workflows/qemu.yaml | 41 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml index feb24429..747fecb1 100644 --- a/.github/workflows/qemu.yaml +++ b/.github/workflows/qemu.yaml @@ -9,6 +9,7 @@ name: QEMU tests jobs: qemu-tests: strategy: + fail-fast: false matrix: toolchain: [ stable, nightly, 1.68.0 ] target: @@ -41,6 +42,16 @@ jobs: continue-on-error: ${{ matrix.experimental || false }} steps: - uses: actions/checkout@v4 + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-${{ matrix.toolchain }}- - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} @@ -48,20 +59,26 @@ jobs: - name: Install QEMU run: | - sudo apt-get update - sudo apt-get install -y qemu-system-misc + set -euo pipefail + for i in 1 2 3; do + sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-misc && break || sleep 5 + done + qemu-system-riscv64 --version || true + qemu-system-riscv32 --version || true - name: Build example for QEMU run: | + set -euo pipefail FEATURES_ARG="" if [ -n "${{ matrix.features }}" ]; then FEATURES_ARG="--features=${{ matrix.features }}" fi - RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --release ${FEATURES_ARG} + RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --locked --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --release ${FEATURES_ARG} - name: Run example on QEMU (riscv32) if: startsWith(matrix.target, 'riscv32') run: | + set -euo pipefail FEATURES_LABEL="" if [ -n "${{ matrix.features }}" ]; then FEATURES_LABEL=" (${{ matrix.features }})" @@ -80,6 +97,7 @@ jobs: - name: Run example on QEMU (riscv64) if: startsWith(matrix.target, 'riscv64') run: | + set -euo pipefail FEATURES_LABEL="" if [ -n "${{ matrix.features }}" ]; then FEATURES_LABEL=" (${{ matrix.features }})" @@ -94,20 +112,3 @@ jobs: fi exit $exitcode } - - qemu-tests-check: - needs: - - qemu-tests - runs-on: ubuntu-latest - if: always() - steps: - - run: | - RESULT="${{ needs.qemu-tests.result }}" - if [ "$RESULT" = "failure" ]; then - echo "QEMU tests failed" - exit 1 - elif [ "$RESULT" = "skipped" ]; then - echo "QEMU tests skipped" - exit 1 - fi - echo "QEMU tests passed" From ddc05b9daa8046745c5d94ebe7ed9dc0ae7b39b4 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Fri, 14 Nov 2025 18:33:17 +0530 Subject: [PATCH 4/9] Fix QEMU CI --- .github/workflows/qemu.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml index 747fecb1..6697bfbe 100644 --- a/.github/workflows/qemu.yaml +++ b/.github/workflows/qemu.yaml @@ -73,7 +73,8 @@ jobs: if [ -n "${{ matrix.features }}" ]; then FEATURES_ARG="--features=${{ matrix.features }}" fi - RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --locked --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --release ${FEATURES_ARG} + + RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --release ${FEATURES_ARG} - name: Run example on QEMU (riscv32) if: startsWith(matrix.target, 'riscv32') From 873c05164d9ba2a4ed702d1294f9dfe8e3c16afb Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Fri, 14 Nov 2025 18:48:57 +0530 Subject: [PATCH 5/9] Update QEMU CI --- .github/workflows/qemu.yaml | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml index 6697bfbe..ced6ecd2 100644 --- a/.github/workflows/qemu.yaml +++ b/.github/workflows/qemu.yaml @@ -61,10 +61,10 @@ jobs: run: | set -euo pipefail for i in 1 2 3; do - sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-misc && break || sleep 5 + sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-riscv qemu-system-misc opensbi && break || sleep 5 done - qemu-system-riscv64 --version || true - qemu-system-riscv32 --version || true + qemu-system-riscv64 --version + qemu-system-riscv32 --version - name: Build example for QEMU run: | @@ -86,7 +86,16 @@ jobs: else FEATURES_LABEL=" (no features)" fi - timeout 30 qemu-system-riscv32 -machine sifive_u -nographic -serial mon:stdio -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || { + + if [[ "${{ matrix.features }}" == *"s-mode"* ]]; then + echo "Skipping QEMU run for riscv32 S-mode${FEATURES_LABEL}" + exit 0 + fi + QEMU_ARGS="-machine virt -nographic -serial mon:stdio -bios none" + if [ "${{ matrix.example }}" = "multi_core" ]; then + QEMU_ARGS="$QEMU_ARGS -smp 4" + fi + timeout 60 qemu-system-riscv32 $QEMU_ARGS -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || { exitcode=$? if [ $exitcode -eq 124 ]; then echo "ERROR: QEMU timeout${FEATURES_LABEL}" @@ -105,7 +114,16 @@ jobs: else FEATURES_LABEL=" (no features)" fi - timeout 30 qemu-system-riscv64 -machine sifive_u -nographic -serial mon:stdio -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || { + QEMU_ARGS="-machine virt -nographic -serial mon:stdio" + if [[ "${{ matrix.features }}" == *"s-mode"* ]]; then + : + else + QEMU_ARGS="$QEMU_ARGS -bios none" + fi + if [ "${{ matrix.example }}" = "multi_core" ]; then + QEMU_ARGS="$QEMU_ARGS -smp 4" + fi + timeout 60 qemu-system-riscv64 $QEMU_ARGS -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || { exitcode=$? if [ $exitcode -eq 124 ]; then echo "ERROR: QEMU timeout${FEATURES_LABEL}" From fb3fce2b216f77f6b57db70e1c4f3a53e1fb4306 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Fri, 14 Nov 2025 18:54:00 +0530 Subject: [PATCH 6/9] Fix package issue --- .github/workflows/qemu.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml index ced6ecd2..3a1f939e 100644 --- a/.github/workflows/qemu.yaml +++ b/.github/workflows/qemu.yaml @@ -61,7 +61,7 @@ jobs: run: | set -euo pipefail for i in 1 2 3; do - sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-riscv qemu-system-misc opensbi && break || sleep 5 + sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-misc opensbi && break || sleep 5 done qemu-system-riscv64 --version qemu-system-riscv32 --version From 96edd3d3e2e580bb0a65cebbd28de18df9f8738b Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Sat, 15 Nov 2025 12:37:02 +0530 Subject: [PATCH 7/9] Add QEMU CI to add expected hello outputs --- .../riscv32imac-unknown-none-elf/hello.run | 1 + .github/workflows/qemu.yaml | 80 +------------------ Cargo.toml | 1 + .../riscv32imac-unknown-none-elf/hello.run | 0 riscv-rt/Cargo.toml | 1 + riscv-rt/examples/device_s_mode.x | 14 ++++ riscv-rt/examples/device_virt_m.x | 11 +++ riscv-rt/examples/device_virt_s.x | 11 +++ riscv-rt/examples/hello.rs | 34 ++++++++ xtask/Cargo.toml | 7 ++ xtask/src/main.rs | 53 ++++++++++++ 11 files changed, 137 insertions(+), 76 deletions(-) create mode 100644 .github/workflows/ci/expected/riscv32imac-unknown-none-elf/hello.run create mode 100644 ci/expected/riscv32imac-unknown-none-elf/hello.run create mode 100644 riscv-rt/examples/device_s_mode.x create mode 100644 riscv-rt/examples/device_virt_m.x create mode 100644 riscv-rt/examples/device_virt_s.x create mode 100644 riscv-rt/examples/hello.rs create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/main.rs diff --git a/.github/workflows/ci/expected/riscv32imac-unknown-none-elf/hello.run b/.github/workflows/ci/expected/riscv32imac-unknown-none-elf/hello.run new file mode 100644 index 00000000..37fa813f --- /dev/null +++ b/.github/workflows/ci/expected/riscv32imac-unknown-none-elf/hello.run @@ -0,0 +1 @@ +QEMU 10.1.2 monitor - type 'help' for more information diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml index 3a1f939e..973a3d13 100644 --- a/.github/workflows/qemu.yaml +++ b/.github/workflows/qemu.yaml @@ -13,31 +13,18 @@ jobs: matrix: toolchain: [ stable, nightly, 1.68.0 ] target: - - riscv32i-unknown-none-elf - - riscv32im-unknown-none-elf - - riscv32imc-unknown-none-elf - riscv32imac-unknown-none-elf - - riscv32imafc-unknown-none-elf - - riscv64imac-unknown-none-elf - riscv64gc-unknown-none-elf example: - - empty - - multi_core + - hello features: - "" - - s-mode - - single-hart - - v-trap - - s-mode,single-hart,v-trap include: - # Nightly is only for reference and allowed to fail - toolchain: nightly experimental: true exclude: - toolchain: 1.68.0 - target: riscv32im-unknown-none-elf - - toolchain: 1.68.0 - target: riscv32imafc-unknown-none-elf + target: riscv32imac-unknown-none-elf runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental || false }} steps: @@ -66,68 +53,9 @@ jobs: qemu-system-riscv64 --version qemu-system-riscv32 --version - - name: Build example for QEMU + - name: Run xtask QEMU test run: | set -euo pipefail - FEATURES_ARG="" - if [ -n "${{ matrix.features }}" ]; then - FEATURES_ARG="--features=${{ matrix.features }}" - fi - - RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --release ${FEATURES_ARG} + cargo run --package xtask -- qemu --target ${{ matrix.target }} --example ${{ matrix.example }} - - name: Run example on QEMU (riscv32) - if: startsWith(matrix.target, 'riscv32') - run: | - set -euo pipefail - FEATURES_LABEL="" - if [ -n "${{ matrix.features }}" ]; then - FEATURES_LABEL=" (${{ matrix.features }})" - else - FEATURES_LABEL=" (no features)" - fi - if [[ "${{ matrix.features }}" == *"s-mode"* ]]; then - echo "Skipping QEMU run for riscv32 S-mode${FEATURES_LABEL}" - exit 0 - fi - QEMU_ARGS="-machine virt -nographic -serial mon:stdio -bios none" - if [ "${{ matrix.example }}" = "multi_core" ]; then - QEMU_ARGS="$QEMU_ARGS -smp 4" - fi - timeout 60 qemu-system-riscv32 $QEMU_ARGS -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || { - exitcode=$? - if [ $exitcode -eq 124 ]; then - echo "ERROR: QEMU timeout${FEATURES_LABEL}" - exit 1 - fi - exit $exitcode - } - - - name: Run example on QEMU (riscv64) - if: startsWith(matrix.target, 'riscv64') - run: | - set -euo pipefail - FEATURES_LABEL="" - if [ -n "${{ matrix.features }}" ]; then - FEATURES_LABEL=" (${{ matrix.features }})" - else - FEATURES_LABEL=" (no features)" - fi - QEMU_ARGS="-machine virt -nographic -serial mon:stdio" - if [[ "${{ matrix.features }}" == *"s-mode"* ]]; then - : - else - QEMU_ARGS="$QEMU_ARGS -bios none" - fi - if [ "${{ matrix.example }}" = "multi_core" ]; then - QEMU_ARGS="$QEMU_ARGS -smp 4" - fi - timeout 60 qemu-system-riscv64 $QEMU_ARGS -kernel target/${{ matrix.target }}/release/examples/${{ matrix.example }} || { - exitcode=$? - if [ $exitcode -eq 124 ]; then - echo "ERROR: QEMU timeout${FEATURES_LABEL}" - exit 1 - fi - exit $exitcode - } diff --git a/Cargo.toml b/Cargo.toml index c8391343..65b34667 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "riscv-types", "tests-build", "tests-trybuild", + "xtask", ] default-members = [ diff --git a/ci/expected/riscv32imac-unknown-none-elf/hello.run b/ci/expected/riscv32imac-unknown-none-elf/hello.run new file mode 100644 index 00000000..e69de29b diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index 7afdbc76..a1402323 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -32,6 +32,7 @@ defmt = { version = "1.0.1", optional = true } [dev-dependencies] panic-halt = "1.0.0" +riscv-semihosting = { path = "../riscv-semihosting", version = "0.2.1" } [features] pre-init = [] diff --git a/riscv-rt/examples/device_s_mode.x b/riscv-rt/examples/device_s_mode.x new file mode 100644 index 00000000..494882f6 --- /dev/null +++ b/riscv-rt/examples/device_s_mode.x @@ -0,0 +1,14 @@ +MEMORY +{ + RAM : ORIGIN = 0x80200000, LENGTH = 16K + FLASH : ORIGIN = 0x20000000, LENGTH = 4M +} + +REGION_ALIAS("REGION_TEXT", FLASH); +REGION_ALIAS("REGION_RODATA", FLASH); +REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_BSS", RAM); +REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_STACK", RAM); + +INCLUDE link.x diff --git a/riscv-rt/examples/device_virt_m.x b/riscv-rt/examples/device_virt_m.x new file mode 100644 index 00000000..ee4e920a --- /dev/null +++ b/riscv-rt/examples/device_virt_m.x @@ -0,0 +1,11 @@ +MEMORY +{ + RAM : ORIGIN = 0x80000000, LENGTH = 16M +} +REGION_ALIAS("REGION_TEXT", RAM); +REGION_ALIAS("REGION_RODATA", RAM); +REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_BSS", RAM); +REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_STACK", RAM); +INCLUDE link.x diff --git a/riscv-rt/examples/device_virt_s.x b/riscv-rt/examples/device_virt_s.x new file mode 100644 index 00000000..2f9a9585 --- /dev/null +++ b/riscv-rt/examples/device_virt_s.x @@ -0,0 +1,11 @@ +MEMORY +{ + RAM : ORIGIN = 0x80200000, LENGTH = 16M +} +REGION_ALIAS("REGION_TEXT", RAM); +REGION_ALIAS("REGION_RODATA", RAM); +REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_BSS", RAM); +REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_STACK", RAM); +INCLUDE link.x diff --git a/riscv-rt/examples/hello.rs b/riscv-rt/examples/hello.rs new file mode 100644 index 00000000..00ea3db1 --- /dev/null +++ b/riscv-rt/examples/hello.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] +extern crate panic_halt; +use riscv_rt::entry; +const UART_BASE: usize = 0x1000_0000; +const UART_THR: usize = UART_BASE + 0; +const UART_IER: usize = UART_BASE + 1; +const UART_FCR: usize = UART_BASE + 2; +const UART_LCR: usize = UART_BASE + 3; +const UART_LSR: usize = UART_BASE + 5; +const LCR_DLAB: u8 = 1 << 7; +const LCR_8N1: u8 = 0x03; +const LSR_THRE: u8 = 1 << 5; +unsafe fn uart_write_reg(off: usize, v: u8) { (off as *mut u8).write_volatile(v); } +unsafe fn uart_read_reg(off: usize) -> u8 { (off as *const u8).read_volatile() } +fn uart_init() { + unsafe { + uart_write_reg(UART_LCR, LCR_DLAB); + uart_write_reg(UART_THR, 0x01); + uart_write_reg(UART_IER, 0x00); + uart_write_reg(UART_LCR, LCR_8N1); + uart_write_reg(UART_FCR, 0x07); + } +} +fn uart_write_byte(b: u8) { + unsafe { while (uart_read_reg(UART_LSR) & LSR_THRE) == 0 {} uart_write_reg(UART_THR, b); } +} +fn uart_write_str(s: &str) { for &b in s.as_bytes() { uart_write_byte(b); } } +#[entry] +fn main() -> ! { + uart_init(); + uart_write_str("HELLO_QEMU\n"); + loop {} +} diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 00000000..13979c34 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 00000000..c86e0a90 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,53 @@ +use std::{process::{Command, Stdio}, fs, path::PathBuf, thread, time::Duration}; +use anyhow::{bail, Context}; + +fn main() -> anyhow::Result<()> { + let mut args = std::env::args().skip(1).collect::>(); + if args.is_empty() || args[0] != "qemu" { bail!("usage: cargo run -p xtask -- qemu --target --example "); } + args.remove(0); + let mut target = None; let mut example = None; let mut features: Option = None; + let mut i=0; while i < args.len() { match args[i].as_str() { + "--target" => { target = Some(args.get(i+1).context("missing target")?.clone()); i+=2; }, + "--example" => { example = Some(args.get(i+1).context("missing example")?.clone()); i+=2; }, + "--features" => { features = Some(args.get(i+1).context("missing features")?.clone()); i+=2; }, + _ => { bail!("unknown arg {}", args[i]); } + }} + let target = target.context("--target required")?; + let example = example.context("--example required")?; + let mut rustflags = "-C link-arg=-Triscv-rt/examples/device_virt_m.x".to_string(); + if let Some(f) = &features { if f.contains("s-mode") { rustflags = "-C link-arg=-Triscv-rt/examples/device_virt_s.x".into(); } } + + let mut cmd = Command::new("cargo"); + cmd.env("RUSTFLAGS", rustflags) + .args(["build","--package","riscv-rt","--release","--target", &target, "--example", &example]); + cmd.apply_features(features.as_deref()); + let status = cmd.status()?; + if !status.success() { bail!("build failed"); } + + let qemu = if target.starts_with("riscv32") { "qemu-system-riscv32" } else { "qemu-system-riscv64" }; + let mut qemu_args = vec!["-machine","virt","-nographic","-serial","stdio","-monitor","none"]; + if !features.as_deref().unwrap_or("").contains("s-mode") { qemu_args.push("-bios"); qemu_args.push("none"); } + let kernel_path = format!("target/{}/release/examples/{}", target, example); + let mut child = Command::new(qemu).args(&qemu_args).arg("-kernel").arg(&kernel_path).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().context("running qemu")?; + thread::sleep(Duration::from_secs(2)); + let _ = child.kill(); + let output = child.wait_with_output()?; + let raw_stdout = String::from_utf8_lossy(&output.stdout).into_owned(); + let stdout = raw_stdout.lines().filter(|l| !l.contains("QEMU") && !l.contains("monitor")).collect::>().join("\n"); + let stdout = if stdout.is_empty() { String::new() } else { format!("{}\n", stdout.trim()) }; + + let expected_path: PathBuf = ["ci","expected", &target, &format!("{}.run", example)].iter().collect(); + if !expected_path.exists() { fs::create_dir_all(expected_path.parent().unwrap())?; fs::write(&expected_path, stdout.as_bytes())?; bail!("expected output created; re-run CI"); } + let expected = fs::read_to_string(&expected_path)?; + if expected != stdout { bail!("output mismatch\nexpected: {}\nactual: {}", expected, stdout); } + if !stdout.is_empty() { println!("{}", stdout.trim_end()); } + Ok(()) +} + +trait CmdExt { fn apply_features(&mut self, f: Option<&str>) -> &mut Self; } +impl CmdExt for std::process::Command { + fn apply_features(&mut self, f: Option<&str>) -> &mut Self { + if let Some(feat)=f { self.arg("--features").arg(feat); } + self + } +} From 5aa3c002f27f72fdafdc7bd39cd2afa650c187e5 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Sat, 15 Nov 2025 13:27:51 +0530 Subject: [PATCH 8/9] Fix qemu CI --- .github/workflows/qemu.yaml | 95 ++++++++++--------- .../riscv64gc-unknown-none-elf/hello.run | 0 2 files changed, 50 insertions(+), 45 deletions(-) create mode 100644 ci/expected/riscv64gc-unknown-none-elf/hello.run diff --git a/.github/workflows/qemu.yaml b/.github/workflows/qemu.yaml index 973a3d13..d21ef87a 100644 --- a/.github/workflows/qemu.yaml +++ b/.github/workflows/qemu.yaml @@ -1,61 +1,66 @@ +name: QEMU tests on: - push: - branches: [ master ] - pull_request: merge_group: + pull_request: + push: + branches: + - master -name: QEMU tests +env: + CARGO_TERM_COLOR: always jobs: - qemu-tests: + # Verify the example output with run-pass tests + testexamples: + name: QEMU run + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: - toolchain: [ stable, nightly, 1.68.0 ] - target: - - riscv32imac-unknown-none-elf - - riscv64gc-unknown-none-elf - example: - - hello - features: - - "" - include: + input: + - toolchain: stable + target: riscv32imac-unknown-none-elf + qemu-system: riscv32 + example: hello + - toolchain: nightly - experimental: true - exclude: - - toolchain: 1.68.0 target: riscv32imac-unknown-none-elf - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || false }} + qemu-system: riscv32 + example: hello + + - toolchain: stable + target: riscv64gc-unknown-none-elf + qemu-system: riscv64 + example: hello + + - toolchain: nightly + target: riscv64gc-unknown-none-elf + qemu-system: riscv64 + example: hello + steps: - - uses: actions/checkout@v4 - - name: Cache cargo - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-${{ matrix.toolchain }}- - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.toolchain }} - targets: ${{ matrix.target }} - + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure Rust target ${{ matrix.input.target }} + run: | + rustup toolchain install ${{ matrix.input.toolchain }} + rustup default ${{ matrix.input.toolchain }} + rustup target add ${{ matrix.input.target }} + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + - name: Install QEMU run: | - set -euo pipefail - for i in 1 2 3; do - sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-misc opensbi && break || sleep 5 - done - qemu-system-riscv64 --version - qemu-system-riscv32 --version - - - name: Run xtask QEMU test + sudo apt update + sudo apt install -y qemu-system-${{ matrix.input.qemu-system }} + + - name: Check which QEMU is used run: | - set -euo pipefail - cargo run --package xtask -- qemu --target ${{ matrix.target }} --example ${{ matrix.example }} + which qemu-system-${{ matrix.input.qemu-system }} + + - name: Run-pass tests + run: cargo run --package xtask -- qemu --target ${{ matrix.input.target }} --example ${{ matrix.input.example }} diff --git a/ci/expected/riscv64gc-unknown-none-elf/hello.run b/ci/expected/riscv64gc-unknown-none-elf/hello.run new file mode 100644 index 00000000..e69de29b From d8062fbb97783e8ddb3f1c70e537b78ac55ab370 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Sat, 15 Nov 2025 13:30:08 +0530 Subject: [PATCH 9/9] Fix formatting --- riscv-rt/examples/hello.rs | 19 ++++-- xtask/src/main.rs | 136 ++++++++++++++++++++++++++++++------- 2 files changed, 126 insertions(+), 29 deletions(-) diff --git a/riscv-rt/examples/hello.rs b/riscv-rt/examples/hello.rs index 00ea3db1..aa9a2977 100644 --- a/riscv-rt/examples/hello.rs +++ b/riscv-rt/examples/hello.rs @@ -11,8 +11,12 @@ const UART_LSR: usize = UART_BASE + 5; const LCR_DLAB: u8 = 1 << 7; const LCR_8N1: u8 = 0x03; const LSR_THRE: u8 = 1 << 5; -unsafe fn uart_write_reg(off: usize, v: u8) { (off as *mut u8).write_volatile(v); } -unsafe fn uart_read_reg(off: usize) -> u8 { (off as *const u8).read_volatile() } +unsafe fn uart_write_reg(off: usize, v: u8) { + (off as *mut u8).write_volatile(v); +} +unsafe fn uart_read_reg(off: usize) -> u8 { + (off as *const u8).read_volatile() +} fn uart_init() { unsafe { uart_write_reg(UART_LCR, LCR_DLAB); @@ -23,9 +27,16 @@ fn uart_init() { } } fn uart_write_byte(b: u8) { - unsafe { while (uart_read_reg(UART_LSR) & LSR_THRE) == 0 {} uart_write_reg(UART_THR, b); } + unsafe { + while (uart_read_reg(UART_LSR) & LSR_THRE) == 0 {} + uart_write_reg(UART_THR, b); + } +} +fn uart_write_str(s: &str) { + for &b in s.as_bytes() { + uart_write_byte(b); + } } -fn uart_write_str(s: &str) { for &b in s.as_bytes() { uart_write_byte(b); } } #[entry] fn main() -> ! { uart_init(); diff --git a/xtask/src/main.rs b/xtask/src/main.rs index c86e0a90..c681b722 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,53 +1,139 @@ -use std::{process::{Command, Stdio}, fs, path::PathBuf, thread, time::Duration}; use anyhow::{bail, Context}; +use std::{ + fs, + path::PathBuf, + process::{Command, Stdio}, + thread, + time::Duration, +}; fn main() -> anyhow::Result<()> { let mut args = std::env::args().skip(1).collect::>(); - if args.is_empty() || args[0] != "qemu" { bail!("usage: cargo run -p xtask -- qemu --target --example "); } + if args.is_empty() || args[0] != "qemu" { + bail!("usage: cargo run -p xtask -- qemu --target --example "); + } args.remove(0); - let mut target = None; let mut example = None; let mut features: Option = None; - let mut i=0; while i < args.len() { match args[i].as_str() { - "--target" => { target = Some(args.get(i+1).context("missing target")?.clone()); i+=2; }, - "--example" => { example = Some(args.get(i+1).context("missing example")?.clone()); i+=2; }, - "--features" => { features = Some(args.get(i+1).context("missing features")?.clone()); i+=2; }, - _ => { bail!("unknown arg {}", args[i]); } - }} + let mut target = None; + let mut example = None; + let mut features: Option = None; + let mut i = 0; + while i < args.len() { + match args[i].as_str() { + "--target" => { + target = Some(args.get(i + 1).context("missing target")?.clone()); + i += 2; + } + "--example" => { + example = Some(args.get(i + 1).context("missing example")?.clone()); + i += 2; + } + "--features" => { + features = Some(args.get(i + 1).context("missing features")?.clone()); + i += 2; + } + _ => { + bail!("unknown arg {}", args[i]); + } + } + } let target = target.context("--target required")?; let example = example.context("--example required")?; let mut rustflags = "-C link-arg=-Triscv-rt/examples/device_virt_m.x".to_string(); - if let Some(f) = &features { if f.contains("s-mode") { rustflags = "-C link-arg=-Triscv-rt/examples/device_virt_s.x".into(); } } + if let Some(f) = &features { + if f.contains("s-mode") { + rustflags = "-C link-arg=-Triscv-rt/examples/device_virt_s.x".into(); + } + } let mut cmd = Command::new("cargo"); - cmd.env("RUSTFLAGS", rustflags) - .args(["build","--package","riscv-rt","--release","--target", &target, "--example", &example]); + cmd.env("RUSTFLAGS", rustflags).args([ + "build", + "--package", + "riscv-rt", + "--release", + "--target", + &target, + "--example", + &example, + ]); cmd.apply_features(features.as_deref()); let status = cmd.status()?; - if !status.success() { bail!("build failed"); } + if !status.success() { + bail!("build failed"); + } - let qemu = if target.starts_with("riscv32") { "qemu-system-riscv32" } else { "qemu-system-riscv64" }; - let mut qemu_args = vec!["-machine","virt","-nographic","-serial","stdio","-monitor","none"]; - if !features.as_deref().unwrap_or("").contains("s-mode") { qemu_args.push("-bios"); qemu_args.push("none"); } + let qemu = if target.starts_with("riscv32") { + "qemu-system-riscv32" + } else { + "qemu-system-riscv64" + }; + let mut qemu_args = vec![ + "-machine", + "virt", + "-nographic", + "-serial", + "stdio", + "-monitor", + "none", + ]; + if !features.as_deref().unwrap_or("").contains("s-mode") { + qemu_args.push("-bios"); + qemu_args.push("none"); + } let kernel_path = format!("target/{}/release/examples/{}", target, example); - let mut child = Command::new(qemu).args(&qemu_args).arg("-kernel").arg(&kernel_path).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().context("running qemu")?; + let mut child = Command::new(qemu) + .args(&qemu_args) + .arg("-kernel") + .arg(&kernel_path) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("running qemu")?; thread::sleep(Duration::from_secs(2)); let _ = child.kill(); let output = child.wait_with_output()?; let raw_stdout = String::from_utf8_lossy(&output.stdout).into_owned(); - let stdout = raw_stdout.lines().filter(|l| !l.contains("QEMU") && !l.contains("monitor")).collect::>().join("\n"); - let stdout = if stdout.is_empty() { String::new() } else { format!("{}\n", stdout.trim()) }; + let stdout = raw_stdout + .lines() + .filter(|l| !l.contains("QEMU") && !l.contains("monitor")) + .collect::>() + .join("\n"); + let stdout = if stdout.is_empty() { + String::new() + } else { + format!("{}\n", stdout.trim()) + }; - let expected_path: PathBuf = ["ci","expected", &target, &format!("{}.run", example)].iter().collect(); - if !expected_path.exists() { fs::create_dir_all(expected_path.parent().unwrap())?; fs::write(&expected_path, stdout.as_bytes())?; bail!("expected output created; re-run CI"); } + let expected_path: PathBuf = ["ci", "expected", &target, &format!("{}.run", example)] + .iter() + .collect(); + if !expected_path.exists() { + fs::create_dir_all(expected_path.parent().unwrap())?; + fs::write(&expected_path, stdout.as_bytes())?; + bail!("expected output created; re-run CI"); + } let expected = fs::read_to_string(&expected_path)?; - if expected != stdout { bail!("output mismatch\nexpected: {}\nactual: {}", expected, stdout); } - if !stdout.is_empty() { println!("{}", stdout.trim_end()); } + if expected != stdout { + bail!( + "output mismatch\nexpected: {}\nactual: {}", + expected, + stdout + ); + } + if !stdout.is_empty() { + println!("{}", stdout.trim_end()); + } Ok(()) } -trait CmdExt { fn apply_features(&mut self, f: Option<&str>) -> &mut Self; } +trait CmdExt { + fn apply_features(&mut self, f: Option<&str>) -> &mut Self; +} impl CmdExt for std::process::Command { fn apply_features(&mut self, f: Option<&str>) -> &mut Self { - if let Some(feat)=f { self.arg("--features").arg(feat); } + if let Some(feat) = f { + self.arg("--features").arg(feat); + } self } }