]> git.ipfire.org Git - thirdparty/u-boot.git/blobdiff - cmd/mvebu/bubt.c
Merge branch 'next'
[thirdparty/u-boot.git] / cmd / mvebu / bubt.c
index 825d4beb899c7e3eb5b3e58508b506a76d4314bc..49797b23144b3bcac7ca269f137e84be0ab74a28 100644 (file)
 #include <vsprintf.h>
 #include <errno.h>
 #include <dm.h>
+#include <fuse.h>
+#include <mach/efuse.h>
 
 #include <spi_flash.h>
 #include <spi.h>
 #include <nand.h>
+#include <scsi.h>
 #include <usb.h>
 #include <fs.h>
 #include <mmc.h>
@@ -121,6 +124,17 @@ struct a38x_main_hdr_v1 {
        u8  checksum;              /* 0x1F      */
 };
 
+/*
+ * Header for the optional headers, version 1 (Armada 370/XP/375/38x/39x)
+ */
+struct a38x_opt_hdr_v1 {
+       u8      headertype;
+       u8      headersz_msb;
+       u16     headersz_lsb;
+       u8      data[0];
+};
+#define A38X_OPT_HDR_V1_SECURE_TYPE    0x1
+
 struct a38x_boot_mode {
        unsigned int id;
        const char *name;
@@ -176,6 +190,11 @@ static int mmc_burn_image(size_t image_size)
 #ifdef CONFIG_BLK
        struct blk_desc *blk_desc;
 #endif
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+       u8              part;
+       u8              orig_part;
+#endif
+
        mmc = find_mmc_device(mmc_dev_num);
        if (!mmc) {
                printf("No SD/MMC/eMMC card found\n");
@@ -189,6 +208,38 @@ static int mmc_burn_image(size_t image_size)
                return err;
        }
 
+#ifdef CONFIG_BLK
+       blk_desc = mmc_get_blk_desc(mmc);
+       if (!blk_desc) {
+               printf("Error - failed to obtain block descriptor\n");
+               return -ENODEV;
+       }
+#endif
+
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+#ifdef CONFIG_BLK
+       orig_part = blk_desc->hwpart;
+#else
+       orig_part = mmc->block_dev.hwpart;
+#endif
+
+       part = (mmc->part_config >> 3) & PART_ACCESS_MASK;
+
+       if (part == 7)
+               part = 0;
+
+#ifdef CONFIG_BLK
+       err = blk_dselect_hwpart(blk_desc, part);
+#else
+       err = mmc_switch_part(mmc, part);
+#endif
+
+       if (err) {
+               printf("Error - MMC partition switch failed\n");
+               return err;
+       }
+#endif
+
        /* SD reserves LBA-0 for MBR and boots from LBA-1,
         * MMC/eMMC boots from LBA-0
         */
@@ -198,11 +249,6 @@ static int mmc_burn_image(size_t image_size)
        if (image_size % mmc->write_bl_len)
                blk_count += 1;
 
-       blk_desc = mmc_get_blk_desc(mmc);
-       if (!blk_desc) {
-               printf("Error - failed to obtain block descriptor\n");
-               return -ENODEV;
-       }
        blk_written = blk_dwrite(blk_desc, start_lba, blk_count,
                                 (void *)get_load_addr());
 #else
@@ -214,6 +260,17 @@ static int mmc_burn_image(size_t image_size)
                                                 start_lba, blk_count,
                                                 (void *)get_load_addr());
 #endif /* CONFIG_BLK */
+
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+#ifdef CONFIG_BLK
+       err = blk_dselect_hwpart(blk_desc, orig_part);
+#else
+       err = mmc_switch_part(mmc, orig_part);
+#endif
+       if (err)
+               printf("Error - MMC failed to switch back to original partition\n");
+#endif
+
        if (blk_written != blk_count) {
                printf("Error - written %#lx blocks\n", blk_written);
                return -ENOSPC;
@@ -277,6 +334,143 @@ static int is_mmc_active(void)
 }
 #endif /* CONFIG_DM_MMC */
 
+/********************************************************************
+ *     SATA services
+ ********************************************************************/
+#if defined(CONFIG_SCSI) && defined(CONFIG_BLK)
+static int sata_burn_image(size_t image_size)
+{
+#if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_32BIT)
+       lbaint_t        start_lba;
+       lbaint_t        blk_count;
+       ulong           blk_written;
+       struct blk_desc *blk_desc;
+#ifdef CONFIG_ARMADA_3700
+       struct disk_partition info;
+       int             part;
+#endif
+
+       scsi_scan(false);
+
+       blk_desc = blk_get_devnum_by_uclass_id(UCLASS_SCSI, 0);
+       if (!blk_desc)
+               return -ENODEV;
+
+#ifdef CONFIG_ARMADA_3700
+       /*
+        * 64-bit Armada 3700 BootROM loads SATA firmware from
+        * GPT 'Marvell Armada 3700 Boot partition' or from
+        * MBR 'M' (0x4d) partition.
+        */
+       switch (blk_desc->part_type) {
+       case PART_TYPE_DOS:
+               for (part = 1; part <= 4; part++) {
+                       info.sys_ind = 0;
+                       if (part_get_info(blk_desc, part, &info))
+                               continue;
+                       if (info.sys_ind == 'M')
+                               break;
+               }
+               if (part > 4) {
+                       printf("Error - cannot find MBR 'M' (0x4d) partition on SATA disk\n");
+                       return -ENODEV;
+               }
+               start_lba = info.start;
+               break;
+       case PART_TYPE_EFI:
+               for (part = 1; part <= 64; part++) {
+                       info.type_guid[0] = 0;
+                       if (part_get_info(blk_desc, part, &info))
+                               continue;
+                       /* Check for GPT type GUID of 'Marvell Armada 3700 Boot partition' */
+                       if (strcmp(info.type_guid, "6828311A-BA55-42A4-BCDE-A89BB5EDECAE") == 0)
+                               break;
+               }
+               if (part > 64) {
+                       printf("Error - cannot find GPT 'Marvell Armada 3700 Boot partition' on SATA disk\n");
+                       return -ENODEV;
+               }
+               start_lba = info.start;
+               break;
+       default:
+               printf("Error - no partitions on SATA disk\n");
+               return -ENODEV;
+       }
+#else
+       /* 32-bit Armada BootROM loads SATA firmware from the sector 1. */
+       start_lba = 1;
+#endif
+
+       blk_count = image_size / blk_desc->blksz;
+       if (image_size % blk_desc->blksz)
+               blk_count += 1;
+
+       blk_written = blk_dwrite(blk_desc, start_lba, blk_count,
+                                (void *)get_load_addr());
+
+       if (blk_written != blk_count) {
+               printf("Error - written %#lx blocks\n", blk_written);
+               return -ENOSPC;
+       }
+
+       printf("Done!\n");
+       return 0;
+#else
+       return -ENODEV;
+#endif
+}
+
+static size_t sata_read_file(const char *file_name)
+{
+       loff_t act_read = 0;
+       struct udevice *dev;
+       int rc;
+
+       /* try to recognize storage devices immediately */
+       scsi_scan(false);
+
+       /* Try to recognize storage devices immediately */
+       blk_first_device(UCLASS_SCSI, &dev);
+       if (!dev) {
+               printf("Error: SATA device not found\n");
+               return 0;
+       }
+
+       /* Always load from scsi 0 */
+       if (fs_set_blk_dev("scsi", "0", FS_TYPE_ANY)) {
+               printf("Error: SATA 0 not found\n");
+               return 0;
+       }
+
+       /* Perfrom file read */
+       rc = fs_read(file_name, get_load_addr(), 0, 0, &act_read);
+       if (rc)
+               return 0;
+
+       return act_read;
+}
+
+static int is_sata_active(void)
+{
+       return 1;
+}
+#else /* CONFIG_SCSI */
+static int sata_burn_image(size_t image_size)
+{
+       return -ENODEV;
+}
+
+static size_t sata_read_file(const char *file_name)
+{
+       return 0;
+}
+
+static int is_sata_active(void)
+{
+       return 0;
+}
+#endif /* CONFIG_SCSI */
+
 /********************************************************************
  *     SPI services
  ********************************************************************/
@@ -486,16 +680,18 @@ enum bubt_devices {
        BUBT_DEV_NET = 0,
        BUBT_DEV_USB,
        BUBT_DEV_MMC,
+       BUBT_DEV_SATA,
        BUBT_DEV_SPI,
        BUBT_DEV_NAND,
 
        BUBT_MAX_DEV
 };
 
-struct bubt_dev bubt_devs[BUBT_MAX_DEV] = {
+static struct bubt_dev bubt_devs[BUBT_MAX_DEV] = {
        {"tftp", tftp_read_file, NULL, is_tftp_active},
        {"usb",  usb_read_file,  NULL, is_usb_active},
        {"mmc",  mmc_read_file,  mmc_burn_image, is_mmc_active},
+       {"sata", sata_read_file, sata_burn_image,  is_sata_active},
        {"spi",  NULL, spi_burn_image,  is_spi_active},
        {"nand", NULL, nand_burn_image, is_nand_active},
 };
@@ -511,7 +707,7 @@ static int bubt_write_file(struct bubt_dev *dst, size_t image_size)
 }
 
 #if defined(CONFIG_ARMADA_8K)
-u32 do_checksum32(u32 *start, int32_t len)
+static u32 do_checksum32(u32 *start, int32_t len)
 {
        u32 sum = 0;
        u32 *startp = start;
@@ -529,9 +725,8 @@ static int check_image_header(void)
 {
        struct mvebu_image_header *hdr =
                        (struct mvebu_image_header *)get_load_addr();
-       u32 header_len = hdr->prolog_size;
        u32 checksum;
-       u32 checksum_ref = hdr->prolog_checksum;
+       u32 checksum_ref;
 
        /*
         * For now compare checksum, and magic. Later we can
@@ -543,18 +738,23 @@ static int check_image_header(void)
                return -ENOEXEC;
        }
 
-       /* The checksum value is discarded from checksum calculation */
-       hdr->prolog_checksum = 0;
+       checksum_ref = hdr->prolog_checksum;
+       checksum = do_checksum32((u32 *)hdr, hdr->prolog_size);
+       checksum -= hdr->prolog_checksum;
+       if (checksum != checksum_ref) {
+               printf("Error: Bad Prolog checksum. 0x%x != 0x%x\n",
+                      checksum, checksum_ref);
+               return -ENOEXEC;
+       }
 
-       checksum = do_checksum32((u32 *)hdr, header_len);
+       checksum_ref = hdr->boot_image_checksum;
+       checksum = do_checksum32((u32 *)((u8 *)hdr + hdr->prolog_size), hdr->boot_image_size);
        if (checksum != checksum_ref) {
                printf("Error: Bad Image checksum. 0x%x != 0x%x\n",
                       checksum, checksum_ref);
                return -ENOEXEC;
        }
 
-       /* Restore the checksum before writing */
-       hdr->prolog_checksum = checksum_ref;
        printf("Image checksum...OK!\n");
 
        return 0;
@@ -688,27 +888,94 @@ static uint8_t image_checksum8(const void *start, size_t len)
        return csum;
 }
 
+static uint32_t image_checksum32(const void *start, size_t len)
+{
+       u32 csum = 0;
+       const u32 *p = start;
+
+       while (len) {
+               csum += *p;
+               ++p;
+               len -= sizeof(u32);
+       }
+
+       return csum;
+}
+
 static int check_image_header(void)
 {
        u8 checksum;
+       u32 checksum32, exp_checksum32;
+       u32 offset, size;
        const struct a38x_main_hdr_v1 *hdr =
                (struct a38x_main_hdr_v1 *)get_load_addr();
-       const size_t image_size = a38x_header_size(hdr);
+       const size_t hdr_size = a38x_header_size(hdr);
 
-       if (!image_size)
+       if (!hdr_size)
                return -ENOEXEC;
 
-       checksum = image_checksum8(hdr, image_size);
+       checksum = image_checksum8(hdr, hdr_size);
        checksum -= hdr->checksum;
        if (checksum != hdr->checksum) {
-               printf("Error: Bad A38x image checksum. 0x%x != 0x%x\n",
+               printf("Error: Bad A38x image header checksum. 0x%x != 0x%x\n",
                       checksum, hdr->checksum);
                return -ENOEXEC;
        }
 
+       offset = le32_to_cpu(hdr->srcaddr);
+       size = le32_to_cpu(hdr->blocksize);
+
+       if (hdr->blockid == 0x78) /* SATA id */
+               offset *= 512;
+
+       if (offset % 4 != 0 || size < 4 || size % 4 != 0) {
+               printf("Error: Bad A38x image blocksize.\n");
+               return -ENOEXEC;
+       }
+
+       checksum32 = image_checksum32((u8 *)hdr + offset, size - 4);
+       exp_checksum32 = *(u32 *)((u8 *)hdr + offset + size - 4);
+       if (checksum32 != exp_checksum32) {
+               printf("Error: Bad A38x image data checksum. 0x%08x != 0x%08x\n",
+                      checksum32, exp_checksum32);
+               return -ENOEXEC;
+       }
+
        printf("Image checksum...OK!\n");
        return 0;
 }
+
+#if defined(CONFIG_ARMADA_38X)
+static int a38x_image_is_secure(const struct a38x_main_hdr_v1 *hdr)
+{
+       const size_t hdr_size = a38x_header_size(hdr);
+       struct a38x_opt_hdr_v1 *ohdr;
+       u32 ohdr_size;
+
+       if (hdr->version != 1)
+               return 0;
+
+       if (!hdr->ext)
+               return 0;
+
+       ohdr = (struct a38x_opt_hdr_v1 *)(hdr + 1);
+       do {
+               if (ohdr->headertype == A38X_OPT_HDR_V1_SECURE_TYPE)
+                       return 1;
+
+               ohdr_size = (ohdr->headersz_msb << 16) | le16_to_cpu(ohdr->headersz_lsb);
+
+               if (!*((u8 *)ohdr + ohdr_size - 4))
+                       break;
+
+               ohdr = (struct a38x_opt_hdr_v1 *)((u8 *)ohdr + ohdr_size);
+               if ((u8 *)ohdr >= (u8 *)hdr + hdr_size)
+                       break;
+       } while (1);
+
+       return 0;
+}
+#endif
 #else /* Not ARMADA? */
 static int check_image_header(void)
 {
@@ -717,20 +984,63 @@ static int check_image_header(void)
 }
 #endif
 
+#if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_38X)
+static u64 fuse_read_u64(u32 bank)
+{
+       u32 val[2];
+       int ret;
+
+       ret = fuse_read(bank, 0, &val[0]);
+       if (ret < 0)
+               return -1;
+
+       ret = fuse_read(bank, 1, &val[1]);
+       if (ret < 0)
+               return -1;
+
+       return ((u64)val[1] << 32) | val[0];
+}
+#endif
+
+#if defined(CONFIG_ARMADA_3700)
+static inline u8 maj3(u8 val)
+{
+       /* return majority vote of 3 bits */
+       return ((val & 0x7) == 3 || (val & 0x7) > 4) ? 1 : 0;
+}
+#endif
+
 static int bubt_check_boot_mode(const struct bubt_dev *dst)
 {
 #if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_32BIT)
        int mode;
+#if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_38X)
+       int secure_mode;
+#endif
 #if defined(CONFIG_ARMADA_3700)
        const struct tim_boot_flash_sign *boot_modes = tim_boot_flash_signs;
        const struct common_tim_data *hdr =
                (struct common_tim_data *)get_load_addr();
        u32 id = hdr->boot_flash_sign;
+       int is_secure = hdr->trusted != 0;
+       u64 otp_secure_bits = fuse_read_u64(1);
+       int otp_secure_boot = ((maj3(otp_secure_bits >> 0) << 0) |
+                              (maj3(otp_secure_bits >> 4) << 1)) == 2;
+       unsigned int otp_boot_device = (maj3(otp_secure_bits >> 48) << 0) |
+                                      (maj3(otp_secure_bits >> 52) << 1) |
+                                      (maj3(otp_secure_bits >> 56) << 2) |
+                                      (maj3(otp_secure_bits >> 60) << 3);
 #elif defined(CONFIG_ARMADA_32BIT)
        const struct a38x_boot_mode *boot_modes = a38x_boot_modes;
        const struct a38x_main_hdr_v1 *hdr =
                (struct a38x_main_hdr_v1 *)get_load_addr();
        u32 id = hdr->blockid;
+#if defined(CONFIG_ARMADA_38X)
+       int is_secure = a38x_image_is_secure(hdr);
+       u64 otp_secure_bits = fuse_read_u64(EFUSE_LINE_SECURE_BOOT);
+       int otp_secure_boot = otp_secure_bits & 0x1;
+       unsigned int otp_boot_device = (otp_secure_bits >> 8) & 0x7;
+#endif
 #endif
 
        for (mode = 0; boot_modes[mode].name; mode++) {
@@ -743,15 +1053,42 @@ static int bubt_check_boot_mode(const struct bubt_dev *dst)
                return -ENOEXEC;
        }
 
-       if (strcmp(boot_modes[mode].name, dst->name) == 0)
-               return 0;
+       if (strcmp(boot_modes[mode].name, dst->name) != 0) {
+               printf("Error: image meant to be booted from \"%s\", not \"%s\"!\n",
+                      boot_modes[mode].name, dst->name);
+               return -ENOEXEC;
+       }
 
-       printf("Error: image meant to be booted from \"%s\", not \"%s\"!\n",
-              boot_modes[mode].name, dst->name);
-       return -ENOEXEC;
-#else
-       return 0;
+#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_3700)
+       if (otp_secure_bits == (u64)-1) {
+               printf("Error: cannot read OTP secure bits\n");
+               return -ENOEXEC;
+       } else {
+               if (otp_secure_boot && !is_secure) {
+                       printf("Error: secure boot is enabled in OTP but image does not have secure boot header!\n");
+                       return -ENOEXEC;
+               } else if (!otp_secure_boot && is_secure) {
+#if defined(CONFIG_ARMADA_3700)
+                       /*
+                        * Armada 3700 BootROM rejects trusted image when secure boot is not enabled.
+                        * Armada 385 BootROM accepts image with secure boot header also when secure boot is not enabled.
+                        */
+                       printf("Error: secure boot is disabled in OTP but image has secure boot header!\n");
+                       return -ENOEXEC;
 #endif
+               } else if (otp_boot_device && otp_boot_device != id) {
+                       for (secure_mode = 0; boot_modes[secure_mode].name; secure_mode++) {
+                               if (boot_modes[secure_mode].id == otp_boot_device)
+                                       break;
+                       }
+                       printf("Error: boot source is set to \"%s\" in OTP but image is for \"%s\"!\n",
+                              boot_modes[secure_mode].name ?: "unknown", dst->name);
+                       return -ENOEXEC;
+               }
+       }
+#endif
+#endif
+       return 0;
 }
 
 static int bubt_verify(const struct bubt_dev *dst)
@@ -810,7 +1147,7 @@ static int bubt_is_dev_active(struct bubt_dev *dev)
        return 1;
 }
 
-struct bubt_dev *find_bubt_dev(char *dev_name)
+static struct bubt_dev *find_bubt_dev(char *dev_name)
 {
        int dev;
 
@@ -831,12 +1168,14 @@ struct bubt_dev *find_bubt_dev(char *dev_name)
 #define DEFAULT_BUBT_DST "nand"
 #elif defined(CONFIG_MVEBU_MMC_BOOT)
 #define DEFAULT_BUBT_DST "mmc"
+#elif defined(CONFIG_MVEBU_SATA_BOOT)
+#define DEFAULT_BUBT_DST "sata"
 #else
 #define DEFAULT_BUBT_DST "error"
 #endif
 #endif /* DEFAULT_BUBT_DST */
 
-int do_bubt_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+static int do_bubt_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
        struct bubt_dev *src, *dst;
        size_t image_size;
@@ -908,8 +1247,8 @@ U_BOOT_CMD(
        "Burn a u-boot image to flash",
        "[file-name] [destination [source]]\n"
        "\t-file-name     The image file name to burn. Default = " CONFIG_MVEBU_UBOOT_DFLT_NAME "\n"
-       "\t-destination   Flash to burn to [spi, nand, mmc]. Default = " DEFAULT_BUBT_DST "\n"
-       "\t-source        The source to load image from [tftp, usb, mmc]. Default = " DEFAULT_BUBT_SRC "\n"
+       "\t-destination   Flash to burn to [spi, nand, mmc, sata]. Default = " DEFAULT_BUBT_DST "\n"
+       "\t-source        The source to load image from [tftp, usb, mmc, sata]. Default = " DEFAULT_BUBT_SRC "\n"
        "Examples:\n"
        "\tbubt - Burn flash-image.bin from tftp to active boot device\n"
        "\tbubt flash-image-new.bin nand - Burn flash-image-new.bin from tftp to NAND flash\n"