kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 74a49a2db72bf05eebaf4d1f0e7b57be7c896dac
parent 9aa8d6e18cf373b4ec23486eeb96c824103b8997
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 16 Jun 2026 12:41:10 -0700

test-cross: enable MMU+FPU in aa64 bare stub; isolate exec-seam stdin

The freestanding-aa64 coarse lane (frontback_amalgam O0/O1) timed out: the
aa64 boot stub called main straight from reset, where the MMU is off (RAM is
Device-nGnRnE, so an unaligned wide store faults) and the FPU traps. With no
exception vectors installed the fault spins at the vector base forever -> qemu
timeout (rc 124). Harden the stub to enable FP/SIMD (CPACR_EL1) and install an
identity MMU map marking RAM as Normal memory, matching the rv32 stub and what
a real aarch64 bootloader does before running general C.

Also harden the execution seam against stdin draining: cross_test reads its run
list on a private fd 9 and feeds each runner </dev/null; exec_vm teardown stops
waiting on a poweroff the unprivileged ssh user may not be permitted to run and
SIGTERMs qemu (our exec'd child) directly.

Diffstat:
Mscripts/cross_test.sh | 13+++++++++----
Mtest/lib/exec_bare.sh | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mtest/lib/exec_vm.sh | 19+++++++++++++------
3 files changed, 79 insertions(+), 15 deletions(-)

diff --git a/scripts/cross_test.sh b/scripts/cross_test.sh @@ -221,13 +221,18 @@ cross_run_all() { local list="$1" local kind label tag exe out err exp want_file rc got want barch [ -s "$list" ] || { echo "cross: no linked artifacts to execute"; return; } - while IFS='|' read -r kind label tag exe out err exp want_file; do + # Read the list on a private fd (9), not stdin: the runners below spawn + # children that read host stdin (qemu-system -nographic muxes the guest UART + # onto stdio; VM readiness ssh probes forward stdin), which would otherwise + # drain the list mid-loop and truncate execution after the first such case. + # Each runner also gets </dev/null so it can never block on or consume stdin. + while IFS='|' read -r kind label tag exe out err exp want_file <&9; do case "$kind" in bare) barch="$(_exec_target_arch "$tag")" - exec_bare_run_image "$barch" "$exe" "$out" "$err" ;; + exec_bare_run_image "$barch" "$exe" "$out" "$err" </dev/null ;; *) - exec_target_run "$tag" "$exe" "$out" "$err" ;; + exec_target_run "$tag" "$exe" "$out" "$err" </dev/null ;; esac rc="${RUN_RC:-127}"; got="$(cat "$out" 2>/dev/null)" want=""; [ "$want_file" != "-" ] && want="$(cat "$want_file")" @@ -238,7 +243,7 @@ cross_run_all() { else kit_pass "$label:run" fi - done < "$list" + done 9< "$list" } # ---- full lanes ------------------------------------------------------------ diff --git a/test/lib/exec_bare.sh b/test/lib/exec_bare.sh @@ -187,6 +187,17 @@ exec_bare_run() { # arch obj work rcfile -> echoes reason; 0 ran / 2 build-fai _bare_emit_aa64() { local dir="$1" + # The CPU comes out of reset at EL1 with the FPU trapping and the MMU off. + # Two things general C needs are therefore set up before calling main: + # * CPACR_EL1.FPEN = 0b11 — else the first float/double op traps. + # * an identity MMU map marking RAM as Normal memory — with the MMU off + # all data accesses are Device-nGnRnE, which enforces natural alignment, + # so the wide unaligned stores kit emits (e.g. an 8-byte aggregate-init + # store landing on a 4-byte-aligned slot) take an alignment fault. We + # have no exception vectors, so a fault spins at the vector base forever + # (timeout). Normal memory permits the unaligned access, like a real OS. + # Sysregs use the generic S<op0>_<op1>_C<crn>_C<crm>_<op2> spelling (kit-as + # only names a handful by mnemonic). cat > "$dir/start.S" <<'EOF' .section .text.start,"ax",@progbits .globl _start @@ -195,7 +206,42 @@ _start: add x0, x0, :lo12:stack_top and x0, x0, #0xfffffffffffffff0 // AArch64 faults on a misaligned SP; the mov sp, x0 // linker-script .bss lump may not 16-align - bl main // stack_top, so force it here. + // stack_top, so force it here. + + movz x0, #0x30, lsl #16 // CPACR_EL1.FPEN = 0b11 (don't trap FP/SIMD) + msr S3_0_C1_C0_2, x0 + isb + + movz x0, #0x00ff // MAIR_EL1 attr0 = 0xFF (Normal WB) + msr S3_0_C10_C2_0, x0 + movz x0, #0x3519 // TCR_EL1: T0SZ=25, 4KB granule, WB/IS + movk x0, #0x0080, lsl #16 // table walks, EPD1=1 (no TTBR1), + movk x0, #0x0001, lsl #32 // IPS=36-bit + msr S3_0_C2_C0_2, x0 + adrp x1, l1_table // fill the 512-entry L1 table with 1GB + add x1, x1, :lo12:l1_table // identity blocks, all Normal memory + mov x2, #0 + movz x3, #0x0701 // block desc: AF | SH=inner | AttrIdx0 | 01 +.Lmap: + lsl x4, x2, #30 // output PA = index * 1GB == input VA + orr x4, x4, x3 + str x4, [x1, x2, lsl #3] + add x2, x2, #1 + cmp x2, #512 + b.lo .Lmap + msr S3_0_C2_C0_0, x1 // TTBR0_EL1 = l1_table + dsb sy + isb + mrs x0, S3_0_C1_C0_0 // SCTLR_EL1: enable MMU + caches + orr x0, x0, #(1 << 0) // M (MMU) + orr x0, x0, #(1 << 2) // C (data cache) + orr x0, x0, #(1 << 12) // I (instruction cache) + movn x5, #(1 << 1) // A=0: allow unaligned on Normal memory + and x0, x0, x5 + msr S3_0_C1_C0_0, x0 + isb + + bl main adrp x1, semihost_args add x1, x1, :lo12:semihost_args str x0, [x1, #8] @@ -210,6 +256,11 @@ semihost_args: .quad 0x20026 .quad 0 +.section .bss.pgtbl,"aw",@nobits +.balign 4096 +l1_table: + .zero 4096 + .section .bss.stack,"aw",@nobits .balign 16 stack_bottom: @@ -220,10 +271,11 @@ EOF ENTRY(_start) SECTIONS { . = 0x40080000; - .text : ALIGN(8) { *(.text.start) *(.text*) } - .rodata : ALIGN(8) { *(.rodata*) } - .data : ALIGN(8) { *(.data*) } - .bss : ALIGN(16) { *(.bss*) *(COMMON) . = ALIGN(., 16); } + .text : ALIGN(8) { *(.text.start) *(.text*) } + .rodata : ALIGN(8) { *(.rodata*) } + .data : ALIGN(8) { *(.data*) } + .bss.pgtbl : ALIGN(4096) { *(.bss.pgtbl) } /* L1 table needs 4K alignment */ + .bss : ALIGN(16) { *(.bss*) *(COMMON) . = ALIGN(., 16); } /DISCARD/ : { *(.comment) } } EOF diff --git a/test/lib/exec_vm.sh b/test/lib/exec_vm.sh @@ -138,18 +138,25 @@ exec_vm_teardown_all() { for vmid in $EXEC_VM_STARTED; do case "$vmid" in windows) - "$EXEC_VM_SCRIPTS/windows_vm.sh" stop >/dev/null 2>&1 || true ;; + "$EXEC_VM_SCRIPTS/windows_vm.sh" stop </dev/null >/dev/null 2>&1 || true ;; freebsd-*) vmarch="${vmid#freebsd-}" - "$EXEC_VM_SCRIPTS/freebsd_vm.sh" ssh "$vmarch" \ - 'sync; shutdown -p now' >/dev/null 2>&1 || true + # Best-effort guest-side flush only — do NOT wait on a full + # poweroff: the unprivileged `kit` ssh user may not be allowed + # to run shutdown(8), so waiting on it just stalls teardown for + # the whole 30s timeout. (</dev/null so this ssh can't read the + # caller's stdin — e.g. the EXIT-trap's inherited terminal.) + "$EXEC_VM_SCRIPTS/freebsd_vm.sh" ssh "$vmarch" 'sync' \ + </dev/null >/dev/null 2>&1 || true if [ -f "$EXEC_VM_WORK/$vmid.pid" ]; then pid="$(cat "$EXEC_VM_WORK/$vmid.pid")" - for _ in $(seq 1 30); do + # qemu is our exec'd child (run_arch execs it, so the saved + # pid IS qemu): SIGTERM makes it quit promptly. Wait a few + # seconds, then SIGKILL as a backstop. + kill "$pid" 2>/dev/null || true + for _ in $(seq 1 5); do kill -0 "$pid" 2>/dev/null || break; sleep 1 done - kill -0 "$pid" 2>/dev/null && kill "$pid" 2>/dev/null || true - sleep 1 kill -0 "$pid" 2>/dev/null && kill -9 "$pid" 2>/dev/null || true wait "$pid" 2>/dev/null || true fi ;;