Files
reverse-engineering-anything/scripts/verify/firmware/devicetree.mjs
T
morluto 4082375e73 fix(verifiers): preserve results and own cleanup through completion
Attempt independent resource cleanup without losing primary failures, retain unresolved owners, and publish completion after release. Bound existing producer commands, preserve helper return values, and settle startup and fixture children on failure.

Validation: 29 focused boundary tests passed; actual helper-value and import-bound filesystem cleanup controls passed. The real inert-child termination control and targeted JavaScript binding checks passed, alongside typecheck, lint and formatting. Live Ghidra, Cutter, Electron GUI and Windows addon qualification was not run.
2026-11-10 17:03:59 -05:00

98 lines
3.1 KiB
JavaScript

import assert from "node:assert/strict";
import { createPackageVerifierCommands } from "../../lib/verify-package-commands.mjs";
import { withVerifierCleanup } from "../../lib/verifier-cleanup.mjs";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
// Optional verification tool only; the runtime inspector never starts dtc.
const dtc = process.env.REA_DTC_PATH ?? "dtc";
const root = mkdtempSync(join(tmpdir(), "rea-dtb-verification-"));
const commands = await createPackageVerifierCommands({
workspace: {
path: root,
close: async () => rmSync(root, { recursive: true, force: true }),
},
});
const exec = async (command, args) =>
(
await commands.exec(command, args, {
timeout: 120_000,
maxBuffer: 16 * 1024 * 1024,
})
).stdout;
await withVerifierCleanup(async () => {
assert.match(await exec(dtc, ["--version"]), /DTC 1\.8\.1\b/);
const source = join(root, "board.dts"),
blob = join(root, "board.dtb");
writeFileSync(
source,
`/dts-v1/;
/memreserve/ 0x80000000 0x1000;
/ {
compatible = "rea,verification"; model = "REA DTB verification";
#address-cells = <1>; #size-cells = <1>;
chosen { bootargs = "console=ttyS0"; stdout-path = "serial0"; };
aliases { serial0 = &serial; };
reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges;
region@80000000 { reg = <0x80000000 0x1000>; no-map; };
};
serial: serial@1000 { reg = <0x1000 0x100>; phandle = <1>; };
consumer { remote-endpoint = <&serial>; };
};\n`,
);
await exec(dtc, ["-I", "dts", "-O", "dtb", "-o", blob, source]);
const decompiled = await exec(dtc, ["-I", "dtb", "-O", "dts", blob]);
const evidence = JSON.parse(
await exec(process.execPath, [
"scripts/rea.mjs",
"inspect-devicetree",
blob,
"--json",
]),
);
const report = evidence.normalized_result;
// This controlled fixture uses one opening/closing brace per node line.
const paths = [],
stack = [];
for (const line of decompiled.split("\n")) {
const begin = /^\s*([^\s{}]+)\s*\{\s*$/.exec(line);
if (begin) {
stack.push(begin[1]);
paths.push(stack.length === 1 ? "/" : `/${stack.slice(1).join("/")}`);
}
if (/^\s*};/.test(line)) stack.pop();
}
assert.deepEqual(
report.nodes.map((n) => n.path),
paths,
);
assert.equal(report.summary.bootargs, "console=ttyS0");
assert.deepEqual(report.summary.aliases, [
{ name: "serial0", path: "/serial@1000" },
]);
assert.equal(report.references[0].target, "/serial@1000");
assert.equal(report.reservations[0].address, "0x80000000");
assert.equal(
report.nodes
.find((n) => n.path.endsWith("region@80000000"))
.properties.find((p) => p.name === "reg").derived.reg[0].size,
"0x1000",
);
assert.equal(readFileSync(blob).readUInt32BE(0), 0xd00dfeed);
console.log(
JSON.stringify(
{
dtc: "1.8.1",
matched_node_paths: paths,
summary: report.summary,
reservations: report.reservations,
references: report.references,
},
null,
2,
),
);
}, [() => commands.close()]);