spl: Allow to load a FIT containing U-Boot from FS
authorLokesh Vutla <lokeshvutla@ti.com>
Tue, 24 May 2016 05:04:38 +0000 (10:34 +0530)
committerTom Rini <trini@konsulko.com>
Fri, 27 May 2016 19:41:39 +0000 (15:41 -0400)
This provides a way to load a FIT containing U-Boot and a selection of device
tree files from a File system. Making sure that all the reads and writes
are aligned to their respective needs.

Tested-by: Michal Simek <michal.simek@xilinx.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Tom Rini <trini@konsulko.com>
Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
[trini: Make this still apply with Michal's alignment change for 'fit']
Signed-off-by: Tom Rini <trini@konsulko.com>
common/spl/spl_fit.c
common/spl/spl_mmc.c
include/spl.h

index 79cc06ed0b548bd86c635ba28772bcc06f401ae2..fb69f1d1685ebd7a1fa7d0de2789ac272ea49a17 100644 (file)
@@ -87,6 +87,42 @@ static int spl_fit_select_fdt(const void *fdt, int images, int *fdt_offsetp)
        return -ENOENT;
 }
 
+static int get_aligned_image_offset(struct spl_load_info *info, int offset)
+{
+       /*
+        * If it is a FS read, get the first address before offset which is
+        * aligned to ARCH_DMA_MINALIGN. If it is raw read return the
+        * block number to which offset belongs.
+        */
+       if (info->filename)
+               return offset & ~(ARCH_DMA_MINALIGN - 1);
+
+       return offset / info->bl_len;
+}
+
+static int get_aligned_image_overhead(struct spl_load_info *info, int offset)
+{
+       /*
+        * If it is a FS read, get the difference between the offset and
+        * the first address before offset which is aligned to
+        * ARCH_DMA_MINALIGN. If it is raw read return the offset within the
+        * block.
+        */
+       if (info->filename)
+               return offset & (ARCH_DMA_MINALIGN - 1);
+
+       return offset % info->bl_len;
+}
+
+static int get_aligned_image_size(struct spl_load_info *info, int data_size,
+                                 int offset)
+{
+       if (info->filename)
+               return data_size + get_aligned_image_overhead(info, offset);
+
+       return (data_size + info->bl_len - 1) / info->bl_len;
+}
+
 int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fit)
 {
        int sectors;
@@ -96,7 +132,7 @@ int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fit)
        void *load_ptr;
        int fdt_offset, fdt_len;
        int data_offset, data_size;
-       int base_offset;
+       int base_offset, align_len = ARCH_DMA_MINALIGN - 1;
        int src_sector;
        void *dst;
 
@@ -124,7 +160,7 @@ int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fit)
         */
        fit = (void *)(CONFIG_SYS_TEXT_BASE - size - info->bl_len);
        fit = (void *)ALIGN((ulong)fit, 8);
-       sectors = (size + info->bl_len - 1) / info->bl_len;
+       sectors = get_aligned_image_size(info, size, 0);
        count = info->read(info, sector, sectors, fit);
        debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu\n",
              sector, sectors, fit, count);
@@ -157,19 +193,23 @@ int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fit)
         * byte will be at 'load'. This may mean we need to load it starting
         * before then, since we can only read whole blocks.
         */
-       sectors = (data_size + info->bl_len - 1) / info->bl_len;
        data_offset += base_offset;
+       sectors = get_aligned_image_size(info, data_size, data_offset);
        load_ptr = (void *)load;
        debug("U-Boot size %x, data %p\n", data_size, load_ptr);
-       dst = load_ptr - (data_offset % info->bl_len);
+       dst = load_ptr;
 
        /* Read the image */
-       src_sector = sector + data_offset / info->bl_len;
-       debug("image: data_offset=%x, dst=%p, src_sector=%x, sectors=%x\n",
-             data_offset, dst, src_sector, sectors);
+       src_sector = sector + get_aligned_image_offset(info, data_offset);
+       debug("Aligned image read: dst=%p, src_sector=%x, sectors=%x\n",
+             dst, src_sector, sectors);
        count = info->read(info, src_sector, sectors, dst);
        if (count != sectors)
                return -EIO;
+       debug("image: dst=%p, data_offset=%x, size=%x\n", dst, data_offset,
+             data_size);
+       memcpy(dst, dst + get_aligned_image_overhead(info, data_offset),
+              data_size);
 
        /* Figure out which device tree the board wants to use */
        fdt_len = spl_fit_select_fdt(fit, images, &fdt_offset);
@@ -179,14 +219,15 @@ int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fit)
        /*
         * Read the device tree and place it after the image. There may be
         * some extra data before it since we can only read entire blocks.
+        * And also align the destination address to ARCH_DMA_MINALIGN.
         */
-       dst = load_ptr + data_size;
+       dst = (void *)((load + data_size + align_len) & ~align_len);
        fdt_offset += base_offset;
-       sectors = (fdt_len + info->bl_len - 1) / info->bl_len;
-       count = info->read(info, sector + fdt_offset / info->bl_len, sectors,
-                          dst);
-       debug("fit read %x sectors to %x, dst %p, data_offset %x\n",
-             sectors, spl_image.load_addr, dst, fdt_offset);
+       sectors = get_aligned_image_size(info, fdt_len, fdt_offset);
+       src_sector = sector + get_aligned_image_offset(info, fdt_offset);
+       count = info->read(info, src_sector, sectors, dst);
+       debug("Aligned fdt read: dst %p, src_sector = %x, sectors %x\n",
+             dst, src_sector, sectors);
        if (count != sectors)
                return -EIO;
 
@@ -195,7 +236,10 @@ int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fit)
         * After this we will have the U-Boot image and its device tree ready
         * for us to start.
         */
-       memcpy(dst, dst + fdt_offset % info->bl_len, fdt_len);
+       debug("fdt: dst=%p, data_offset=%x, size=%x\n", dst, fdt_offset,
+             fdt_len);
+       memcpy(load_ptr + data_size,
+              dst + get_aligned_image_overhead(info, fdt_offset), fdt_len);
 
        return 0;
 }
index 5676acdde3f282da8062d0c679c02116d590b969..d8058d63999657c2d73b68e13ffd7776a518655f 100644 (file)
@@ -77,6 +77,7 @@ static int mmc_load_image_raw_sector(struct mmc *mmc, unsigned long sector)
                debug("Found FIT\n");
                load.dev = mmc;
                load.priv = NULL;
+               load.filename = NULL;
                load.bl_len = mmc->read_bl_len;
                load.read = h_spl_load_read;
                ret = spl_load_simple_fit(&load, sector, header);
index 335b76a1b15392c0829af6313d13a21afdbaad84..0ae160547d14eb6b38ec8defb605a34f7d988150 100644 (file)
@@ -35,16 +35,28 @@ struct spl_image_info {
  * @dev: Pointer to the device, e.g. struct mmc *
  * @priv: Private data for the device
  * @bl_len: Block length for reading in bytes
+ * @filename: Name of the fit image file.
  * @read: Function to call to read from the device
  */
 struct spl_load_info {
        void *dev;
        void *priv;
        int bl_len;
+       const char *filename;
        ulong (*read)(struct spl_load_info *load, ulong sector, ulong count,
                      void *buf);
 };
 
+/**
+ * spl_load_simple_fit() - Loads a fit image from a device.
+ * @info:      Structure containing the information required to load data.
+ * @sector:    Sector number where FIT image is located in the device
+ * @fdt:       Pointer to the copied FIT header.
+ *
+ * Reads the FIT image @sector in the device. Loads u-boot image to
+ * specified load address and copies the dtb to end of u-boot image.
+ * Returns 0 on success.
+ */
 int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fdt);
 
 #define SPL_COPY_PAYLOAD_ONLY  1