// SPDX-License-Identifier: GPL-2.0 #include <linux/libfdt_env.h> #include <asm/setup.h> #include <libfdt.h> #include "misc.h" #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) #define do_extend_cmdline 1 #else #define do_extend_cmdline 0 #endif #define NR_BANKS 16 static int node_offset(void *fdt, const char *node_path) { int offset = fdt_path_offset(fdt, node_path); if (offset == -FDT_ERR_NOTFOUND) /* Add the node to root if not found, dropping the leading '/' */ offset = fdt_add_subnode(fdt, 0, node_path + 1); return offset; } static int setprop(void *fdt, const char *node_path, const char *property, void *val_array, int size) { int offset = node_offset(fdt, node_path); if (offset < 0) return offset; return fdt_setprop(fdt, offset, property, val_array, size); } static int setprop_string(void *fdt, const char *node_path, const char *property, const char *string) { int offset = node_offset(fdt, node_path); if (offset < 0) return offset; return fdt_setprop_string(fdt, offset, property, string); } static int setprop_cell(void *fdt, const char *node_path, const char *property, uint32_t val) { int offset = node_offset(fdt, node_path); if (offset < 0) return offset; return fdt_setprop_cell(fdt, offset, property, val); } static const void *getprop(const void *fdt, const char *node_path, const char *property, int *len) { int offset = fdt_path_offset(fdt, node_path); if (offset == -FDT_ERR_NOTFOUND) return NULL; return fdt_getprop(fdt, offset, property, len); } static uint32_t get_cell_size(const void *fdt) { int len; uint32_t cell_size = 1; const __be32 *size_len = getprop(fdt, "/", "#size-cells", &len); if (size_len) cell_size = fdt32_to_cpu(*size_len); return cell_size; } static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) { char cmdline[COMMAND_LINE_SIZE]; const char *fdt_bootargs; char *ptr = cmdline; int len = 0; /* copy the fdt command line into the buffer */ fdt_bootargs = getprop(fdt, "/chosen", "bootargs", &len); if (fdt_bootargs) if (len < COMMAND_LINE_SIZE) { memcpy(ptr, fdt_bootargs, len); /* len is the length of the string * including the NULL terminator */ ptr += len - 1; } /* and append the ATAG_CMDLINE */ if (fdt_cmdline) { len = strlen(fdt_cmdline); if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { *ptr++ = ' '; memcpy(ptr, fdt_cmdline, len); ptr += len; } } *ptr = '\0'; setprop_string(fdt, "/chosen", "bootargs", cmdline); } static void hex_str(char *out, uint32_t value) { uint32_t digit; int idx; for (idx = 7; idx >= 0; idx--) { digit = value >> 28; value <<= 4; digit &= 0xf; if (digit < 10) digit += '0'; else digit += 'A'-10; *out++ = digit; } *out = '\0'; } /* * Convert and fold provided ATAGs into the provided FDT. * * Return values: * = 0 -> pretend success * = 1 -> bad ATAG (may retry with another possible ATAG pointer) * < 0 -> error from libfdt */ int atags_to_fdt(void *atag_list, void *fdt, int total_space) { struct tag *atag = atag_list; /* In the case of 64 bits memory size, need to reserve 2 cells for * address and size for each bank */ __be32 mem_reg_property[2 * 2 * NR_BANKS]; int memcount = 0; int ret, memsize; /* make sure we've got an aligned pointer */ if ((u32)atag_list & 0x3) return 1; /* if we get a DTB here we're done already */ if (*(__be32 *)atag_list == cpu_to_fdt32(FDT_MAGIC)) return 0; /* validate the ATAG */ if (atag->hdr.tag != ATAG_CORE || (atag->hdr.size != tag_size(tag_core) && atag->hdr.size != 2)) return 1; /* let's give it all the room it could need */ ret = fdt_open_into(fdt, fdt, total_space); if (ret < 0) return ret; for_each_tag(atag, atag_list) { if (atag->hdr.tag == ATAG_CMDLINE) { /* Append the ATAGS command line to the device tree * command line. * NB: This means that if the same parameter is set in * the device tree and in the tags, the one from the * tags will be chosen. */ if (do_extend_cmdline) merge_fdt_bootargs(fdt, atag->u.cmdline.cmdline); else setprop_string(fdt, "/chosen", "bootargs", atag->u.cmdline.cmdline); } else if (atag->hdr.tag == ATAG_MEM) { if (memcount >= sizeof(mem_reg_property)/4) continue; if (!atag->u.mem.size) continue; memsize = get_cell_size(fdt); if (memsize == 2) { /* if memsize is 2, that means that * each data needs 2 cells of 32 bits, * so the data are 64 bits */ __be64 *mem_reg_prop64 = (__be64 *)mem_reg_property; mem_reg_prop64[memcount++] = cpu_to_fdt64(atag->u.mem.start); mem_reg_prop64[memcount++] = cpu_to_fdt64(atag->u.mem.size); } else { mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.start); mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.size); } } else if (atag->hdr.tag == ATAG_INITRD2) { uint32_t initrd_start, initrd_size; initrd_start = atag->u.initrd.start; initrd_size = atag->u.initrd.size; setprop_cell(fdt, "/chosen", "linux,initrd-start", initrd_start); setprop_cell(fdt, "/chosen", "linux,initrd-end", initrd_start + initrd_size); } else if (atag->hdr.tag == ATAG_SERIAL) { char serno[16+2]; hex_str(serno, atag->u.serialnr.high); hex_str(serno+8, atag->u.serialnr.low); setprop_string(fdt, "/", "serial-number", serno); } } if (memcount) { setprop(fdt, "/memory", "reg", mem_reg_property, 4 * memcount * memsize); } return fdt_pack(fdt); }