xref: /linux/arch/arm/boot/compressed/atags_to_fdt.c (revision 44f57d78)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <asm/setup.h>
3 #include <libfdt.h>
4 
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 
11 #define NR_BANKS 16
12 
13 static 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 
21 static 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 
30 static 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 
39 static 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 
48 static 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 
59 static 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 
70 static 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 
101 static void hex_str(char *out, uint32_t value)
102 {
103 	uint32_t digit;
104 	int idx;
105 
106 	for (idx = 7; idx >= 0; idx--) {
107 		digit = value >> 28;
108 		value <<= 4;
109 		digit &= 0xf;
110 		if (digit < 10)
111 			digit += '0';
112 		else
113 			digit += 'A'-10;
114 		*out++ = digit;
115 	}
116 	*out = '\0';
117 }
118 
119 /*
120  * Convert and fold provided ATAGs into the provided FDT.
121  *
122  * REturn values:
123  *    = 0 -> pretend success
124  *    = 1 -> bad ATAG (may retry with another possible ATAG pointer)
125  *    < 0 -> error from libfdt
126  */
127 int atags_to_fdt(void *atag_list, void *fdt, int total_space)
128 {
129 	struct tag *atag = atag_list;
130 	/* In the case of 64 bits memory size, need to reserve 2 cells for
131 	 * address and size for each bank */
132 	uint32_t mem_reg_property[2 * 2 * NR_BANKS];
133 	int memcount = 0;
134 	int ret, memsize;
135 
136 	/* make sure we've got an aligned pointer */
137 	if ((u32)atag_list & 0x3)
138 		return 1;
139 
140 	/* if we get a DTB here we're done already */
141 	if (*(u32 *)atag_list == fdt32_to_cpu(FDT_MAGIC))
142 	       return 0;
143 
144 	/* validate the ATAG */
145 	if (atag->hdr.tag != ATAG_CORE ||
146 	    (atag->hdr.size != tag_size(tag_core) &&
147 	     atag->hdr.size != 2))
148 		return 1;
149 
150 	/* let's give it all the room it could need */
151 	ret = fdt_open_into(fdt, fdt, total_space);
152 	if (ret < 0)
153 		return ret;
154 
155 	for_each_tag(atag, atag_list) {
156 		if (atag->hdr.tag == ATAG_CMDLINE) {
157 			/* Append the ATAGS command line to the device tree
158 			 * command line.
159 			 * NB: This means that if the same parameter is set in
160 			 * the device tree and in the tags, the one from the
161 			 * tags will be chosen.
162 			 */
163 			if (do_extend_cmdline)
164 				merge_fdt_bootargs(fdt,
165 						   atag->u.cmdline.cmdline);
166 			else
167 				setprop_string(fdt, "/chosen", "bootargs",
168 					       atag->u.cmdline.cmdline);
169 		} else if (atag->hdr.tag == ATAG_MEM) {
170 			if (memcount >= sizeof(mem_reg_property)/4)
171 				continue;
172 			if (!atag->u.mem.size)
173 				continue;
174 			memsize = get_cell_size(fdt);
175 
176 			if (memsize == 2) {
177 				/* if memsize is 2, that means that
178 				 * each data needs 2 cells of 32 bits,
179 				 * so the data are 64 bits */
180 				uint64_t *mem_reg_prop64 =
181 					(uint64_t *)mem_reg_property;
182 				mem_reg_prop64[memcount++] =
183 					cpu_to_fdt64(atag->u.mem.start);
184 				mem_reg_prop64[memcount++] =
185 					cpu_to_fdt64(atag->u.mem.size);
186 			} else {
187 				mem_reg_property[memcount++] =
188 					cpu_to_fdt32(atag->u.mem.start);
189 				mem_reg_property[memcount++] =
190 					cpu_to_fdt32(atag->u.mem.size);
191 			}
192 
193 		} else if (atag->hdr.tag == ATAG_INITRD2) {
194 			uint32_t initrd_start, initrd_size;
195 			initrd_start = atag->u.initrd.start;
196 			initrd_size = atag->u.initrd.size;
197 			setprop_cell(fdt, "/chosen", "linux,initrd-start",
198 					initrd_start);
199 			setprop_cell(fdt, "/chosen", "linux,initrd-end",
200 					initrd_start + initrd_size);
201 		} else if (atag->hdr.tag == ATAG_SERIAL) {
202 			char serno[16+2];
203 			hex_str(serno, atag->u.serialnr.high);
204 			hex_str(serno+8, atag->u.serialnr.low);
205 			setprop_string(fdt, "/", "serial-number", serno);
206 		}
207 	}
208 
209 	if (memcount) {
210 		setprop(fdt, "/memory", "reg", mem_reg_property,
211 			4 * memcount * memsize);
212 	}
213 
214 	return fdt_pack(fdt);
215 }
216