Flattened Devicetree (DTB) codec and overlay application
0

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-fdt / README.md
4.9 kB

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.Value converts property values to and from the standard encodings: 32-bit cells, NUL-terminated strings, 64-bit integers;
  • Fdt.apply_overlay merges 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's fdt_overlay_apply, verified against fdtoverlay in 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
  • 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; fdt for OCaml also encodes and applies overlays.
  • No OCaml DTB codec existed on opam before this package.

License#

ISC License. See LICENSE.md for details.