linux/arch/arm/boot/compressed/atags_to_fdt.c
<<
>>
Prefs
   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
  13static 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
  21static 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
  30static 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
  39static 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
  48static 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
  59static 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
  70static 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
 101static 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 */
 127int 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