Compare commits

...

3 Commits

Author SHA1 Message Date
f91b06da9d fix(kernel): Use per-version tags in Docker build to isolate jobs
Some checks failed
Build kernel module / build (1:6.12.93-1+rpt1, bookworm) (pull_request) Failing after 41s
Build kernel module / build (1:6.18.34-1+rpt1, trixie) (pull_request) Failing after 42s
Build kernel module / release (pull_request) Has been skipped
Prevent race conditions in concurrent job builds by introducing unique
Docker image tags per kernel version. This ensures isolated builds with
correct verm
2026-06-20 04:53:45 +02:00
2aa5104812 feat(kernel): Improve selftest.sh module discovery and error handling
Enhance module selection process by validating vermagic against the running kernel. Add detailed error messages with suggestions for manual path overrides or rebuilding for the current kernel. Ensure robust fallback behavior and improve user guidance.
2026-06-20 04:53:40 +02:00
6e5ec9b84f docs(kernel): Update README with module selection details
Expand documentation on selftest.sh behavior, module matching, and kernel compatibility. Clarify the auto-selection process based on `vermagic` and provide examples to guide users through fallback handling and manual overrides.
2026-06-20 04:53:31 +02:00
3 changed files with 86 additions and 10 deletions

View File

@ -51,23 +51,31 @@ jobs:
if [ ! -e /proc/sys/fs/binfmt_misc/qemu-aarch64 ]; then if [ ! -e /proc/sys/fs/binfmt_misc/qemu-aarch64 ]; then
docker run --privileged --rm tonistiigi/binfmt --install arm64 docker run --privileged --rm tonistiigi/binfmt --install arm64
fi fi
# Per-version image tag. The matrix entries can run concurrently on a
# single runner sharing one Docker daemon; a fixed tag (e.g. iec-kbuild)
# is then a shared mutable name and the builds race — whichever `docker
# build` finishes last wins the tag, so both `docker create` calls
# resolve to the same image and every job emits the same vermagic.
# A unique tag per kernel version isolates them.
img="iec-kbuild:${KERNEL_VERSION//[:\/]/-}"
# Builder image: toolchain + matching raspberrypi kernel headers. # Builder image: toolchain + matching raspberrypi kernel headers.
docker build --platform linux/arm64 \ docker build --platform linux/arm64 \
--build-arg DEBIAN_SUITE="$DEBIAN_SUITE" \ --build-arg DEBIAN_SUITE="$DEBIAN_SUITE" \
--build-arg HEADERS_PKG="$HEADERS_PKG" \ --build-arg HEADERS_PKG="$HEADERS_PKG" \
--build-arg KERNEL_VERSION="$KERNEL_VERSION" \ --build-arg KERNEL_VERSION="$KERNEL_VERSION" \
-t iec-kbuild . -t "$img" .
# Compile inside the container. We use `docker cp` instead of the bind # Compile inside the container. We use `docker cp` instead of the bind
# mount that build-in-docker.sh uses for local builds: under the runner's # mount that build-in-docker.sh uses for local builds: under the runner's
# docker-in-docker, /workspace is a volume the host daemon can't see, so # docker-in-docker, /workspace is a volume the host daemon can't see, so
# `-v "$PWD:/build"` mounts an empty dir and make finds no Makefile. # `-v "$PWD:/build"` mounts an empty dir and make finds no Makefile.
cid=$(docker create --platform linux/arm64 --entrypoint sleep iec-kbuild infinity) cid=$(docker create --platform linux/arm64 --entrypoint sleep "$img" infinity)
docker start "$cid" docker start "$cid"
docker cp ./. "$cid:/build" docker cp ./. "$cid:/build"
docker exec "$cid" /usr/local/bin/docker-entrypoint.sh clean docker exec "$cid" /usr/local/bin/docker-entrypoint.sh clean
docker exec "$cid" /usr/local/bin/docker-entrypoint.sh docker exec "$cid" /usr/local/bin/docker-entrypoint.sh
docker cp "$cid:/build/iec_listener.ko" ./iec_listener.ko docker cp "$cid:/build/iec_listener.ko" ./iec_listener.ko
docker rm -f "$cid" docker rm -f "$cid"
docker rmi "$img" || true
- name: Stage build output (modules/iec_listener_<kernel_version>.ko) - name: Stage build output (modules/iec_listener_<kernel_version>.ko)
working-directory: kernel working-directory: kernel

View File

@ -20,14 +20,35 @@ before connecting the real bus (see PLAN.md §10).
### Running it ### Running it
`selftest.sh` is non-persistent (nothing is installed into `/lib/modules`, no `selftest.sh` is non-persistent (nothing is installed into `/lib/modules`, no
autoload): it checks vermagic, loads the module, runs the self-test, and always autoload): it detects the running kernel, picks the matching module, loads it,
unloads it again. runs the self-test, and always unloads it again.
A released package ships several kernel builds side by side:
```
comodore-iec-emu/
├── selftest.sh
└── modules/
├── iec_listener_1-6.12.93-1+rpt1.ko # built for 6.12.x (bookworm)
└── iec_listener_1-6.18.34-1+rpt1.ko # built for 6.18.x (trixie)
```
You don't pick the file yourself: the script reads `uname -r`, then scans
`modules/` (and a few fallback locations) and selects the `.ko` whose **vermagic**
matches the running kernel. Matching is by vermagic rather than filename because
the packaged name carries a Debian epoch/revision (`1:6.12.93-1+rpt1`) that
`uname -r` (`6.12.93+rpt-rpi-v8`) does not.
```bash ```bash
sudo ./selftest.sh # auto-finds ./ or ~/iec_listener.ko, address 4 sudo ./selftest.sh # auto-selects the matching module, address 4
sudo ./selftest.sh ~/iec_listener.ko --address 5 # runs with ~/iec_listener.ko, address 5 sudo ./selftest.sh --address 5 # same, address 5
sudo ./selftest.sh ~/iec_listener.ko --address 5 # force a specific module file
``` ```
If no module matches the running kernel, the script lists the modules it found
(with the kernel each was built for) and exits — rebuild for the current kernel,
or pass a path explicitly.
It exits non-zero unless the result is a full pass (`0x1F`), so it is usable in It exits non-zero unless the result is a full pass (`0x1F`), so it is usable in
scripts/CI. scripts/CI.

View File

@ -62,13 +62,60 @@ done
case "$ADDRESS" in ''|*[!0-9]*) die "address must be 0-30 (got '$ADDRESS')" ;; esac case "$ADDRESS" in ''|*[!0-9]*) die "address must be 0-30 (got '$ADDRESS')" ;; esac
{ [ "$ADDRESS" -ge 0 ] && [ "$ADDRESS" -le 30 ]; } || die "address out of range 0-30: $ADDRESS" { [ "$ADDRESS" -ge 0 ] && [ "$ADDRESS" -le 30 ]; } || die "address out of range 0-30: $ADDRESS"
command -v modinfo >/dev/null || die "modinfo not found (install kmod)"
# ---- locate the module matching the running kernel ----------------------
# A released package ships one module per supported kernel under modules/.
# The build workflow names each file iec_listener_<kernel_version>.ko, with
# ':' and '/' in the version replaced by '-' (1:6.12.93-1+rpt1 ->
# iec_listener_1-6.12.93-1+rpt1.ko); local builds produce a bare
# iec_listener.ko. The glob below ($MODULE*.ko) matches both.
#
# We select by vermagic, not by filename: the package name carries a Debian
# epoch/revision (1:6.12.93-1+rpt1) that `uname -r` (6.12.93+rpt-rpi-v8) does
# not, so the .ko whose vermagic matches the running kernel is the one to load.
RUNNING="$(uname -r)"
# Where to look, de-duplicated ($(dirname "$0") is often "." when run locally).
SEARCH_DIRS=()
for d in "$(dirname "$0")/modules" "$(dirname "$0")" "./modules" "." "$HOME"; do
skip=0
for s in "${SEARCH_DIRS[@]:-}"; do [ "$s" = "$d" ] && skip=1; done
[ "$skip" = 0 ] && SEARCH_DIRS+=("$d")
done
if [ -z "$KO" ]; then if [ -z "$KO" ]; then
for cand in "./${MODULE}.ko" "$HOME/${MODULE}.ko" "$(dirname "$0")/${MODULE}.ko"; do info "running kernel: $RUNNING"
[ -f "$cand" ] && { KO="$cand"; break; } for dir in "${SEARCH_DIRS[@]}"; do
[ -d "$dir" ] || continue
for cand in "$dir"/${MODULE}*.ko; do
[ -f "$cand" ] || continue
vm="$(modinfo -F vermagic "$cand" 2>/dev/null)" || continue
if [ "${vm%% *}" = "$RUNNING" ]; then
KO="$cand"; break 2
fi
done
done done
fi fi
[ -n "$KO" ] && [ -f "$KO" ] || die "module not found; pass the path, e.g. sudo $0 ~/${MODULE}.ko"
command -v modinfo >/dev/null || die "modinfo not found (install kmod)" if [ -z "$KO" ]; then
msg="no module matching the running kernel ($RUNNING) found.
Pass a path explicitly (sudo $0 path/to/${MODULE}.ko), or rebuild for
this kernel. Modules seen:"
any=0
for dir in "${SEARCH_DIRS[@]}"; do
[ -d "$dir" ] || continue
for cand in "$dir"/${MODULE}*.ko; do
[ -f "$cand" ] || continue
vm="$(modinfo -F vermagic "$cand" 2>/dev/null)"
msg="$msg"$'\n'" $cand (built for ${vm%% *})"
any=1
done
done
[ "$any" = 1 ] || msg="$msg"$'\n'" (none)"
die "$msg"
fi
[ -f "$KO" ] || die "module not found: $KO"
info "module: $KO" info "module: $KO"
info "address: $ADDRESS" info "address: $ADDRESS"