]> Git Repo - J-u-boot.git/blobdiff - tools/kwboot.c
Merge branch 'master-fdt' of https://source.denx.de/u-boot/custodians/u-boot-sh
[J-u-boot.git] / tools / kwboot.c
index f624edc7798f278f06f9b9defcc0ca8fb28b6501..6bef4610ff8f8b3d4b3c7db4b0a748d12aac5cfc 100644 (file)
@@ -61,7 +61,9 @@
  *   SPI-NOR or parallel-NOR. Despite the type name it really can be stored on
  *   parallel-NOR and cannot be stored on other SPI devices, like SPI-NAND.
  *   So it should have been named NOR image, not SPI image. This image type
- *   supports XIP - Execute In Place directly from NOR memory.
+ *   supports XIP - Execute In Place directly from NOR memory. Destination
+ *   address of the XIP image is set to 0xFFFFFFFF and execute address to the
+ *   absolute offset in bytes from the beginning of NOR memory.
  *
  * - IBR_HDR_NAND_ID (0x8B):
  *   NAND image can be stored either at any 2 MB aligned offset in the first
  *
  * - IBR_HDR_UART_ID (0x69):
  *   UART image can be transfered via xmodem protocol over first UART.
+ *   Unlike all other image types, header size stored in the image must be
+ *   multiply of the 128 bytes (for all other image types it can be any size)
+ *   and data part of the image does not have to contain 32-bit checksum
+ *   (all other image types must have valid 32-bit checksum in its data part).
+ *   And data size stored in the image is ignored. A38x BootROM determinates
+ *   size of the data part implicitly by the end of the xmodem transfer.
+ *   A38x BootROM has a bug which cause that BootROM loads data part of UART
+ *   image into RAM target address increased by one byte when source address
+ *   and header size stored in the image header are not same. So UART image
+ *   should be constructed in a way that there is no gap between header and
+ *   data part.
  *
  * - IBR_HDR_I2C_ID (0x4D):
  *   It is unknown for what kind of storage is used this image. It is not
@@ -1455,6 +1468,8 @@ kwboot_xmodem(int tty, const void *_img, size_t size, int baudrate)
         * followed by the header. So align header size to xmodem block size.
         */
        hdrsz += (KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ) % KWBOOT_XM_BLKSZ;
+       if (hdrsz > size)
+               hdrsz = size;
 
        pnum = 1;
 
@@ -1976,6 +1991,54 @@ _inject_baudrate_change_code(void *img, size_t *size, int for_data,
        }
 }
 
+static int
+kwboot_img_guess_sata_blksz(void *img, uint32_t blkoff, uint32_t data_size, size_t total_size)
+{
+       uint32_t sum, *ptr, *end;
+       int blksz;
+
+       /*
+        * Try all possible sector sizes which are power of two,
+        * at least 512 bytes and up to the 32 kB.
+        */
+       for (blksz = 512; blksz < 0x10000; blksz *= 2) {
+               if (blkoff * blksz > total_size ||
+                   blkoff * blksz + data_size > total_size ||
+                   data_size % 4)
+                       break;
+
+               /*
+                * Calculate data checksum and if it matches
+                * then tried blksz should be correct.
+                */
+               ptr = img + blkoff * blksz;
+               end = (void *)ptr + data_size - 4;
+               for (sum = 0; ptr < end; ptr++)
+                       sum += *ptr;
+
+               if (sum == *end)
+                       return blksz;
+       }
+
+       /* Fallback to 512 bytes */
+       return 512;
+}
+
+static const char *
+kwboot_img_type(uint8_t blockid)
+{
+       switch (blockid) {
+       case IBR_HDR_I2C_ID: return "I2C";
+       case IBR_HDR_SPI_ID: return "SPI";
+       case IBR_HDR_NAND_ID: return "NAND";
+       case IBR_HDR_SATA_ID: return "SATA";
+       case IBR_HDR_PEX_ID: return "PEX";
+       case IBR_HDR_UART_ID: return "UART";
+       case IBR_HDR_SDIO_ID: return "SDIO";
+       default: return "unknown";
+       }
+}
+
 static int
 kwboot_img_patch(void *img, size_t *size, int baudrate)
 {
@@ -1989,8 +2052,10 @@ kwboot_img_patch(void *img, size_t *size, int baudrate)
 
        hdr = img;
 
-       if (*size < sizeof(struct main_hdr_v1))
+       if (*size < sizeof(struct main_hdr_v1)) {
+               fprintf(stderr, "Invalid image header size\n");
                goto err;
+       }
 
        image_ver = kwbimage_version(img);
        if (image_ver != 0 && image_ver != 1) {
@@ -2000,18 +2065,24 @@ kwboot_img_patch(void *img, size_t *size, int baudrate)
 
        hdrsz = kwbheader_size(hdr);
 
-       if (*size < hdrsz)
+       if (*size < hdrsz) {
+               fprintf(stderr, "Invalid image header size\n");
                goto err;
+       }
+
+       kwboot_printv("Detected kwbimage v%d with %s boot signature\n", image_ver, kwboot_img_type(hdr->blockid));
 
        csum = kwboot_hdr_csum8(hdr) - hdr->checksum;
-       if (csum != hdr->checksum)
+       if (csum != hdr->checksum) {
+               fprintf(stderr, "Image has invalid header checksum stored in image header\n");
                goto err;
+       }
 
        srcaddr = le32_to_cpu(hdr->srcaddr);
 
        switch (hdr->blockid) {
        case IBR_HDR_SATA_ID:
-               hdr->srcaddr = cpu_to_le32(srcaddr * 512);
+               hdr->srcaddr = cpu_to_le32(srcaddr * kwboot_img_guess_sata_blksz(img, srcaddr, le32_to_cpu(hdr->blocksize), *size));
                break;
 
        case IBR_HDR_PEX_ID:
@@ -2022,15 +2093,21 @@ kwboot_img_patch(void *img, size_t *size, int baudrate)
        case IBR_HDR_SPI_ID:
                if (hdr->destaddr == cpu_to_le32(0xFFFFFFFF)) {
                        kwboot_printv("Patching destination and execution addresses from SPI/NOR XIP area to DDR area 0x00800000\n");
-                       hdr->destaddr = cpu_to_le32(0x00800000);
-                       hdr->execaddr = cpu_to_le32(0x00800000);
+                       hdr->destaddr = cpu_to_le32(0x00800000 + le32_to_cpu(hdr->srcaddr));
+                       hdr->execaddr = cpu_to_le32(0x00800000 + le32_to_cpu(hdr->execaddr));
                }
                break;
        }
 
-       if (hdrsz > le32_to_cpu(hdr->srcaddr) ||
-           *size < le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize))
+       if (hdrsz > le32_to_cpu(hdr->srcaddr)) {
+               fprintf(stderr, "Image has invalid data offset stored in image header\n");
+               goto err;
+       }
+
+       if (*size < le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize)) {
+               fprintf(stderr, "Image has invalid data size stored in image header\n");
                goto err;
+       }
 
        for_each_opt_hdr_v1 (ohdr, hdr) {
                if (!opt_hdr_v1_valid_size(ohdr, (const uint8_t *)hdr + hdrsz)) {
@@ -2050,6 +2127,8 @@ kwboot_img_patch(void *img, size_t *size, int baudrate)
                        goto err;
                }
                kwboot_img_grow_data_right(img, size, sizeof(uint32_t));
+               /* Update the 32-bit data checksum */
+               *kwboot_img_csum32_ptr(img) = kwboot_img_csum32(img);
        }
 
        if (!kwboot_img_has_ddr_init(img) &&
@@ -2139,6 +2218,29 @@ kwboot_img_patch(void *img, size_t *size, int baudrate)
 
                kwboot_printv("Aligning image header to Xmodem block size\n");
                kwboot_img_grow_hdr(img, size, grow);
+               hdrsz += grow;
+
+               /*
+                * kwbimage v1 contains header size field and for UART type it
+                * must be set to the aligned xmodem header size because BootROM
+                * rounds header size down to xmodem block size.
+                */
+               if (kwbimage_version(img) == 1) {
+                       hdr->headersz_msb = hdrsz >> 16;
+                       hdr->headersz_lsb = cpu_to_le16(hdrsz & 0xffff);
+               }
+       }
+
+       /* Header size and source address must be same for UART type due to A38x BootROM bug */
+       if (hdrsz != le32_to_cpu(hdr->srcaddr)) {
+               if (is_secure) {
+                       fprintf(stderr, "Cannot align image with secure header\n");
+                       goto err;
+               }
+
+               kwboot_printv("Removing gap between image header and data\n");
+               memmove(img + hdrsz, img + le32_to_cpu(hdr->srcaddr), le32_to_cpu(hdr->blocksize));
+               hdr->srcaddr = cpu_to_le32(hdrsz);
        }
 
        hdr->checksum = kwboot_hdr_csum8(hdr) - csum;
This page took 0.033439 seconds and 4 git commands to generate.