From f1dc5e7f6e236e8f001d5a295a3d4c39477e95ac Mon Sep 17 00:00:00 2001 From: Arti Zirk Date: Fri, 30 Oct 2026 17:06:49 +0200 Subject: [PATCH] udpstream: reboot is 1-in-2 on the real unit, not a working hand-back Attempt 1 (16:58:45) came back: port 771 reopened at 88 s, the unit pinged, and a VRAM screenshot showed the Terminal Manager. Attempt 2 (17:04:15, after a power cycle) never came back -- no ping, no rdebug at 130 s, monitor asleep, operator reports it stuck. The previous commit recorded attempt 1 as "it works, no power cycle needed". That was a single sample and attempt 2 falsified it. Section 72.3 now records both runs as a table, the 1-of-2 rate, and the two corrections made on the way (first "hung", then "works"). Untested hypothesis: the reset path does not reset the LANCE, and the payload hands over with bus-master DMA enabled; an in-flight transfer or pending interrupt may leave the board-window handshake at 0xC00000B0/0xC0000100 waiting forever. The single-variable test would be CSR0_STOP before the jump. vtcmd.py reboot requires --force again and states the measured rate. --- harness/mame/NETWORK_REPORT.md | 63 ++++++++++++++++++++-------------- harness/udpstream/vtcmd.py | 14 ++++++-- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/harness/mame/NETWORK_REPORT.md b/harness/mame/NETWORK_REPORT.md index 42fe12d..90d282b 100644 --- a/harness/mame/NETWORK_REPORT.md +++ b/harness/mame/NETWORK_REPORT.md @@ -4693,38 +4693,49 @@ anyway, which is the condition we reproduce. MAME evidence: after the reply, the PC leaves the payload (`0x018Cxxxx`) for ROM (`0x3FB43E40` at the next sample) and the payload stops answering. -### 72.3 It works on the real machine — and a correction +### 72.3 On the real machine it succeeded once and hung the second time — it is NOT reliable -`reboot` was issued on the unit at **16:58:45** while the payload owned the LANCE. -The reply came back (`control -> bit 0x3f200040`, status 0), the payload stopped -answering, and the screen went black. +Two attempts, both issued while the payload owned the LANCE, both acknowledged +(`control -> bit 0x3f200040`, status 0), both followed by a screen that lost +sync (the monitor went to sleep) rather than showing the power-on circle. -**That is POST, not a hang.** Port 771 reopened **88 s** later, the unit answered -ping, and a VRAM screenshot (`dwt/screenshot.py --real`) shows the machine fully -back: +| | attempt 1 | attempt 2 | +|---|---|---| +| time | 16:58:45 | 17:04:15 | +| payload state | `RXF=11` | `RXF=15` (fresh power cycle before it) | +| port 771 back | **yes, at 88 s** | **no**, still down at 130 s | +| ping | yes | no | +| display | Terminal Manager (VRAM screenshot) | monitor asleep, operator: "stuck" | -``` -Terminal Manager V2.2 Copyright (C) 1991 Digital Equipment Corporation -Messages: Debug connection from IP host 100.103.255.254 -Ethernet Address 08-00-2B-16-4B-5A IP 100.103.100.103 -LAT Host Name LAT_08002B164B5A Memory Remaining: 4M -``` +Attempt 1 looked like a working hand-back: 88 s later rdebug reopened, the unit +answered ping, and the VRAM screenshot showed the Terminal Manager with +`08-00-2B-16-4B-5A`, `100.103.100.103`, `LAT_08002B164B5A` and the clean-boot +`Ethernet Port Error - 24` banner (section 65). -with the documented `Ethernet Port Error - 24` clean-boot banner (section 65). -So a taken-over unit **can hand itself back without a power cycle**, which was -the point of the command. +**Attempt 2 never came back.** So the reset-vector jump is a **1-in-2** operation +on this machine, and a power cycle is still required when it fails. -**Correction, recorded on purpose.** An earlier reading of this same run called -it "black and hung, needs a power cycle", because the observation was taken -inside the ~90 s POST window. That conclusion was wrong, and it is the exact -mistake this project's own rule 3 warns about — one sample, taken early, reported -as a property. The screen being black is normal: the reset path reprograms the -video window (`0xC00000B0`, `0xC0000100` in the trace above) and does not -re-enable the display until POST is done. +**Hypothesis (untested).** The reset path does not reset the LANCE, and the +payload hands over with the chip started and bus-master DMA enabled. If a +transfer is in flight — or an interrupt is pending — when control jumps, the +firmware's early board-window handshake (`0xC00000B0` / `0xC0000100` in the trace +above) can wait forever, which would look exactly like this: dead display, no +network, no progress. On the successful run the chip happened to be idle. -`vtcmd.py reboot` therefore prints the expected ~90 s and warns not to power -cycle during it. `jump` and `reboot` both transmit their reply and wait for the -descriptor to complete before transferring control, so the host always gets its +The single-variable experiment that would test it: `CSR0_STOP` (and mask +interrupts) immediately before the jump, so the reset path starts with a quiet +LANCE. That has **not** been run. + +**Two corrections are recorded here on purpose.** First, an earlier reading of +attempt 1 called it "black and hung, needs a power cycle", because the +observation was taken inside the ~90 s window. Then, after attempt 1 returned, +the opposite claim — "it works, no power cycle needed" — was made from the same +single sample. Attempt 2 falsified that. Both mistakes are the same mistake rule 3 +of this project warns about: a conclusion drawn from one observation. + +`vtcmd.py reboot` therefore requires `--force` and states that it worked 1 of 2 +times. `jump` and `reboot` still transmit their reply and wait for the descriptor +to complete before transferring control, so the host always gets its acknowledgement first. ### 72.4 There is no vendor reboot to jump to (checked in Ghidra) diff --git a/harness/udpstream/vtcmd.py b/harness/udpstream/vtcmd.py index b570e01..39462ff 100644 --- a/harness/udpstream/vtcmd.py +++ b/harness/udpstream/vtcmd.py @@ -151,6 +151,8 @@ def main(): help="busy-wait instead of time.sleep while pacing; " "sleep granularity is ~250us and bunches frames " "into microbursts that the switch drops") + ap.add_argument("--force", action="store_true", + help="reboot: required, it succeeded 1 of 2 times and can hang") ap.add_argument("--terse", action="store_true", help="one CSV line per run, for sweeps and for the report") ap.add_argument("--timeout", type=float, default=2.0) @@ -190,10 +192,16 @@ def main(): return 0 if a.cmd in ("jump", "reboot"): + if a.cmd == "reboot" and not a.force: + print("refusing reboot without --force.") + print(" It is NOT reliable: 1 success out of 2 attempts on the real") + print(" VT1200. Attempt 1 came back in 88 s; attempt 2 never came") + print(" back (no ping, no rdebug, monitor asleep) and needed a power") + print(" cycle. The screen always loses sync, so the monitor sleeps") + print(" for the whole window. See NETWORK_REPORT.md 72.3.") + return 2 if a.cmd == "reboot": - print("rebooting: the screen goes black while POST runs; the OS is") - print(" back in about 90 s (measured 88 s) and rdebug :771 returns") - print(" on its own. Do not power cycle it during that window.") + print("rebooting: it may take ~90 s and it may never come back.") cmd = 4 if a.cmd == "jump" else 5 status, data = request(sock, a.iface, cmd, a.addr, 0, a.timeout) if status is None: