/* * dtb_reloc.c - grow the devicetree the way libfdt does, in a * buffer with room to spare. firmware cannot edit a packed fdt * in place, new properties shift everything behind them, so the * blob is copied into scratch verbatim, the free space after * totalsize is the room the insert code shifts into, then the * walkers patch the copy and the kernel gets its address. * * The layout follows the devicetree specification: header, * struct block, strings block, free space. The rebuild copies * header, struct, strings, fixes the offsets in the new header, * and leaves the gap between struct and strings as the room new * properties will consume. * * Copyright (C) 2026 Bradley Morgan */ #include #include #include #include #define FDT_BEGIN_NODE 1 #define FDT_END_NODE 2 #define FDT_PROP 3 #define FDT_NOP 4 #define FDT_END 9 static uint32_t be32(const void *p) { const uint8_t *b = p; return ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | (uint32_t)b[3]; } /* * copy the blob into the scratch, grow bytes of headroom after * the end. returns the new blob address or 0 on a short buffer. */ uintptr_t tb_dtb_relocate(uintptr_t dtb, void *scratch, size_t scratch_size, size_t grow) { uint8_t *in = (uint8_t *)dtb; uint8_t *out = scratch; uint32_t totalsize; if (be32(in) != 0xd00dfeed) return 0; totalsize = be32(in + 4); if (scratch_size < (size_t)totalsize + grow) return 0; /* * verbatim copy, byte for byte. the grow room is the free * scratch after totalsize, the insert code shifts the * strings block into it. an interior gap between the * struct and strings blocks only invites the walkers to * count it as tree. */ for (uint32_t i = 0; i < totalsize; i++) out[i] = in[i]; (void)grow; return (uintptr_t)out; }