Skip to content

Support modeling the OF device-tree (/proc/device-tree, /sys/firmware/devicetree/base) — node injection is rejected by immutable special filesystems #92

Description

@lacraig2

Summary

Penguin can inject/model pseudofiles under most kernel special filesystems
(procfs, sysfs, devfs), but it cannot model the OF device-tree exposed at
/proc/device-tree/* and /sys/firmware/devicetree/base/*. Firmware that reads
its hardware description from the device tree (model, platform, compatible,
per-peripheral nodes) therefore sees the donor machine's tree (e.g. the generic
QEMU malta compatible = "mti,malta") instead of anything representative of the
target, and there is currently no way to override it.

Root cause

The device-tree case is not "a missing backend" — it's that the existing
backends use the wrong primitive for this filesystem. Both pseudofile backends
model a path by creating a node in the target fs:

  • portal_procfs.cproc_create_data(...)
  • portal_sysfs.csysfs_create_file(...)

The OF device-tree does not accept node creation:

  • procfs: /proc/device-tree already exists (it's a symlink to
    /sys/firmware/devicetree/base), so creating under it collides —
    portal_procfs: Collision: 'device-tree' exists but is a file.
  • sysfs: /sys/firmware/devicetree/base/ is a view of the kernel's
    unflattened OF tree (of_root). Its kobjects reject arbitrary attribute
    creation — sysfs_create_file returns 0/fails:
    plugins.sysfs: Failed to register sysfs 'firmware/devicetree/base/model' (kernel returned 0).

Node-creation is simply not how the OF tree is populated. It is unflattened from
the FDT at boot into an in-kernel struct device_node tree; the procfs and
sysfs paths are both read-only projections of that tree.

Why this matters (device-agnostic)

This is a general rehosting gap, not tied to any one target. Any firmware whose
early boot / board-detection / identity logic consults the device tree is
affected. Common consumers:

  • board-detection and model/platform/compatible reads (identity, sysinfo,
    board.json population);
  • drivers and init scripts that enumerate peripherals from DT nodes (GPIO/LED
    controllers, switches, i2c/spi devices, pinctrl) — when the node is absent the
    driver never binds, cascading into missing /sys/class/* entries that then
    have to be stubbed one by one downstream;
  • anything that keys behavior off /proc/device-tree/* values.

Because the model/platform/compatible come from the donor machine, the guest's
notion of "what hardware am I" is fixed to the emulator's board, which is often
the first thing target firmware branches on.

Proposed fix

Option A (preferred): patch the live OF tree

After boot the OF tree is a mutable in-kernel struct device_node tree
rooted at of_root. Both the procfs and sysfs views derive from it, so a single
override is reflected in both — and it matches exactly what a real DTB would have
provided. Add a portal op that manipulates the live tree via the kernel's own
GPL-exported OF APIs:

  • of_find_node_by_path() / of_get_child_by_name() to locate/attach;
  • of_update_property() / of_add_property() (or of_changeset_*) to set
    property values;
  • of_attach_node() / node allocation to add missing nodes.

Surface it in penguin config as a device_tree: (or devicetree:) section, e.g.:

device_tree:
  /:
    model:      "Example Vendor ExampleBoard"
    compatible: ["vendor,board", "vendor,soc"]
  /some-peripheral@10000000:
    compatible: "vendor,widget"
    status:     "okay"

This is minimal, generic, and fixes procfs + sysfs simultaneously.

Option B (fallback, more general): read-path interception

Intercept the read / ->show of an already-existing immutable special file
and substitute contents without creating a node. Heavier, but covers the OF tree
plus any other read-only kernel-backed file where node creation is refused.

Acceptance criteria

  • A rehosting config can set arbitrary /proc/device-tree/* (equivalently
    /sys/firmware/devicetree/base/*) property values and add nodes.
  • Overrides are visible through both the procfs and sysfs projections.
  • Works on the donor kernels Penguin ships (no dependency on a specific board);
    no regression to existing procfs/sysfs/devfs pseudofile modeling.

Notes

Today the workaround is to override the consuming userland scripts via
static_files/lib_inject, which is per-consumer and brittle. A driver-level
OF override removes that whole class of workaround.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions