From 52d908b013a27155cf65b96e784883def869efa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EC=A7=80=20=E1=9A=A0=D7=93=20=28Jiji=20Freya=20D?= =?UTF-8?q?aniel=29=20Maslowski?= Date: Sun, 7 Jun 2026 20:05:51 +0200 Subject: [PATCH 1/3] rework parser loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 지지 ᚠד (Jiji Freya Daniel) Maslowski --- app/page.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index b94d52a..12e6c24 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -20,6 +20,7 @@ const nodeTypes = { export default function Home() { const [fbuf, setFbuf] = useState(null); const [inProgress, setInProgress] = useState(false); + const [parser, setParser] = useState(null); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); @@ -39,8 +40,6 @@ export default function Home() { setInProgress(true); setTimeout(async () => { try { - // TODO: only do this once - const parser = await import("../parser/pkg"); const res = await parser.parse_dtb([...data]); const tree = transform(res.root); const f = getNodesEdges(tree); @@ -75,10 +74,18 @@ export default function Home() { } }, [filesContent]); + useEffect(() => { + import("../parser/pkg").then((p) => setParser(p)); + }, []); + const fileName = plainFiles.length > 0 ? plainFiles[0].name : ""; const pending = loading || inProgress; + if (!parser) { + return
Loading...
; + } + return (
From 891767a9d2479ad5556321ed03006184ad07d028 Mon Sep 17 00:00:00 2001 From: Daniel Maslowski Date: Thu, 22 Aug 2024 23:01:45 +0200 Subject: [PATCH 2/3] WIP: extend information in Node Signed-off-by: Daniel Maslowski --- app/DTNode.stories.tsx | 3 +- app/DTNode.tsx | 83 ++++++++++++++++++++++++++---------------- app/compat-db.json | 8 ++++ app/lib.ts | 28 ++++++++++---- 4 files changed, 83 insertions(+), 39 deletions(-) diff --git a/app/DTNode.stories.tsx b/app/DTNode.stories.tsx index 1b12cf8..cf422c4 100644 --- a/app/DTNode.stories.tsx +++ b/app/DTNode.stories.tsx @@ -11,7 +11,7 @@ const meta = { baseAddr: {}, compat: {}, }, - status: { control: 'radio', options: ['okay', 'disabled'] }, + status: { control: 'radio', options: ['okay', 'disabled', undefined] }, }, } satisfies Meta; @@ -23,6 +23,7 @@ export const Simple: Story = { data: { label: "UART", baseAddr: "0x0c00_0000", + compat: "ns16550a", }, status: "okay", }, diff --git a/app/DTNode.tsx b/app/DTNode.tsx index 2813e65..1efefff 100644 --- a/app/DTNode.tsx +++ b/app/DTNode.tsx @@ -1,31 +1,26 @@ import { memo, useState } from "react"; import { Handle, NodeProps, Position } from "reactflow"; import compatDb from "./compat-db.json"; +import genericNames from "./generic-names.json"; type DTStatus = "okay" | "disabled"; -type DotColor = "blue" | "red"; - -const getDotColor = (status?: DTStatus): DotColor | null => { - switch(status) { - case "okay": return "blue"; - case "disabled": return "red"; - default: return null; - } -} +const dotColors: Record = { + okay: "blue", + disabled: "red" +}; export const Dot: FC<{ status?: DTStatus }> = ({ status }) => { if (!status) { return null; } - const color = getDotColor(status); + const color = dotColors[status]; return ( -
+
@@ -33,10 +28,12 @@ export const Dot: FC<{ status?: DTStatus }> = ({ status }) => { ); }; -const docsbaseUrl = "https://docs.kernel.org" +const docsBaseUrl = "https://docs.kernel.org" +const drvBaseUrl = "https://elixir.bootlin.com/linux/HEAD/source/drivers"; +//const drvBaseUrl = "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers"; const dtBaseUrl = "https://www.kernel.org/doc/Documentation/devicetree/bindings"; -type DocsCategory = "binding" | "docs"; +type DocsCategory = "binding" | "docs" | "driver"; type DocsEntry = { category: DocsCategory; @@ -46,7 +43,8 @@ type DocsEntry = { const getBaseUrl = (category: DocsCategory): string => { switch(category) { case "binding": return dtBaseUrl; - case "docs": return docsbaseUrl; + case "docs": return docsBaseUrl; + case "driver": return drvBaseUrl; } }; @@ -83,37 +81,60 @@ const Compat: FC<{ compat?: string; }> = ({ compat }) => { ); }; +export const Extra: FC<{ data: object }> = ({ data }) => { + const { resets, clks, phandle, phySupply, phyHandle } = data; + + const e = JSON.stringify({ resets, clks, phandle, phySupply, phyHandle }, null, 2); + + return
{e}
+}; + export const DataNode: FC<{ data: object; status?: DTStatus }> = ({ data, status, }) => { - if (!data) { - return null; - } - + const extraClass = genericNames.includes(data.label) ? "generic" : ""; return (
- {data.label} - {data.baseAddr} - - +
{data.label}
+
+ {data.model} + {data.baseAddr} + + + {data.extra} + +
); diff --git a/app/compat-db.json b/app/compat-db.json index 76f96c1..2c86eb1 100644 --- a/app/compat-db.json +++ b/app/compat-db.json @@ -27,6 +27,10 @@ "category": "binding", "path": "arm/cpus.yaml" }, + "arm,cortex-a7-pmu": { + "category": "binding", + "path": "arm/pmu.yaml" + }, "brcm,bcm2835-cprman": { "category": "binding", "path": "clock/brcm,bcm2835-cprman.txt" @@ -38,5 +42,9 @@ "spidev": { "category": "docs", "path": "spi/spidev.html" + }, + "brcm,bcm2835-audio": { + "category": "driver", + "path": "staging/vc04_services/bcm2835-audio/bcm2835.c" } } diff --git a/app/lib.ts b/app/lib.ts index 174baac..31617d3 100644 --- a/app/lib.ts +++ b/app/lib.ts @@ -66,6 +66,17 @@ const getPropStr = (n: DTNode, pname: string): string | null => { return p ? p.join(", ") : null; }; +const getExtra = (n: DTNode) => { + if (n.name === "aliases" | n.name === "chosen") { + const ps = n.props.map((p) => { + const [k, v] = p; + return `${k}=${u8ArrToStr(v)}`; + }); + return ps.join("\n"); + } + return null; +}; + // transform a node's props into numbers and strings, omitting many const transformNode = (n: DTNode): DTNode => { const name = n.name || "root"; @@ -81,6 +92,8 @@ const transformNode = (n: DTNode): DTNode => { const cnames = getStringProp(n, "clock-names"); const compat = getStringProp(n, "compatible"); const status = getStringProp(n, "status"); + const model = getStringProp(n, "model"); + const extra = getExtra(n); return { name, ...(phandle ? { phandle: phandle[0] } : null), @@ -92,6 +105,8 @@ const transformNode = (n: DTNode): DTNode => { ...(cnames ? { cnames } : null), ...(compat ? { compat } : null), ...(status ? { status } : null), + ...(model ? { model } : null), + extra, }; }; @@ -103,7 +118,7 @@ export const transform = (n: DTNode, id: string = "10000") => { } }; -const NODE_WIDTH = 160; +const NODE_WIDTH = 260; const NODE_HEIGHT = 80; const weightedNode = (node: DTNode): DTNode => { @@ -142,22 +157,21 @@ export const getNodesEdges = (tree: DTNode) => { const nodes: TransformedNode[] = []; const edges: TransformedEdge[] = []; const rec = (n: DTNode, d: number = 1, baseX: number = 0, baseY: number = 0) => { - const [name, addr] = n.name.split("@"); + const { id, name, ...data } = n; + const [label, addr] = name.split("@"); const baseAddr = transformAddr(addr); nodes.push({ - id: n.id, + id, type: NodeType.custom, position: { x: baseX + n.size * NODE_WIDTH / 2, y: baseY + d * NODE_HEIGHT, }, data: { - label: name, + label, baseAddr, - size: n.size, - compat: n.compat, - status: n.status, + ...data, }, }); let offset = baseX; From 4155a618e7ca0a2ce321489389f861ba1107ce1c Mon Sep 17 00:00:00 2001 From: Daniel Maslowski Date: Sun, 29 Mar 2026 10:41:17 +0200 Subject: [PATCH 3/3] WIP: quick and dirty, FIT image props, phandles Unfortunately, there is no field indicating that it is a FIT. Neither is there type information in the binary, so we "know". Signed-off-by: Daniel Maslowski --- app/DTNode.tsx | 44 ++++++++++++++++++++++--- app/lib.ts | 88 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 119 insertions(+), 13 deletions(-) diff --git a/app/DTNode.tsx b/app/DTNode.tsx index 1efefff..8b90728 100644 --- a/app/DTNode.tsx +++ b/app/DTNode.tsx @@ -82,11 +82,45 @@ const Compat: FC<{ compat?: string; }> = ({ compat }) => { }; export const Extra: FC<{ data: object }> = ({ data }) => { - const { resets, clks, phandle, phySupply, phyHandle } = data; - - const e = JSON.stringify({ resets, clks, phandle, phySupply, phyHandle }, null, 2); - - return
{e}
+ const { + resets, + clocks, + mboxes, + phandle, + phySupply, + phyHandle, + } = data; + const { + description, + type, + arch, + os, + kernel, + compression, + load, + entry, + } = data; + + const e = JSON.stringify({ + resets, + clocks, + mboxes, + phandle, + phySupply, + phyHandle, + }, null, 2); + const x = JSON.stringify({ + description, + type, + arch, + os, + kernel, + compression, + load, + entry, + }, null, 2); + + return
{e}{x}
}; export const DataNode: FC<{ data: object; status?: DTStatus }> = ({ diff --git a/app/lib.ts b/app/lib.ts index 31617d3..d3ddbe2 100644 --- a/app/lib.ts +++ b/app/lib.ts @@ -86,14 +86,45 @@ const transformNode = (n: DTNode): DTNode => { // TODO: make list of props that are refs const phyHandle = getProp(n, "phy-handle"); const phySupply = getProp(n, "phy-supply"); + const mboxes = getProp(n, "mboxes"); const resets = getProp(n, "resets"); const dmas = getProp(n, "dmas"); - const clks = getProp(n, "clocks"); + const clocks = getProp(n, "clocks"); const cnames = getStringProp(n, "clock-names"); const compat = getStringProp(n, "compatible"); const status = getStringProp(n, "status"); const model = getStringProp(n, "model"); + + const fitStrings = [ + "description", + "type", + "arch", + "os", + "kernel", + "compression", + ]; + const fit = fitStrings.reduce((a, p) => { + const s = getStringProp(n, p); + if (s) { + a[p] = s; + } + return a; + }, {}); + const fitVals = [ + "load", + "entry", + ]; + const fitV = fitVals.reduce((a, p) => { + const s = getProp(n, p); + if (s) { + // console.info({ [p]: s }); + a[p] = padHexStr(s[0].toString(16)); + } + return a; + }, {}); + const extra = getExtra(n); + return { name, ...(phandle ? { phandle: phandle[0] } : null), @@ -101,11 +132,15 @@ const transformNode = (n: DTNode): DTNode => { ...(phyHandle ? { phyHandle: phyHandle[0] } : null), ...(resets ? { resets } : null), ...(dmas ? { dmas } : null), - ...(clks ? { clks } : null), + ...(clocks ? { clocks } : null), ...(cnames ? { cnames } : null), ...(compat ? { compat } : null), ...(status ? { status } : null), ...(model ? { model } : null), + ...(mboxes ? { mboxes } : null), + + ...fit, + ...fitV, extra, }; }; @@ -118,8 +153,8 @@ export const transform = (n: DTNode, id: string = "10000") => { } }; -const NODE_WIDTH = 260; -const NODE_HEIGHT = 80; +const NODE_WIDTH = 320; +const NODE_HEIGHT = 480; const weightedNode = (node: DTNode): DTNode => { if (node.children && node.children.length > 0) { @@ -138,11 +173,11 @@ const weightedNode = (node: DTNode): DTNode => { * Format to hex with leading 0x, padded with zeroes to groups of four digits. * At least print 8 digits, but omit the first 4 of 12 if they are all 0. */ -const transformAddr = (addr: string): string => { - if (addr === undefined) { +const padHexStr = (val: string): string => { + if (val === undefined) { return ""; } - const padded = addr.padStart(12, "0"); + const padded = val.padStart(12, "0"); const p1 = padded.substr(0, 4); const p2 = padded.substr(4, 4); const p3 = padded.substr(8, 4); @@ -156,10 +191,12 @@ const transformAddr = (addr: string): string => { export const getNodesEdges = (tree: DTNode) => { const nodes: TransformedNode[] = []; const edges: TransformedEdge[] = []; + // map phandle -> id + const phandles = {}; const rec = (n: DTNode, d: number = 1, baseX: number = 0, baseY: number = 0) => { const { id, name, ...data } = n; const [label, addr] = name.split("@"); - const baseAddr = transformAddr(addr); + const baseAddr = padHexStr(addr); nodes.push({ id, @@ -175,6 +212,12 @@ export const getNodesEdges = (tree: DTNode) => { }, }); let offset = baseX; + + // TODO: store ID + #*-size + const phandle = data.phandle; + if (phandle != null) { + phandles[phandle] = id; + } n.children.forEach((c: DTNode, i: number) => { edges.push({ id: `${n.id}${c.id}`, @@ -187,5 +230,34 @@ export const getNodesEdges = (tree: DTNode) => { }; const t = weightedNode(tree); rec(t); + console.info({ phandles }); + nodes.forEach((n) => { + if (n.data.clocks) { + const ref = n.data.clocks[0]; + const refId = phandles[ref]; + if (refId !== undefined) { + const e = { + id: `clocks-${refId}_${n.id}`, + source: n.id, + target: refId, + }; + console.info({ ...e }); + edges.push(e); + } + } + if (n.data.mboxes) { + const ref = n.data.mboxes[0]; + const refId = phandles[ref]; + if (refId !== undefined) { + const e = { + id: `mboxes-${refId}_${n.id}`, + source: n.id, + target: refId, + }; + console.info({ ...e }); + edges.push(e); + } + } + }); return { nodes, edges }; };