fix(loader): exit the harness on completion, and add LOADER_END=0 #9
No files matched your search
+41
-1
@@ -56,5 +56,45 @@ The script pokes the loader at `0x01880000` and the COFF at `LOADER_BUF`
|
||||
network, the rdebug service or a firmware transport.
|
||||
|
||||
Knobs: `LOADER_MODE=1` (callable/return instead of debug-resume),
|
||||
`LOADER_A8_DESC=1` (descriptor in `A8`), `LOADER_SNAP=1` (PNG at `LOADER_END`),
|
||||
`LOADER_A8_DESC=1` (descriptor in `A8`), `LOADER_SNAP=1` (PNG at the exit frame),
|
||||
`LOADER_VERBOSE=1`, `LOADER_TRACE=1`.
|
||||
|
||||
### Environment
|
||||
|
||||
| variable | default | meaning |
|
||||
|---|---|---|
|
||||
| `LOADER_AT` | `2500` | frame at which the loader and COFF are poked and `PC` is set |
|
||||
| `LOADER_END` | `2700` | fixed inspection/exit frame; **`0` = run until completion** |
|
||||
| `LOADER_MAX` | `6000` | `LOADER_END=0` only: give up (with a warning) at this frame |
|
||||
| `LOADER_BIN` | `dwt/loader/build/loader.bin` | loader payload poked at bit `0x01880000` |
|
||||
| `LOADER_COFF` | `dwt/loader/build/marker.coff` | app COFF poked at `LOADER_BUF` |
|
||||
| `LOADER_BUF` | `0x01900000` | COFF buffer bit address |
|
||||
| `LOADER_OUT` | `dwt/loader/evidence` | directory for the optional PNG |
|
||||
| `LOADER_SNAP` | `0` | `1` writes `loader_frame<frame>.png` at the exit frame |
|
||||
|
||||
### Completion and `LOADER_END=0`
|
||||
|
||||
The harness detects completion when all three hold:
|
||||
|
||||
1. the loader has written `RESULT_STATUS` (it is `0xFFFFFFFF` until then),
|
||||
2. `PC` is outside `[LOADER_BASE, LOADER_BASE+0x400)`, and
|
||||
3. the result block has been unchanged for 10 frames.
|
||||
|
||||
On detection it prints one line and sets `done` once:
|
||||
|
||||
```
|
||||
LOADER: complete at frame 2510 status=00000000
|
||||
```
|
||||
|
||||
With a **fixed** `LOADER_END` the run still exits at that frame, so the snapshot
|
||||
filename and every existing fixed-frame capture are unchanged; the completion
|
||||
line is informational and suppresses the startup warning. With **`LOADER_END=0`**
|
||||
the run exits as soon as completion is detected, and if it is not detected by
|
||||
`LOADER_MAX` it prints
|
||||
|
||||
```
|
||||
LOADER: warning: app still in startup at LOADER_MAX=6000
|
||||
```
|
||||
|
||||
and exits there. With a fixed `LOADER_END` that is reached first, the warning is
|
||||
`LOADER: warning: app still in startup at LOADER_END=<n>`.
|
||||
+87
-24
@@ -17,9 +17,10 @@
|
||||
-- LOADER_COFF app COFF (default dwt/loader/build/marker.coff)
|
||||
-- LOADER_BUF COFF buffer bit address (default 0x01900000)
|
||||
-- LOADER_AT injection frame (default 2500)
|
||||
-- LOADER_END inspection/exit frame (default 2700)
|
||||
-- LOADER_END inspection/exit frame (default 2700; 0 = wait for completion)
|
||||
-- LOADER_MAX LOADER_END=0 only: give up after this many frames (default 6000)
|
||||
-- LOADER_OUT evidence directory (default dwt/loader/evidence)
|
||||
-- LOADER_SNAP 1 = write a PNG at LOADER_END (default 0)
|
||||
-- LOADER_SNAP 1 = write a PNG at the exit frame (default 0)
|
||||
-- LOADER_VERBOSE 1 = print the result block every 25 frames
|
||||
|
||||
local function hex(v, w) return string.format("%0" .. (w or 8) .. "X", v) end
|
||||
@@ -37,6 +38,9 @@ end
|
||||
|
||||
local AT = tonumber(os.getenv("LOADER_AT") or "2500")
|
||||
local ENDF = tonumber(os.getenv("LOADER_END") or "2700")
|
||||
local MAXF = tonumber(os.getenv("LOADER_MAX") or "6000")
|
||||
local WAIT = ENDF == 0
|
||||
local STABLE = 10
|
||||
local BIN = os.getenv("LOADER_BIN") or "dwt/loader/build/loader.bin"
|
||||
local COFF = os.getenv("LOADER_COFF") or "dwt/loader/build/marker.coff"
|
||||
local BUF = tonumber(os.getenv("LOADER_BUF") or "0x01900000")
|
||||
@@ -177,6 +181,42 @@ local function result_dump()
|
||||
r32(RESULT + 0x140), r32(RESULT + 0x160)))
|
||||
end
|
||||
|
||||
-- The loader writes RESULT_STATUS last (it is 0xFFFFFFFF until then). The
|
||||
-- whole result block is the completion signal: status written, PC out of the
|
||||
-- loader's entry page, and the block unchanged for STABLE frames. This keeps
|
||||
-- a write that is still in progress from looking final.
|
||||
local RESULT_CELLS = { 0x000, 0x020, 0x040, 0x060, 0x080, 0x0A0, 0x0C0,
|
||||
0x0E0, 0x100, 0x120, 0x140, 0x160, 0x180 }
|
||||
|
||||
local function result_signature()
|
||||
local t = {}
|
||||
for i, off in ipairs(RESULT_CELLS) do
|
||||
t[i] = string.format("%08X", r32(RESULT + off))
|
||||
end
|
||||
return table.concat(t, " ")
|
||||
end
|
||||
|
||||
local stable_signature = nil
|
||||
local stable_frames = 0
|
||||
|
||||
local function complete()
|
||||
local st = r32(RESULT + 0x000)
|
||||
local pc = cpu.state['PC'].value
|
||||
if st == 0xFFFFFFFF or (pc >= LOADER_BASE and pc < LOADER_BASE + 0x400) then
|
||||
stable_signature = nil
|
||||
stable_frames = 0
|
||||
return false
|
||||
end
|
||||
local sig = result_signature()
|
||||
if sig == stable_signature then
|
||||
stable_frames = stable_frames + 1
|
||||
else
|
||||
stable_signature = sig
|
||||
stable_frames = 1
|
||||
end
|
||||
return stable_frames >= STABLE
|
||||
end
|
||||
|
||||
local function inject()
|
||||
-- 1. loader itself
|
||||
local n, bad = 0, 0
|
||||
@@ -273,6 +313,26 @@ local function inspect()
|
||||
print(string.format("LOADER: PC at inspection = %s", hex(cpu.state['PC'].value)))
|
||||
end
|
||||
|
||||
local finished = false
|
||||
local function finish()
|
||||
if finished then return end
|
||||
finished = true
|
||||
if injected then
|
||||
inspect()
|
||||
if SNAP then
|
||||
local screen = nil
|
||||
for _, s in pairs(machine.screens) do screen = s break end
|
||||
if screen then
|
||||
local out = OUT .. "/loader_frame" .. frames .. ".png"
|
||||
local ok, err = pcall(function() return screen:snapshot(out) end)
|
||||
print(string.format("LOADER: snapshot %s ok=%s %s",
|
||||
out, tostring(ok), tostring(err or "")))
|
||||
end
|
||||
end
|
||||
end
|
||||
machine:exit()
|
||||
end
|
||||
|
||||
emu.register_frame_done(function()
|
||||
frames = frames + 1
|
||||
if frames == AT and not injected then
|
||||
@@ -283,20 +343,23 @@ emu.register_frame_done(function()
|
||||
dbg:command("trace off")
|
||||
print("LOADER: trace written to /tmp/opencode/ld.tr")
|
||||
end
|
||||
if injected and not done then
|
||||
if injected then
|
||||
if TRACE and frames <= AT + 40 then
|
||||
print(string.format("LOADER: trace f=%d PC=%s SP=%s A14=%s status=%s entry=%s",
|
||||
frames, hex(cpu.state['PC'].value), hex(cpu.state['SP'].value),
|
||||
hex(cpu.state['A14'].value), hex(r32(RESULT + 0x000)),
|
||||
hex(r32(RESULT + 0x020))))
|
||||
end
|
||||
local st = r32(RESULT + 0x000)
|
||||
-- status 0 means OK; nonzero is final too once parsing has begun
|
||||
if st ~= 0xFFFFFFFF and not done then
|
||||
-- The loader writes 0 on success. A nonzero status is final
|
||||
-- only after it has been written once (it is 0 from power-on,
|
||||
-- so require the loader to have run: PC no longer at entry or
|
||||
-- the app counter moved).
|
||||
-- Status 0 means OK; any nonzero status the loader wrote is final too.
|
||||
-- done is set exactly once, on the frame completion is detected.
|
||||
if not done and complete() then
|
||||
done = true
|
||||
print(string.format("LOADER: complete at frame %d status=%s",
|
||||
frames, hex(r32(RESULT + 0x000))))
|
||||
if WAIT then
|
||||
finish()
|
||||
return
|
||||
end
|
||||
end
|
||||
if VERBOSE and frames % 25 == 0 then
|
||||
result_dump()
|
||||
@@ -312,22 +375,22 @@ emu.register_frame_done(function()
|
||||
end
|
||||
end
|
||||
end
|
||||
if frames >= ENDF then
|
||||
if injected then
|
||||
done = true
|
||||
inspect()
|
||||
if SNAP then
|
||||
local screen = nil
|
||||
for _, s in pairs(machine.screens) do screen = s break end
|
||||
if screen then
|
||||
local out = OUT .. "/loader_frame" .. frames .. ".png"
|
||||
local ok, err = pcall(function() return screen:snapshot(out) end)
|
||||
print(string.format("LOADER: snapshot %s ok=%s %s",
|
||||
out, tostring(ok), tostring(err or "")))
|
||||
end
|
||||
if WAIT then
|
||||
-- LOADER_END=0: run until completion, but never forever.
|
||||
if frames >= MAXF then
|
||||
if not done then
|
||||
print(string.format("LOADER: warning: app still in startup at LOADER_MAX=%d", MAXF))
|
||||
end
|
||||
finish()
|
||||
end
|
||||
machine:exit()
|
||||
elseif frames >= ENDF then
|
||||
-- A fixed LOADER_END still exits at that frame (the snapshot contract;
|
||||
-- existing tests read loader_frame<ENDF>.png). Completion is reported
|
||||
-- above but only LOADER_END=0 asks to exit early.
|
||||
if not done then
|
||||
print(string.format("LOADER: warning: app still in startup at LOADER_END=%d", ENDF))
|
||||
end
|
||||
finish()
|
||||
end
|
||||
end, "vt1200-loader")
|
||||
|
||||
|
||||
Reference in new issue
Block a user