fdt#
Flattened Devicetree (DTB) codec for OCaml.
fdt decodes and encodes the flattened devicetree blob format defined
by chapter 5 of the Devicetree Specification v0.4, and applies
devicetree overlays (.dtbo) with the fragment / __fixups__
convention implemented by libfdt and the Linux kernel:
- a devicetree is a tree of nodes with raw byte-string property values, plus the blob-level metadata (boot CPU id, memory reservations);
Fdt.Valueconverts property values to and from the standard encodings: 32-bit cells, NUL-terminated strings, 64-bit integers;Fdt.apply_overlaymerges a compiled overlay into a base tree, shifting overlay phandles past the base's, patching__local_fixups__and__fixups__references through__symbols__, and rewriting overlay labels to their merged paths — the same semantics as libfdt'sfdt_overlay_apply, verified againstfdtoverlayin the interop tests.
Everything is pure OCaml on the wire codec combinators; decoding
returns result and never raises, encoding always emits a version-17
blob with a deduplicated strings block.
Installation#
$ opam install fdt
If opam cannot find the package, add the overlay repository first:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install fdt
Usage#
Build a tree, encode it, and read it back:
# let uart =
Fdt.Node.v "serial@7e201000"
~properties:
[
("compatible", Fdt.Value.string "arm,pl011");
("reg", Fdt.Value.cells (List.map Optint.of_int [ 0x7e201000; 0x200 ]));
("status", Fdt.Value.string "disabled");
]
in
let t = Fdt.v (Fdt.Node.v "" ~children:[ Fdt.Node.v "soc" ~children:[ uart ] ]) in
Fdt.of_string (Fdt.to_string t) = Ok t
- : bool = true
Look up nodes by path and read typed property values:
# let t =
Fdt.v
(Fdt.Node.v ""
~children:
[
Fdt.Node.v "soc"
~children:
[
Fdt.Node.v "serial@7e201000"
~properties:
[
( "clock-frequency",
Fdt.Value.u32 (Optint.of_int 48_000_000) );
];
];
])
in
match Fdt.find t "/soc/serial@7e201000" with
| Some uart -> Option.bind (Fdt.Node.property uart "clock-frequency") Fdt.Value.to_u32
| None -> None
- : Optint.t option = Some <abstr>
Apply an overlay to a base tree, as a bootloader would before handing
the devicetree to the kernel — a fragment targets a node by path and
its __overlay__ content is merged in (decode .dtb/.dtbo files
with Fdt.of_string to get the same shapes):
# let base =
Fdt.v
(Fdt.Node.v ""
~children:
[
Fdt.Node.v "soc"
~children:
[
Fdt.Node.v "serial@7e201000"
~properties:[ ("status", Fdt.Value.string "disabled") ];
];
])
in
let overlay =
Fdt.v
(Fdt.Node.v ""
~children:
[
Fdt.Node.v "fragment@0"
~properties:
[ ("target-path", Fdt.Value.string "/soc/serial@7e201000") ]
~children:
[
Fdt.Node.v "__overlay__"
~properties:[ ("status", Fdt.Value.string "okay") ];
];
])
in
match Fdt.apply_overlay ~base overlay with
| Ok t ->
Option.bind (Fdt.find t "/soc/serial@7e201000") (fun uart ->
Option.bind (Fdt.Node.property uart "status") Fdt.Value.to_string)
| Error _ -> None
- : string option = Some "okay"
The dts rendering of any tree is available through Fdt.pp.
Testing#
The unit suite cites the specification section each test encodes. The
interop suite compiles test/interop/dtc/cases/*.dts with dtc -@ and
merges them with fdtoverlay, then checks this library decodes the
blobs and reproduces fdtoverlay's output; regenerate the committed
traces with:
$ REGEN=1 dune build @regen
Related Work#
- dtc / libfdt — the reference C implementation and compiler; this library follows its overlay-application semantics and is tested against it.
- fdt (Rust) — a read-only no_std DTB
parser;
fdtfor OCaml also encodes and applies overlays. - No OCaml DTB codec existed on opam before this package.
License#
ISC License. See LICENSE.md for details.