uboot/common/spl/spl_fit.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016 Google, Inc
   3 * Written by Simon Glass <sjg@chromium.org>
   4 *
   5 * SPDX-License-Identifier:     GPL-2.0+
   6 */
   7
   8#include <common.h>
   9#include <errno.h>
  10#include <image.h>
  11#include <libfdt.h>
  12#include <spl.h>
  13
  14static ulong fdt_getprop_u32(const void *fdt, int node, const char *prop)
  15{
  16        const u32 *cell;
  17        int len;
  18
  19        cell = fdt_getprop(fdt, node, prop, &len);
  20        if (len != sizeof(*cell))
  21                return -1U;
  22        return fdt32_to_cpu(*cell);
  23}
  24
  25static int spl_fit_select_fdt(const void *fdt, int images, int *fdt_offsetp)
  26{
  27        const char *name, *fdt_name;
  28        int conf, node, fdt_node;
  29        int len;
  30
  31        *fdt_offsetp = 0;
  32        conf = fdt_path_offset(fdt, FIT_CONFS_PATH);
  33        if (conf < 0) {
  34                debug("%s: Cannot find /configurations node: %d\n", __func__,
  35                      conf);
  36                return -EINVAL;
  37        }
  38        for (node = fdt_first_subnode(fdt, conf);
  39             node >= 0;
  40             node = fdt_next_subnode(fdt, node)) {
  41                name = fdt_getprop(fdt, node, "description", &len);
  42                if (!name) {
  43#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
  44                        printf("%s: Missing FDT description in DTB\n",
  45                               __func__);
  46#endif
  47                        return -EINVAL;
  48                }
  49                if (board_fit_config_name_match(name))
  50                        continue;
  51
  52                debug("Selecting config '%s'", name);
  53                fdt_name = fdt_getprop(fdt, node, FIT_FDT_PROP, &len);
  54                if (!fdt_name) {
  55                        debug("%s: Cannot find fdt name property: %d\n",
  56                              __func__, len);
  57                        return -EINVAL;
  58                }
  59
  60                debug(", fdt '%s'\n", fdt_name);
  61                fdt_node = fdt_subnode_offset(fdt, images, fdt_name);
  62                if (fdt_node < 0) {
  63                        debug("%s: Cannot find fdt node '%s': %d\n",
  64                              __func__, fdt_name, fdt_node);
  65                        return -EINVAL;
  66                }
  67
  68                *fdt_offsetp = fdt_getprop_u32(fdt, fdt_node, "data-offset");
  69                len = fdt_getprop_u32(fdt, fdt_node, "data-size");
  70                debug("FIT: Selected '%s'\n", name);
  71
  72                return len;
  73        }
  74
  75#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
  76        printf("No matching DT out of these options:\n");
  77        for (node = fdt_first_subnode(fdt, conf);
  78             node >= 0;
  79             node = fdt_next_subnode(fdt, node)) {
  80                name = fdt_getprop(fdt, node, "description", &len);
  81                printf("   %s\n", name);
  82        }
  83#endif
  84
  85        return -ENOENT;
  86}
  87
  88static int get_aligned_image_offset(struct spl_load_info *info, int offset)
  89{
  90        /*
  91         * If it is a FS read, get the first address before offset which is
  92         * aligned to ARCH_DMA_MINALIGN. If it is raw read return the
  93         * block number to which offset belongs.
  94         */
  95        if (info->filename)
  96                return offset & ~(ARCH_DMA_MINALIGN - 1);
  97
  98        return offset / info->bl_len;
  99}
 100
 101static int get_aligned_image_overhead(struct spl_load_info *info, int offset)
 102{
 103        /*
 104         * If it is a FS read, get the difference between the offset and
 105         * the first address before offset which is aligned to
 106         * ARCH_DMA_MINALIGN. If it is raw read return the offset within the
 107         * block.
 108         */
 109        if (info->filename)
 110                return offset & (ARCH_DMA_MINALIGN - 1);
 111
 112        return offset % info->bl_len;
 113}
 114
 115static int get_aligned_image_size(struct spl_load_info *info, int data_size,
 116                                  int offset)
 117{
 118        data_size = data_size + get_aligned_image_overhead(info, offset);
 119
 120        if (info->filename)
 121                return data_size;
 122
 123        return (data_size + info->bl_len - 1) / info->bl_len;
 124}
 125
 126int spl_load_simple_fit(struct spl_image_info *spl_image,
 127                        struct spl_load_info *info, ulong sector, void *fit)
 128{
 129        int sectors;
 130        ulong size, load;
 131        unsigned long count;
 132        int node, images;
 133        void *load_ptr;
 134        int fdt_offset, fdt_len;
 135        int data_offset, data_size;
 136        int base_offset, align_len = ARCH_DMA_MINALIGN - 1;
 137        int src_sector;
 138        void *dst, *src;
 139
 140        /*
 141         * Figure out where the external images start. This is the base for the
 142         * data-offset properties in each image.
 143         */
 144        size = fdt_totalsize(fit);
 145        size = (size + 3) & ~3;
 146        base_offset = (size + 3) & ~3;
 147
 148        /*
 149         * So far we only have one block of data from the FIT. Read the entire
 150         * thing, including that first block, placing it so it finishes before
 151         * where we will load the image.
 152         *
 153         * Note that we will load the image such that its first byte will be
 154         * at the load address. Since that byte may be part-way through a
 155         * block, we may load the image up to one block before the load
 156         * address. So take account of that here by subtracting an addition
 157         * block length from the FIT start position.
 158         *
 159         * In fact the FIT has its own load address, but we assume it cannot
 160         * be before CONFIG_SYS_TEXT_BASE.
 161         */
 162        fit = (void *)((CONFIG_SYS_TEXT_BASE - size - info->bl_len -
 163                        align_len) & ~align_len);
 164        sectors = get_aligned_image_size(info, size, 0);
 165        count = info->read(info, sector, sectors, fit);
 166        debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu\n",
 167              sector, sectors, fit, count);
 168        if (count == 0)
 169                return -EIO;
 170
 171        /* find the firmware image to load */
 172        images = fdt_path_offset(fit, FIT_IMAGES_PATH);
 173        if (images < 0) {
 174                debug("%s: Cannot find /images node: %d\n", __func__, images);
 175                return -1;
 176        }
 177        node = fdt_first_subnode(fit, images);
 178        if (node < 0) {
 179                debug("%s: Cannot find first image node: %d\n", __func__, node);
 180                return -1;
 181        }
 182
 183        /* Get its information and set up the spl_image structure */
 184        data_offset = fdt_getprop_u32(fit, node, "data-offset");
 185        data_size = fdt_getprop_u32(fit, node, "data-size");
 186        load = fdt_getprop_u32(fit, node, "load");
 187        debug("data_offset=%x, data_size=%x\n", data_offset, data_size);
 188        spl_image->load_addr = load;
 189        spl_image->entry_point = load;
 190        spl_image->os = IH_OS_U_BOOT;
 191
 192        /*
 193         * Work out where to place the image. We read it so that the first
 194         * byte will be at 'load'. This may mean we need to load it starting
 195         * before then, since we can only read whole blocks.
 196         */
 197        data_offset += base_offset;
 198        sectors = get_aligned_image_size(info, data_size, data_offset);
 199        load_ptr = (void *)load;
 200        debug("U-Boot size %x, data %p\n", data_size, load_ptr);
 201        dst = load_ptr;
 202
 203        /* Read the image */
 204        src_sector = sector + get_aligned_image_offset(info, data_offset);
 205        debug("Aligned image read: dst=%p, src_sector=%x, sectors=%x\n",
 206              dst, src_sector, sectors);
 207        count = info->read(info, src_sector, sectors, dst);
 208        if (count != sectors)
 209                return -EIO;
 210        debug("image: dst=%p, data_offset=%x, size=%x\n", dst, data_offset,
 211              data_size);
 212        src = dst + get_aligned_image_overhead(info, data_offset);
 213
 214#ifdef CONFIG_SPL_FIT_IMAGE_POST_PROCESS
 215        board_fit_image_post_process((void **)&src, (size_t *)&data_size);
 216#endif
 217
 218        memcpy(dst, src, data_size);
 219
 220        /* Figure out which device tree the board wants to use */
 221        fdt_len = spl_fit_select_fdt(fit, images, &fdt_offset);
 222        if (fdt_len < 0)
 223                return fdt_len;
 224
 225        /*
 226         * Read the device tree and place it after the image. There may be
 227         * some extra data before it since we can only read entire blocks.
 228         * And also align the destination address to ARCH_DMA_MINALIGN.
 229         */
 230        dst = (void *)((load + data_size + align_len) & ~align_len);
 231        fdt_offset += base_offset;
 232        sectors = get_aligned_image_size(info, fdt_len, fdt_offset);
 233        src_sector = sector + get_aligned_image_offset(info, fdt_offset);
 234        count = info->read(info, src_sector, sectors, dst);
 235        debug("Aligned fdt read: dst %p, src_sector = %x, sectors %x\n",
 236              dst, src_sector, sectors);
 237        if (count != sectors)
 238                return -EIO;
 239
 240        /*
 241         * Copy the device tree so that it starts immediately after the image.
 242         * After this we will have the U-Boot image and its device tree ready
 243         * for us to start.
 244         */
 245        debug("fdt: dst=%p, data_offset=%x, size=%x\n", dst, fdt_offset,
 246              fdt_len);
 247        src = dst + get_aligned_image_overhead(info, fdt_offset);
 248        dst = load_ptr + data_size;
 249
 250#ifdef CONFIG_SPL_FIT_IMAGE_POST_PROCESS
 251        board_fit_image_post_process((void **)&src, (size_t *)&fdt_len);
 252#endif
 253
 254        memcpy(dst, src, fdt_len);
 255
 256        return 0;
 257}
 258