forked from arti/vt1200
udpstream: the payload announces itself; test-osboot.sh has no sleep
Loading the COFF over rdebug pokes thousands of words one at a time, so how
long the payload takes to appear varies by tens of seconds -- a fixed sleep is
either wasteful or too short. The payload now broadcasts three frames the
moment its ring is armed: ethertype 0x88B6, 'K' 'S', opcode 0xFF, magic
"LANC". `vtcmd.py wait` listens for it; measured at 8.7 s in MAME.
Two details found by measurement:
* The announce must use TX slot 0, because the chip transmits only from its
ring position -- arming slot 7 while the position is 0 never sends
anything (the first attempt did exactly that and was silently lost).
* After the announce the reply cursor advances to slot 1 to match the chip's
new position, so replies do not collide with the announce descriptor.
txfull then stays 0 across consecutive replies; before this it read 1.
test-osboot.sh ties the hand-back together with no sleep: listen, load, wait
for the announce, stats, osboot, wait for :771. Report sections 73.3/73.4.
This commit is contained in:
1 parent
f74bd7ae05
commit
ed185279ca
5 files changed
+181
-6
No files matched your search
@@ -4854,8 +4854,46 @@ Also observed: the display comes back **before** the network. At 32 s the
|
||||
operator could see the screen while `:771` was still closed; the interface comes
|
||||
up later in the OS boot.
|
||||
|
||||
**This is 1 of 1.** It is the vendor's own entry, it does exactly what a payload
|
||||
breaks (STOP the LANCE, re-program the board window), and it worked first time —
|
||||
but this section has already recorded three conclusions drawn from single
|
||||
observations, so the status is "one confirmed success", not "reliable". A second
|
||||
run is needed before it is treated as the standard hand-back.
|
||||
### 73.3 Second run: 2 of 2, also 40 s
|
||||
|
||||
Repeated at **17:14:10**, and this time the machine started from a state
|
||||
`osboot` itself had produced (no power cycle in between), rather than fresh from
|
||||
one:
|
||||
|
||||
```
|
||||
=== osboot #2 at 17:14:10 ===
|
||||
osboot: control -> bit 0x3f5788c0, status 0
|
||||
rdebug back after 40s at 17:14:50
|
||||
pings
|
||||
```
|
||||
|
||||
A second screenshot shows the Terminal Manager live again — `08-00-2B-16-4B-5A`,
|
||||
`100.103.100.103`, `LAT_08002B164B5A`, 4 MB free, and the boot-time
|
||||
`Attempting to set font path. / Using local fonts.` messages.
|
||||
|
||||
**2 of 2, both 40 s**, from two different starting states. That is enough to
|
||||
treat `osboot` as the hand-back, and it is the only command in this report whose
|
||||
success rate is above zero on the real machine.
|
||||
|
||||
### 73.4 The payload announces itself, so the host does not sleep
|
||||
|
||||
Loading the COFF over rdebug pokes thousands of words one at a time, so how long
|
||||
the payload takes to appear varies by tens of seconds; a fixed sleep is either
|
||||
wasteful or too short. The payload therefore transmits an **announce** the
|
||||
moment its RX ring is armed:
|
||||
|
||||
* three broadcast frames, ethertype `0x88B6`, `'K' 'S'`, opcode `0xFF`, magic
|
||||
`"LANC"` at `[17..20]`;
|
||||
* back-to-back, with no clock involved (three because this is a half-duplex
|
||||
segment and one lost broadcast should not cost a cycle);
|
||||
* sent from TX **slot 0**, because the chip transmits only from its ring
|
||||
position and arming any other slot does nothing until the ring reaches it (a
|
||||
first attempt on slot 7 was silently never sent — measured);
|
||||
* after the announce the reply cursor advances to slot 1, matching where the
|
||||
chip's ring position now is, so replies never collide with the announce
|
||||
descriptor; announce skips are not counted in `txfull`, which stays the reply
|
||||
path's diagnostic.
|
||||
|
||||
`vtcmd.py wait` listens for it, and `test-osboot.sh` ties the whole hand-back
|
||||
together with **no sleep**: listen, load, wait for the announce, `stats`,
|
||||
`osboot`, wait for `:771`.
|
||||
Binary file not shown.
@@ -338,6 +338,55 @@ int main()
|
||||
rxf = 0; bytes = 0; cmds = 0; laststat = 0xEEEE; txslot = 0; ncmd = 0;
|
||||
chaindrop = 0; txfull = 0; errbits = 0; jaddr = 0; dojump = 0;
|
||||
|
||||
/* ---- announce: the ring is armed, so say hello on the wire ----------
|
||||
* Three broadcast frames, ethertype 0x88B6, cmd 0xFF, magic "LANC".
|
||||
* A host waiting for this knows the payload is live and does not have to
|
||||
* guess with a sleep. The frame is sent three times because this is a
|
||||
* half-duplex segment and one lost broadcast should not cost a cycle.
|
||||
* No clock is involved: back-to-back is enough at 10 Mbit. */
|
||||
{
|
||||
unsigned int a;
|
||||
for (a = 0; a < 6; a++) {
|
||||
wb(TXBUF + a * 8, 0xFF); /* broadcast dst */
|
||||
}
|
||||
wb(TXBUF + 6 * 8, 0x08); wb(TXBUF + 7 * 8, 0x00);
|
||||
wb(TXBUF + 8 * 8, 0x2B); wb(TXBUF + 9 * 8, 0x16);
|
||||
wb(TXBUF + 10 * 8, 0x4B); wb(TXBUF + 11 * 8, 0x5A);
|
||||
wb(TXBUF + 12 * 8, 0x88); wb(TXBUF + 13 * 8, 0xB6);
|
||||
wb(TXBUF + 14 * 8, 'K'); wb(TXBUF + 15 * 8, 'S');
|
||||
wb(TXBUF + 16 * 8, 0xFF); /* announce opcode */
|
||||
wb(TXBUF + 17 * 8, 0x4C); wb(TXBUF + 18 * 8, 0x41);
|
||||
wb(TXBUF + 19 * 8, 0x4E); wb(TXBUF + 20 * 8, 0x43); /* "LANC" */
|
||||
for (a = 21; a < 60; a++) {
|
||||
wb(TXBUF + a * 8, 0);
|
||||
}
|
||||
for (a = 0; a < 3; a++) {
|
||||
/* Slot 0 is where the chip's ring position starts, and it only
|
||||
* transmits from that position -- arming any other slot does
|
||||
* nothing until the ring gets there. Announce skips are not
|
||||
* counted in txfull, which is the reply path's diagnostic. */
|
||||
for (j = 0; j < 8000; j++) {
|
||||
if (!(r16(TXR + 16) & TMD1_OWN)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r16(TXR + 16) & TMD1_OWN) {
|
||||
break; /* still busy: stop trying */
|
||||
}
|
||||
w16(TXR + 48, 0);
|
||||
w16(TXR + 32, (0x10000 - 60) & 0xFFFF);
|
||||
w16(TXR + 16, TMD1_OWN | TMD1_STP | TMD1_ENP);
|
||||
csr_w(0, 0x0040 | CSR0_TDMD);
|
||||
for (j = 0; j < 60000; j++) {
|
||||
if (!(r16(TXR + 16) & TMD1_OWN)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* the chip's TX ring position is now 1, so replies must start there */
|
||||
txslot = 1;
|
||||
}
|
||||
|
||||
/* ---- main loop: receive, count, and serve commands ---- */
|
||||
for (idle = 0; ; idle++) {
|
||||
for (i = 0; i < NRX; i++) {
|
||||
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/bin/sh
|
||||
# test-osboot.sh -- take the LANCE over, then hand it back with `osboot`.
|
||||
#
|
||||
# sudo ./test-osboot.sh [iface] [host]
|
||||
#
|
||||
# The point of this script is that it contains no sleep. The payload
|
||||
# broadcasts an announce frame (ethertype 0x88B6, opcode 0xFF, magic "LANC")
|
||||
# as soon as its RX ring is armed, so the host starts listening first and acts
|
||||
# the moment the payload is really up. Loading the COFF over rdebug is slow
|
||||
# (thousands of word pokes), and a fixed sleep either wastes seconds or is too
|
||||
# short -- waiting for the announce is exact.
|
||||
#
|
||||
# Steps:
|
||||
# 1. listen for the announce (background)
|
||||
# 2. load dwt/loader/hostload.py, which pokes and jumps into the payload
|
||||
# 3. when the announce arrives, read stats and send `osboot`
|
||||
# 4. wait for the terminal's rdebug port to come back
|
||||
#
|
||||
# `osboot` jumps to the OS cold start at 0x3F5788C0, which STOPs the LANCE and
|
||||
# re-programs the board window before restarting the OS. Measured: 40 s twice.
|
||||
|
||||
IFACE=${1:-vt1200}
|
||||
HOST=${2:-100.103.100.103}
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
ROOT=$(cd "$HERE/../.." && pwd)
|
||||
LOG=${LOG:-/tmp/vt1200-osboot.log}
|
||||
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "run as root: raw sockets and rdebug need it" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "[*] listening for the announce on $IFACE"
|
||||
python3 "$HERE/vtcmd.py" wait --iface "$IFACE" --wait-timeout 240 &
|
||||
LISTENER=$!
|
||||
|
||||
echo "[*] loading $(basename "$HERE")/build/lance_cmd.coff onto $HOST"
|
||||
python3 "$ROOT/dwt/loader/hostload.py" --host "$HOST" \
|
||||
--coff "$HERE/build/lance_cmd.coff" --real --no-screenshot \
|
||||
> "$LOG" 2>&1 &
|
||||
|
||||
if ! wait "$LISTENER"; then
|
||||
echo "[!] no announce: the payload did not start (see $LOG)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] payload is up, stats:"
|
||||
python3 "$HERE/vtcmd.py" stats --iface "$IFACE" || true
|
||||
|
||||
echo "[*] handing back with osboot"
|
||||
python3 "$HERE/vtcmd.py" osboot --iface "$IFACE" || exit 1
|
||||
|
||||
echo "[*] waiting for rdebug :771 on $HOST"
|
||||
T0=$(date +%s)
|
||||
i=0
|
||||
while [ $i -lt 36 ]; do
|
||||
sleep 5
|
||||
i=$((i + 1))
|
||||
if timeout 2 sh -c "</dev/tcp/$HOST/771" 2>/dev/null; then
|
||||
echo "[+] back after $(( $(date +%s) - T0 ))s"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "[!] did not come back within 180 s" >&2
|
||||
exit 1
|
||||
@@ -13,6 +13,8 @@ Protocol, ethertype 0x88B6, both directions:
|
||||
cmd 4 jump : [17..20] address (BIT) -- reply is sent, then control moves
|
||||
cmd 5 reboot: no arguments -- jumps to the ROM reset vector (wedges the
|
||||
real machine; kept only because MAME tolerates it)
|
||||
cmd 0xFF announce: sent BY the payload, three broadcasts, magic "LANC"
|
||||
right after its ring is armed -- `vtcmd.py wait` listens for 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.
|
||||
@@ -132,7 +134,7 @@ def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("cmd", choices=["stats", "read", "poke", "jump",
|
||||
"reboot", "osboot", "bench"])
|
||||
"reboot", "osboot", "wait", "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)
|
||||
@@ -157,6 +159,8 @@ def main():
|
||||
"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("--wait-timeout", type=float, default=180.0,
|
||||
help="wait: how long to listen for the payload's announce")
|
||||
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)
|
||||
@@ -177,6 +181,24 @@ def main():
|
||||
print(" %04x %s" % (i, data[i:i + 16].hex(" ")))
|
||||
return 0
|
||||
|
||||
if a.cmd == "wait":
|
||||
# the payload broadcasts this the moment its RX ring is armed, so the
|
||||
# host does not have to sleep and hope
|
||||
sock.settimeout(1.0)
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < a.wait_timeout:
|
||||
try:
|
||||
data, _ = sock.recvfrom(2048)
|
||||
except socket.timeout:
|
||||
continue
|
||||
if (len(data) >= 21 and data[12:14] == b"\x88\xb6"
|
||||
and data[14:16] == b"KS" and data[16] == 0xFF):
|
||||
mac = ":".join("%02x" % b for b in data[6:12])
|
||||
print("announce from %s after %.1f s" % (mac, time.time() - t0))
|
||||
return 0
|
||||
print("no announce within %gs" % a.wait_timeout)
|
||||
return 1
|
||||
|
||||
if a.cmd == "poke":
|
||||
if a.file:
|
||||
with open(a.file, "rb") as f:
|
||||
|
||||
Reference in new issue
Block a user