From: Patrick Delaunay <patrick.delau...@st.com> Signed-off-by: Patrick Delaunay <patrick.delau...@st.com> Signed-off-by: Patrick Delaunay <patrick.delauna...@gmail.com> ---
Kconfig | 12 ++ cmd/gpt.c | 98 ++++++++-- cmd/mtdparts.c | 103 ++++++++++- cmd/part.c | 48 ++++- disk/part_efi.c | 526 ++++++++++++++++++++++++++++++++++++++++++++++++----- doc/README.gpt.mtd | 189 +++++++++++++++++++ include/part.h | 13 +- include/uuid.h | 1 + lib/uuid.c | 33 ++++ 9 files changed, 944 insertions(+), 79 deletions(-) create mode 100644 doc/README.gpt.mtd diff --git a/Kconfig b/Kconfig index 1263d0b..c2388e1 100644 --- a/Kconfig +++ b/Kconfig @@ -335,6 +335,18 @@ config ARCH_FIXUP_FDT endmenu # Boot images +config EFI_PARTITION_MTD + bool "Support GPT over MTD" + help + The GPT partition is normally defined only for block device with + built-in controller which manage flash translation layer + This option activate the GPT partition support over RAW device + using the MTD framework + - manage partition over MTD devices (as flash: NOR and NAND) + - extract MTD information + - update command gpt, mtdparts and part + NB: depends on EFI_PARTITION + source "common/Kconfig" source "cmd/Kconfig" diff --git a/cmd/gpt.c b/cmd/gpt.c index 897596a..9e398e9 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,10 @@ #include <div64.h> #include <memalign.h> +#ifdef CONFIG_EFI_PARTITION_MTD +#include <linux/mtd/mtd.h> +#endif + #ifndef CONFIG_PARTITION_UUIDS #error CONFIG_PARTITION_UUIDS must be enabled for CONFIG_CMD_GPT to be enabled #endif @@ -168,7 +172,8 @@ static bool found_key(const char *str, const char *key) * @return - zero on success, otherwise error * */ -static int set_gpt_info(struct blk_desc *dev_desc, +static int set_gpt_info(unsigned int lba, + unsigned int blksz, const char *str_part, char **str_disk_guid, disk_partition_t **partitions, @@ -183,8 +188,7 @@ static int set_gpt_info(struct blk_desc *dev_desc, uint64_t size_ll, start_ll; lbaint_t offset = 0; - debug("%s: lba num: 0x%x %d\n", __func__, - (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); + debug("%s: lba num: 0x%x %d\n", __func__, lba, lba); if (str_part == NULL) return -1; @@ -302,7 +306,7 @@ static int set_gpt_info(struct blk_desc *dev_desc, parts[i].size = 0; } else { size_ll = ustrtoull(p, &p, 0); - parts[i].size = lldiv(size_ll, dev_desc->blksz); + parts[i].size = lldiv(size_ll, blksz); } free(val); @@ -313,7 +317,7 @@ static int set_gpt_info(struct blk_desc *dev_desc, if (extract_env(val, &p)) p = val; start_ll = ustrtoull(p, &p, 0); - parts[i].start = lldiv(start_ll, dev_desc->blksz); + parts[i].start = lldiv(start_ll, blksz); free(val); } @@ -337,6 +341,16 @@ err: return errno; } +static void print_gpt_info_err(int ret) +{ + if (ret == -1) + printf("No partition list provided\n"); + if (ret == -2) + printf("Missing disk guid\n"); + if ((ret == -3) || (ret == -4)) + printf("Partition list incomplete\n"); +} + static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part) { int ret; @@ -344,16 +358,13 @@ static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part) u8 part_count = 0; disk_partition_t *partitions = NULL; + if (!str_part) + return -1; /* fill partitions */ - ret = set_gpt_info(blk_dev_desc, str_part, + ret = set_gpt_info(blk_dev_desc->lba, blk_dev_desc->blksz, str_part, &str_disk_guid, &partitions, &part_count); if (ret) { - if (ret == -1) - printf("No partition list provided\n"); - if (ret == -2) - printf("Missing disk guid\n"); - if ((ret == -3) || (ret == -4)) - printf("Partition list incomplete\n"); + print_gpt_info_err(ret); return -1; } @@ -376,7 +387,7 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) int ret = 0; /* fill partitions */ - ret = set_gpt_info(blk_dev_desc, str_part, + ret = set_gpt_info(blk_dev_desc->lba, blk_dev_desc->blksz, str_part, &str_disk_guid, &partitions, &part_count); if (ret) { if (ret == -1) { @@ -402,6 +413,35 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; } +#ifdef CONFIG_EFI_PARTITION_MTD +static int gpt_default_mtd(struct mtd_info *mtd_info, const char *str_part) +{ + int ret; + char *str_disk_guid; + u8 part_count = 0; + disk_partition_t *partitions = NULL; + unsigned int lba = lldiv(mtd_info->size, MTD_LBA_SIZE); + + if (!str_part) + return -1; + + /* fill partitions */ + ret = set_gpt_info(lba, MTD_LBA_SIZE, str_part, + &str_disk_guid, &partitions, &part_count); + if (ret) { + print_gpt_info_err(ret); + return -1; + } + + /* save partitions layout to disk */ + ret = gpt_restore_mtd(mtd_info, str_disk_guid, partitions, part_count); + free(str_disk_guid); + free(partitions); + + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -418,6 +458,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) int dev = 0; char *ep; struct blk_desc *blk_dev_desc = NULL; +#ifdef CONFIG_EFI_PARTITION_MTD + struct mtd_info *mtd_info = NULL; + char mtd_dev[16]; +#endif if (argc < 4 || argc > 5) return CMD_RET_USAGE; @@ -428,17 +472,38 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_USAGE; } blk_dev_desc = blk_get_dev(argv[2], dev); +#ifdef CONFIG_EFI_PARTITION_MTD if (!blk_dev_desc) { + sprintf(mtd_dev, "%s%d", argv[2], dev); + mtd_info = get_mtd_device_nm(mtd_dev); + if (IS_ERR(mtd_info)) + ret = CMD_RET_FAILURE; + } +#else + if (!blk_dev_desc) + ret = CMD_RET_FAILURE; +#endif + if (ret) { printf("%s: %s dev %d NOT available\n", __func__, argv[2], dev); - return CMD_RET_FAILURE; + return ret; } if ((strcmp(argv[1], "write") == 0) && (argc == 5)) { printf("Writing GPT: "); - ret = gpt_default(blk_dev_desc, argv[4]); + if (blk_dev_desc) + ret = gpt_default(blk_dev_desc, argv[4]); +#ifdef CONFIG_EFI_PARTITION_MTD + else if (mtd_info) + ret = gpt_default_mtd(mtd_info, argv[4]); +#endif + else + ret = CMD_RET_FAILURE; } else if ((strcmp(argv[1], "verify") == 0)) { - ret = gpt_verify(blk_dev_desc, argv[4]); + if (blk_dev_desc) + ret = gpt_verify(blk_dev_desc, argv[4]); + else + ret = CMD_RET_FAILURE; printf("Verify GPT: "); } else { return CMD_RET_USAGE; @@ -451,6 +516,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("success!\n"); return CMD_RET_SUCCESS; + } U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index b9b160d..ed2065f 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -1933,6 +1933,7 @@ static int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 0; } +#define PART_ADD_DESC_MAXLEN 64 /** * Routine implementing u-boot mtdparts command. Initialize/update default global * partition list and process user partition request (list, add, del). @@ -1966,6 +1967,97 @@ static int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, } } +#ifdef CONFIG_EFI_PARTITION_MTD + /* mtdparts gpt <mtd-dev> [GUID] */ + if ((argc == 3 || argc == 4) && (strncmp(argv[1], "gpt", 3) == 0)) { + char tmpbuf[PART_ADD_DESC_MAXLEN]; + u8 type, num; + struct mtdids *id; + int part_id; + struct mtd_info *mtd; + struct mtd_device *dev; + struct mtd_device *dev_tmp; + disk_partition_t info; +#ifdef CONFIG_PARTITION_TYPE_GUID + char guid_str[UUID_STR_LEN + 1]; + char *p_guid_filter = NULL; +#endif + struct part_info *p; + + if (argc == 4) { +#ifdef CONFIG_PARTITION_TYPE_GUID + p_guid_filter = guid_str; + if (uuid_guid_parse_str(argv[3], guid_str)) { + printf("invalid type gui %s\n", argv[3]); + return 1; + } + debug("filtered type gui %s (%s)\n", argv[3], guid_str); +#else + puts("type GUI not support\n"); +#endif + } + + if (mtd_id_parse(argv[2], NULL, &type, &num) != 0) { + printf("invalid MTD device %s\n", argv[2]); + return 1; + } + + /* this may be the first run, initialize lists if needed + and make sure we are in sync with env variables */ + mtdparts_init(); + /* don't treat error (mtdparts can be empty) */ + + id = id_find(type, num); + if (id == NULL) { + printf("no such device %s defined in mtdids variable\n", + argv[2]); + return 1; + } + + if (get_mtd_info(type, num, &mtd) || (mtd == NULL)) { + printf("no such MTD device %s\n", argv[2]); + return 1; + } + + for (part_id = 1; part_id <= GPT_ENTRY_NUMBERS; part_id++) { + if (part_get_info_efi_mtd(mtd, part_id, &info)) + break; /* Stop for first non valid partition */ + +#ifdef CONFIG_PARTITION_TYPE_GUID + if (p_guid_filter && + strcmp(p_guid_filter, info.type_guid)) + continue; +#endif + + sprintf(tmpbuf, "%s:0x%llx@0x%llx(%s)", + id->mtd_id, + (u64)(info.size * info.blksz), + (u64)(info.start * info.blksz), + info.name); + debug("add tmpbuf: %s\n", tmpbuf); + + if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev)) + return 1; + + debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(type), num, + id->mtd_id); + + p = list_entry(dev->parts.next, struct part_info, link); + + dev_tmp = device_find(type, num); + + if (dev_tmp == NULL) + device_add(dev); + else if (part_add(dev_tmp, p) != 0) + return 1; + } + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, resetting to null\n"); + return 1; + } + return 0; + } +#endif /* make sure we are in sync with env variables */ if (mtdparts_init() != 0) return 1; @@ -1977,7 +2069,6 @@ static int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { -#define PART_ADD_DESC_MAXLEN 64 char tmpbuf[PART_ADD_DESC_MAXLEN]; #if defined(CONFIG_CMD_MTDPARTS_SPREAD) struct mtd_info *mtd; @@ -2083,6 +2174,10 @@ static char mtdparts_help_text[] = "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition, padding size by skipping bad blocks\n" #endif +#ifdef CONFIG_EFI_PARTITION_MTD + "mtdparts gpt <mtd-dev> [<GUID>]\n" + " - add partitions for device from gpt, filterd by GUID type\n" +#endif "mtdparts default\n" " - reset partition table to defaults\n" #if defined(CONFIG_CMD_MTDPARTS_SPREAD) @@ -2112,7 +2207,11 @@ static char mtdparts_help_text[] = "<size> := standard linux memsize OR '-' to denote all remaining space\n" "<offset> := partition start offset within the device\n" "<name> := '(' NAME ')'\n" - "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)"; + "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)" +#ifdef CONFIG_EFI_PARTITION_MTD + "<GUID> := partition guid" +#endif + ; #endif U_BOOT_CMD( diff --git a/cmd/part.c b/cmd/part.c index 414031e..4ea0fb2 100644 --- a/cmd/part.c +++ b/cmd/part.c @@ -52,11 +52,15 @@ static int do_part_uuid(int argc, char * const argv[]) static int do_part_list(int argc, char * const argv[]) { int ret; - struct blk_desc *desc; + struct blk_desc *desc = NULL; char *var = NULL; bool bootable = false; int i; +#ifdef CONFIG_EFI_PARTITION_MTD + struct mtd_info *mtd = NULL; + char mtd_dev[16]; +#endif if (argc < 2) return CMD_RET_USAGE; @@ -81,10 +85,19 @@ static int do_part_list(int argc, char * const argv[]) return CMD_RET_USAGE; } - ret = blk_get_device_by_str(argv[0], argv[1], &desc); - if (ret < 0) - return 1; - +#ifdef CONFIG_EFI_PARTITION_MTD + if ((strlen(argv[0]) + strlen(argv[1])) < sizeof(mtd_dev)) { + sprintf(mtd_dev, "%s%s", argv[0], argv[1]); + mtd = get_mtd_device_nm(mtd_dev); + } + if (IS_ERR(mtd)) { +#endif + ret = blk_get_device_by_str(argv[0], argv[1], &desc); + if (ret < 0) + return 1; +#ifdef CONFIG_EFI_PARTITION_MTD + } +#endif if (var != NULL) { int p; char str[512] = { '\0', }; @@ -92,10 +105,21 @@ static int do_part_list(int argc, char * const argv[]) for (p = 1; p < 128; p++) { char t[5]; - int r = part_get_info(desc, p, &info); + int r; + if (desc) { + r = part_get_info(desc, p, &info); - if (r != 0) - continue; + if (r != 0) + continue; + } +#ifdef CONFIG_EFI_PARTITION_MTD + else { + r = part_get_info_efi_mtd(mtd, p, &info); + /* Stop for first non valid partition */ + if (r != 0) + break; + } +#endif if (bootable && !info.bootable) continue; @@ -107,8 +131,14 @@ static int do_part_list(int argc, char * const argv[]) return 0; } +#ifndef CONFIG_EFI_PARTITION_MTD part_print(desc); - +#else + if (desc) + part_print(desc); + else + part_print_efi_mtd(mtd); +#endif return 0; } diff --git a/disk/part_efi.c b/disk/part_efi.c index 1924338..6ce64f6 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -10,6 +10,7 @@ * 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 <asm/unaligned.h> #include <common.h> #include <command.h> @@ -20,6 +21,10 @@ #include <part_efi.h> #include <linux/ctype.h> +#ifdef CONFIG_EFI_PARTITION_MTD +#include <linux/mtd/mtd.h> +#endif + DECLARE_GLOBAL_DATA_PTR; #ifdef HAVE_BLOCK_DEVICE @@ -159,11 +164,12 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h) uint64_t val; /* recalculate the values for the Backup GPT Header */ - val = le64_to_cpu(gpt_h->my_lba); - gpt_h->my_lba = gpt_h->alternate_lba; - gpt_h->alternate_lba = cpu_to_le64(val); - gpt_h->partition_entry_lba = - cpu_to_le64(le64_to_cpu(gpt_h->last_usable_lba) + 1); + val = le64_to_cpu(gpt_h->alternate_lba); + gpt_h->alternate_lba = gpt_h->my_lba; + gpt_h->my_lba = cpu_to_le64(val); + val -= (le32_to_cpu(gpt_h->num_partition_entries) * + le32_to_cpu(gpt_h->sizeof_partition_entry)); + gpt_h->partition_entry_lba = cpu_to_le64(val); gpt_h->header_crc32 = 0; calc_crc32 = efi_crc32((const unsigned char *)gpt_h, @@ -171,36 +177,16 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h) gpt_h->header_crc32 = cpu_to_le32(calc_crc32); } + #ifdef CONFIG_EFI_PARTITION -/* - * Public Functions (include/part.h) - */ -void part_print_efi(struct blk_desc *dev_desc) +static void part_print_gpt(gpt_header *gpt_head, + gpt_entry *gpt_pte) { - ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); - gpt_entry *gpt_pte = NULL; int i = 0; char uuid[37]; 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__); - } - } - - debug("%s: gpt-entry at %p\n", __func__, gpt_pte); - printf("Part\tStart LBA\tEnd LBA\t\tName\n"); printf("\tAttributes\n"); printf("\tType GUID\n"); @@ -227,6 +213,60 @@ void part_print_efi(struct blk_desc *dev_desc) uuid_bin_to_str(uuid_bin, uuid, UUID_STR_FORMAT_GUID); printf("\tguid:\t%s\n", uuid); } +} + +static void part_get_disk_info(int part, unsigned int blksz, + gpt_entry *gpt_pte, disk_partition_t *info) +{ + /* The 'lbaint_t' casting may limit the maximum disk size to 2 TB */ + info->start = (lbaint_t)le64_to_cpu(gpt_pte[part - 1].starting_lba); + /* The ending LBA is inclusive, to calculate size, add 1 to it */ + info->size = (lbaint_t)le64_to_cpu(gpt_pte[part - 1].ending_lba) + 1 + - info->start; + info->blksz = blksz; + + sprintf((char *)info->name, "%s", print_efiname(&gpt_pte[part - 1])); + strcpy((char *)info->type, "U-Boot"); + info->bootable = is_bootable(&gpt_pte[part - 1]); +#ifdef CONFIG_PARTITION_UUIDS + uuid_bin_to_str(gpt_pte[part - 1].unique_partition_guid.b, info->uuid, + UUID_STR_FORMAT_GUID); +#endif +#ifdef CONFIG_PARTITION_TYPE_GUID + uuid_bin_to_str(gpt_pte[part - 1].partition_type_guid.b, + info->type_guid, UUID_STR_FORMAT_GUID); +#endif + + debug("%s: start 0x" LBAF ", size 0x" LBAF ", name %s\n", __func__, + info->start, info->size, info->name); +} + +/* + * Public Functions (include/part.h) + */ +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; + + /* 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__); + } + } + + debug("%s: gpt-entry at %p\n", __func__, gpt_pte); + + part_print_gpt(gpt_head, gpt_pte); /* Remember to free pte */ free(gpt_pte); @@ -268,28 +308,7 @@ int part_get_info_efi(struct blk_desc *dev_desc, int part, return -1; } - /* The 'lbaint_t' casting may limit the maximum disk size to 2 TB */ - info->start = (lbaint_t)le64_to_cpu(gpt_pte[part - 1].starting_lba); - /* The ending LBA is inclusive, to calculate size, add 1 to it */ - info->size = (lbaint_t)le64_to_cpu(gpt_pte[part - 1].ending_lba) + 1 - - info->start; - info->blksz = dev_desc->blksz; - - sprintf((char *)info->name, "%s", - print_efiname(&gpt_pte[part - 1])); - strcpy((char *)info->type, "U-Boot"); - info->bootable = is_bootable(&gpt_pte[part - 1]); -#ifdef CONFIG_PARTITION_UUIDS - uuid_bin_to_str(gpt_pte[part - 1].unique_partition_guid.b, info->uuid, - UUID_STR_FORMAT_GUID); -#endif -#ifdef CONFIG_PARTITION_TYPE_GUID - uuid_bin_to_str(gpt_pte[part - 1].partition_type_guid.b, - info->type_guid, UUID_STR_FORMAT_GUID); -#endif - - debug("%s: start 0x" LBAF ", size 0x" LBAF ", name %s\n", __func__, - info->start, info->size, info->name); + part_get_disk_info(part, dev_desc->blksz, gpt_pte, info); /* Remember to free pte */ free(gpt_pte); @@ -945,3 +964,410 @@ U_BOOT_PART_TYPE(a_efi) = { .test = part_test_efi, }; #endif +#ifdef CONFIG_EFI_PARTITION_MTD +/* + * Private function prototypes + */ + +static int is_gpt_valid_mtd(struct mtd_info *mtd, + void **buf, + gpt_header **pgpt_head, + gpt_entry **pgpt_pte); + +static int gpt_fill_header_mtd(struct mtd_info *mtd, gpt_header *gpt_h, + char *str_guid, int parts_count); + +/** + * is_gpt_valid_mtd() - tests one GPT header and PTEs for validity + * + * lba is the logical block address of the GPT header to test + * pgpt_head is a GPT header ptr, filled on return. + * pgpt_pte is a PTEs ptr, filled on return. + * + * Description: returns 1 if valid, 0 on error. + * If valid, returns pointers to PTEs. + */ + +static int is_gpt_valid_mtd(struct mtd_info *mtd, + void **buf, + gpt_header **pgpt_head, + gpt_entry **pgpt_pte) +{ + gpt_header *gpt_h; + gpt_entry *gpt_e; + uint32_t size; + uint64_t offset; + int ret; + size_t retlen; + lbaint_t lba, my_lba; + u32 gpt_e_size = PAD_SIZE(GPT_ENTRY_NUMBERS * sizeof(gpt_entry), + MTD_LBA_SIZE); + + if (!mtd || !pgpt_head || !pgpt_pte) { + printf("%s: Invalid Argument(s)\n", __func__); + return 0; + } + *pgpt_head = NULL; + *pgpt_pte = NULL; + size = gpt_e_size + 2 * MTD_LBA_SIZE; + *buf = malloc(size); + + /* skip bad block */ + offset = 0; + while (mtd_block_isbad(mtd, offset)) { + printf("bad block at 0x%llx\n", offset); + offset += mtd->erasesize; + if (offset >= mtd->size) + goto error; + } + + debug("primary offset = 0x%llx\n", offset); + /* Read primary GPT from device */ + ret = mtd_read(mtd, offset, size, &retlen, *buf); + if (ret || (retlen != size)) { + printf("*** ERROR: Can't read primary GPT ***\n"); + goto error; + } + + lba = lldiv(mtd->size, MTD_LBA_SIZE); + + /* determine start of GPT Header & Entries in the buffer */ + gpt_h = *buf + (1 * MTD_LBA_SIZE); + gpt_e = *buf + (2 * MTD_LBA_SIZE); + my_lba = lldiv(offset, MTD_LBA_SIZE) + GPT_PRIMARY_PARTITION_TABLE_LBA; + + if (!validate_gpt_header(gpt_h, my_lba, lba) && + !validate_gpt_entries(gpt_h, gpt_e)) + goto end; + + printf("%s: *** ERROR: Invalid GPT ***\n", __func__); + + /* skip bad block */ + offset = mtd->size; + while (mtd_block_isbad(mtd, offset - mtd->erasesize)) { + offset -= mtd->erasesize; + printf("bad block at 0x%llx\n", offset); + if (offset <= mtd->erasesize) + goto error; + } + + /* Read backup GPT from device : end of the last valid block */ + my_lba = lldiv(offset, MTD_LBA_SIZE) - 1; + size = gpt_e_size + MTD_LBA_SIZE; + offset -= size; + + debug("backup offset = 0x%llx\n", offset); + ret = mtd_read(mtd, offset, size, &retlen, *buf); + if (ret || (retlen != size)) { + printf("*** ERROR: Can't read backup GPT ***\n"); + goto error; + } + + /* determine start of GPT Header & Entries in the buffer */ + gpt_h = *buf + gpt_e_size; + gpt_e = *buf; + + if (validate_gpt_header(gpt_h, my_lba, lba) || + validate_gpt_entries(gpt_h, gpt_e)) { + printf("%s: *** ERROR: Invalid Backup GPT ***\n", + __func__); + goto error; + } + printf("%s: *** Using Backup GPT ***\n", + __func__); + +end: + /* We're done, all's well */ + *pgpt_head = gpt_h; + *pgpt_pte = gpt_e; + return 1; + +error: + free(*buf); + return 0; +} + + +static int gpt_fill_header_mtd(struct mtd_info *mtd, gpt_header *gpt_h, + char *str_guid, int parts_count) +{ + uint32_t erasesize; + uint64_t offset_p_gpt, offset_s_gpt; + __le64 my_lba; + __le64 alternate_lba; + u32 gpt_e_size = PAD_SIZE(GPT_ENTRY_NUMBERS * sizeof(gpt_entry), + MTD_LBA_SIZE); + + /* one mtd block is not enought for MBR and GPT */ + if ((gpt_e_size + 2 * MTD_LBA_SIZE) > mtd->erasesize) + return -1; + + erasesize = lldiv(mtd->erasesize, MTD_LBA_SIZE); + + /* GPT location : skip bad block */ + offset_p_gpt = 0; + while (mtd_block_isbad(mtd, offset_p_gpt)) { + printf("bad block at 0x%llx\n", offset_p_gpt); + offset_p_gpt += mtd->erasesize; + if (offset_p_gpt >= mtd->size) + return -1; + } + /* primary GPT : 17 first LBA of last good block */ + my_lba = lldiv(offset_p_gpt, MTD_LBA_SIZE) + 1; + + offset_s_gpt = mtd->size; + while (mtd_block_isbad(mtd, offset_s_gpt - mtd->erasesize)) { + offset_s_gpt -= mtd->erasesize; + printf("bad block at 0x%llx\n", offset_s_gpt); + if (offset_s_gpt <= mtd->erasesize) + return -1; + } + /* secondary GPT : 16 LBA of last good block */ + alternate_lba = lldiv(offset_s_gpt, MTD_LBA_SIZE) - 1; + + /* only one good block (primary and secondary on same block) */ + if (offset_p_gpt == ((offset_s_gpt - mtd->erasesize))) + return -1; + + gpt_h->signature = cpu_to_le64(GPT_HEADER_SIGNATURE); + 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(my_lba); + gpt_h->alternate_lba = cpu_to_le64(alternate_lba); + gpt_h->first_usable_lba = cpu_to_le64(my_lba - 1 + erasesize); + gpt_h->last_usable_lba = cpu_to_le64(alternate_lba - erasesize); + gpt_h->partition_entry_lba = cpu_to_le64(my_lba + 1); + 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; + gpt_h->partition_entry_array_crc32 = 0; + + if (uuid_str_to_bin(str_guid, gpt_h->disk_guid.b, UUID_STR_FORMAT_GUID)) + return -1; + + return 0; +} + +static int mtd_erase_write_block(struct mtd_info *mtd, + uint64_t offset, + void *buf, + size_t len) +{ + struct erase_info erase; + int ret; + size_t retlen; + u_char *verify_buf; + + erase.mtd = mtd; + erase.len = mtd->erasesize; + erase.addr = offset & ~(mtd->erasesize - 1); /* aligned erase offset */ + erase.callback = NULL; + + debug(" erase at 0x%llx\n", erase.addr); + ret = mtd_erase(mtd, &erase); + if (ret != 0) { + printf("** mark bad block 0x%llx on MTD device %s **\n", + erase.addr, mtd->name); + ret = mtd_block_markbad(mtd, erase.addr); + if (ret) + error("cannot mark bad at offset 0x%llx, error %d", + offset, ret); + return -1; + } + + /* mtd write */ + ret = mtd_write(mtd, offset, len, &retlen, buf); + if (ret != 0 || retlen != len) { + error("Write error %d\n", ret); + return -2; + } + + verify_buf = malloc(len); + ret = mtd_read(mtd, offset, len, &retlen, verify_buf); + if (ret) { + error("Read failed 0x%x, %d\n", (u32)offset, ret); + free(verify_buf); + return -3; + } + + if (memcmp(buf, verify_buf, len)) { + error("Verify failed at 0x%x\n", (u32)offset); + return -4; + } + + free(verify_buf); + + return 0; +} + +static int write_gpt_table_mtd(struct mtd_info *mtd, + gpt_header *gpt_h, gpt_entry *gpt_e) +{ + const int pte_blk_cnt = PAD_COUNT((gpt_h->num_partition_entries + * sizeof(gpt_entry)), MTD_LBA_SIZE); + u32 calc_crc32; + uint64_t offset; + void *buf; + legacy_mbr *p_mbr; + u32 gpt_e_size = PAD_SIZE(GPT_ENTRY_NUMBERS * sizeof(gpt_entry), + MTD_LBA_SIZE); + u32 buf_size, p_size, s_size; + + p_size = gpt_e_size + 2 * MTD_LBA_SIZE; + s_size = roundup(gpt_e_size + MTD_LBA_SIZE, mtd->writesize); + buf_size = max(p_size, s_size); + buf = malloc(buf_size); + + /* Generate CRC for the Primary GPT Header */ + calc_crc32 = efi_crc32((const unsigned char *)gpt_e, + le32_to_cpu(gpt_h->num_partition_entries) * + le32_to_cpu(gpt_h->sizeof_partition_entry)); + gpt_h->partition_entry_array_crc32 = cpu_to_le32(calc_crc32); + + calc_crc32 = efi_crc32((const unsigned char *)gpt_h, + le32_to_cpu(gpt_h->header_size)); + gpt_h->header_crc32 = cpu_to_le32(calc_crc32); + + memset(buf, 0x0, p_size); + + /* Setup the Protective MBR */ + p_mbr = buf; + p_mbr->signature = MSDOS_MBR_SIGNATURE; + p_mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT; + p_mbr->partition_record[0].start_sect = 1; + p_mbr->partition_record[0].nr_sects = (u32) lldiv(mtd->size, + MTD_LBA_SIZE) - 1; + + /* GPT Header (1 block) */ + memcpy(buf + (GPT_PRIMARY_PARTITION_TABLE_LBA * MTD_LBA_SIZE), + gpt_h, sizeof(*gpt_h)); + memcpy(buf + ((GPT_PRIMARY_PARTITION_TABLE_LBA + 1) * MTD_LBA_SIZE), + gpt_e, sizeof(gpt_entry) * pte_blk_cnt); + + offset = ALIGN(le64_to_cpu(gpt_h->my_lba - + GPT_PRIMARY_PARTITION_TABLE_LBA) + * MTD_LBA_SIZE, + mtd->erasesize); + + debug("primary offset = 0x%llx\n", offset); + + if (mtd_erase_write_block(mtd, offset, buf, p_size)) + goto err; + + /* backup GPT */ + prepare_backup_gpt_header(gpt_h); + + memset(buf, 0x0, s_size); + + offset = (le64_to_cpu(gpt_h->my_lba) + 1) * MTD_LBA_SIZE - s_size; + memcpy(buf + s_size - MTD_LBA_SIZE, gpt_h, sizeof(*gpt_h)); + memcpy(buf + s_size - MTD_LBA_SIZE - gpt_e_size, + gpt_e, sizeof(gpt_entry) * pte_blk_cnt); + + + debug("backup offset = 0x%llx\n", offset); + + if (mtd_erase_write_block(mtd, offset, buf, s_size)) + goto err; + + free(buf); + debug("GPT successfully written to MTD device!\n"); + return 0; + + err: + free(buf); + printf("** Can't write to MTD device %s **\n", mtd->name); + return -1; +} + +/* + * Public Functions (include/part.h) + */ + +int gpt_restore_mtd(struct mtd_info *mtd, char *str_disk_guid, + disk_partition_t *partitions, int parts_count) +{ + int ret; + gpt_header *gpt_h = calloc(1, PAD_SIZE(sizeof(gpt_header), + MTD_LBA_SIZE)); + gpt_entry *gpt_e; + + if (gpt_h == NULL) { + printf("%s: calloc failed!\n", __func__); + return -1; + } + + gpt_e = calloc(1, PAD_SIZE(GPT_ENTRY_NUMBERS + * sizeof(gpt_entry), + MTD_LBA_SIZE)); + if (gpt_e == NULL) { + printf("%s: calloc failed!\n", __func__); + free(gpt_h); + return -1; + } + + /* Generate Primary GPT header (LBA1) */ + ret = gpt_fill_header_mtd(mtd, gpt_h, str_disk_guid, parts_count); + if (ret) + goto err; + + /* Generate partition entries */ + ret = gpt_fill_pte(gpt_h, gpt_e, partitions, parts_count); + if (ret) + goto err; + + /* Write GPT partition table */ + ret = write_gpt_table_mtd(mtd, gpt_h, gpt_e); + +err: + free(gpt_e); + free(gpt_h); + return ret; +} + +void part_print_efi_mtd(struct mtd_info *mtd) +{ + void *buf; + gpt_header *gpt_head; + gpt_entry *gpt_pte; + + if (!is_gpt_valid_mtd(mtd, &buf, &gpt_head, &gpt_pte)) + return; + + part_print_gpt(gpt_head, gpt_pte); + + /* Remember to free buffer */ + free(buf); + return; +} + +int part_get_info_efi_mtd(struct mtd_info *mtd, int part, + disk_partition_t *info) +{ + void *buf; + gpt_header *gpt_head; + gpt_entry *gpt_pte; + + if (!mtd || !info || part < 1) { + printf("%s: Invalid Argument(s)\n", __func__); + return -1; + } + + if (!is_gpt_valid_mtd(mtd, &buf, &gpt_head, &gpt_pte)) + return -1; + + if (part > le32_to_cpu(gpt_head->num_partition_entries) || + !is_pte_valid(&gpt_pte[part - 1])) { + debug("%s: *** ERROR: Invalid partition number %d ***\n", + __func__, part); + free(buf); + return -1; + } + + part_get_disk_info(part, MTD_LBA_SIZE, gpt_pte, info); + + /* Remember to free buffer */ + free(buf); + return 0; +} +#endif /* CONFIG_EFI_PARTITION_MTD */ diff --git a/doc/README.gpt.mtd b/doc/README.gpt.mtd new file mode 100644 index 0000000..d7b21f3 --- /dev/null +++ b/doc/README.gpt.mtd @@ -0,0 +1,189 @@ +# +# +# SPDX-License-Identifier: GPL-2.0+ + +Glossary: +======== +- GPT (GUID Partition Table) - it is the EFI standard part +- MTD (Memory Technology Device) - abstraction layer for RAW flah device + +Introduction: +============= +This document describes the GPT support on MTD and usage of +the associated command in u-boot. + +The GPT is normally defined only for block device with built-in controller +which manage flash translation layer (FTL) as MMC, SD, USB or ATA. + +For raw flash device, the MTD partionning are usally defined in U-Boot +environment and provided to kernel (see mtdparts command). + +U-Boot support GPT for MTD device to save the partition informations of +raw flash device directly in this device (usefull for CONFIG_ENV_IS_NOWHERE) +and to use these informations to support MTD devices in DISTRO without +hardcoded MTD partitioning in U-Boot environment + +The GPT support on top of MTD is defined under CONFIG_EFI_PARTITION_MTD. + +PS: DISTRO boot cmd is no yet updated + +GPT on MTD brief explanation: +============================= + +The GPT standard is respected (header and field meaning). + +The GPT header and each partition need to be eraseblock-align to allow +individual udpate for header or partiton. + +The primary and the backup GPT header are located in the first and +the last -not bad- eraseblock. + +We assume that one eraseblock is enought for MBR and GPT header +(size = 17 KB for 128 entry) + +So it not compatible with the feature CONFIG_SPI_FLASH_USE_4K_SECTORS + + Layout: + ------- + + ---------------------------------------------------------- + LBA 0-33 |Protective MBR + Primary GPT | 1rst eraseblock + -------------------------------------------------- + LBA 34 | (not used) | + LBA N-1 | | + ---------------------------------------------------------- + LBA N |Partition 1 | 2nd eraseblock + | | = first usable + ----------------------------------- + |Partition 2 | + | | + ----------------------------------- last-1 eraseblock + LBA M |Partition n | = last usable + ---------------------------------------------------------- + LBA M+1 | (not used) | last eraseblock + LBA -35 | | + -------------------------------------------------- + LBA -1 to -34 |Backup GPT Header | + ---------------------------------------------------------- + + NB: this layout change (LBA0 and LBA-1) if bad block are present + + GPT header: + ----------- + + for details of GPT header, see README.gpt + + "Current LBA" and "Backup LBA" give the location of the primary and + secondary GPT header. + + All the bad block are detected and skipped when the GPT header is build. + + For Primary GPT: + + - Current LBA = 2nd LBA of the first good eraseblock + + - Backup LBA = Last LBA of the last good block + + - First usable LBA = First LBA of the first usable eraseblock + i.e. the block after primary GPT one + Current LBA - 1 + erase block size + + - Last usable LBA = Last LBA of the last usable eraseblock + i.e. the block before backup GPT one + Backup LBA - erase block size + + Bad Block management (NAND): + --------------------------- + As the bad blocks are skipped, Current LBA and Backup LBA give the real + location of primary and backup GPT header + + warning: the first and the last usable LBA are not guarantee + to be in a good block + + + Typically, the last 4 erase blocks for NAND are used for the bad block + table (BBT, see bbt_options and NAND_BBT_USE_FLASH). + They are indicated bad by MTD framework and they are also skipped. + So the layout for NAND with bad : + + NAND layout with bad blocks: + ---------------------------------------------------------- + N * BAD (*) | | skipped bad + ----------------------------------------------------------- + |Protective MBR | 1rst good eraseblock + |Primary GPT Header | + | | + ------------------------------------------- + | | = first usable + | | + | Partitions | + | | + | | = last usable + ------------------------------------------- + | | + |Backup GPT Header | last good eraseblock + ---------------------------------------------------------- + M * BAD (*) | | skipped bad + ---------------------------------------------------------- + BBT => BAD (*) | | skipped 4 blocks + ---------------------------------------------------------- + (*) BBT and block marked bad are skipped + + +Drawbacks: +========== +1. 2 eraseblocks are used in the device just to save the GPT headers + (primary and backup) + +2. for device with back block (NAND), any read request can disturb the device + so the GPT header should be refreshed when one fixable bit-flip ECC error is + detected... + but it is not the case today + +3. for gpt write the eraseblock for the primary or backup GPT header + (expected good) can become bad for the erase request + + And then u-boot will mark this block bad + + In this case the first / last usable LBA need to change and to skip the new + bad block, so header need to be recomputed + => the requested gpt write command failed to force new request + PS: the next request will work (skip the new bad block) + + And as the first / last usable LBA can change the same partionning after + the block is marked bad + +GPT on MTD commands: +==================== +some command are modified to support GPT on MTD devices under CONFIG_EFI_PARTITION_MTD + + Creating GPT on MTD partitions: + ------------------------------- + + 1. Define partition layout in the environment. + "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + name=kernel,size=60MiB,uuid=...;" + + 2. From u-boot prompt type: + gpt write nand 0 $partitions + gpt write nor 0 $partitions + + List GPT partition on MTD: + --------------------------------- + part list nand 0 + part list nor 0 + + to find bootable partition (used for distro): + + part list nand 0 -bootable nand_boot_part + part list nor 0 -bootable nor_boot_part + + NB: bootable partition can be used with UBIFS + + Generate MTD partitions from GPT: + --------------------------------- + mtdparts gpt nand0 + mtdparts gpt nor0 + + Then to check mtd partition created with: + mtdparts diff --git a/include/part.h b/include/part.h index 0979005..0660c27 100644 --- a/include/part.h +++ b/include/part.h @@ -9,6 +9,7 @@ #include <blk.h> #include <ide.h> +#include <linux/mtd/mtd.h> struct block_drvr { char *name; @@ -308,7 +309,6 @@ 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, const int parts_count); - /** * is_valid_gpt_buf() - Ensure that the Primary GPT information is valid * @@ -363,7 +363,16 @@ 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, gpt_header *gpt_head, gpt_entry **gpt_pte); -#endif + +#ifdef CONFIG_EFI_PARTITION_MTD +#define MTD_LBA_SIZE 512 +int gpt_restore_mtd(struct mtd_info *mtd, char *str_disk_guid, + disk_partition_t *partitions, int parts_count); +void part_print_efi_mtd(struct mtd_info *mtd_info); +int part_get_info_efi_mtd(struct mtd_info *mtd, int part, + disk_partition_t *info); +#endif /* CONFIG_EFI_PARTITION_MTD */ +#endif /* CONFIG_EFI_PARTITION */ #ifdef CONFIG_DOS_PARTITION /** diff --git a/include/uuid.h b/include/uuid.h index c3f423f..f444095 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -40,6 +40,7 @@ void uuid_bin_to_str(unsigned char *uuid_bin, char *uuid_str, int str_format); int uuid_guid_get_bin(const char *guid_str, unsigned char *guid_bin); int uuid_guid_get_str(unsigned char *guid_bin, char *guid_str); #endif +int uuid_guid_parse_str(const char *str, char *guid_str); void gen_rand_uuid(unsigned char *uuid_bin); void gen_rand_uuid_str(char *uuid_str, int str_format); #endif diff --git a/lib/uuid.c b/lib/uuid.c index c8584ed..eb8d8eb 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -136,6 +136,39 @@ int uuid_guid_get_str(unsigned char *guid_bin, char *guid_str) #endif /* + * uuid_guid_parse_str() - this function parse string for GUID. + * + * @param guid_str - pointer to string with partition type guid [37B] + * or GUID shorcut + * @param guid_str - pointer to allocated partition type string [37B] + */ +int uuid_guid_parse_str(const char *str, char *guid_str) +{ +#ifdef CONFIG_PARTITION_TYPE_GUID + int i; +#endif + + if (guid_str == NULL) + return -ENODEV; + + if (uuid_str_valid(str)) { + memcpy(guid_str, str, UUID_STR_LEN + 1); + return 0; + } + +#ifdef CONFIG_PARTITION_TYPE_GUID + for (i = 0; i < ARRAY_SIZE(list_guid); i++) { + if (!strcmp(list_guid[i].string, str)) { + uuid_bin_to_str((unsigned char *)list_guid[i].guid.b, + guid_str, UUID_STR_FORMAT_GUID); + return 0; + } + } +#endif + return -ENODEV; +} + +/* * uuid_str_to_bin() - convert string UUID or GUID to big endian binary data. * * @param uuid_str - pointer to UUID or GUID string [37B] or GUID shorcut -- 1.9.1 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot