Merge tag 'efi-2020-01-rc2' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
[oweals/u-boot.git] / disk / part_efi.c
index b5928e5abec2018f92c852a26770084aba82d144..51fa4a74ab25b3801400d9383641d110b62e5efb 100644 (file)
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2008 RuggedCom, Inc.
  * Richard Retanubun <RichardRetanubun@RuggedCom.com>
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 /*
 #include <asm/unaligned.h>
 #include <common.h>
 #include <command.h>
+#include <fdtdec.h>
 #include <ide.h>
-#include <inttypes.h>
 #include <malloc.h>
 #include <memalign.h>
 #include <part_efi.h>
+#include <linux/compiler.h>
 #include <linux/ctype.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#ifdef HAVE_BLOCK_DEVICE
+/*
+ * GUID for basic data partions.
+ */
+static const efi_guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID;
+
+#ifdef CONFIG_HAVE_BLOCK_DEVICE
 /**
  * efi_crc32() - EFI version of crc32 function
  * @buf: buffer to calculate crc32 of
@@ -46,6 +51,8 @@ static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
 static gpt_entry *alloc_read_gpt_entries(struct blk_desc *dev_desc,
                                         gpt_header *pgpt_head);
 static int is_pte_valid(gpt_entry * pte);
+static int find_valid_gpt(struct blk_desc *dev_desc, gpt_header *gpt_head,
+                         gpt_entry **pgpt_pte);
 
 static char *print_efiname(gpt_entry *pte)
 {
@@ -61,7 +68,7 @@ static char *print_efiname(gpt_entry *pte)
        return name;
 }
 
-static efi_guid_t system_guid = PARTITION_SYSTEM_GUID;
+static const efi_guid_t system_guid = PARTITION_SYSTEM_GUID;
 
 static inline int is_bootable(gpt_entry *p)
 {
@@ -77,11 +84,11 @@ static int validate_gpt_header(gpt_header *gpt_h, lbaint_t lba,
        uint32_t calc_crc32;
 
        /* Check the GPT header signature */
-       if (le64_to_cpu(gpt_h->signature) != GPT_HEADER_SIGNATURE) {
+       if (le64_to_cpu(gpt_h->signature) != GPT_HEADER_SIGNATURE_UBOOT) {
                printf("%s signature is wrong: 0x%llX != 0x%llX\n",
                       "GUID Partition Table Header",
                       le64_to_cpu(gpt_h->signature),
-                      GPT_HEADER_SIGNATURE);
+                      GPT_HEADER_SIGNATURE_UBOOT);
                return -1;
        }
 
@@ -176,28 +183,39 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h)
  * Public Functions (include/part.h)
  */
 
+/*
+ * UUID is displayed as 32 hexadecimal digits, in 5 groups,
+ * separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters
+ */
+int get_disk_guid(struct blk_desc * dev_desc, char *guid)
+{
+       ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz);
+       gpt_entry *gpt_pte = NULL;
+       unsigned char *guid_bin;
+
+       /* This function validates AND fills in the GPT header and PTE */
+       if (find_valid_gpt(dev_desc, gpt_head, &gpt_pte) != 1)
+               return -EINVAL;
+
+       guid_bin = gpt_head->disk_guid.b;
+       uuid_bin_to_str(guid_bin, guid, UUID_STR_FORMAT_GUID);
+
+       /* Remember to free pte */
+       free(gpt_pte);
+       return 0;
+}
+
 void part_print_efi(struct blk_desc *dev_desc)
 {
        ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz);
        gpt_entry *gpt_pte = NULL;
        int i = 0;
-       char uuid[37];
+       char uuid[UUID_STR_LEN + 1];
        unsigned char *uuid_bin;
 
        /* This function validates AND fills in the GPT header and PTE */
-       if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA,
-                        gpt_head, &gpt_pte) != 1) {
-               printf("%s: *** ERROR: Invalid GPT ***\n", __func__);
-               if (is_gpt_valid(dev_desc, (dev_desc->lba - 1),
-                                gpt_head, &gpt_pte) != 1) {
-                       printf("%s: *** ERROR: Invalid Backup GPT ***\n",
-                              __func__);
-                       return;
-               } else {
-                       printf("%s: ***        Using Backup GPT ***\n",
-                              __func__);
-               }
-       }
+       if (find_valid_gpt(dev_desc, gpt_head, &gpt_pte) != 1)
+               return;
 
        debug("%s: gpt-entry at %p\n", __func__, gpt_pte);
 
@@ -246,19 +264,8 @@ int part_get_info_efi(struct blk_desc *dev_desc, int part,
        }
 
        /* This function validates AND fills in the GPT header and PTE */
-       if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA,
-                       gpt_head, &gpt_pte) != 1) {
-               printf("%s: *** ERROR: Invalid GPT ***\n", __func__);
-               if (is_gpt_valid(dev_desc, (dev_desc->lba - 1),
-                                gpt_head, &gpt_pte) != 1) {
-                       printf("%s: *** ERROR: Invalid Backup GPT ***\n",
-                              __func__);
-                       return -1;
-               } else {
-                       printf("%s: ***        Using Backup GPT ***\n",
-                              __func__);
-               }
-       }
+       if (find_valid_gpt(dev_desc, gpt_head, &gpt_pte) != 1)
+               return -1;
 
        if (part > le32_to_cpu(gpt_head->num_partition_entries) ||
            !is_pte_valid(&gpt_pte[part - 1])) {
@@ -275,8 +282,8 @@ int part_get_info_efi(struct blk_desc *dev_desc, int part,
                     - info->start;
        info->blksz = dev_desc->blksz;
 
-       sprintf((char *)info->name, "%s",
-                       print_efiname(&gpt_pte[part - 1]));
+       snprintf((char *)info->name, sizeof(info->name), "%s",
+                print_efiname(&gpt_pte[part - 1]));
        strcpy((char *)info->type, "U-Boot");
        info->bootable = is_bootable(&gpt_pte[part - 1]);
 #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
@@ -317,13 +324,22 @@ static int part_test_efi(struct blk_desc *dev_desc)
 static int set_protective_mbr(struct blk_desc *dev_desc)
 {
        /* Setup the Protective MBR */
-       ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, p_mbr, 1);
-       memset(p_mbr, 0, sizeof(*p_mbr));
-
+       ALLOC_CACHE_ALIGN_BUFFER_PAD(legacy_mbr, p_mbr, 1, dev_desc->blksz);
        if (p_mbr == NULL) {
                printf("%s: calloc failed!\n", __func__);
                return -1;
        }
+
+       /* Read MBR to backup boot code if it exists */
+       if (blk_dread(dev_desc, 0, 1, p_mbr) != 1) {
+               pr_err("** Can't read from device %d **\n", dev_desc->devnum);
+               return -1;
+       }
+
+       /* Clear all data in MBR except of backed up boot code */
+       memset((char *)p_mbr + MSDOS_MBR_BOOT_CODE_SIZE, 0, sizeof(*p_mbr) -
+                       MSDOS_MBR_BOOT_CODE_SIZE);
+
        /* Append signature */
        p_mbr->signature = MSDOS_MBR_SIGNATURE;
        p_mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT;
@@ -366,8 +382,8 @@ int write_gpt_table(struct blk_desc *dev_desc,
        if (blk_dwrite(dev_desc, 1, 1, gpt_h) != 1)
                goto err;
 
-       if (blk_dwrite(dev_desc, 2, pte_blk_cnt, gpt_e)
-           != pte_blk_cnt)
+       if (blk_dwrite(dev_desc, le64_to_cpu(gpt_h->partition_entry_lba),
+                      pte_blk_cnt, gpt_e) != pte_blk_cnt)
                goto err;
 
        prepare_backup_gpt_header(gpt_h);
@@ -388,11 +404,11 @@ int write_gpt_table(struct blk_desc *dev_desc,
        return -1;
 }
 
-int gpt_fill_pte(gpt_header *gpt_h, gpt_entry *gpt_e,
-               disk_partition_t *partitions, int parts)
+int gpt_fill_pte(struct blk_desc *dev_desc,
+                gpt_header *gpt_h, gpt_entry *gpt_e,
+                disk_partition_t *partitions, int parts)
 {
        lbaint_t offset = (lbaint_t)le64_to_cpu(gpt_h->first_usable_lba);
-       lbaint_t start;
        lbaint_t last_usable_lba = (lbaint_t)
                        le64_to_cpu(gpt_h->last_usable_lba);
        int i, k;
@@ -405,27 +421,44 @@ int gpt_fill_pte(gpt_header *gpt_h, gpt_entry *gpt_e,
        char *str_type_guid;
        unsigned char *bin_type_guid;
 #endif
+       size_t hdr_start = gpt_h->my_lba;
+       size_t hdr_end = hdr_start + 1;
+
+       size_t pte_start = gpt_h->partition_entry_lba;
+       size_t pte_end = pte_start +
+               gpt_h->num_partition_entries * gpt_h->sizeof_partition_entry /
+               dev_desc->blksz;
 
        for (i = 0; i < parts; i++) {
                /* partition starting lba */
-               start = partitions[i].start;
-               if (start && (start < offset)) {
-                       printf("Partition overlap\n");
-                       return -1;
-               }
+               lbaint_t start = partitions[i].start;
+               lbaint_t size = partitions[i].size;
+
                if (start) {
-                       gpt_e[i].starting_lba = cpu_to_le64(start);
-                       offset = start + partitions[i].size;
+                       offset = start + size;
                } else {
-                       gpt_e[i].starting_lba = cpu_to_le64(offset);
-                       offset += partitions[i].size;
+                       start = offset;
+                       offset += size;
                }
+
+               /*
+                * If our partition overlaps with either the GPT
+                * header, or the partition entry, reject it.
+                */
+               if (((start < hdr_end && hdr_start < (start + size)) ||
+                    (start < pte_end && pte_start < (start + size)))) {
+                       printf("Partition overlap\n");
+                       return -1;
+               }
+
+               gpt_e[i].starting_lba = cpu_to_le64(start);
+
                if (offset > (last_usable_lba + 1)) {
                        printf("Partitions layout exceds disk size\n");
                        return -1;
                }
                /* partition ending lba */
-               if ((i == parts - 1) && (partitions[i].size == 0))
+               if ((i == parts - 1) && (size == 0))
                        /* extend the last partition to maximuim */
                        gpt_e[i].ending_lba = gpt_h->last_usable_lba;
                else
@@ -444,19 +477,19 @@ int gpt_fill_pte(gpt_header *gpt_h, gpt_entry *gpt_e,
                } else {
                        /* default partition type GUID */
                        memcpy(bin_type_guid,
-                              &PARTITION_BASIC_DATA_GUID, 16);
+                              &partition_basic_data_guid, 16);
                }
 #else
                /* partition type GUID */
                memcpy(gpt_e[i].partition_type_guid.b,
-                       &PARTITION_BASIC_DATA_GUID, 16);
+                       &partition_basic_data_guid, 16);
 #endif
 
 #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
                str_uuid = partitions[i].uuid;
                bin_uuid = gpt_e[i].unique_partition_guid.b;
 
-               if (uuid_str_to_bin(str_uuid, bin_uuid, UUID_STR_FORMAT_STD)) {
+               if (uuid_str_to_bin(str_uuid, bin_uuid, UUID_STR_FORMAT_GUID)) {
                        printf("Partition no. %d: invalid guid: %s\n",
                                i, str_uuid);
                        return -1;
@@ -485,23 +518,72 @@ int gpt_fill_pte(gpt_header *gpt_h, gpt_entry *gpt_e,
                debug("%s: name: %s offset[%d]: 0x" LBAF
                      " size[%d]: 0x" LBAF "\n",
                      __func__, partitions[i].name, i,
-                     offset, i, partitions[i].size);
+                     offset, i, size);
        }
 
        return 0;
 }
 
+static uint32_t partition_entries_offset(struct blk_desc *dev_desc)
+{
+       uint32_t offset_blks = 2;
+       uint32_t __maybe_unused offset_bytes;
+       int __maybe_unused config_offset;
+
+#if defined(CONFIG_EFI_PARTITION_ENTRIES_OFF)
+       /*
+        * Some architectures require their SPL loader at a fixed
+        * address within the first 16KB of the disk.  To avoid an
+        * overlap with the partition entries of the EFI partition
+        * table, the first safe offset (in bytes, from the start of
+        * the disk) for the entries can be set in
+        * CONFIG_EFI_PARTITION_ENTRIES_OFF.
+        */
+       offset_bytes =
+               PAD_TO_BLOCKSIZE(CONFIG_EFI_PARTITION_ENTRIES_OFF, dev_desc);
+       offset_blks = offset_bytes / dev_desc->blksz;
+#endif
+
+#if defined(CONFIG_OF_CONTROL)
+       /*
+        * Allow the offset of the first partition entires (in bytes
+        * from the start of the device) to be specified as a property
+        * of the device tree '/config' node.
+        */
+       config_offset = fdtdec_get_config_int(gd->fdt_blob,
+                                             "u-boot,efi-partition-entries-offset",
+                                             -EINVAL);
+       if (config_offset != -EINVAL) {
+               offset_bytes = PAD_TO_BLOCKSIZE(config_offset, dev_desc);
+               offset_blks = offset_bytes / dev_desc->blksz;
+       }
+#endif
+
+       debug("efi: partition entries offset (in blocks): %d\n", offset_blks);
+
+       /*
+        * The earliest LBA this can be at is LBA#2 (i.e. right behind
+        * the (protective) MBR and the GPT header.
+        */
+       if (offset_blks < 2)
+               offset_blks = 2;
+
+       return offset_blks;
+}
+
 int gpt_fill_header(struct blk_desc *dev_desc, gpt_header *gpt_h,
                char *str_guid, int parts_count)
 {
-       gpt_h->signature = cpu_to_le64(GPT_HEADER_SIGNATURE);
+       gpt_h->signature = cpu_to_le64(GPT_HEADER_SIGNATURE_UBOOT);
        gpt_h->revision = cpu_to_le32(GPT_HEADER_REVISION_V1);
        gpt_h->header_size = cpu_to_le32(sizeof(gpt_header));
        gpt_h->my_lba = cpu_to_le64(1);
        gpt_h->alternate_lba = cpu_to_le64(dev_desc->lba - 1);
-       gpt_h->first_usable_lba = cpu_to_le64(34);
        gpt_h->last_usable_lba = cpu_to_le64(dev_desc->lba - 34);
-       gpt_h->partition_entry_lba = cpu_to_le64(2);
+       gpt_h->partition_entry_lba =
+               cpu_to_le64(partition_entries_offset(dev_desc));
+       gpt_h->first_usable_lba =
+               cpu_to_le64(le64_to_cpu(gpt_h->partition_entry_lba) + 32);
        gpt_h->num_partition_entries = cpu_to_le32(GPT_ENTRY_NUMBERS);
        gpt_h->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry));
        gpt_h->header_crc32 = 0;
@@ -516,25 +598,27 @@ int gpt_fill_header(struct blk_desc *dev_desc, gpt_header *gpt_h,
 int gpt_restore(struct blk_desc *dev_desc, char *str_disk_guid,
                disk_partition_t *partitions, int parts_count)
 {
-       int ret;
-
-       gpt_header *gpt_h = calloc(1, PAD_TO_BLOCKSIZE(sizeof(gpt_header),
-                                                      dev_desc));
+       gpt_header *gpt_h;
        gpt_entry *gpt_e;
+       int ret, size;
 
+       size = PAD_TO_BLOCKSIZE(sizeof(gpt_header), dev_desc);
+       gpt_h = malloc_cache_aligned(size);
        if (gpt_h == NULL) {
                printf("%s: calloc failed!\n", __func__);
                return -1;
        }
+       memset(gpt_h, 0, size);
 
-       gpt_e = calloc(1, PAD_TO_BLOCKSIZE(GPT_ENTRY_NUMBERS
-                                              * sizeof(gpt_entry),
-                                              dev_desc));
+       size = PAD_TO_BLOCKSIZE(GPT_ENTRY_NUMBERS * sizeof(gpt_entry),
+                               dev_desc);
+       gpt_e = malloc_cache_aligned(size);
        if (gpt_e == NULL) {
                printf("%s: calloc failed!\n", __func__);
                free(gpt_h);
                return -1;
        }
+       memset(gpt_e, 0, size);
 
        /* Generate Primary GPT header (LBA1) */
        ret = gpt_fill_header(dev_desc, gpt_h, str_disk_guid, parts_count);
@@ -542,7 +626,7 @@ int gpt_restore(struct blk_desc *dev_desc, char *str_disk_guid,
                goto err;
 
        /* Generate partition entries */
-       ret = gpt_fill_pte(gpt_h, gpt_e, partitions, parts_count);
+       ret = gpt_fill_pte(dev_desc, gpt_h, gpt_e, partitions, parts_count);
        if (ret)
                goto err;
 
@@ -555,9 +639,18 @@ err:
        return ret;
 }
 
-static void gpt_convert_efi_name_to_char(char *s, efi_char16_t *es, int n)
+/**
+ * gpt_convert_efi_name_to_char() - convert u16 string to char string
+ *
+ * TODO: this conversion only supports ANSI characters
+ *
+ * @s: target buffer
+ * @es:        u16 string to be converted
+ * @n: size of target buffer
+ */
+static void gpt_convert_efi_name_to_char(char *s, void *es, int n)
 {
-       char *ess = (char *)es;
+       char *ess = es;
        int i, j;
 
        memset(s, '\0', n);
@@ -583,6 +676,10 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head,
                       __func__);
                return -1;
        }
+
+       /* Free pte before allocating again */
+       free(*gpt_pte);
+
        if (is_gpt_valid(dev_desc, (dev_desc->lba - 1),
                         gpt_head, gpt_pte) != 1) {
                printf("%s: *** ERROR: Invalid Backup GPT ***\n",
@@ -610,7 +707,7 @@ int gpt_verify_partitions(struct blk_desc *dev_desc,
 
        for (i = 0; i < parts; i++) {
                if (i == gpt_head->num_partition_entries) {
-                       error("More partitions than allowed!\n");
+                       pr_err("More partitions than allowed!\n");
                        return -1;
                }
 
@@ -623,7 +720,7 @@ int gpt_verify_partitions(struct blk_desc *dev_desc,
 
                if (strncmp(efi_str, (char *)partitions[i].name,
                            sizeof(partitions->name))) {
-                       error("Partition name: %s does not match %s!\n",
+                       pr_err("Partition name: %s does not match %s!\n",
                              efi_str, (char *)partitions[i].name);
                        return -1;
                }
@@ -640,7 +737,7 @@ int gpt_verify_partitions(struct blk_desc *dev_desc,
                        if ((i == parts - 1) && (partitions[i].size == 0))
                                continue;
 
-                       error("Partition %s size: %llu does not match %llu!\n",
+                       pr_err("Partition %s size: %llu does not match %llu!\n",
                              efi_str, (unsigned long long)gpt_part_size,
                              (unsigned long long)partitions[i].size);
                        return -1;
@@ -661,7 +758,7 @@ int gpt_verify_partitions(struct blk_desc *dev_desc,
                      (unsigned long long)partitions[i].start);
 
                if (le64_to_cpu(gpt_e[i].starting_lba) != partitions[i].start) {
-                       error("Partition %s start: %llu does not match %llu!\n",
+                       pr_err("Partition %s start: %llu does not match %llu!\n",
                              efi_str, le64_to_cpu(gpt_e[i].starting_lba),
                              (unsigned long long)partitions[i].start);
                        return -1;
@@ -811,26 +908,53 @@ static int is_pmbr_valid(legacy_mbr * mbr)
  * gpt is a GPT header ptr, filled on return.
  * ptes is a PTEs ptr, filled on return.
  *
- * Description: returns 1 if valid,  0 on error.
+ * Description: returns 1 if valid,  0 on error, 2 if ignored header
  * If valid, returns pointers to PTEs.
  */
 static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
                        gpt_header *pgpt_head, gpt_entry **pgpt_pte)
 {
+       /* Confirm valid arguments prior to allocation. */
        if (!dev_desc || !pgpt_head) {
                printf("%s: Invalid Argument(s)\n", __func__);
                return 0;
        }
 
+       ALLOC_CACHE_ALIGN_BUFFER_PAD(legacy_mbr, mbr, 1, dev_desc->blksz);
+
+       /* Read MBR Header from device */
+       if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1) {
+               printf("*** ERROR: Can't read MBR header ***\n");
+               return 0;
+       }
+
        /* Read GPT Header from device */
        if (blk_dread(dev_desc, (lbaint_t)lba, 1, pgpt_head) != 1) {
                printf("*** ERROR: Can't read GPT header ***\n");
                return 0;
        }
 
+       /* Invalid but nothing to yell about. */
+       if (le64_to_cpu(pgpt_head->signature) == GPT_HEADER_CHROMEOS_IGNORE) {
+               debug("ChromeOS 'IGNOREME' GPT header found and ignored\n");
+               return 2;
+       }
+
        if (validate_gpt_header(pgpt_head, (lbaint_t)lba, dev_desc->lba))
                return 0;
 
+       if (dev_desc->sig_type == SIG_TYPE_NONE) {
+               efi_guid_t empty = {};
+               if (memcmp(&pgpt_head->disk_guid, &empty, sizeof(empty))) {
+                       dev_desc->sig_type = SIG_TYPE_GUID;
+                       memcpy(&dev_desc->guid_sig, &pgpt_head->disk_guid,
+                             sizeof(empty));
+               } else if (mbr->unique_mbr_signature != 0) {
+                       dev_desc->sig_type = SIG_TYPE_MBR;
+                       dev_desc->mbr_sig = mbr->unique_mbr_signature;
+               }
+       }
+
        /* Read and allocate Partition Table Entries */
        *pgpt_pte = alloc_read_gpt_entries(dev_desc, pgpt_head);
        if (*pgpt_pte == NULL) {
@@ -847,6 +971,40 @@ static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
        return 1;
 }
 
+/**
+ * find_valid_gpt() - finds a valid GPT header and PTEs
+ *
+ * gpt is a GPT header ptr, filled on return.
+ * ptes is a PTEs ptr, filled on return.
+ *
+ * Description: returns 1 if found a valid gpt,  0 on error.
+ * If valid, returns pointers to PTEs.
+ */
+static int find_valid_gpt(struct blk_desc *dev_desc, gpt_header *gpt_head,
+                         gpt_entry **pgpt_pte)
+{
+       int r;
+
+       r = is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head,
+                        pgpt_pte);
+
+       if (r != 1) {
+               if (r != 2)
+                       printf("%s: *** ERROR: Invalid GPT ***\n", __func__);
+
+               if (is_gpt_valid(dev_desc, (dev_desc->lba - 1), gpt_head,
+                                pgpt_pte) != 1) {
+                       printf("%s: *** ERROR: Invalid Backup GPT ***\n",
+                              __func__);
+                       return 0;
+               }
+               if (r != 2)
+                       printf("%s: ***        Using Backup GPT ***\n",
+                              __func__);
+       }
+       return 1;
+}
+
 /**
  * alloc_read_gpt_entries(): reads partition entries from disk
  * @dev_desc