License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[linux-block.git] / arch / arm / boot / compressed / atags_to_fdt.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
b90b9a38
NP
2#include <asm/setup.h>
3#include <libfdt.h>
4
d0f34a11
GR
5#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND)
6#define do_extend_cmdline 1
7#else
8#define do_extend_cmdline 0
9#endif
10
1c2f87c2
LA
11#define NR_BANKS 16
12
b90b9a38
NP
13static int node_offset(void *fdt, const char *node_path)
14{
15 int offset = fdt_path_offset(fdt, node_path);
16 if (offset == -FDT_ERR_NOTFOUND)
17 offset = fdt_add_subnode(fdt, 0, node_path);
18 return offset;
19}
20
21static int setprop(void *fdt, const char *node_path, const char *property,
22 uint32_t *val_array, int size)
23{
24 int offset = node_offset(fdt, node_path);
25 if (offset < 0)
26 return offset;
27 return fdt_setprop(fdt, offset, property, val_array, size);
28}
29
30static int setprop_string(void *fdt, const char *node_path,
31 const char *property, const char *string)
32{
33 int offset = node_offset(fdt, node_path);
34 if (offset < 0)
35 return offset;
36 return fdt_setprop_string(fdt, offset, property, string);
37}
38
39static int setprop_cell(void *fdt, const char *node_path,
40 const char *property, uint32_t val)
41{
42 int offset = node_offset(fdt, node_path);
43 if (offset < 0)
44 return offset;
45 return fdt_setprop_cell(fdt, offset, property, val);
46}
47
d0f34a11
GR
48static const void *getprop(const void *fdt, const char *node_path,
49 const char *property, int *len)
50{
51 int offset = fdt_path_offset(fdt, node_path);
52
53 if (offset == -FDT_ERR_NOTFOUND)
54 return NULL;
55
56 return fdt_getprop(fdt, offset, property, len);
57}
58
faefd550
GC
59static uint32_t get_cell_size(const void *fdt)
60{
61 int len;
62 uint32_t cell_size = 1;
63 const uint32_t *size_len = getprop(fdt, "/", "#size-cells", &len);
64
65 if (size_len)
66 cell_size = fdt32_to_cpu(*size_len);
67 return cell_size;
68}
69
d0f34a11
GR
70static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
71{
72 char cmdline[COMMAND_LINE_SIZE];
73 const char *fdt_bootargs;
74 char *ptr = cmdline;
75 int len = 0;
76
77 /* copy the fdt command line into the buffer */
78 fdt_bootargs = getprop(fdt, "/chosen", "bootargs", &len);
79 if (fdt_bootargs)
80 if (len < COMMAND_LINE_SIZE) {
81 memcpy(ptr, fdt_bootargs, len);
82 /* len is the length of the string
83 * including the NULL terminator */
84 ptr += len - 1;
85 }
86
87 /* and append the ATAG_CMDLINE */
88 if (fdt_cmdline) {
89 len = strlen(fdt_cmdline);
90 if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) {
91 *ptr++ = ' ';
92 memcpy(ptr, fdt_cmdline, len);
93 ptr += len;
94 }
95 }
96 *ptr = '\0';
97
98 setprop_string(fdt, "/chosen", "bootargs", cmdline);
99}
100
b90b9a38
NP
101/*
102 * Convert and fold provided ATAGs into the provided FDT.
103 *
104 * REturn values:
105 * = 0 -> pretend success
106 * = 1 -> bad ATAG (may retry with another possible ATAG pointer)
107 * < 0 -> error from libfdt
108 */
109int atags_to_fdt(void *atag_list, void *fdt, int total_space)
110{
111 struct tag *atag = atag_list;
faefd550
GC
112 /* In the case of 64 bits memory size, need to reserve 2 cells for
113 * address and size for each bank */
114 uint32_t mem_reg_property[2 * 2 * NR_BANKS];
b90b9a38 115 int memcount = 0;
faefd550 116 int ret, memsize;
b90b9a38
NP
117
118 /* make sure we've got an aligned pointer */
119 if ((u32)atag_list & 0x3)
120 return 1;
121
122 /* if we get a DTB here we're done already */
123 if (*(u32 *)atag_list == fdt32_to_cpu(FDT_MAGIC))
124 return 0;
125
126 /* validate the ATAG */
127 if (atag->hdr.tag != ATAG_CORE ||
128 (atag->hdr.size != tag_size(tag_core) &&
129 atag->hdr.size != 2))
130 return 1;
131
132 /* let's give it all the room it could need */
133 ret = fdt_open_into(fdt, fdt, total_space);
134 if (ret < 0)
135 return ret;
136
137 for_each_tag(atag, atag_list) {
138 if (atag->hdr.tag == ATAG_CMDLINE) {
d0f34a11
GR
139 /* Append the ATAGS command line to the device tree
140 * command line.
141 * NB: This means that if the same parameter is set in
142 * the device tree and in the tags, the one from the
143 * tags will be chosen.
144 */
145 if (do_extend_cmdline)
146 merge_fdt_bootargs(fdt,
147 atag->u.cmdline.cmdline);
148 else
149 setprop_string(fdt, "/chosen", "bootargs",
150 atag->u.cmdline.cmdline);
b90b9a38
NP
151 } else if (atag->hdr.tag == ATAG_MEM) {
152 if (memcount >= sizeof(mem_reg_property)/4)
153 continue;
a106b21a
MZ
154 if (!atag->u.mem.size)
155 continue;
faefd550
GC
156 memsize = get_cell_size(fdt);
157
158 if (memsize == 2) {
159 /* if memsize is 2, that means that
160 * each data needs 2 cells of 32 bits,
161 * so the data are 64 bits */
162 uint64_t *mem_reg_prop64 =
163 (uint64_t *)mem_reg_property;
164 mem_reg_prop64[memcount++] =
165 cpu_to_fdt64(atag->u.mem.start);
166 mem_reg_prop64[memcount++] =
167 cpu_to_fdt64(atag->u.mem.size);
168 } else {
169 mem_reg_property[memcount++] =
170 cpu_to_fdt32(atag->u.mem.start);
171 mem_reg_property[memcount++] =
172 cpu_to_fdt32(atag->u.mem.size);
173 }
174
b90b9a38
NP
175 } else if (atag->hdr.tag == ATAG_INITRD2) {
176 uint32_t initrd_start, initrd_size;
177 initrd_start = atag->u.initrd.start;
178 initrd_size = atag->u.initrd.size;
179 setprop_cell(fdt, "/chosen", "linux,initrd-start",
180 initrd_start);
181 setprop_cell(fdt, "/chosen", "linux,initrd-end",
182 initrd_start + initrd_size);
183 }
184 }
185
faefd550
GC
186 if (memcount) {
187 setprop(fdt, "/memory", "reg", mem_reg_property,
188 4 * memcount * memsize);
189 }
b90b9a38
NP
190
191 return fdt_pack(fdt);
192}