1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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
|