From patchwork Wed Nov 30 11:01:23 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Delaunay X-Patchwork-Id: 700967 X-Patchwork-Delegate: scottwood@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 3tTHZZ4vWJz9t37 for ; Wed, 30 Nov 2016 22:03:02 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="vEXYiD9x"; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 41187A7640; Wed, 30 Nov 2016 12:03:01 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id FoL6JPBDc0YP; Wed, 30 Nov 2016 12:03:01 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id CC123A75FD; Wed, 30 Nov 2016 12:03:00 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 54263A75FD for ; Wed, 30 Nov 2016 12:02:56 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id FzWkcc-2Ci2W for ; Wed, 30 Nov 2016 12:02:56 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-wm0-f66.google.com (mail-wm0-f66.google.com [74.125.82.66]) by theia.denx.de (Postfix) with ESMTPS id 18B23A75FA for ; Wed, 30 Nov 2016 12:02:52 +0100 (CET) Received: by mail-wm0-f66.google.com with SMTP id g23so28888496wme.1 for ; Wed, 30 Nov 2016 03:02:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=fvR0bYBl5ni61gnCegVzuyYP0i2eQo658xbXfRR6dYA=; b=vEXYiD9xuGgbJkCQQmVi/sbRnc7aglwZ/3WzSZU5Ij+UqX0zODwYgicjHw6Jb6pjRu oRyTdhznKf9r4Mp5kXDNE5zEJOdy5WZD6l6Dm5GNm0CU+0QhqMXV5PNj8ezKxjsQ1O6V eKJaJNm+xa8DNhMtvdmkFIprbdSUactMD47j1NAdrN1TlVkMJQRDOhtD1EXpPc1RHelZ j91J77O8Ebc9fCk7exlCoYI7U2EZzOPvzRx9U0w+eBkfOxHo6VWuT9oJT2jxGcJ832Dk MMDZJX6b4wBIjWDNZ3Cty49JW4PArC8MXbFg/RkQCR/smrcPGmI0AeoZQMqK3uEXX1gr xgIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=fvR0bYBl5ni61gnCegVzuyYP0i2eQo658xbXfRR6dYA=; b=fCjNueZtJpXbASJ5bGn2ijskC9UuZQU940B6d5tejthhIumc5gI5Trk0y33THBQKJ8 Gv756jyrm4hkG9655YYAkoP16fAYnw1ML4NXZelMxuQNxUiaoLYpjCt8xdgOiLn3IBR2 pDmf1A0+8/Agbm6go7328fUi5hGTZrPjlVVGN64yc+gwwHFONyL7WuC9CaGDaYxH9ZUd X+Pw8JhkRUFzQi9aI63m69+0xMbzRsnoLelr640IcG3mvyOQE7rMNwtNrmUbnJpjRmrT JxtVTNBOzwBVeKLFijAGr6nTFmLjS69ihh3dwF3rQL0YZ+P4UR29kTEw85FkzK0Ua9oK w7nA== X-Gm-Message-State: AKaTC01f1k8veLwnHAmLwsZgmlOb63WOlAzFbEVeBRc/r/zEaLBx3rkfgRS+W28ENtAmBw== X-Received: by 10.28.211.72 with SMTP id k69mr27272833wmg.138.1480503771838; Wed, 30 Nov 2016 03:02:51 -0800 (PST) Received: from lmenx29a.st.com. ([37.167.0.81]) by smtp.gmail.com with ESMTPSA id ba10sm72300835wjb.32.2016.11.30.03.02.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 30 Nov 2016 03:02:51 -0800 (PST) From: Patrick Delaunay To: u-boot@lists.denx.de Date: Wed, 30 Nov 2016 12:01:23 +0100 Message-Id: <1480503692-17255-4-git-send-email-patrick.delaunay73@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1480503692-17255-1-git-send-email-patrick.delaunay73@gmail.com> References: <1480503692-17255-1-git-send-email-patrick.delaunay73@gmail.com> Cc: Ben Whitten , Franck Albesa , Hans de Goede , Stefan Roese , Stephen Warren , Stefan Agner , Michal Simek , Patrick Delaunay , Steve Rae , Gerald Baeza , Patrick Delaunay , Daniel Allred , Andreas Dannenberg Subject: [U-Boot] [RFC PATCH v2 3/8] disk: part: add support of GPT partitioning over MTD X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Patrick Delaunay Signed-off-by: Patrick Delaunay Signed-off-by: Patrick Delaunay --- Changes in v2: None Kconfig | 12 ++ disk/part_efi.c | 445 +++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/README.gpt.mtd | 189 +++++++++++++++++++++++ include/part.h | 13 +- 4 files changed, 657 insertions(+), 2 deletions(-) create mode 100644 doc/README.gpt.mtd diff --git a/Kconfig b/Kconfig index 529858a..1ce39d5 100644 --- a/Kconfig +++ b/Kconfig @@ -309,6 +309,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/disk/part_efi.c b/disk/part_efi.c index a23c8ea..f409bb0 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -21,6 +21,10 @@ #include #include +#ifdef CONFIG_EFI_PARTITION_MTD +#include +#endif + DECLARE_GLOBAL_DATA_PTR; #ifdef HAVE_BLOCK_DEVICE @@ -960,3 +964,444 @@ U_BOOT_PART_TYPE(a_efi) = { .test = part_test_efi, }; #endif + +#ifdef CONFIG_EFI_PARTITION_MTD +static int is_primary_gpt_valid_mtd(struct mtd_info *mtd, + lbaint_t lba, + 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 my_lba; + u32 gpt_e_size = PAD_SIZE(GPT_ENTRY_NUMBERS * sizeof(gpt_entry), + MTD_LBA_SIZE); + + size = gpt_e_size + 2 * MTD_LBA_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) { + printf("*** ERROR: too many bad block ***\n"); + return -1; + } + } + + 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"); + return -1; + } + + /* 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)) { + *pgpt_head = gpt_h; + *pgpt_pte = gpt_e; + return 0; + } + return -1; +} + +static int is_secondary_gpt_valid_mtd(struct mtd_info *mtd, + lbaint_t lba, + 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 my_lba; + u32 gpt_e_size = PAD_SIZE(GPT_ENTRY_NUMBERS * sizeof(gpt_entry), + MTD_LBA_SIZE); + + size = gpt_e_size + 2 * MTD_LBA_SIZE; + /* 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) { + printf("*** ERROR: too many bad block ***\n"); + return -1; + } + } + + /* Read backup GPT from device : end of the last valid block */ + 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"); + return -1; + } + + /* determine start of GPT Header & Entries in the buffer */ + gpt_h = *buf + gpt_e_size; + gpt_e = *buf; + my_lba = lldiv(offset, MTD_LBA_SIZE) - 1; + + if (!validate_gpt_header(gpt_h, my_lba, lba) && + !validate_gpt_entries(gpt_h, gpt_e)) { + *pgpt_head = gpt_h; + *pgpt_pte = gpt_e; + return 0; + } + return -1; +} + +/** + * 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) +{ + u32 gpt_e_size = PAD_SIZE(GPT_ENTRY_NUMBERS * sizeof(gpt_entry), + MTD_LBA_SIZE); + lbaint_t lba; + + if (!mtd || !pgpt_head || !pgpt_pte) { + printf("%s: Invalid Argument(s)\n", __func__); + return 0; + } + *pgpt_head = NULL; + *pgpt_pte = NULL; + *buf = malloc(gpt_e_size + 2 * MTD_LBA_SIZE); + lba = lldiv(mtd->size, MTD_LBA_SIZE); + + if (!is_primary_gpt_valid_mtd(mtd, lba, buf, pgpt_head, pgpt_pte)) + return 1; + + printf("%s: *** ERROR: Invalid GPT ***\n", __func__); + + if (!is_secondary_gpt_valid_mtd(mtd, lba, buf, pgpt_head, pgpt_pte)) { + printf("%s: *** Using Backup GPT ***\n", __func__); + return 1; + } + printf("%s: *** ERROR: Invalid Backup GPT ***\n", __func__); + + 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 void prepare_mbr_gpt_header_mtd(void *buf, + struct mtd_info *mtd, + gpt_header *gpt_h, + gpt_entry *gpt_e) +{ + legacy_mbr *p_mbr = buf; + u32 calc_crc32; + + /* 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); + + /* Setup the Protective MBR */ + 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; +} + + +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); + uint64_t offset; + void *buf; + 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); + memset(buf, 0x0, p_size); + + prepare_mbr_gpt_header_mtd(buf, mtd, gpt_h, gpt_e); + + /* 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; + + 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..fbc8f82 --- /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 #include +#include 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 /**