+// 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>
+#include <u-boot/crc.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
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)
{
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)
+static int get_bootable(gpt_entry *p)
{
- return p->attributes.fields.legacy_bios_bootable ||
- !memcmp(&(p->partition_type_guid), &system_guid,
- sizeof(efi_guid_t));
+ int ret = 0;
+
+ if (!memcmp(&p->partition_type_guid, &system_guid, sizeof(efi_guid_t)))
+ ret |= PART_EFI_SYSTEM_PARTITION;
+ if (p->attributes.fields.legacy_bios_bootable)
+ ret |= PART_BOOTABLE;
+ return ret;
}
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;
}
* 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);
}
/* 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])) {
- 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]);
+ info->bootable = get_bootable(&gpt_pte[part - 1]);
#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
uuid_bin_to_str(gpt_pte[part - 1].unique_partition_guid.b, info->uuid,
UUID_STR_FORMAT_GUID);
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) {
- error("** Can't read from device %d **\n", dev_desc->devnum);
+ 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;
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);
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;
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
} 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)
memset(&gpt_e[i].attributes, 0,
sizeof(gpt_entry_attributes));
- if (partitions[i].bootable)
+ if (partitions[i].bootable & PART_BOOTABLE)
gpt_e[i].attributes.fields.legacy_bios_bootable = 1;
/* partition name */
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;
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);
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;
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);
__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",
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;
}
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;
}
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;
(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;
* 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) {
}
/**
+ * 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
* @gpt - GPT header