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