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