summaryrefslogtreecommitdiff
path: root/.github/workflows/module_qemu.yml
diff options
context:
space:
mode:
Diffstat (limited to '.github/workflows/module_qemu.yml')
-rw-r--r--.github/workflows/module_qemu.yml151
1 files changed, 151 insertions, 0 deletions
diff --git a/.github/workflows/module_qemu.yml b/.github/workflows/module_qemu.yml
new file mode 100644
index 00000000..7cc62709
--- /dev/null
+++ b/.github/workflows/module_qemu.yml
@@ -0,0 +1,151 @@
+name: module_qemu
+
+# Copyright (c) 2026 Eclipse ThreadX contributors
+# SPDX-License-Identifier: MIT
+# Portions of this file were generated with AI assistance.
+#
+# Builds the module execution harness in test/tx/cmake/module_qemu for each
+# Cortex-M module port it covers and EXECUTES every image on QEMU's MPS2
+# board for that core, asserting each image's self-reported result.
+#
+# Why this exists: until this workflow landed, no Cortex-M module port ran in
+# CI. gcc_check compiles the module manager sources once per port and links
+# nothing that loads a module; the host tests under test/tx/module_manager
+# compile a handful of portable module manager sources against stubs. A
+# module's start-up, its kernel calls through the supervisor call, the MPU
+# programming on every context switch and the fault path that terminates a
+# misbehaving module are only exercised by executing them, and r52_fvp does
+# that for the Cortex-R52 alone.
+#
+# What it covers, per port:
+#
+# - cortex_m3 on mps2-an385 and cortex_m4 on mps2-an386, the latter built
+# for its FPU with the hard-float ABI;
+# - a protected module loaded, started, exercising the kernel, stopped and
+# unloaded twice, with its memory given back;
+# - a protected module faulting on a read of kernel memory, reported to the
+# application and terminated -- which is also the proof that QEMU's MPU is
+# enforcing at all;
+# - the same module built privileged and unprotected, whose identical read
+# succeeds.
+#
+# QEMU comes from the Ubuntu archive. The images report through semihosting
+# and end the simulation themselves, so a hang shows up as the runner's
+# timeout rather than as a pass.
+
+on:
+ # push as well as pull_request, for the reason gcc_check.yml states at
+ # length: dev needs its own baseline so a bad squash-merge is caught then
+ # rather than by whoever opens the next pull request.
+ push:
+ branches: [ master, dev ]
+ paths:
+ - ".github/workflows/module_qemu.yml"
+ - "common/**"
+ - "common_modules/**"
+ - "ports_module/cortex_m3/**"
+ - "ports_module/cortex_m4/**"
+ - "test/tx/cmake/module_qemu/**"
+ - "scripts/tx_ci_common.sh"
+ pull_request:
+ branches: [ master, dev ]
+ # Duplicated rather than shared through a YAML anchor, as in r52_fvp.yml.
+ # **Edit both.**
+ paths:
+ - ".github/workflows/module_qemu.yml"
+ - "common/**"
+ - "common_modules/**"
+ - "ports_module/cortex_m3/**"
+ - "ports_module/cortex_m4/**"
+ - "test/tx/cmake/module_qemu/**"
+ - "scripts/tx_ci_common.sh"
+
+permissions:
+ contents: read
+
+jobs:
+ module_qemu:
+ runs-on: ubuntu-24.04
+ timeout-minutes: 30
+
+ env:
+ # Pinned deliberately and shared with gcc_check.yml, ci_cortex_m.yml and
+ # r52_fvp.yml through one cache entry; change them together.
+ GCC_VERSION: 14.3.rel1
+
+ steps:
+ - name: Check out the repository
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+
+ # Path and key match gcc_check.yml's AArch32 entry exactly, so the
+ # workflows share one cache entry.
+ - name: Cache the AArch32 Arm GNU toolchain
+ id: cache-arm32
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: toolchain
+ key: arm-gnu-toolchain-${{ env.GCC_VERSION }}-x86_64-arm-none-eabi
+
+ # The checksum suffix is .sha256asc and not .sha256; see gcc_check.yml.
+ - name: Install the AArch32 Arm GNU toolchain
+ if: steps.cache-arm32.outputs.cache-hit != 'true'
+ run: |
+ set -eu
+ base="https://developer.arm.com/-/media/Files/downloads/gnu/${GCC_VERSION}/binrel"
+ archive="arm-gnu-toolchain-${GCC_VERSION}-x86_64-arm-none-eabi.tar.xz"
+ mkdir -p toolchain && cd toolchain
+ curl -fsSLO "$base/$archive"
+ curl -fsSLO "$base/$archive.sha256asc"
+ sha256sum -c "$archive.sha256asc"
+ tar xf "$archive"
+ rm -f "$archive"
+
+ # Through the bounded, retried apt helpers in tx_ci_common.sh, for the
+ # reasons that file records: on this runner pool a bare apt call can stall
+ # for hours. Ninja is installed only if the image stopped shipping it.
+ - name: Install QEMU
+ run: |
+ set -eu
+ . scripts/tx_ci_common.sh
+ packages="qemu-system-arm"
+ command -v ninja >/dev/null 2>&1 || packages="$packages ninja-build"
+ retry sudo "${TIMEOUT[@]}" apt-get "${APT_OPTIONS[@]}" update || true
+ # shellcheck disable=SC2086
+ retry sudo "${TIMEOUT[@]}" apt-get "${APT_OPTIONS[@]}" install -y --no-install-recommends $packages
+ qemu-system-arm --version | head -1
+
+ - name: Build and run the module harness
+ run: |
+ set -eu
+ export PATH="$GITHUB_WORKSPACE/toolchain/arm-gnu-toolchain-${GCC_VERSION}-x86_64-arm-none-eabi/bin:$PATH"
+ arm-none-eabi-gcc --version | head -1
+
+ status=0
+ for port in cortex_m3 cortex_m4; do
+ build="build/module_qemu_$port"
+ echo ""
+ echo "=============================================================="
+ echo "== $port"
+ echo "=============================================================="
+
+ if ! cmake -S test/tx/cmake/module_qemu -B "$build" -G Ninja \
+ -DCMAKE_TOOLCHAIN_FILE="$PWD/test/tx/cmake/module_qemu/arm-none-eabi-qemu.cmake" \
+ -DMODULE_QEMU_PORT="$port"; then
+ echo "::error::$port: CMake configure failed"
+ status=1
+ continue
+ fi
+ if ! cmake --build "$build"; then
+ echo "::error::$port: the images did not build"
+ status=1
+ continue
+ fi
+ # --no-tests=error: the tests are registered only when QEMU and
+ # Python are found, and ctest's default would pass on none.
+ if ! ctest --test-dir "$build" --output-on-failure --no-tests=error; then
+ echo "::error::$port: one or more images failed on QEMU, or no tests were registered"
+ status=1
+ fi
+ done
+
+ exit $status