diff --git a/Sources/Application/Application.swift b/Sources/Application/Application.swift index fec8c6b..9215156 100644 --- a/Sources/Application/Application.swift +++ b/Sources/Application/Application.swift @@ -1623,6 +1623,21 @@ struct Application { uartPutDec(UInt64(kernel_genet34_restore())) uartPuts("\n") + // Runtime V139: PWM GPIO12 ALT0 pin-mux. UART token only. + // Fail-closed FSEL readback. No output claim. No boot event emit. + // No EL0 enter after GENET. + uartPuts("runtime v139: PWM pin-mux\n") + let pwm3_ok_boot = kernel_pwm3_selftest() + uartPuts("pwm3 ok=") + uartPutDec(UInt64(pwm3_ok_boot)) + uartPuts(" version=139 pin=") + uartPutDec(UInt64(kernel_pwm3_pin())) + uartPuts(" alt=") + uartPutDec(UInt64(kernel_pwm3_alt())) + uartPuts(" restore=") + uartPutDec(UInt64(kernel_pwm3_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())) diff --git a/Sources/Application/UARTShell.swift b/Sources/Application/UARTShell.swift index 7f4fe68..5c0c9ed 100644 --- a/Sources/Application/UARTShell.swift +++ b/Sources/Application/UARTShell.swift @@ -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" + "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" func printShellReady() { uartPuts("shell ready \(SHELL_COMMAND_LIST)\n") @@ -3075,6 +3075,19 @@ func printGenet34() { uartPuts("\n") } +func printPwm3() { + _ = kernel_pwm3_selftest() + uartPuts("pwm3 ok=") + uartPutDec(UInt64(kernel_pwm3_ok())) + uartPuts(" version=139 pin=") + uartPutDec(UInt64(kernel_pwm3_pin())) + uartPuts(" alt=") + uartPutDec(UInt64(kernel_pwm3_alt())) + uartPuts(" restore=") + uartPutDec(UInt64(kernel_pwm3_restore())) + uartPuts("\n") +} + func printPoolName(_ pool: UInt32) { var i: UInt32 = 0 let n = kernel_pool_name_len(pool) @@ -4091,6 +4104,8 @@ func dispatchShellCommand(_ commandStart: UInt32, _ commandLen: UInt32, _ reques printGenet33() } else if shellBufferSliceEquals(commandStart, commandLen, "genet34") { printGenet34() + } else if shellBufferSliceEquals(commandStart, commandLen, "pwm3") { + printPwm3() } else if shellBufferSliceEquals(commandStart, commandLen, "pools") { printPools() } else if shellBufferSliceEquals(commandStart, commandLen, "poolcheck") { diff --git a/Sources/Support/include/Support.h b/Sources/Support/include/Support.h index d4a798d..afb85b8 100644 --- a/Sources/Support/include/Support.h +++ b/Sources/Support/include/Support.h @@ -1605,6 +1605,13 @@ int kernel_pwm2_ok(void); unsigned int kernel_pwm2_clk(void); unsigned int kernel_pwm2_en(void); +// V139: PWM GPIO12 ALT0 pin-mux after GENET. Fail-closed FSEL. No EL0. +int kernel_pwm3_selftest(void); +int kernel_pwm3_ok(void); +unsigned int kernel_pwm3_pin(void); +unsigned int kernel_pwm3_alt(void); +unsigned int kernel_pwm3_restore(void); + // V82: one bounded BSC1 write to a vacant address. Honest nack=1. No boot emit. int kernel_i2c2_selftest(void); int kernel_i2c2_ok(void); diff --git a/Sources/Support/kernel_pwm3.c b/Sources/Support/kernel_pwm3.c new file mode 100644 index 0000000..9f52fc3 --- /dev/null +++ b/Sources/Support/kernel_pwm3.c @@ -0,0 +1,83 @@ +// Runtime V139: BCM2711 PWM GPIO12 ALT0 pin-mux after GENET. +// Fail-closed GPFSEL1 readback of ALT0, then leftover restore. +// UART GPIO14/15 FSEL bits must stay unchanged. No PUP/PDN writes +// (do not touch REG0). No PWM CTL/DAT (V93 already poked those). +// No output claim. No EL0 enter (I-abort esr=0xbf000002 elr=0x100002000 +// after GENET DMA). No boot event emit. + +#include "Support.h" +#include + +#define GPIO_BASE 0xFE200000UL +#define G32(off) (*(volatile uint32_t *)(GPIO_BASE + (unsigned long)(off))) + +#define GPFSEL1 0x04U /* GPIO10..19 */ + +#define PWM3_PIN 12U +#define PWM3_FSEL_SHIFT 6U /* (12-10)*3 */ +#define FSEL_MASK 7U +#define FSEL_INPUT 0U +#define FSEL_ALT0 4U +#define UART_FSEL_MASK (0x3FU << 12) /* GPIO14+15 in GPFSEL1 */ + +static int pwm3_probed; +static int pwm3_ok_val; +static unsigned int pwm3_pin_val; +static unsigned int pwm3_alt_val; +static unsigned int pwm3_restore_val; + +int kernel_pwm3_selftest(void) { + if (pwm3_probed) return pwm3_ok_val; + pwm3_probed = 1; + pwm3_ok_val = 0; + pwm3_pin_val = PWM3_PIN; + pwm3_alt_val = 0; + pwm3_restore_val = 0; + + if (kernel_pwm2_selftest() == 0) return 0; + + unsigned int saved = G32(GPFSEL1); + if (saved == 0xFFFFFFFFU) return 0; + + unsigned int uart_bits = saved & UART_FSEL_MASK; + unsigned int fsel12 = (saved >> PWM3_FSEL_SHIFT) & FSEL_MASK; + + /* Non-vacuous: if leftover is already ALT0, prove the field can move. */ + if (fsel12 == FSEL_ALT0) { + unsigned int tmp = saved & ~(FSEL_MASK << PWM3_FSEL_SHIFT); + tmp |= (FSEL_INPUT << PWM3_FSEL_SHIFT); + G32(GPFSEL1) = tmp; + unsigned int rb = G32(GPFSEL1); + if (((rb >> PWM3_FSEL_SHIFT) & FSEL_MASK) != FSEL_INPUT || + (rb & UART_FSEL_MASK) != uart_bits) { + G32(GPFSEL1) = saved; + return 0; + } + } + + unsigned int cur = G32(GPFSEL1); + unsigned int next = cur; + next &= ~(FSEL_MASK << PWM3_FSEL_SHIFT); + next |= (FSEL_ALT0 << PWM3_FSEL_SHIFT); + G32(GPFSEL1) = next; + + unsigned int muxed = G32(GPFSEL1); + if (((muxed >> PWM3_FSEL_SHIFT) & FSEL_MASK) == FSEL_ALT0 && + (muxed & UART_FSEL_MASK) == uart_bits) { + pwm3_alt_val = 1; + } + + G32(GPFSEL1) = saved; + if (G32(GPFSEL1) == saved) pwm3_restore_val = 1; + + if (pwm3_alt_val == 1U && pwm3_restore_val == 1U) { + pwm3_ok_val = 1; + return 1; + } + return 0; +} + +int kernel_pwm3_ok(void) { return pwm3_ok_val; } +unsigned int kernel_pwm3_pin(void) { return pwm3_pin_val; } +unsigned int kernel_pwm3_alt(void) { return pwm3_alt_val; } +unsigned int kernel_pwm3_restore(void) { return pwm3_restore_val; } diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 4063ff2..8c6db72 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -151,6 +151,7 @@ Turn the static identity-map MMU into a real virtual-memory system and run code - [x] V91 GPIO42 output + GPLEV readback after GENET (`gpio2 ok=1 version=91 pin=42 set=1 clr=1`). Kernel ACT LED; restore FSEL; no jumper; no EL0. - [x] V92 system timer C1 match after GENET (`stimer2 ok=1 version=92 chan=1 match=1`). ARM C1 only; no C0/C2 writes; park after match; no EL0. - [x] V93 PWM clock enable + CTL poke after GENET (`pwm2 ok=1 version=93 clk=1 en=1`). CM_PWM OSC + PWM0 PWEN1 readback; restore; no pin-mux; no output claim; no EL0. +- [x] V139 PWM GPIO12 ALT0 pin-mux after GENET (`pwm3 ok=1 version=139 pin=12 alt=1 restore=1`). Fail-closed `GPFSEL1` ALT0 readback + leftover restore; UART GPIO14/15 FSEL bits unchanged; no pull writes; no output claim; 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. diff --git a/scripts/netboot/net-iterate.sh b/scripts/netboot/net-iterate.sh index 29a674e..4300765 100755 --- a/scripts/netboot/net-iterate.sh +++ b/scripts/netboot/net-iterate.sh @@ -491,6 +491,8 @@ PY && printf '%s' "$serial_delta" | grep -qa "genet33 ok=1 version=137 rflush=" \ && printf '%s' "$serial_delta" | grep -qa "runtime v138: GENET EXT OOB" \ && printf '%s' "$serial_delta" | grep -qa "genet34 ok=1 version=138 oob=" \ + && printf '%s' "$serial_delta" | grep -qa "runtime v139: PWM pin-mux" \ + && printf '%s' "$serial_delta" | grep -qa "pwm3 ok=1 version=139 pin=12 alt=" \ && 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" \ @@ -810,6 +812,8 @@ PY probe_shell "genet33" "^genet33 ok=1 version=137 rflush=" # V138: EXT RGMII OOB_DISABLE writeback. No DMA. No EL0. probe_shell "genet34" "^genet34 ok=1 version=138 oob=" + # V139: PWM GPIO12 ALT0 pin-mux. Fail-closed FSEL. No EL0. + probe_shell "pwm3" "^pwm3 ok=1 version=139 pin=12 alt=" # probe shell: req-status probe_shell "req id=25 cmd=status" "^resp id=25 ok=1 cmd=status end" # probe shell: canceltest diff --git a/scripts/netboot/netboot-doctor.sh b/scripts/netboot/netboot-doctor.sh index a51dbe0..5928ced 100755 --- a/scripts/netboot/netboot-doctor.sh +++ b/scripts/netboot/netboot-doctor.sh @@ -351,6 +351,8 @@ while [ "$SECONDS" -lt "$deadline" ]; do && printf '%s' "$serial_delta" | grep -q "genet33 ok=1 version=137 rflush=" \ && printf '%s' "$serial_delta" | grep -q "runtime v138: GENET EXT OOB" \ && printf '%s' "$serial_delta" | grep -q "genet34 ok=1 version=138 oob=" \ + && printf '%s' "$serial_delta" | grep -q "runtime v139: PWM pin-mux" \ + && printf '%s' "$serial_delta" | grep -q "pwm3 ok=1 version=139 pin=12 alt=" \ && 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" \ diff --git a/tests/test_runtime_pwm3_v139_contract.py b/tests/test_runtime_pwm3_v139_contract.py new file mode 100644 index 0000000..4b059f4 --- /dev/null +++ b/tests/test_runtime_pwm3_v139_contract.py @@ -0,0 +1,76 @@ +"""EPIC G V139: PWM GPIO12 ALT0 pin-mux after GENET. Fail-closed FSEL. No EL0.""" + +import pathlib + + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +def read_repo(path: str) -> str: + return (ROOT / path).read_text() + + +def test_pwm3_pin_mux_fail_closed_without_el0() -> None: + load = read_repo("Sources/Support/kernel_pwm3.c") + pwm_ro = read_repo("Sources/Support/kernel_pwm.c") + pwm2 = read_repo("Sources/Support/kernel_pwm2.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 "0xFE200000" in load + assert "GPFSEL1" in load + assert "12U" in load or "GPIO12" in load + assert "ALT0" in load + assert "kernel_pwm2_selftest" in load + assert "int kernel_pwm3_selftest(void);" in support + assert "unsigned int kernel_pwm3_pin(void);" in support + assert "unsigned int kernel_pwm3_alt(void);" in support + assert "unsigned int kernel_pwm3_restore(void);" in support + assert 'uartPuts("runtime v139: PWM pin-mux\\n")' in app + assert 'uartPuts("pwm3 ok=")' in app + assert "kernel_event_emit(KERNEL_EVENT_KIND_SELFTEST, 139" not in app + assert "func printPwm3()" in shell + assert 'shellBufferSliceEquals(commandStart, commandLen, "pwm3")' in shell + assert ",genet34,pwm3" in shell + assert 'grep -qa "pwm3 ok=1 version=139 pin=12 alt="' in iterate + assert 'probe_shell "pwm3" "^pwm3 ok=1 version=139 pin=12 alt="' in iterate + assert 'grep -q "pwm3 ok=1 version=139 pin=12 alt="' in doctor + assert "V139 PWM GPIO12 ALT0 pin-mux" in roadmap + assert "kernel_pwm3_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 + # Fail-closed FSEL readback; no output claim; UART 14/15 pulls untouched. + assert "GPFSEL1" in load + assert "PUP_PDN" not in load + assert "0xE4" not in load + assert "GPSET" not in load + assert "GPCLR" not in load + assert "PWM_CTL" not in load + assert "PWM_DAT1" not in load + assert "0xFE20C800" 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 "G32(PWM0_BASE, PWM_CTL) =" not in pwm_ro + assert "GPFSEL" not in pwm2 + 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_pwm3_selftest" in after