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 <[email protected]>
AuthorDate: Mon Oct 11 10:48:44 2021 +0800

    fs/partition: support parse gpt partition
    
    Signed-off-by: Jiuzhu Dong <[email protected]>
---
 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,

Reply via email to