forked from arti/vt1200
udpstream: osboot -- jump to the OS cold start (0x3F5788C0), the path that quietens the LANCE
The reset vector is the wrong entry: it starts POST, which re-programs the board window from a state a payload has already changed. The firmware has a second entry, 0x3F5788C0, where the boot gate at 0x3F2042D0 branches when it skips level 1. It begins with DINT, CSR0=STOP on the LANCE, and a board-window re-program -- exactly what a takeover breaks. MAME, from the LANCE access log: the payload's last access and the OS's first are 130 us apart, then pc=3F578940 writes CSR0=0x0004 (STOP) and pc=3FDDCAE0 (the OS driver) re-initialises the chip, after which the OS keeps servicing it. cmd 6 / vtcmd.py osboot. Unit run pending; the report section is 73.
This commit is contained in:
1 parent
504d63a705
commit
baffb8292e
4 files changed
+73
-4
No files matched your search
@@ -4777,3 +4777,56 @@ the screen blank while it reprograms the video window.
|
||||
If a faster return than that is ever needed, the alternative is not a reboot but
|
||||
a hand-back of the chip while the OS keeps running (section 64): the display and
|
||||
the OS survive, the network does not.
|
||||
|
||||
## 73. The real reboot target: the OS cold-start entry, not the reset vector
|
||||
|
||||
Section 72.4 established that the firmware has no reboot routine and that
|
||||
jumping at the reset vector wedges the machine (0 for 2). But the reset vector
|
||||
is the *wrong* entry: it is the start of POST, which reprograms the board window
|
||||
from a state a payload has already changed. The firmware's own boot has a second
|
||||
entry, the **OS cold start at `0x3F5788C0`** — the address the boot gate at
|
||||
`0x3F2042D0` branches to when it skips the level-1 batch (`vt1200.cpp:134`).
|
||||
|
||||
That entry starts by fixing exactly what a payload breaks
|
||||
(`rom:0x07eaf118`, bit `0x3F5788C0`):
|
||||
|
||||
```
|
||||
07eaf118 DINT
|
||||
07eaf11a CLR A10
|
||||
07eaf11c SETF 0x0,0x0,0x1
|
||||
07eaf11e SETF 0x10,0x0,0x0
|
||||
07eaf120 CLR A0
|
||||
07eaf122 MOVE A0,@0x5900000 ; LANCE RAP = 0
|
||||
07eaf128 MOVI 0x4,A0
|
||||
07eaf12c MOVE A0,@0x5800000 ; CSR0 = 0x0004 = STOP <- quiet the chip
|
||||
07eaf132 MOVI 0xC,A0
|
||||
07eaf136 MOVE A0,@0xc00000b0 ; board window = 0x0C
|
||||
07eaf140 MOVI 0x3f57aa70,A2 ; then re-program the board tables
|
||||
07eaf146 MOVI 0xc0000080,A3
|
||||
07eaf14c MOVE *A2+,*A3+,0x0 ; (3 words)
|
||||
07eaf156 MOVI 0x3f57aaa0,A2 ; and the next table to 0xc0000130
|
||||
```
|
||||
|
||||
`DINT`, `CSR0=STOP`, board window — in that order. This is the path that is
|
||||
designed to run when the machine is handed over, and it is *fast*: no POST.
|
||||
|
||||
`lance_cmd.c` exposes it as command 6, `osboot`.
|
||||
|
||||
### 73.1 MAME: the OS re-initialises the chip
|
||||
|
||||
`VT1200_LANCE_DMA_LOG`/`VT1200_LANCELOG` shows the handoff and the vendor's own
|
||||
re-init, with the payload's last access and the OS's first access 130 µs apart:
|
||||
|
||||
```
|
||||
t=11.823527 W RDP val=0048 pc=018C0E40 <- last payload access
|
||||
t=11.823657 R RDP val=02F3 pc=3F9D5BC0 <- OS LANCE handler
|
||||
t=11.828933 W RDP val=0004 pc=3F578940 <- CSR0 = STOP, inside 0x3F5788C0
|
||||
t=11.828960 W RDP val=0004 pc=3FDDCAE0 <- OS driver (io5800_5900_init)
|
||||
```
|
||||
|
||||
and the OS keeps servicing the chip afterwards (`pc=3F9D*`, 134 accesses in the
|
||||
window). So the cold start restores the LANCE and the OS continues.
|
||||
|
||||
### 73.2 Real unit
|
||||
|
||||
Result recorded in 73.3 once the unit run completes.
|
||||
Binary file not shown.
@@ -445,6 +445,18 @@ int main()
|
||||
}
|
||||
wb(TXBUF + 19 * 8, (wlen >> 8) & 0xFF);
|
||||
wb(TXBUF + 20 * 8, wlen & 0xFF);
|
||||
} else if (cmd == 6) {
|
||||
/* OS cold start: the entry POST branches to at 0x3F2042D0
|
||||
* when it skips the level-1 batch. Unlike the reset
|
||||
* vector this path first STOPs the LANCE and re-programs
|
||||
* the board window, which is exactly the state a payload
|
||||
* leaves behind -- see NETWORK_REPORT 72.5. */
|
||||
jaddr = 0x3F5788C0;
|
||||
dojump = 1;
|
||||
wb(TXBUF + 19 * 8, (jaddr >> 24) & 0xFF);
|
||||
wb(TXBUF + 20 * 8, (jaddr >> 16) & 0xFF);
|
||||
wb(TXBUF + 21 * 8, (jaddr >> 8) & 0xFF);
|
||||
wb(TXBUF + 22 * 8, jaddr & 0xFF);
|
||||
} else if (cmd == 4 || cmd == 5) {
|
||||
/* jump (4) to an address, or reboot (5): jump to the ROM
|
||||
* reset vector, which is the same entry the CPU takes at
|
||||
|
||||
@@ -11,7 +11,11 @@ Protocol, ethertype 0x88B6, both directions:
|
||||
cmd 2 stats: no arguments
|
||||
cmd 3 poke : [17..20] address (BIT), [21..22] len, [23..] the bytes
|
||||
cmd 4 jump : [17..20] address (BIT) -- reply is sent, then control moves
|
||||
cmd 5 reboot: no arguments -- jumps to the ROM reset vector
|
||||
cmd 5 reboot: no arguments -- jumps to the ROM reset vector (wedges the
|
||||
real machine; kept only because MAME tolerates it)
|
||||
cmd 6 osboot: no arguments -- jumps to the OS cold-start entry 0x3F5788C0,
|
||||
the path POST takes when it skips level 1. That path STOPs
|
||||
the LANCE and re-programs the board window first.
|
||||
reply : [16] cmd echoed, [17..18] status, [19..] data
|
||||
|
||||
Every address in this protocol is a BIT address, the way the TMS34010 sees
|
||||
@@ -128,7 +132,7 @@ def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("cmd", choices=["stats", "read", "poke", "jump",
|
||||
"reboot", "bench"])
|
||||
"reboot", "osboot", "bench"])
|
||||
ap.add_argument("--iface", default="vt1200")
|
||||
ap.add_argument("--addr", type=lambda v: int(v, 0), default=0)
|
||||
ap.add_argument("--len", type=int, default=32)
|
||||
@@ -191,7 +195,7 @@ def main():
|
||||
% (len(blob), a.addr, a.addr >> 3, status))
|
||||
return 0
|
||||
|
||||
if a.cmd in ("jump", "reboot"):
|
||||
if a.cmd in ("jump", "reboot", "osboot"):
|
||||
if a.cmd == "reboot" and not a.force:
|
||||
print("refusing reboot without --force.")
|
||||
print(" It has NEVER worked on the real VT1200: 0 of 2 attempts.")
|
||||
@@ -202,7 +206,7 @@ def main():
|
||||
return 2
|
||||
if a.cmd == "reboot":
|
||||
print("rebooting: it may take ~90 s and it may never come back.")
|
||||
cmd = 4 if a.cmd == "jump" else 5
|
||||
cmd = 4 if a.cmd == "jump" else (5 if a.cmd == "reboot" else 6)
|
||||
status, data = request(sock, a.iface, cmd, a.addr, 0, a.timeout)
|
||||
if status is None:
|
||||
print("no reply")
|
||||
|
||||
Reference in new issue
Block a user