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