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