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