linux/arch/arm/boot/compressed/atags_to_fdt.c
<<
>>
Prefs
   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
  14static 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
  23static 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
  32static 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
  41static 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
  50static 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
  61static 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
  72static 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
 103static 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 */
 129int 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