Linux-libre 5.4.49-gnu
[librecmc/linux-libre.git] / arch / arm / boot / compressed / atags_to_fdt.c
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 }