summaryrefslogtreecommitdiff
path: root/common/dtb_reloc.c
blob: c3e0fe5e04ccf8cf71f3921c6caa802e03eaa231 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/*
 * 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 <brads@mainlining.org>
 */

#include <string.h>
#include <endian.h>
#include <boot.h>
#include <dtb_patch.h>

#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;
}