You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/10/30 06:24:18 UTC
[incubator-nuttx] 02/02: fs/partition: support parse gpt partition
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit b5b00c6ba325e0f4aa24b8fd022bdef5586e3a65
Author: Jiuzhu Dong <do...@xiaomi.com>
AuthorDate: Mon Oct 11 10:48:44 2021 +0800
fs/partition: support parse gpt partition
Signed-off-by: Jiuzhu Dong <do...@xiaomi.com>
---
fs/partition/Kconfig | 4 +
fs/partition/Make.defs | 4 +
fs/partition/fs_gpt.c | 482 ++++++++++++++++++++++++++++++++++++++++++++
fs/partition/fs_partition.c | 4 +
fs/partition/partition.h | 6 +
5 files changed, 500 insertions(+)
diff --git a/fs/partition/Kconfig b/fs/partition/Kconfig
index a2f0b70..8221ba4 100644
--- a/fs/partition/Kconfig
+++ b/fs/partition/Kconfig
@@ -15,6 +15,10 @@ config MBR_PARTITION
bool "MBR support"
default n
+config GPT_PARTITION
+ bool "GPT support"
+ default n
+
endmenu
endif
diff --git a/fs/partition/Make.defs b/fs/partition/Make.defs
index 78d72b6..738cf2d 100644
--- a/fs/partition/Make.defs
+++ b/fs/partition/Make.defs
@@ -32,6 +32,10 @@ ifeq ($(CONFIG_MBR_PARTITION),y)
CSRCS += fs_mbr.c
endif
+ifeq ($(CONFIG_GPT_PARTITION),y)
+CSRCS += fs_gpt.c
+endif
+
# Include partition build support
DEPPATH += --dep-path partition
diff --git a/fs/partition/fs_gpt.c b/fs/partition/fs_gpt.c
new file mode 100644
index 0000000..55753fc
--- /dev/null
+++ b/fs/partition/fs_gpt.c
@@ -0,0 +1,482 @@
+/****************************************************************************
+ * fs/partition/fs_gpt.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <crc32.h>
+#include <ctype.h>
+#include <debug.h>
+#include <endian.h>
+
+#include <nuttx/kmalloc.h>
+
+#include "partition.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define GPT_BLOCK_SIZE 512
+#define GPT_HEADER_SIGNATURE 0x5452415020494645ull
+#define GPT_PARTNAME_MAX_SIZE (72 / sizeof(uint16_t))
+#define GPT_LBA_TO_BLOCK(lba, blk) ((le64toh(lba) * 512 + (blk) -1) / (blk))
+#define GPT_MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gpt_guid_s
+{
+ uint8_t b[16];
+};
+
+/* For limited backward compatibility, the space of the legacy MBR is still
+ * reserved in the GPT specification, but it is now used in a way that
+ * prevents MBR-based disk utilities from misrecognizing and possibly
+ * overwriting GPT disks. This is referred to as a protective MBR.
+ */
+
+begin_packed_struct struct legacy_partition_s
+{
+ uint8_t boot_ind; /* 0x80 - active */
+ uint8_t head; /* Starting head */
+ uint8_t sector; /* Starting sector */
+ uint8_t cyl; /* Starting cylinder */
+ uint8_t sys_ind; /* What partition type */
+ uint8_t end_head; /* End head */
+ uint8_t end_sector; /* End sector */
+ uint8_t end_cyl; /* End cylinder */
+ uint32_t start_sect; /* Starting sector counting from 0 */
+ uint32_t nr_sects; /* Nr of sectors in partition */
+} end_packed_struct;
+
+/* The partition table header defines the usable blocks on the disk.
+ * It also defines the number and size of the partition entries that
+ * make up the partition table (offsets 80 and 84 in the table).
+ */
+
+begin_packed_struct struct gpt_header_s
+{
+ uint64_t signature; /* EFI PART */
+ uint32_t revision; /* Revision info */
+ uint32_t header_size; /* Header size in little endian */
+ uint32_t header_crc32; /* CRC32 of header (offset +0 up to header size) */
+ uint32_t reserved1; /* Must be zero */
+ uint64_t my_lba; /* Current LBA (location of this header copy) */
+ uint64_t alternate_lba; /* Backup LBA (location of the other header copy) */
+ uint64_t first_usable_lba; /* First usable LBA for partitions primary partition table last LBA + 1 */
+ uint64_t last_usable_lba; /* Last usable LBA secondary partition table first LBA − 1 */
+ struct gpt_guid_s disk_guid; /* Disk GUID in mixed endian */
+ uint64_t partition_entry_lba; /* Starting LBA of array of partition entries (always 2 in primary copy) */
+ uint32_t num_partition_entries; /* Number of partition entries in array */
+ uint32_t sizeof_partition_entry; /* Size of a single partition entry */
+ uint32_t partition_entry_array_crc32; /* CRC32 of partition entries array in little endian */
+
+ /* The rest of the logical block is reserved by UEFI and must be zero.
+ * EFI standard handles this by:
+ *
+ * uint8_t reserved2[ BlockSize - 92 ];
+ */
+} end_packed_struct;
+
+/* After the header, the Partition Entry Array describes partitions,
+ * using a minimum size of 128 bytes for each entry block.
+ */
+
+/* The 64-bit partition table attributes are shared between 48-bit
+ * common attributes for all partition types, and 16-bit
+ * type-specific attributes
+ */
+
+begin_packed_struct struct gpt_entry_attributes_s
+{
+ uint64_t required_to_function:1;
+ uint64_t reserved:47;
+ uint64_t type_guid_specific:16;
+} end_packed_struct;
+
+begin_packed_struct struct gpt_entry_s
+{
+ struct gpt_guid_s partition_type_guid; /* Partition type GUID */
+ struct gpt_guid_s unique_partition_guid; /* Unique partition GUID */
+ uint64_t starting_lba; /* First LBA */
+ uint64_t ending_lba; /* Last LBA */
+ struct gpt_entry_attributes_s attributes; /* Attribute flags */
+ uint16_t partition_name[GPT_PARTNAME_MAX_SIZE]; /* Partition name */
+} end_packed_struct;
+
+begin_packed_struct struct gpt_ptable_s
+{
+ uint8_t mbr[512];
+ union
+ {
+ struct gpt_header_s gpt_header;
+ uint8_t gpt[512];
+ } u;
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct gpt_guid_s g_null_guid;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: gpt_last_lba
+ *
+ * Description:
+ * Return number of last logical block of device, 0 on error.
+ *
+ * Input Parameters:
+ * state - The partition table state
+ *
+ * Returned Value:
+ * Returns last LBA value on success, 0 on error.
+ * This is stored (by sd and ide-geometry) in
+ * the part[0] entry for this disk, and is the number of
+ * physical sectors available on the disk.
+ *
+ ****************************************************************************/
+
+static inline blkcnt_t gpt_last_lba(FAR struct partition_state_s *state)
+{
+ return (state->nblocks * state->blocksize + GPT_BLOCK_SIZE - 1) /
+ GPT_BLOCK_SIZE - 1;
+}
+
+/****************************************************************************
+ * Name: gpt_alloc_verify_entries()
+ *
+ * Description:
+ * reads and verifies partition entries from disk
+ *
+ * Input Parameters:
+ * state - the handle of partition state
+ * gpt - a GPT header ptr.
+ *
+ * Returned Value:
+ * Returns ptes on success, NULL on error.
+ * Allocates space for PTEs based on information found in @gpt.
+ * Notes: remember to free pte when you're done!
+ *
+ ****************************************************************************/
+
+static FAR struct gpt_entry_s *
+gpt_alloc_verify_entries(FAR struct partition_state_s *state,
+ FAR struct gpt_header_s *gpt)
+{
+ FAR struct gpt_entry_s *pte;
+ unsigned long from;
+ unsigned long size;
+ unsigned long blk;
+ uint32_t crc;
+ int ret;
+
+ size = le32toh(gpt->num_partition_entries) *
+ le32toh(gpt->sizeof_partition_entry);
+ if (!size)
+ {
+ return NULL;
+ }
+
+ blk = (size + (state->blocksize -1)) / state->blocksize;
+ pte = kmm_zalloc(blk * state->blocksize);
+ if (!pte)
+ {
+ return NULL;
+ }
+
+ from = GPT_LBA_TO_BLOCK(gpt->partition_entry_lba,
+ state->blocksize);
+ ret = read_partition_block(state, pte, from, blk);
+ if (ret < 0)
+ {
+ kmm_free(pte);
+ ferr("Read ptr from block failed:%d.\n", ret);
+ return NULL;
+ }
+
+ /* Check the GUID Partition Table Entry Array CRC */
+
+ crc = crc32part((FAR const uint8_t *)pte, size, ~0l) ^ ~0l;
+ if (crc != le32toh(gpt->partition_entry_array_crc32))
+ {
+ ferr("GUID Partitition Entry Array CRC check failed.\n");
+ kmm_free(pte);
+ return NULL;
+ }
+
+ return pte;
+}
+
+/****************************************************************************
+ * Name: gpt_header_is_valid
+ *
+ * Description:
+ * tests one GPT header for validity
+ *
+ * Input Parameters:
+ * state - The partition table state
+ * gpt - is a GPT header ptr.
+ * lba - is the logical block address of the GPT header to test
+ *
+ * Returned Value:
+ * Returns 0 if valid, a negative errno returned on error.
+ *
+ ****************************************************************************/
+
+static int gpt_header_is_valid(FAR struct partition_state_s *state,
+ FAR struct gpt_header_s *gpt, blkcnt_t lba)
+{
+ uint32_t crc;
+ uint32_t origcrc;
+ blkcnt_t lastlba;
+
+ /* Check the GPT header signature */
+
+ if (le64toh(gpt->signature) != GPT_HEADER_SIGNATURE)
+ {
+ ferr("GUID Partition Table Header signature is wrong:"
+ "0x%" PRIx64 " != 0x%" PRIx64 "\n",
+ le64toh(gpt->signature), GPT_HEADER_SIGNATURE);
+ return -EINVAL;
+ }
+
+ /* Check the GUID Partition Table CRC */
+
+ origcrc = gpt->header_crc32;
+ gpt->header_crc32 = 0;
+ crc = crc32part((FAR const uint8_t *)gpt,
+ le32toh(gpt->header_size), ~0l) ^ ~0l;
+ if (crc != le32toh(origcrc))
+ {
+ ferr("GUID Partition Table Header CRC is wrong: %" PRIx32
+ " != %" PRIx32 "\n", crc, le32toh(origcrc));
+ return -EINVAL;
+ }
+
+ gpt->header_crc32 = origcrc;
+
+ /* Check that the my_lba entry points to the LBA that contains
+ * the GUID Partition Table
+ */
+
+ if (le64toh(gpt->my_lba) != lba)
+ {
+ ferr("GPT: my_lba incorrect: %" PRIx64 " != %" PRIx64 "\n",
+ le64toh(gpt->my_lba), lba);
+ return -EINVAL;
+ }
+
+ /* Check the first_usable_lba and last_usable_lba are within the disk. */
+
+ lastlba = gpt_last_lba(state);
+ if (le64toh(gpt->first_usable_lba) > lastlba)
+ {
+ ferr("GPT: first_usable_lba incorrect: %" PRId64 " > %" PRId64 "\n",
+ le64toh(gpt->first_usable_lba), lastlba);
+ return -EINVAL;
+ }
+
+ if (le64toh(gpt->last_usable_lba) > lastlba)
+ {
+ ferr("GPT: last_usable_lba incorrect: %" PRId64 " > %" PRId64 "\n",
+ le64toh(gpt->last_usable_lba), lastlba);
+ return -EINVAL;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: gpt_pte_is_valid()
+ *
+ * Description:
+ * tests one PTE for validity
+ *
+ * Input Parameters:
+ * pte is the pte to check
+ * lastlba is last lba of the disk
+ *
+ * Returned Value:
+ * Returns 1 if valid, 0 on error.
+ *
+ ****************************************************************************/
+
+static inline int gpt_pte_is_valid(FAR const struct gpt_entry_s *pte,
+ blkcnt_t lastlba)
+{
+ if (!memcmp(&pte->partition_type_guid, &g_null_guid,
+ sizeof(g_null_guid)) ||
+ le64toh(pte->starting_lba) > lastlba ||
+ le64toh(pte->ending_lba) > lastlba)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void gpt_part_set_name(FAR struct gpt_entry_s *pte,
+ FAR char *dest, size_t len)
+{
+ int i;
+
+ if (--len > GPT_PARTNAME_MAX_SIZE)
+ {
+ len = GPT_PARTNAME_MAX_SIZE;
+ }
+
+ for (i = 0; i < len; i++)
+ {
+ uint8_t c = pte->partition_name[i];
+ dest[i] = (c && !isprint(c)) ? '.' : c;
+ }
+
+ dest[i] = 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: parse_gpt_partition
+ *
+ * Description:
+ * parse the gpt(EFI GUID Partition Table) partition.
+ *
+ * Input Parameters:
+ * state - The partition table state
+ * handler - The function to be called for each found partition
+ * arg - A caller provided value to return with the handler
+ *
+ * Returned Value:
+ * Zero on success; A negated errno value is returned on a failure
+ *
+ ****************************************************************************/
+
+int parse_gpt_partition(FAR struct partition_state_s *state,
+ partition_handler_t handler,
+ FAR void *arg)
+{
+ FAR struct legacy_partition_s *pmbr;
+ FAR struct gpt_ptable_s *ptbl;
+ FAR struct gpt_header_s *gpt;
+ FAR struct gpt_entry_s *ptes;
+ struct partition_s pentry;
+ int nb_part;
+ int count;
+ int ret;
+
+ /* Read GPT Ptable (LBA0 + LBA1) */
+
+ count = (sizeof(struct gpt_ptable_s) + (state->blocksize - 1)) /
+ state->blocksize;
+ ptbl = kmm_malloc(count * state->blocksize);
+ if (!ptbl)
+ {
+ return -ENOMEM;
+ }
+
+ ret = read_partition_block(state, ptbl, 0, count);
+ if (ret < 0)
+ {
+ goto err;
+ }
+
+ /* Verify mbr is valid */
+
+ pmbr = (FAR struct legacy_partition_s *)&ptbl->mbr[0x1be];
+ if (pmbr->sys_ind != 0xee)
+ {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Verify gpt header is valid */
+
+ gpt = &(ptbl->u.gpt_header);
+ ret = gpt_header_is_valid(state, gpt, 1);
+ if (ret >= 0)
+ {
+ /* Verify gpt header is valid */
+
+ ptes = gpt_alloc_verify_entries(state, gpt);
+ }
+
+ if (ret < 0 || !ptes)
+ {
+ /* Read and Verify backup gpt header is valid */
+
+ finfo("Primary GPT is invalid, using alternate GPT.\n");
+
+ count = (GPT_BLOCK_SIZE + state->blocksize - 1) / state->blocksize;
+ ret = read_partition_block(state, ptbl,
+ GPT_LBA_TO_BLOCK(gpt->alternate_lba,
+ state->blocksize), count);
+ if (ret < 0)
+ {
+ goto err;
+ }
+
+ gpt = (FAR struct gpt_header_s *)ptbl;
+ ret = gpt_header_is_valid(state, gpt,
+ le64toh(gpt->alternate_lba));
+ if (ret >= 0)
+ {
+ /* Verify gpt header is valid */
+
+ ptes = gpt_alloc_verify_entries(state, gpt);
+ }
+ }
+
+ if (ret < 0 || !ptes)
+ {
+ finfo("Alternate GPT is also invalid!!\n");
+ goto err;
+ }
+
+ nb_part = le32toh(gpt->num_partition_entries);
+ for (pentry.index = 0; pentry.index < nb_part; pentry.index++)
+ {
+ pentry.firstblock = GPT_LBA_TO_BLOCK(ptes[pentry.index].starting_lba,
+ state->blocksize);
+ pentry.nblocks = GPT_LBA_TO_BLOCK(ptes[pentry.index].ending_lba,
+ state->blocksize) -
+ pentry.firstblock;
+ pentry.blocksize = state->blocksize;
+ gpt_part_set_name(&ptes[pentry.index], pentry.name,
+ sizeof(pentry.name));
+ handler(&pentry, arg);
+ }
+
+ kmm_free(ptes);
+err:
+ kmm_free(ptbl);
+ return ret;
+}
diff --git a/fs/partition/fs_partition.c b/fs/partition/fs_partition.c
index 95b5799..1c52897 100644
--- a/fs/partition/fs_partition.c
+++ b/fs/partition/fs_partition.c
@@ -53,6 +53,10 @@ static const partition_parser_t g_parser[] =
parse_ptable_partition,
#endif
+#ifdef CONFIG_GPT_PARTITION
+ parse_gpt_partition,
+#endif
+
#ifdef CONFIG_MBR_PARTITION
parse_mbr_partition,
#endif
diff --git a/fs/partition/partition.h b/fs/partition/partition.h
index 2358202..8c79b2c 100644
--- a/fs/partition/partition.h
+++ b/fs/partition/partition.h
@@ -58,6 +58,12 @@ int parse_ptable_partition(FAR struct partition_state_s *state,
FAR void *arg);
#endif
+#ifdef CONFIG_GPT_PARTITION
+int parse_gpt_partition(FAR struct partition_state_s *state,
+ partition_handler_t handler,
+ FAR void *arg);
+#endif
+
#ifdef CONFIG_MBR_PARTITION
int parse_mbr_partition(FAR struct partition_state_s *state,
partition_handler_t handler,