Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Sources/Application/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1712,6 +1712,20 @@ struct Application {
uartPutDec(UInt64(kernel_gpclk_restore()))
uartPuts("\n")

// Runtime V146: GPCLK1 clock enable. UART token only.
// Fail-closed CM_GP1 OSC+ENAB + leftover restore.
// No pin-mux. No output claim. No SMI. No UART2. No BSC0. No SPI0 TA.
// No boot event emit. No EL0 enter after GENET.
uartPuts("runtime v146: GPCLK1 probe\n")
let gpclk1_ok_boot = kernel_gpclk1_selftest()
uartPuts("gpclk1 ok=")
uartPutDec(UInt64(gpclk1_ok_boot))
uartPuts(" version=146 clk=")
uartPutDec(UInt64(kernel_gpclk1_clk()))
uartPuts(" restore=")
uartPutDec(UInt64(kernel_gpclk1_restore()))
uartPuts("\n")

kernel_event_emit(KERNEL_EVENT_KIND_SELFTEST, 22, UInt(kernel_pool_selftest()), UInt(kernel_pool_count()))
kernel_event_emit(KERNEL_EVENT_KIND_SELFTEST, 23, UInt(heap_fragmentation_selftest()), UInt(kernel_pool_pressure_selftest()))
kernel_event_emit(KERNEL_EVENT_KIND_SELFTEST, 24, UInt(kernel_driver_registry_selftest()), UInt(kernel_driver_count()))
Expand Down
15 changes: 14 additions & 1 deletion Sources/Application/UARTShell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func uartPutShellBufferSlice(_ start: UInt32, _ len: UInt32) {
}

let SHELL_COMMAND_LIST =
"commands=help,protocol,status,heap,queues,tasks,tasks2,kobjects,drivers,drivercheck,mailboxes,sendtest,supervisor,health,handlecheck,capcheck,events,runtime,agent,certificate,sched,sched2,sched3,sched4,sched5,sched6,sched7,sched8,sched9,sched10,sched11,sched12,cores,locks,runqueues,diag,irqs,timers,memcheck,faults,retained,retained-clear,memmap,mmu,pools,poolcheck,heapfrag,poolstats,frames,heapcheck,framecheck,stress,frameprobe,bootcert,canceltest,taskcheck,channeltest,bootcheck,soak,heap-invalid-free-test,heap-double-free-test,panic-test,fault-test,reboot,vmm,asplit,el0,syscall,uaccess,usermode,process,loader,multiprocess,sdhci,card,block,fat32,mailbox,framebuf,console,pcie,vl805,xhci,genet,genet2,genet3,genet4,gpio,genet5,genet6,genet7,genet8,genet9,genet10,genet11,genet12,i2c,pwm,i2c2,spi2,stimer,sdload,sdls,sdfile,sdovl,sdovf,sdiss,gpio2,stimer2,pwm2,gpio3,stimer3,mboxt,mboxc,wdog2,mboxv,rng,dma2,sdwr,sdmk,sdrd,sdst,sdscr,sdss,sdbus,sdmb,sdsw,sdmw,sdbc,sdfi,sdfb,sdfm,sdrm,genet13,genet14,genet15,genet16,genet17,genet18,genet19,genet20,genet21,genet22,genet23,genet24,genet25,genet26,genet27,genet28,genet29,genet30,genet31,genet32,genet33,genet34,pwm3,gpio4,pwm4,pcm,auxspi1,gpclk"
"commands=help,protocol,status,heap,queues,tasks,tasks2,kobjects,drivers,drivercheck,mailboxes,sendtest,supervisor,health,handlecheck,capcheck,events,runtime,agent,certificate,sched,sched2,sched3,sched4,sched5,sched6,sched7,sched8,sched9,sched10,sched11,sched12,cores,locks,runqueues,diag,irqs,timers,memcheck,faults,retained,retained-clear,memmap,mmu,pools,poolcheck,heapfrag,poolstats,frames,heapcheck,framecheck,stress,frameprobe,bootcert,canceltest,taskcheck,channeltest,bootcheck,soak,heap-invalid-free-test,heap-double-free-test,panic-test,fault-test,reboot,vmm,asplit,el0,syscall,uaccess,usermode,process,loader,multiprocess,sdhci,card,block,fat32,mailbox,framebuf,console,pcie,vl805,xhci,genet,genet2,genet3,genet4,gpio,genet5,genet6,genet7,genet8,genet9,genet10,genet11,genet12,i2c,pwm,i2c2,spi2,stimer,sdload,sdls,sdfile,sdovl,sdovf,sdiss,gpio2,stimer2,pwm2,gpio3,stimer3,mboxt,mboxc,wdog2,mboxv,rng,dma2,sdwr,sdmk,sdrd,sdst,sdscr,sdss,sdbus,sdmb,sdsw,sdmw,sdbc,sdfi,sdfb,sdfm,sdrm,genet13,genet14,genet15,genet16,genet17,genet18,genet19,genet20,genet21,genet22,genet23,genet24,genet25,genet26,genet27,genet28,genet29,genet30,genet31,genet32,genet33,genet34,pwm3,gpio4,pwm4,pcm,auxspi1,gpclk,gpclk1"

func printShellReady() {
uartPuts("shell ready \(SHELL_COMMAND_LIST)\n")
Expand Down Expand Up @@ -3149,6 +3149,17 @@ func printGpclk() {
uartPuts("\n")
}

func printGpclk1() {
_ = kernel_gpclk1_selftest()
uartPuts("gpclk1 ok=")
uartPutDec(UInt64(kernel_gpclk1_ok()))
uartPuts(" version=146 clk=")
uartPutDec(UInt64(kernel_gpclk1_clk()))
uartPuts(" restore=")
uartPutDec(UInt64(kernel_gpclk1_restore()))
uartPuts("\n")
}

func printPoolName(_ pool: UInt32) {
var i: UInt32 = 0
let n = kernel_pool_name_len(pool)
Expand Down Expand Up @@ -4177,6 +4188,8 @@ func dispatchShellCommand(_ commandStart: UInt32, _ commandLen: UInt32, _ reques
printAuxspi1()
} else if shellBufferSliceEquals(commandStart, commandLen, "gpclk") {
printGpclk()
} else if shellBufferSliceEquals(commandStart, commandLen, "gpclk1") {
printGpclk1()
} else if shellBufferSliceEquals(commandStart, commandLen, "pools") {
printPools()
} else if shellBufferSliceEquals(commandStart, commandLen, "poolcheck") {
Expand Down
6 changes: 6 additions & 0 deletions Sources/Support/include/Support.h
Original file line number Diff line number Diff line change
Expand Up @@ -1659,6 +1659,12 @@ int kernel_gpclk_ok(void);
unsigned int kernel_gpclk_clk(void);
unsigned int kernel_gpclk_restore(void);

// V146: GPCLK1 enable after GENET. Fail-closed CM_GP1 readback. No EL0.
int kernel_gpclk1_selftest(void);
int kernel_gpclk1_ok(void);
unsigned int kernel_gpclk1_clk(void);
unsigned int kernel_gpclk1_restore(void);

// V84: system timer CLO/CHI + four compare slots. Read-only. No boot emit.
int kernel_stimer_selftest(void);
int kernel_stimer_ok(void);
Expand Down
1 change: 1 addition & 0 deletions Sources/Support/kernel_gpclk.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// UART2 after GENET hung AXI/UART — do not retry UART2-5.
// Do not probe BSC0 after GENET (AXI hang, UART dies).
// Do not assert SPI0 TA after GENET (hung boot).
// CM_SMI 0xFE1010B0 after GENET hung AXI/UART (4.9W) — do not retry CM_SMI or SMI CS.
// No EL0 enter (I-abort esr=0xbf000002 elr=0x100002000 after GENET DMA).
// No boot event emit.

Expand Down
89 changes: 89 additions & 0 deletions Sources/Support/kernel_gpclk1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Runtime V146: BCM2711 GPCLK1 clock enable after GENET.
// New unused CM block (CM_GP1 0xFE101078). Fail-closed OSC+ENAB
// write+readback + leftover restore. No pin-mux. No output claim.
// V145 SMI clock manager after GENET hung AXI/UART — do not retry SMI.
// UART2 after GENET hung AXI/UART — do not retry UART2-5.
// Do not probe BSC0 after GENET (AXI hang, UART dies).
// Do not assert SPI0 TA after GENET (hung boot).
// No EL0 enter (I-abort esr=0xbf000002 elr=0x100002000 after GENET DMA).
// No boot event emit.

#include "Support.h"
#include <stdint.h>

#define CM_GP1CTL 0xFE101078UL
#define CM_GP1DIV 0xFE10107CUL
#define CM32(addr) (*(volatile uint32_t *)(addr))

#define CM_PASSWD 0x5A000000U
#define CM_SRC_OSC 1U
#define CM_ENAB (1U << 4)
#define CM_BUSY (1U << 7)
#define CM_SRC_MASK 0x0FU
#define CM_PROG (CM_ENAB | CM_SRC_MASK)

#define GPCLK1_DIVI 50U
#define GPCLK1_WAIT_TICKS 108000UL /* ~2ms @ 54MHz CNTPCT */

static int gpclk1_probed;
static int gpclk1_ok_val;
static unsigned int gpclk1_clk_val;
static unsigned int gpclk1_restore_val;

static int gpclk1_wait_busy(unsigned int want) {
unsigned long start = read_cntpct();
while ((read_cntpct() - start) < GPCLK1_WAIT_TICKS) {
unsigned int ctl = CM32(CM_GP1CTL);
if (ctl == 0xFFFFFFFFU) return 0;
if (want) {
if ((ctl & CM_BUSY) != 0U) return 1;
} else if ((ctl & CM_BUSY) == 0U) {
return 1;
}
}
return 0;
}

int kernel_gpclk1_selftest(void) {
if (gpclk1_probed) return gpclk1_ok_val;
gpclk1_probed = 1;
gpclk1_ok_val = 0;
gpclk1_clk_val = 0;
gpclk1_restore_val = 0;

unsigned int saved_cm_ctl = CM32(CM_GP1CTL);
unsigned int saved_cm_div = CM32(CM_GP1DIV);
if (saved_cm_ctl == 0xFFFFFFFFU || saved_cm_div == 0xFFFFFFFFU) {
return 0;
}

CM32(CM_GP1CTL) = CM_PASSWD | (saved_cm_ctl & 0x0FU);
if (gpclk1_wait_busy(0) == 0) {
CM32(CM_GP1CTL) = CM_PASSWD | (saved_cm_ctl & 0xFFU);
return 0;
}

CM32(CM_GP1DIV) = CM_PASSWD | (GPCLK1_DIVI << 12);
CM32(CM_GP1CTL) = CM_PASSWD | CM_ENAB | CM_SRC_OSC;
if (gpclk1_wait_busy(1) != 0) gpclk1_clk_val = 1;

CM32(CM_GP1CTL) = CM_PASSWD | (saved_cm_ctl & 0x0FU);
(void)gpclk1_wait_busy(0);
CM32(CM_GP1DIV) = CM_PASSWD | (saved_cm_div & 0x00FFFFFFU);
CM32(CM_GP1CTL) = CM_PASSWD | (saved_cm_ctl & 0xFFU);

if ((CM32(CM_GP1CTL) & CM_PROG) == (saved_cm_ctl & CM_PROG) &&
(CM32(CM_GP1DIV) & 0x00FFFFFFU) == (saved_cm_div & 0x00FFFFFFU)) {
gpclk1_restore_val = 1;
}

if (gpclk1_clk_val == 1U && gpclk1_restore_val == 1U) {
gpclk1_ok_val = 1;
return 1;
}
return 0;
}

int kernel_gpclk1_ok(void) { return gpclk1_ok_val; }
unsigned int kernel_gpclk1_clk(void) { return gpclk1_clk_val; }
unsigned int kernel_gpclk1_restore(void) { return gpclk1_restore_val; }
4 changes: 2 additions & 2 deletions Sources/Support/memory_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
#define KERNEL_BOOT_STACK_BASE 0x00070000UL
#define KERNEL_BOOT_STACK_LIMIT 0x00080000UL
#define KERNEL_IMAGE_MMU_BASE 0x00080000UL
#define KERNEL_IMAGE_MMU_LIMIT 0x00100000UL
#define KERNEL_SPARE_BASE 0x00100000UL
#define KERNEL_IMAGE_MMU_LIMIT 0x00120000UL
#define KERNEL_SPARE_BASE 0x00120000UL
#define KERNEL_RETAINED_LIMIT 0x00400000UL
#define KERNEL_HEAP_BASE 0x00400000UL
#define KERNEL_HEAP_LIMIT 0x00800000UL
Expand Down
2 changes: 2 additions & 0 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ Turn the static identity-map MMU into a real virtual-memory system and run code
- [x] V142 PCM/I2S enable after GENET (`pcm ok=1 version=142 clk=1 en=1 restore=1`). Fail-closed `CM_PCM` OSC + `PCM_CS` EN write+readback + leftover restore; no pin-mux; no TX/RX/FIFO; no output/audio claim; no EL0.
- [x] V143 AUX SPI1 enable after GENET (`auxspi1 ok=1 version=143 en=1 restore=1`). Fail-closed `AUXENB` SPI1 bit + leftover restore; no pin-mux; no transfer; no mini-UART change. BSC0 `0xFE205000` after GENET hung AXI/UART — do not retry. No EL0.
- [x] V144 GPCLK0 enable after GENET (`gpclk ok=1 version=144 clk=1 restore=1`). Fail-closed `CM_GP0CTL` OSC+ENAB write+readback + leftover restore; no pin-mux; no output claim. UART2 `0xFE201400` after GENET hung AXI/UART — do not retry UART2–5. No EL0.
- [ ] V145 SMI clock enable — blocked. `CM_SMICTL` `0xFE1010B0` after GENET hung AXI/UART (4.9W, silent UART after `v120.bin` complete). Do not retry CM_SMI or SMI CS `0xFE600000`. UART2–5 / BSC0 / SPI0 TA also hung — do not retry. No EL0.
- [x] V146 GPCLK1 enable after GENET (`gpclk1 ok=1 version=146 clk=1 restore=1`). Fail-closed `CM_GP1CTL` OSC+ENAB write+readback + leftover restore; no pin-mux; no output claim. Image past `0x100000` overlapped spare and hung GENET — `KERNEL_IMAGE_MMU_LIMIT`/`KERNEL_SPARE_BASE` raised to `0x120000`. SMI / UART2–5 / BSC0 / SPI0 TA hung after GENET — do not retry. No EL0.
- [x] V94 GPIO26 PUP_PDN write+readback after GENET (`gpio3 ok=1 version=94 pin=26 up=1 dn=1`). REG1 only; restore; no UART; no EL0.
- [x] V95 system timer C3 match after GENET (`stimer3 ok=1 version=95 chan=3 match=1`). ARM C3 only; no C0/C1/C2 writes; park after match; no EL0.
- [x] V96 mailbox GET_TEMPERATURE after GENET (`mboxt ok=1 version=96 temp=`). Millidegrees; fail-closed range; no EL0.
Expand Down
4 changes: 4 additions & 0 deletions scripts/netboot/net-iterate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ PY
&& printf '%s' "$serial_delta" | grep -qa "auxspi1 ok=1 version=143 en=" \
&& printf '%s' "$serial_delta" | grep -qa "runtime v144: GPCLK probe" \
&& printf '%s' "$serial_delta" | grep -qa "gpclk ok=1 version=144 clk=" \
&& printf '%s' "$serial_delta" | grep -qa "runtime v146: GPCLK1 probe" \
&& printf '%s' "$serial_delta" | grep -qa "gpclk1 ok=1 version=146 clk=" \
&& printf '%s' "$serial_delta" | grep -qa "vmmcheck ok=1" \
&& printf '%s' "$serial_delta" | grep -qa "asplit ok=1 version=46" \
&& printf '%s' "$serial_delta" | grep -qa "el0 ok=1 version=47" \
Expand Down Expand Up @@ -834,6 +836,8 @@ PY
probe_shell "auxspi1" "^auxspi1 ok=1 version=143 en="
# V144: GPCLK0 clock enable. Fail-closed CM_GP0 readback. No EL0.
probe_shell "gpclk" "^gpclk ok=1 version=144 clk="
# V146: GPCLK1 clock enable. Fail-closed CM_GP1 readback. No EL0.
probe_shell "gpclk1" "^gpclk1 ok=1 version=146 clk="
# probe shell: req-status
probe_shell "req id=25 cmd=status" "^resp id=25 ok=1 cmd=status end"
# probe shell: canceltest
Expand Down
2 changes: 2 additions & 0 deletions scripts/netboot/netboot-doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ while [ "$SECONDS" -lt "$deadline" ]; do
&& printf '%s' "$serial_delta" | grep -q "auxspi1 ok=1 version=143 en=" \
&& printf '%s' "$serial_delta" | grep -q "runtime v144: GPCLK probe" \
&& printf '%s' "$serial_delta" | grep -q "gpclk ok=1 version=144 clk=" \
&& printf '%s' "$serial_delta" | grep -q "runtime v146: GPCLK1 probe" \
&& printf '%s' "$serial_delta" | grep -q "gpclk1 ok=1 version=146 clk=" \
&& printf '%s' "$serial_delta" | grep -q "vmmcheck ok=1" \
&& printf '%s' "$serial_delta" | grep -q "asplit ok=1 version=46" \
&& printf '%s' "$serial_delta" | grep -q "el0 ok=1 version=47" \
Expand Down
91 changes: 91 additions & 0 deletions tests/test_runtime_gpclk1_v146_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""EPIC G V146: GPCLK1 enable after GENET. Fail-closed CM_GP1 readback. No EL0."""

import pathlib


ROOT = pathlib.Path(__file__).resolve().parents[1]


def read_repo(path: str) -> str:
return (ROOT / path).read_text()


def test_gpclk1_enable_fail_closed_without_el0() -> None:
load = read_repo("Sources/Support/kernel_gpclk1.c")
gp0 = read_repo("Sources/Support/kernel_gpclk.c")
support = read_repo("Sources/Support/include/Support.h")
app = read_repo("Sources/Application/Application.swift")
shell = read_repo("Sources/Application/UARTShell.swift")
iterate = read_repo("scripts/netboot/net-iterate.sh")
doctor = read_repo("scripts/netboot/netboot-doctor.sh")
scheduler = read_repo("Sources/Support/kernel_scheduler.c")
roadmap = read_repo("docs/ROADMAP.md")

assert "0xFE101078" in load
assert "0xFE10107C" in load
assert "CM_GP1CTL" in load
assert "CM_ENAB" in load
assert "int kernel_gpclk1_selftest(void);" in support
assert "unsigned int kernel_gpclk1_clk(void);" in support
assert "unsigned int kernel_gpclk1_restore(void);" in support
assert 'uartPuts("runtime v146: GPCLK1 probe\\n")' in app
assert 'uartPuts("gpclk1 ok=")' in app
assert "kernel_event_emit(KERNEL_EVENT_KIND_SELFTEST, 146" not in app
assert "func printGpclk1()" in shell
assert 'shellBufferSliceEquals(commandStart, commandLen, "gpclk1")' in shell
assert ",gpclk,gpclk1" in shell
assert 'grep -qa "gpclk1 ok=1 version=146 clk="' in iterate
assert 'probe_shell "gpclk1" "^gpclk1 ok=1 version=146 clk="' in iterate
assert 'grep -q "gpclk1 ok=1 version=146 clk="' in doctor
assert "V146 GPCLK1 enable" in roadmap
assert "kernel_gpclk1_selftest" not in scheduler
assert "alloc_dma" not in load
assert "kernel_event_emit" not in load
assert "kernel_enter_el0_and_wait" not in load
assert "0xbf000002" in load
assert "watchdog_reset_now" not in load
# New unused GPCLK1 CM block. Do not retry SMI / UART2-5 / BSC0 / SPI0 TA.
assert "0xFE1010B0" not in load
assert "0xFE600000" not in load
assert "0xFE201400" not in load
assert "0xFE201000" not in load
assert "0xFE205000" not in load
assert "0xFE804000" not in load
assert "0xFE203000" not in load
assert "0xFE20C000" not in load
assert "0xFE20C800" not in load
assert "0xFE215000" not in load
assert "UARTCR" not in load
assert "PCM_CS" not in load
assert "AUXENB" not in load
assert "PWM_DAT1" not in load
assert "GPREN" not in load
assert "GPEDS" not in load
assert "GPFSEL" not in load
assert "ALT0" not in load
assert "SPI_CS_TA" not in load
assert "0xFE204000" not in load
assert "PUP_PDN" not in load
assert "RBUF_64B_EN" not in load
assert "DMA_TX_DO_CSUM" not in load
assert "CMD_LCL_LOOP_EN" not in load
assert "EXT_RGMII_OOB_CTRL" not in load
assert "RBUF_HFB_EN" not in load
assert "CM_SMI" not in load
assert "0xFE101078" not in gp0
assert "ping -c 2" in iterate
assert "SOCK_STREAM" in iterate
assert "sched12" in iterate


def test_no_boot_el0_enter_after_genet12() -> None:
"""sys_socket after GENET DMA I-aborted; do not ship a second boot EL0 enter."""
app = read_repo("Sources/Application/Application.swift")
after = app.split("kernel_genet12_selftest", 1)[1]
assert "kernel_enter_el0_and_wait" not in after
assert "kernel_socket_selftest" not in after
assert "kernel_gpclk1_selftest" in after
assert "kernel_smi_selftest" not in after
assert "kernel_uart2_selftest" not in after
assert "kernel_bsc0_selftest" not in after
assert "kernel_spi3_selftest" not in after