mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 11:56:12 +00:00
* feat(artifacts): add U-Boot legacy uImage reader (part of #7129) Recognize legacy uImages by their 0x27051956 magic and inventory them as containers: 64-byte header facts (arch/os/type/compression raw and named, load/entry addresses, name, timestamp) with header and data CRC32 verified, stored payload extractable as payload.bin, and multi-file images split from the stored size table into image-<n>.bin. Compressed payloads stay as stored; nothing is executed or decompressed. Wires the format through classification, the artifact reader dispatch, extraction, and the evidence/snapshot format vocabularies. Related to #7129 (uImage part only); HEX/SREC and FIT are out of scope. * fix(uimage): preserve aligned component ranges and exact header facts * fix(uimage): retain buffered members through consumption and cancellation * fix(uimage): preserve active read and validation failure precedence --------- Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
72 lines
2.6 KiB
TypeScript
72 lines
2.6 KiB
TypeScript
import { crc32 } from "node:zlib";
|
|
|
|
import {
|
|
UIMAGE_HEADER_SIZE,
|
|
UIMAGE_MAGIC,
|
|
UIMAGE_NAME_LENGTH,
|
|
} from "../../src/domain/uimageFormat.js";
|
|
|
|
export interface UImageFixtureOptions {
|
|
readonly arch?: number;
|
|
readonly os?: number;
|
|
readonly type?: number;
|
|
readonly compression?: number;
|
|
readonly loadAddress?: number;
|
|
readonly entryPoint?: number;
|
|
readonly timestamp?: number;
|
|
readonly name?: string;
|
|
/** Raw stored payload bytes; for multi images pass the size table + parts. */
|
|
readonly payload?: Buffer;
|
|
/** Corrupt the header CRC after computing it (keeps a valid magic). */
|
|
readonly corruptHeaderCrc?: boolean;
|
|
/** Corrupt the data CRC after computing it. */
|
|
readonly corruptDataCrc?: boolean;
|
|
}
|
|
|
|
/** Build a synthetic legacy uImage with correctly computed CRCs. */
|
|
export const buildUImage = (options: UImageFixtureOptions = {}): Buffer => {
|
|
const payload = options.payload ?? Buffer.from("uimage-payload");
|
|
const header = Buffer.alloc(UIMAGE_HEADER_SIZE);
|
|
header.writeUInt32BE(UIMAGE_MAGIC, 0);
|
|
header.writeUInt32BE(options.timestamp ?? 1700000000, 8);
|
|
header.writeUInt32BE(payload.length, 12);
|
|
header.writeUInt32BE(options.loadAddress ?? 0x8000, 16);
|
|
header.writeUInt32BE(options.entryPoint ?? 0x8000, 20);
|
|
// Data CRC first: corrupting it must keep the header CRC valid, so the
|
|
// header CRC is computed over the final header bytes below.
|
|
const dataCrc =
|
|
(crc32(payload) >>> 0) ^ (options.corruptDataCrc === true ? 0xff : 0);
|
|
header.writeUInt32BE(dataCrc >>> 0, 24);
|
|
header[28] = options.os ?? 5;
|
|
header[29] = options.arch ?? 2;
|
|
header[30] = options.type ?? 2;
|
|
header[31] = options.compression ?? 0;
|
|
const name = Buffer.from(options.name ?? "test-kernel", "utf8");
|
|
name.copy(header, 32, 0, Math.min(name.length, UIMAGE_NAME_LENGTH));
|
|
const headerCrc = crc32(header) >>> 0;
|
|
header.writeUInt32BE(
|
|
options.corruptHeaderCrc === true ? (headerCrc ^ 0xff) >>> 0 : headerCrc,
|
|
4,
|
|
);
|
|
return Buffer.concat([header, payload]);
|
|
};
|
|
|
|
/** Size table + parts layout for a multi-file image payload. */
|
|
export const buildMultiPayload = (parts: readonly Buffer[]): Buffer => {
|
|
const table = Buffer.alloc((parts.length + 1) * 4);
|
|
parts.forEach((part, index) => {
|
|
table.writeUInt32BE(part.length, index * 4);
|
|
});
|
|
const stored: Buffer[] = [table];
|
|
parts.forEach((part, index) => {
|
|
if (part.length === 0)
|
|
throw new Error(
|
|
"A zero size terminates the uImage table; it cannot describe a component",
|
|
);
|
|
stored.push(part);
|
|
if (index + 1 < parts.length)
|
|
stored.push(Buffer.alloc((4 - (part.length % 4)) % 4));
|
|
});
|
|
return Buffer.concat(stored);
|
|
};
|