1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/libfdt_env.h>
6 #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND)
7 #define do_extend_cmdline 1
9 #define do_extend_cmdline 0
14 static int node_offset(void *fdt, const char *node_path)
16 int offset = fdt_path_offset(fdt, node_path);
17 if (offset == -FDT_ERR_NOTFOUND)
18 /* Add the node to root if not found, dropping the leading '/' */
19 offset = fdt_add_subnode(fdt, 0, node_path + 1);
23 static int setprop(void *fdt, const char *node_path, const char *property,
24 void *val_array, int size)
26 int offset = node_offset(fdt, node_path);
29 return fdt_setprop(fdt, offset, property, val_array, size);
32 static int setprop_string(void *fdt, const char *node_path,
33 const char *property, const char *string)
35 int offset = node_offset(fdt, node_path);
38 return fdt_setprop_string(fdt, offset, property, string);
41 static int setprop_cell(void *fdt, const char *node_path,
42 const char *property, uint32_t val)
44 int offset = node_offset(fdt, node_path);
47 return fdt_setprop_cell(fdt, offset, property, val);
50 static const void *getprop(const void *fdt, const char *node_path,
51 const char *property, int *len)
53 int offset = fdt_path_offset(fdt, node_path);
55 if (offset == -FDT_ERR_NOTFOUND)
58 return fdt_getprop(fdt, offset, property, len);
61 static uint32_t get_cell_size(const void *fdt)
64 uint32_t cell_size = 1;
65 const __be32 *size_len = getprop(fdt, "/", "#size-cells", &len);
68 cell_size = fdt32_to_cpu(*size_len);
72 static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
74 char cmdline[COMMAND_LINE_SIZE];
75 const char *fdt_bootargs;
79 /* copy the fdt command line into the buffer */
80 fdt_bootargs = getprop(fdt, "/chosen", "bootargs", &len);
82 if (len < COMMAND_LINE_SIZE) {
83 memcpy(ptr, fdt_bootargs, len);
84 /* len is the length of the string
85 * including the NULL terminator */
89 /* and append the ATAG_CMDLINE */
91 len = strlen(fdt_cmdline);
92 if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) {
94 memcpy(ptr, fdt_cmdline, len);
100 setprop_string(fdt, "/chosen", "bootargs", cmdline);
103 static void hex_str(char *out, uint32_t value)
108 for (idx = 7; idx >= 0; idx--) {
122 * Convert and fold provided ATAGs into the provided FDT.
125 * = 0 -> pretend success
126 * = 1 -> bad ATAG (may retry with another possible ATAG pointer)
127 * < 0 -> error from libfdt
129 int atags_to_fdt(void *atag_list, void *fdt, int total_space)
131 struct tag *atag = atag_list;
132 /* In the case of 64 bits memory size, need to reserve 2 cells for
133 * address and size for each bank */
134 __be32 mem_reg_property[2 * 2 * NR_BANKS];
138 /* make sure we've got an aligned pointer */
139 if ((u32)atag_list & 0x3)
142 /* if we get a DTB here we're done already */
143 if (*(__be32 *)atag_list == cpu_to_fdt32(FDT_MAGIC))
146 /* validate the ATAG */
147 if (atag->hdr.tag != ATAG_CORE ||
148 (atag->hdr.size != tag_size(tag_core) &&
149 atag->hdr.size != 2))
152 /* let's give it all the room it could need */
153 ret = fdt_open_into(fdt, fdt, total_space);
157 for_each_tag(atag, atag_list) {
158 if (atag->hdr.tag == ATAG_CMDLINE) {
159 /* Append the ATAGS command line to the device tree
161 * NB: This means that if the same parameter is set in
162 * the device tree and in the tags, the one from the
163 * tags will be chosen.
165 if (do_extend_cmdline)
166 merge_fdt_bootargs(fdt,
167 atag->u.cmdline.cmdline);
169 setprop_string(fdt, "/chosen", "bootargs",
170 atag->u.cmdline.cmdline);
171 } else if (atag->hdr.tag == ATAG_MEM) {
172 if (memcount >= sizeof(mem_reg_property)/4)
174 if (!atag->u.mem.size)
176 memsize = get_cell_size(fdt);
179 /* if memsize is 2, that means that
180 * each data needs 2 cells of 32 bits,
181 * so the data are 64 bits */
182 __be64 *mem_reg_prop64 =
183 (__be64 *)mem_reg_property;
184 mem_reg_prop64[memcount++] =
185 cpu_to_fdt64(atag->u.mem.start);
186 mem_reg_prop64[memcount++] =
187 cpu_to_fdt64(atag->u.mem.size);
189 mem_reg_property[memcount++] =
190 cpu_to_fdt32(atag->u.mem.start);
191 mem_reg_property[memcount++] =
192 cpu_to_fdt32(atag->u.mem.size);
195 } else if (atag->hdr.tag == ATAG_INITRD2) {
196 uint32_t initrd_start, initrd_size;
197 initrd_start = atag->u.initrd.start;
198 initrd_size = atag->u.initrd.size;
199 setprop_cell(fdt, "/chosen", "linux,initrd-start",
201 setprop_cell(fdt, "/chosen", "linux,initrd-end",
202 initrd_start + initrd_size);
203 } else if (atag->hdr.tag == ATAG_SERIAL) {
205 hex_str(serno, atag->u.serialnr.high);
206 hex_str(serno+8, atag->u.serialnr.low);
207 setprop_string(fdt, "/", "serial-number", serno);
212 setprop(fdt, "/memory", "reg", mem_reg_property,
213 4 * memcount * memsize);
216 return fdt_pack(fdt);