123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-05-05 linzhenxing first version
- */
- #include <rtthread.h>
- #include <dfs_fs.h>
- #include <drivers/gpt.h>
- #include <drivers/mmcsd_core.h>
- #define DBG_TAG "GPT"
- #ifdef RT_SDIO_DEBUG
- #define DBG_LVL DBG_LOG
- #else
- #define DBG_LVL DBG_INFO
- #endif /* RT_SDIO_DEBUG */
- #include <rtdbg.h>
- #define min(a, b) a < b ? a : b
- static int force_gpt = 0;
- static gpt_header *_gpt;
- static gpt_entry *_ptes;
- #define GPT_TYPE 1
- #define MBR_TYPE 0
- static inline int efi_guidcmp (gpt_guid_t left, gpt_guid_t right)
- {
- return rt_memcmp(&left, &right, sizeof (gpt_guid_t));
- }
- static uint32_t last_lba(struct rt_mmcsd_card *card)
- {
- RT_ASSERT(card != RT_NULL);
- return (card->card_sec_cnt) - 1;
- }
- static inline int pmbr_part_valid(gpt_mbr_record *part)
- {
- if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT)
- {
- goto invalid;
- }
- /* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */
- if ((uint32_t)(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA)
- {
- goto invalid;
- }
- return GPT_MBR_PROTECTIVE;
- invalid:
- return 0;
- }
- /*
- *
- * return ret
- * ret = 0, invalid mbr
- * ret = 1, protect mbr
- * ret = 2, hybrid mbr
- */
- int is_pmbr_valid(legacy_mbr *mbr, uint64_t total_sectors)
- {
- uint32_t sz = 0;
- int i, part = 0, ret = 0; /* invalid by default */
- if (!mbr || (uint16_t)(mbr->signature) != MSDOS_MBR_SIGNATURE)
- {
- goto done;
- }
- for (i = 0; i < 4; i++)
- {
- ret = pmbr_part_valid(&mbr->partition_record[i]);
- if (ret == GPT_MBR_PROTECTIVE)
- {
- part = i;
- /*
- * Ok, we at least know that there's a protective MBR,
- * now check if there are other partition types for
- * hybrid MBR.
- */
- goto check_hybrid;
- }
- }
- if (ret != GPT_MBR_PROTECTIVE)
- {
- goto done;
- }
- check_hybrid:
- for (i = 0; i < 4; i++)
- {
- if ((mbr->partition_record[i].os_type !=
- EFI_PMBR_OSTYPE_EFI_GPT) &&
- (mbr->partition_record[i].os_type != 0x00))
- {
- ret = GPT_MBR_HYBRID;
- }
- }
- /*
- * Protective MBRs take up the lesser of the whole disk
- * or 2 TiB (32bit LBA), ignoring the rest of the disk.
- * Some partitioning programs, nonetheless, choose to set
- * the size to the maximum 32-bit limitation, disregarding
- * the disk size.
- *
- * Hybrid MBRs do not necessarily comply with this.
- *
- * Consider a bad value here to be a warning to support dd'ing
- * an image from a smaller disk to a larger disk.
- */
- if (ret == GPT_MBR_PROTECTIVE)
- {
- sz = (uint32_t)(mbr->partition_record[part].size_in_lba);
- if (sz != (uint32_t) total_sectors - 1 && sz != 0xFFFFFFFF)
- {
- LOG_I("GPT: mbr size in lba (%u) different than whole disk (%u).",
- sz, min(total_sectors - 1, 0xFFFFFFFF));
- }
- }
- done:
- return ret;
- }
- static gpt_entry *alloc_read_gpt_entries(struct rt_mmcsd_card *card, gpt_header *gpt)
- {
- size_t count;
- gpt_entry *pte;
- if (!gpt)
- {
- return RT_NULL;
- }
- count = (size_t)(gpt->num_partition_entries) * (gpt->sizeof_partition_entry);
- if (!count)
- {
- return RT_NULL;
- }
- pte = rt_malloc(count);
- if (!pte)
- return RT_NULL;
- if (read_lba(card, (size_t)(gpt->partition_entry_lba),(uint8_t *)pte, count/512) != RT_EOK)
- {
- rt_free(pte);
- return RT_NULL;
- }
- return pte;
- }
- static gpt_header *alloc_read_gpt_header(struct rt_mmcsd_card *card, size_t lba)
- {
- gpt_header *gpt;
- void *buf;
- buf = rt_malloc(512);
- if (!buf)
- {
- return RT_NULL;
- }
- if (read_lba(card, lba, (uint8_t *)buf, 1) != RT_EOK)
- {
- rt_free(buf);
- return RT_NULL;
- }
- gpt = (gpt_header *)buf;
- return gpt;
- }
- static int is_gpt_valid(struct rt_mmcsd_card *card, size_t lba, gpt_header **gpt, gpt_entry **ptes)
- {
- size_t lastlba;
- if (!ptes || !gpt)
- {
- return 0;
- }
- *gpt = alloc_read_gpt_header(card, lba);
- if (!(*gpt))
- {
- return 0;
- }
- /* Check the GUID Partition Table signature */
- if ((uint64_t)((*gpt)->signature) != GPT_HEADER_SIGNATURE)
- {
- LOG_E("GUID Partition Table Header signature is wrong:"
- "%ld != %ld",(uint64_t)((*gpt)->signature),(uint64_t)GPT_HEADER_SIGNATURE);
- goto fail;
- }
- /* Check the GUID Partition Table header size is too small */
- if ((uint32_t)((*gpt)->header_size) < sizeof(gpt_header))
- {
- LOG_E("GUID Partition Table Header size is too small: %u < %zu",
- (uint32_t)((*gpt)->header_size),sizeof(gpt_header));
- goto fail;
- }
- /* Check that the start_lba entry points to the LBA that contains
- * the GUID Partition Table */
- if ((uint64_t)((*gpt)->start_lba) != lba)
- {
- LOG_E("GPT start_lba incorrect: %ld != %ld",
- (uint64_t)((*gpt)->start_lba),
- (uint64_t)lba);
- goto fail;
- }
- /* Check the first_usable_lba and last_usable_lba are
- * within the disk.
- */
- lastlba = last_lba(card);
- if ((uint64_t)((*gpt)->first_usable_lba) > lastlba)
- {
- LOG_E("GPT: first_usable_lba incorrect: %ld > %ld",
- ((uint64_t)((*gpt)->first_usable_lba)),
- (size_t)lastlba);
- goto fail;
- }
- if ((uint64_t)((*gpt)->last_usable_lba) > lastlba)
- {
- LOG_E("GPT: last_usable_lba incorrect: %ld > %ld",
- (uint64_t)((*gpt)->last_usable_lba),
- (size_t)lastlba);
- goto fail;
- }
- if ((uint64_t)((*gpt)->last_usable_lba) < (uint64_t)((*gpt)->first_usable_lba))
- {
- LOG_E("GPT: last_usable_lba incorrect: %ld > %ld",
- (uint64_t)((*gpt)->last_usable_lba),
- (uint64_t)((*gpt)->first_usable_lba));
- goto fail;
- }
- /* Check that sizeof_partition_entry has the correct value */
- if ((uint32_t)((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) {
- LOG_E("GUID Partition Entry Size check failed.");
- goto fail;
- }
- *ptes = alloc_read_gpt_entries(card, *gpt);
- if (!(*ptes))
- {
- goto fail;
- }
- /* We're done, all's well */
- return 1;
- fail:
- rt_free(*gpt);
- *gpt = RT_NULL;
- return 0;
- }
- /**
- * is_pte_valid() - tests one PTE for validity
- * pte:pte to check
- * lastlba: last lba of the disk
- *
- * Description: returns 1 if valid, 0 on error.
- */
- static inline int is_pte_valid(const gpt_entry *pte, const size_t lastlba)
- {
- if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) ||
- (uint64_t)(pte->starting_lba) > lastlba ||
- (uint64_t)(pte->ending_lba) > lastlba)
- {
- return 0;
- }
- return 1;
- }
- /**
- * compare_gpts() - Search disk for valid GPT headers and PTEs
- * pgpt: primary GPT header
- * agpt: alternate GPT header
- * lastlba: last LBA number
- *
- * Description: Returns nothing. Sanity checks pgpt and agpt fields
- * and prints warnings on discrepancies.
- *
- */
- static void compare_gpts(gpt_header *pgpt, gpt_header *agpt, size_t lastlba)
- {
- int error_found = 0;
- if (!pgpt || !agpt)
- {
- return;
- }
- if ((uint64_t)(pgpt->start_lba) != (uint64_t)(agpt->alternate_lba))
- {
- LOG_I("GPT:Primary header LBA != Alt. header alternate_lba");
- LOG_I("GPT:%lld != %lld",
- (uint64_t)(pgpt->start_lba),
- (uint64_t)(agpt->alternate_lba));
- error_found++;
- }
- if ((uint64_t)(pgpt->alternate_lba) != (uint64_t)(agpt->start_lba))
- {
- LOG_I("GPT:Primary header alternate_lba != Alt. header start_lba");
- LOG_I("GPT:%lld != %lld",
- (uint64_t)(pgpt->alternate_lba),
- (uint64_t)(agpt->start_lba));
- error_found++;
- }
- if ((uint64_t)(pgpt->first_usable_lba) != (uint64_t)(agpt->first_usable_lba))
- {
- LOG_I("GPT:first_usable_lbas don't match.");
- LOG_I("GPT:%lld != %lld",
- (uint64_t)(pgpt->first_usable_lba),
- (uint64_t)(agpt->first_usable_lba));
- error_found++;
- }
- if ((uint64_t)(pgpt->last_usable_lba) != (uint64_t)(agpt->last_usable_lba))
- {
- LOG_I("GPT:last_usable_lbas don't match.");
- LOG_I("GPT:%lld != %lld",
- (uint64_t)(pgpt->last_usable_lba),
- (uint64_t)(agpt->last_usable_lba));
- error_found++;
- }
- if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid))
- {
- LOG_I("GPT:disk_guids don't match.");
- error_found++;
- }
- if ((pgpt->num_partition_entries) != (agpt->num_partition_entries))
- {
- LOG_I("GPT:num_partition_entries don't match: "
- "0x%x != 0x%x",
- (pgpt->num_partition_entries),
- (agpt->num_partition_entries));
- error_found++;
- }
- if ((pgpt->sizeof_partition_entry) != (agpt->sizeof_partition_entry))
- {
- LOG_I("GPT:sizeof_partition_entry values don't match: "
- "0x%x != 0x%x",
- (pgpt->sizeof_partition_entry),
- (agpt->sizeof_partition_entry));
- error_found++;
- }
- if ((pgpt->partition_entry_array_crc32) != (agpt->partition_entry_array_crc32))
- {
- LOG_I("GPT:partition_entry_array_crc32 values don't match: "
- "0x%x != 0x%x",
- (pgpt->partition_entry_array_crc32),
- (agpt->partition_entry_array_crc32));
- error_found++;
- }
- if ((pgpt->alternate_lba) != lastlba)
- {
- LOG_I("GPT:Primary header thinks Alt. header is not at the end of the disk.");
- LOG_I("GPT:%lld != %lld",
- (uint64_t)(pgpt->alternate_lba),
- (size_t)lastlba);
- error_found++;
- }
- if ((agpt->start_lba) != lastlba)
- {
- LOG_I("GPT:Alternate GPT header not at the end of the disk.");
- LOG_I("GPT:%lld != %lld",
- (uint64_t)(agpt->start_lba),
- (size_t)lastlba);
- error_found++;
- }
- if (error_found)
- {
- LOG_I("GPT: Use GNU Parted to correct GPT errors.");
- }
- return;
- }
- /**
- * find_valid_gpt() - Search disk for valid GPT headers and PTEs
- * state: disk parsed partitions
- * gpt: GPT header ptr, filled on return.
- * ptes: PTEs ptr, filled on return.
- *
- * Description: Returns 1 if valid, 0 on error.
- * If valid, returns pointers to newly allocated GPT header and PTEs.
- * Validity depends on PMBR being valid (or being overridden by the
- * 'gpt' kernel command line option) and finding either the Primary
- * GPT header and PTEs valid, or the Alternate GPT header and PTEs
- * valid. If the Primary GPT header is not valid, the Alternate GPT header
- * is not checked unless the 'gpt' kernel command line option is passed.
- * This protects against devices which misreport their size, and forces
- * the user to decide to use the Alternate GPT.
- */
- static int find_valid_gpt(struct rt_mmcsd_card *card, gpt_header **gpt,
- gpt_entry **ptes)
- {
- int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
- gpt_header *pgpt = RT_NULL, *agpt = RT_NULL;
- gpt_entry *pptes = RT_NULL, *aptes = RT_NULL;
- legacy_mbr *legacymbr;
- size_t total_sectors = last_lba(card) + 1;
- size_t lastlba;
- int status = 0;
- if (!ptes)
- {
- return 0;
- }
- lastlba = last_lba(card);
- if (!force_gpt)
- {
- /* This will be added to the EFI Spec. per Intel after v1.02. */
- legacymbr = rt_malloc(512);
- if (!legacymbr)
- {
- goto fail;
- }
- status = read_lba(card, 0, (uint8_t *)legacymbr, 1);
- if (status)
- {
- LOG_I("status:%d", status);
- goto fail;
- }
- good_pmbr = is_pmbr_valid(legacymbr, total_sectors);
- rt_free(legacymbr);
- if (!good_pmbr)
- {
- goto fail;
- }
- rt_kprintf("Device has a %s MBR\n",
- good_pmbr == GPT_MBR_PROTECTIVE ?
- "protective" : "hybrid");
- }
- good_pgpt = is_gpt_valid(card, GPT_PRIMARY_PARTITION_TABLE_LBA,
- &pgpt, &pptes);
- if (good_pgpt)
- {
- good_agpt = is_gpt_valid(card, (pgpt->alternate_lba), &agpt, &aptes);
- if (!good_agpt && force_gpt)
- {
- good_agpt = is_gpt_valid(card, lastlba, &agpt, &aptes);
- }
- /* The obviously unsuccessful case */
- if (!good_pgpt && !good_agpt)
- {
- goto fail;
- }
- compare_gpts(pgpt, agpt, lastlba);
- /* The good cases */
- if (good_pgpt)
- {
- *gpt = pgpt;
- *ptes = pptes;
- rt_free(agpt);
- rt_free(aptes);
- if (!good_agpt)
- {
- LOG_D("Alternate GPT is invalid, using primary GPT.");
- }
- return 1;
- }
- else if (good_agpt)
- {
- *gpt = agpt;
- *ptes = aptes;
- rt_free(pgpt);
- rt_free(pptes);
- LOG_D("Primary GPT is invalid, using alternate GPT.");
- return 1;
- }
- }
- fail:
- rt_free(pgpt);
- rt_free(agpt);
- rt_free(pptes);
- rt_free(aptes);
- *gpt = RT_NULL;
- *ptes = RT_NULL;
- return 0;
- }
- int check_gpt(struct rt_mmcsd_card *card)
- {
- if (!find_valid_gpt(card, &_gpt, &_ptes) || !_gpt || !_ptes)
- {
- rt_free(_gpt);
- rt_free(_ptes);
- return MBR_TYPE;
- }
- return GPT_TYPE;
- }
- int gpt_get_partition_param(struct rt_mmcsd_card *card, struct dfs_partition *part, uint32_t pindex)
- {
- if (!is_pte_valid(&_ptes[pindex], last_lba(card)))
- {
- return -1;
- }
- part->offset = (off_t)(_ptes[pindex].starting_lba);
- part->size = (_ptes[pindex].ending_lba) - (_ptes[pindex].starting_lba) + 1ULL;
- rt_kprintf("found part[%d], begin(sector): %d, end(sector):%d size: ",
- pindex, _ptes[pindex].starting_lba, _ptes[pindex].ending_lba);
- if ((part->size >> 11) == 0)
- {
- rt_kprintf("%d%s", part->size >> 1, "KB\n"); /* KB */
- }
- else
- {
- unsigned int part_size;
- part_size = part->size >> 11; /* MB */
- if ((part_size >> 10) == 0)
- rt_kprintf("%d.%d%s", part_size, (part->size >> 1) & 0x3FF, "MB\n");
- else
- rt_kprintf("%d.%d%s", part_size >> 10, part_size & 0x3FF, "GB\n");
- }
- return 0;
- }
- void gpt_free(void)
- {
- rt_free(_ptes);
- rt_free(_gpt);
- }
|