X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=disk%2Fpart_efi.c;h=de3491821f082e80de02587e97570ce8b5954c78;hb=d16c9d0b3729b1b120caa1b9836660c3bbcd7b87;hp=b5928e5abec2018f92c852a26770084aba82d144;hpb=c83a824e62277162ad35f52879b2316902c0eff5;p=oweals%2Fu-boot.git diff --git a/disk/part_efi.c b/disk/part_efi.c index b5928e5abe..de3491821f 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -1,8 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2008 RuggedCom, Inc. * Richard Retanubun - * - * SPDX-License-Identifier: GPL-2.0+ */ /* @@ -10,19 +9,31 @@ * when CONFIG_SYS_64BIT_LBA is not defined, lbaint_t is 32 bits; this * limits the maximum size of addressable storage to < 2 Terra Bytes */ -#include #include +#include +#include +#include +#include +#include +#include #include +#include #include -#include #include #include #include +#include #include +#include 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 +57,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,13 +74,17 @@ 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, @@ -77,11 +94,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 +193,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); @@ -234,7 +262,7 @@ void part_print_efi(struct blk_desc *dev_desc) } int part_get_info_efi(struct blk_desc *dev_desc, int part, - disk_partition_t *info) + struct disk_partition *info) { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); gpt_entry *gpt_pte = NULL; @@ -246,19 +274,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,10 +292,10 @@ 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]); + 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); @@ -317,13 +334,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 +392,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 +414,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, + struct disk_partition *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 +431,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 +487,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; @@ -467,7 +510,7 @@ int gpt_fill_pte(gpt_header *gpt_h, gpt_entry *gpt_e, 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 */ @@ -485,23 +528,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; @@ -514,27 +606,29 @@ 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) + struct disk_partition *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 +636,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 +649,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 +686,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", @@ -594,7 +701,7 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, } int gpt_verify_partitions(struct blk_desc *dev_desc, - disk_partition_t *partitions, int parts, + struct disk_partition *partitions, int parts, gpt_header *gpt_head, gpt_entry **gpt_pte) { char efi_str[PARTNAME_SZ + 1]; @@ -610,7 +717,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 +730,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 +747,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 +768,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 +918,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 +981,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