mirror of
https://git.yoctoproject.org/meta-arm
synced 2026-06-05 02:20:30 +00:00
arm-bsp/tf-m:cs1k: Add GPT library
The patches added in this commit add a generic GPT library for use in flash devices. Corstone1000 could use these to manage partitions during a firmware update. The patches are all backports from trusted-firmware-m (TF-M) main branch and can be removed if Corstone1000 upgrades when the next version of TF-M is released. Signed-off-by: Frazer Carsley <frazer.carsley@arm.com> Signed-off-by: Jon Mason <jon.mason@arm.com>
This commit is contained in:
committed by
Jon Mason
parent
7b687574a1
commit
5fc4c29db9
+217
@@ -0,0 +1,217 @@
|
||||
From 5fc4f3857739a9fe93819cedc4a8108d33bf8241 Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Thu, 27 Nov 2025 10:35:58 +0000
|
||||
Subject: [PATCH] lib: efi_guid: Added EFI GUID library
|
||||
|
||||
This library can be used to generate version 4 UUID/GUIDs according to
|
||||
RFC 4122 by using a PSA crypto driver. A separate header is provided to
|
||||
give access to the struct without the need of a PSA config.
|
||||
|
||||
Change-Id: Ief35b2a4f565889ba2ea0de82e20dc12f6e824e8
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [3bdd2562ebad3115457ea75e58d0554802e12dba]
|
||||
---
|
||||
CMakeLists.txt | 1 +
|
||||
lib/efi_guid/CMakeLists.txt | 26 ++++++++++++
|
||||
lib/efi_guid/inc/efi_guid.h | 36 +++++++++++++++++
|
||||
lib/efi_guid/inc/efi_guid_structs.h | 62 +++++++++++++++++++++++++++++
|
||||
lib/efi_guid/src/efi_guid.c | 33 +++++++++++++++
|
||||
5 files changed, 158 insertions(+)
|
||||
create mode 100644 lib/efi_guid/CMakeLists.txt
|
||||
create mode 100644 lib/efi_guid/inc/efi_guid.h
|
||||
create mode 100644 lib/efi_guid/inc/efi_guid_structs.h
|
||||
create mode 100644 lib/efi_guid/src/efi_guid.c
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 2560dd15e..b120de697 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -44,6 +44,7 @@ project("Trusted Firmware M" VERSION ${TFM_VERSION} LANGUAGES C CXX ASM)
|
||||
|
||||
add_subdirectory(lib/backtrace)
|
||||
add_subdirectory(lib/ext)
|
||||
+add_subdirectory(lib/efi_guid)
|
||||
add_subdirectory(lib/fih)
|
||||
add_subdirectory(lib/tfm_log)
|
||||
add_subdirectory(lib/tfm_log_unpriv)
|
||||
diff --git a/lib/efi_guid/CMakeLists.txt b/lib/efi_guid/CMakeLists.txt
|
||||
new file mode 100644
|
||||
index 000000000..656eb72ea
|
||||
--- /dev/null
|
||||
+++ b/lib/efi_guid/CMakeLists.txt
|
||||
@@ -0,0 +1,26 @@
|
||||
+#-------------------------------------------------------------------------------
|
||||
+# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
|
||||
+#
|
||||
+# SPDX-License-Identifier: BSD-3-Clause
|
||||
+#
|
||||
+#-------------------------------------------------------------------------------
|
||||
+
|
||||
+add_library(tfm_efi_guid STATIC)
|
||||
+
|
||||
+target_sources(tfm_efi_guid
|
||||
+ PRIVATE
|
||||
+ src/efi_guid.c
|
||||
+)
|
||||
+
|
||||
+target_include_directories(tfm_efi_guid
|
||||
+ PUBLIC
|
||||
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||
+ PRIVATE
|
||||
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
+)
|
||||
+
|
||||
+target_link_libraries(tfm_efi_guid
|
||||
+ PUBLIC
|
||||
+ psa_interface
|
||||
+ psa_crypto_config
|
||||
+)
|
||||
diff --git a/lib/efi_guid/inc/efi_guid.h b/lib/efi_guid/inc/efi_guid.h
|
||||
new file mode 100644
|
||||
index 000000000..81f6ad507
|
||||
--- /dev/null
|
||||
+++ b/lib/efi_guid/inc/efi_guid.h
|
||||
@@ -0,0 +1,36 @@
|
||||
+/*
|
||||
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: BSD-3-Clause
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#ifndef __TFM_EFI_GUID_H__
|
||||
+#define __TFM_EFI_GUID_H__
|
||||
+
|
||||
+#include "psa/crypto.h"
|
||||
+#include "efi_guid_structs.h"
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+extern "C" {
|
||||
+#endif
|
||||
+
|
||||
+/**
|
||||
+ * \brief Generates a random version 4 UUID/GUID using a PSA crypto driver.
|
||||
+ *
|
||||
+ * \note See RFC 4112 for details on UUID/GUIDs.
|
||||
+ *
|
||||
+ * \note See psa_generate_random for possible failures.
|
||||
+ *
|
||||
+ * \param[out] guid Pointer populated with the generated UUID/GUID.
|
||||
+ *
|
||||
+ * \return PSA_SUCCESS on success or a PSA error code on failure.
|
||||
+ *
|
||||
+ */
|
||||
+psa_status_t efi_guid_generate_random(struct efi_guid_t *guid);
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+#endif /* __TFM_EFI_GUID_H__ */
|
||||
diff --git a/lib/efi_guid/inc/efi_guid_structs.h b/lib/efi_guid/inc/efi_guid_structs.h
|
||||
new file mode 100644
|
||||
index 000000000..c89e8f692
|
||||
--- /dev/null
|
||||
+++ b/lib/efi_guid/inc/efi_guid_structs.h
|
||||
@@ -0,0 +1,62 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2021, Linaro Limited
|
||||
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: BSD-3-Clause
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#ifndef __TFM_EFI_GUID_STRUCTS_H__
|
||||
+#define __TFM_EFI_GUID_STRUCTS_H__
|
||||
+
|
||||
+#include <stdint.h>
|
||||
+#include <string.h>
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+extern "C" {
|
||||
+#endif
|
||||
+
|
||||
+#define EFI_GUID_NODE_LEN 6
|
||||
+
|
||||
+/**
|
||||
+ * \brief Representation that makes creating GUIDs slightly easier.
|
||||
+ */
|
||||
+struct efi_guid_t {
|
||||
+ uint32_t time_low; /**< Low 32-bits of timestamp. */
|
||||
+ uint16_t time_mid; /**< Middle 16-bits of timestamp. */
|
||||
+ uint16_t time_hi_and_version; /**< High 12-bits of timestamp with 4-bit version. */
|
||||
+ uint8_t clock_seq_hi_and_reserved; /**< High 4-bits of clock sequence with 4-bit variant. */
|
||||
+ uint8_t clock_seq_low; /**< Low 8-bits of clock sequence. */
|
||||
+ uint8_t node[EFI_GUID_NODE_LEN]; /**< 48-bit spatially unique node identifier. */
|
||||
+};
|
||||
+
|
||||
+static inline int efi_guid_cmp(const struct efi_guid_t *g1,
|
||||
+ const struct efi_guid_t *g2)
|
||||
+{
|
||||
+ return memcmp(g1, g2, sizeof(struct efi_guid_t));
|
||||
+}
|
||||
+
|
||||
+static inline void *efi_guid_cpy(const struct efi_guid_t *src,
|
||||
+ struct efi_guid_t *dst)
|
||||
+{
|
||||
+ return memcpy(dst, src, sizeof(struct efi_guid_t));
|
||||
+}
|
||||
+
|
||||
+/** \brief Helper macro to build an EFI GUID structure from individual values. */
|
||||
+#define MAKE_EFI_GUID(a, b, c, d0, d1, e0, e1, e2, e3, e4, e5) \
|
||||
+ { \
|
||||
+ (a) & 0xffffffff, (b)&0xffff, (c)&0xffff, d0, d1, { \
|
||||
+ (e0), (e1), (e2), (e3), (e4), (e5) \
|
||||
+ } \
|
||||
+ }
|
||||
+
|
||||
+/** \brief Helper macro to build the EFI GUID 00000000-0000-0000-0000-0000000000. */
|
||||
+#define NULL_GUID \
|
||||
+ MAKE_EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
+ 0x00, 0x00, 0x00)
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+#endif /* __TFM_EFI_GUID_STRUCTS_H__ */
|
||||
diff --git a/lib/efi_guid/src/efi_guid.c b/lib/efi_guid/src/efi_guid.c
|
||||
new file mode 100644
|
||||
index 000000000..cb8730a51
|
||||
--- /dev/null
|
||||
+++ b/lib/efi_guid/src/efi_guid.c
|
||||
@@ -0,0 +1,33 @@
|
||||
+/*
|
||||
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: BSD-3-Clause
|
||||
+ */
|
||||
+
|
||||
+#include <stdint.h>
|
||||
+
|
||||
+#include "psa/crypto.h"
|
||||
+
|
||||
+#include "efi_guid_structs.h"
|
||||
+#include "efi_guid.h"
|
||||
+
|
||||
+psa_status_t efi_guid_generate_random(struct efi_guid_t *guid)
|
||||
+{
|
||||
+ const psa_status_t ret = psa_generate_random((uint8_t *)guid, sizeof(*guid));
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* According to RFC 4122, counting bits from the right:
|
||||
+ * - Bits 6 and 7 of clock_seq_hi_and_reserved need to be set to
|
||||
+ * 0 and 1 respecitively
|
||||
+ * - Bits 12 through 15 of time_hi_and_version need to be set to
|
||||
+ * 0b0100
|
||||
+ */
|
||||
+ guid->clock_seq_hi_and_reserved &= 0x3F;
|
||||
+ guid->clock_seq_hi_and_reserved |= 0x80;
|
||||
+ guid->time_hi_and_version &= 0x0FFF;
|
||||
+ guid->time_hi_and_version |= 0x4000;
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
From 4a47d926420bfec4a0e687b6ecc4cf52419e4f58 Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Thu, 27 Nov 2025 10:37:28 +0000
|
||||
Subject: [PATCH] lib: efi_soft_crc: Added EFI CRC library
|
||||
|
||||
This library can be used to perform CRC32 calculations using the
|
||||
standard CRC32 polynomial specified by the UEFI spec 2.10. It does not
|
||||
use a lookup table to save on memory. The polynomial used is in reverse
|
||||
order to match little-endian machines.
|
||||
|
||||
Change-Id: Ifce5a1cbbb3ab394bc748305be213a8467610015
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [3f2b707eaadef8008f333bd5c519c1b2544458ab]
|
||||
---
|
||||
lib/ext/CMakeLists.txt | 1 +
|
||||
lib/ext/efi_soft_crc/CMakeLists.txt | 18 ++++++++++++++
|
||||
lib/ext/efi_soft_crc/inc/efi_soft_crc.h | 33 +++++++++++++++++++++++++
|
||||
lib/ext/efi_soft_crc/src/efi_soft_crc.c | 32 ++++++++++++++++++++++++
|
||||
4 files changed, 84 insertions(+)
|
||||
create mode 100644 lib/ext/efi_soft_crc/CMakeLists.txt
|
||||
create mode 100644 lib/ext/efi_soft_crc/inc/efi_soft_crc.h
|
||||
create mode 100644 lib/ext/efi_soft_crc/src/efi_soft_crc.c
|
||||
|
||||
diff --git a/lib/ext/CMakeLists.txt b/lib/ext/CMakeLists.txt
|
||||
index 05f6b8f14..1dffbacfb 100644
|
||||
--- a/lib/ext/CMakeLists.txt
|
||||
+++ b/lib/ext/CMakeLists.txt
|
||||
@@ -10,6 +10,7 @@ add_subdirectory(qcbor)
|
||||
add_subdirectory(t_cose)
|
||||
add_subdirectory(mbedcrypto)
|
||||
add_subdirectory(cmsis)
|
||||
+add_subdirectory(efi_soft_crc)
|
||||
if(BL2)
|
||||
add_subdirectory(mcuboot)
|
||||
endif()
|
||||
diff --git a/lib/ext/efi_soft_crc/CMakeLists.txt b/lib/ext/efi_soft_crc/CMakeLists.txt
|
||||
new file mode 100644
|
||||
index 000000000..47fd2d507
|
||||
--- /dev/null
|
||||
+++ b/lib/ext/efi_soft_crc/CMakeLists.txt
|
||||
@@ -0,0 +1,18 @@
|
||||
+#-------------------------------------------------------------------------------
|
||||
+# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
|
||||
+#
|
||||
+# SPDX-License-Identifier: BSD-3-Clause
|
||||
+#
|
||||
+#-------------------------------------------------------------------------------
|
||||
+
|
||||
+add_library(tfm_efi_soft_crc STATIC)
|
||||
+
|
||||
+target_sources(tfm_efi_soft_crc
|
||||
+ PRIVATE
|
||||
+ src/efi_soft_crc.c
|
||||
+)
|
||||
+
|
||||
+target_include_directories(tfm_efi_soft_crc
|
||||
+ PUBLIC
|
||||
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||
+)
|
||||
diff --git a/lib/ext/efi_soft_crc/inc/efi_soft_crc.h b/lib/ext/efi_soft_crc/inc/efi_soft_crc.h
|
||||
new file mode 100644
|
||||
index 000000000..77baa8987
|
||||
--- /dev/null
|
||||
+++ b/lib/ext/efi_soft_crc/inc/efi_soft_crc.h
|
||||
@@ -0,0 +1,33 @@
|
||||
+/*
|
||||
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: BSD-3-Clause
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#ifndef __TFM_EFI_SOFT_CRC_H__
|
||||
+#define __TFM_EFI_SOFT_CRC_H__
|
||||
+
|
||||
+#include <stddef.h>
|
||||
+#include <stdint.h>
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+extern "C" {
|
||||
+#endif
|
||||
+
|
||||
+/**
|
||||
+ * \brief Updates a CRC32 calculation using ISO 3309 CRC-32.
|
||||
+ *
|
||||
+ * \param[in] old_crc Existing CRC value (0 for the first calculation).
|
||||
+ * \param[in] buf Buffer of bytes to perform the calculation over.
|
||||
+ * \param[in] len Length of \p buf in bytes.
|
||||
+ *
|
||||
+ * \return The calculated CRC32 value.
|
||||
+ */
|
||||
+uint32_t efi_soft_crc32_update(uint32_t old_crc, const uint8_t *buf, size_t len);
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+#endif /* __TFM_EFI_SOFT_CRC_H__ */
|
||||
diff --git a/lib/ext/efi_soft_crc/src/efi_soft_crc.c b/lib/ext/efi_soft_crc/src/efi_soft_crc.c
|
||||
new file mode 100644
|
||||
index 000000000..041a321cd
|
||||
--- /dev/null
|
||||
+++ b/lib/ext/efi_soft_crc/src/efi_soft_crc.c
|
||||
@@ -0,0 +1,32 @@
|
||||
+/* Copyright (C) 2013 Henry S. Warren Jr. You are free to use, copy,
|
||||
+ * and distribute any of the code on this web site, whether modified
|
||||
+ * by you or not.
|
||||
+ */
|
||||
+
|
||||
+#include "efi_soft_crc.h"
|
||||
+
|
||||
+/* The standard polynomial, in reverse */
|
||||
+#define POLYNOMIAL 0xEDB88320
|
||||
+
|
||||
+/* Algorithmic approach to CRC calculation: avoids lookup tables, slow. Byte
|
||||
+ * reversal is avoided by shifting the crc register right instead of left and
|
||||
+ * by using a reversed 32-bit word to represent the polynomial.
|
||||
+ *
|
||||
+ * Derived from work by Henry S. Warren Jr.
|
||||
+ */
|
||||
+uint32_t efi_soft_crc32_update(uint32_t old_crc32, const uint8_t *buf, size_t len)
|
||||
+{
|
||||
+ register uint32_t crc32 = ~old_crc32;
|
||||
+ uint32_t mask;
|
||||
+
|
||||
+ for ( ; len; --len, ++buf)
|
||||
+ {
|
||||
+ crc32 ^= *buf;
|
||||
+ for (size_t i = 0; i < 8; ++i) {
|
||||
+ mask = -(crc32 & 1);
|
||||
+ crc32 = (crc32 >> 1) ^ (POLYNOMIAL & mask);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ~crc32;
|
||||
+}
|
||||
+1495
File diff suppressed because it is too large
Load Diff
+308
@@ -0,0 +1,308 @@
|
||||
From b3775fd8aa2078e5e86d5cdb4164e6fc31fabbfc Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Wed, 11 Feb 2026 11:16:13 +0000
|
||||
Subject: [PATCH] lib: gpt: Expanded how GPT partition can be identified
|
||||
|
||||
The GUID of a partition is not always known, particularly for tables
|
||||
that have been created outside the control of the user. Therefore,
|
||||
allowing the name or type to be used to identify a partition gives the
|
||||
user greater flexibility. These may not be unique, however, and so must
|
||||
be indexed. A single entry is returned rather than a list so as to not
|
||||
dynamically allocate such memory.
|
||||
|
||||
Change-Id: I7687bfc737b244f6e589c4fe6b61c370daf1b062
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [aede42c1c58b5681fe50bbec3a797f69256d108e]
|
||||
---
|
||||
lib/gpt/inc/gpt.h | 34 +++++++++
|
||||
lib/gpt/src/gpt.c | 68 +++++++++++++++--
|
||||
lib/gpt/unittests/gpt/test_gpt.c | 127 +++++++++++++++++++++++++++++++
|
||||
3 files changed, 223 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
|
||||
index d98abe980..947a6b341 100644
|
||||
--- a/lib/gpt/inc/gpt.h
|
||||
+++ b/lib/gpt/inc/gpt.h
|
||||
@@ -48,6 +48,40 @@ __attribute__((nonnull(1,2)))
|
||||
psa_status_t gpt_entry_read(const struct efi_guid_t *guid,
|
||||
struct partition_entry_t *partition_entry);
|
||||
|
||||
+/**
|
||||
+ * \brief Reads the contents of a partition entry identified by name.
|
||||
+ *
|
||||
+ * \param[in] name Name of the partition to read in unicode.
|
||||
+ * \param[in] index Index to read when multiple entries share the same name.
|
||||
+ * \param[out] partition_entry Populated partition entry on success.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided name at \p index. For example,
|
||||
+ * \p index was 1 (second entry) but only one entry was found.
|
||||
+ */
|
||||
+__attribute__((nonnull(1,3)))
|
||||
+psa_status_t gpt_entry_read_by_name(const char name[GPT_ENTRY_NAME_LENGTH],
|
||||
+ const uint32_t index,
|
||||
+ struct partition_entry_t *partition_entry);
|
||||
+
|
||||
+/**
|
||||
+ * \brief Reads the contents of a partition entry identified by type.
|
||||
+ *
|
||||
+ * \param[in] type Type of the partition to read.
|
||||
+ * \param[in] index Index to read when multiple entries share the same type.
|
||||
+ * \param[out] partition_entry Populated partition entry on success.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided type at \p index. For example,
|
||||
+ * \p index was 1 (second entry) but only one entry was found.
|
||||
+ */
|
||||
+__attribute__((nonnull(1,3)))
|
||||
+psa_status_t gpt_entry_read_by_type(const struct efi_guid_t *type,
|
||||
+ const uint32_t index,
|
||||
+ struct partition_entry_t *partition_entry);
|
||||
+
|
||||
/**
|
||||
* \brief Reads the GPT header from the second block (LBA 1).
|
||||
*
|
||||
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
|
||||
index 99d735797..1662b81c2 100644
|
||||
--- a/lib/gpt/src/gpt.c
|
||||
+++ b/lib/gpt/src/gpt.c
|
||||
@@ -177,6 +177,8 @@ static inline uint64_t partition_entry_lba(const struct gpt_t *table,
|
||||
static inline uint64_t gpt_entry_per_lba_count(void);
|
||||
static psa_status_t count_used_partitions(const struct gpt_t *table,
|
||||
uint32_t *num_used);
|
||||
+static inline void parse_entry(struct gpt_entry_t *entry,
|
||||
+ struct partition_entry_t *partition_entry);
|
||||
static psa_status_t read_from_flash(uint64_t required_lba);
|
||||
static psa_status_t read_entry_from_flash(const struct gpt_t *table,
|
||||
uint32_t array_index,
|
||||
@@ -190,6 +192,8 @@ static psa_status_t find_gpt_entry(const struct gpt_t *table,
|
||||
uint32_t *array_index);
|
||||
static psa_status_t mbr_load(struct mbr_t *mbr);
|
||||
static bool gpt_entry_cmp_guid(const struct gpt_entry_t *entry, const void *guid);
|
||||
+static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const void *name);
|
||||
+static bool gpt_entry_cmp_type(const struct gpt_entry_t *entry, const void *type);
|
||||
|
||||
/* PUBLIC API FUNCTIONS */
|
||||
|
||||
@@ -202,12 +206,37 @@ psa_status_t gpt_entry_read(const struct efi_guid_t *guid,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- partition_entry->start = cached_entry.start;
|
||||
- partition_entry->size = cached_entry.end - cached_entry.start + 1;
|
||||
- memcpy(partition_entry->name, cached_entry.name, GPT_ENTRY_NAME_LENGTH);
|
||||
- partition_entry->attr = cached_entry.attr;
|
||||
- partition_entry->partition_guid = cached_entry.unique_guid;
|
||||
- partition_entry->type_guid = cached_entry.partition_type;
|
||||
+ parse_entry(&cached_entry, partition_entry);
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+psa_status_t gpt_entry_read_by_name(const char name[GPT_ENTRY_NAME_LENGTH],
|
||||
+ const uint32_t index,
|
||||
+ struct partition_entry_t *partition_entry)
|
||||
+{
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ const psa_status_t ret = find_gpt_entry(&primary_gpt, gpt_entry_cmp_name, name, index, &cached_entry, NULL);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ parse_entry(&cached_entry, partition_entry);
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+psa_status_t gpt_entry_read_by_type(const struct efi_guid_t *type,
|
||||
+ const uint32_t index,
|
||||
+ struct partition_entry_t *partition_entry)
|
||||
+{
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ const psa_status_t ret = find_gpt_entry(&primary_gpt, gpt_entry_cmp_type, type, index, &cached_entry, NULL);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ parse_entry(&cached_entry, partition_entry);
|
||||
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
@@ -322,6 +351,18 @@ static inline uint64_t gpt_entry_per_lba_count(void)
|
||||
return num_entries;
|
||||
}
|
||||
|
||||
+/* Copies information from the entry to the user visible structure */
|
||||
+static inline void parse_entry(struct gpt_entry_t *entry,
|
||||
+ struct partition_entry_t *partition_entry)
|
||||
+{
|
||||
+ partition_entry->start = entry->start;
|
||||
+ partition_entry->size = entry->end - entry->start + 1;
|
||||
+ memcpy(partition_entry->name, entry->name, GPT_ENTRY_NAME_LENGTH);
|
||||
+ partition_entry->attr = entry->attr;
|
||||
+ partition_entry->partition_guid = entry->unique_guid;
|
||||
+ partition_entry->type_guid = entry->partition_type;
|
||||
+}
|
||||
+
|
||||
/* Compare the entry with the given guid */
|
||||
static bool gpt_entry_cmp_guid(const struct gpt_entry_t *entry, const void *guid)
|
||||
{
|
||||
@@ -331,6 +372,21 @@ static bool gpt_entry_cmp_guid(const struct gpt_entry_t *entry, const void *guid
|
||||
return efi_guid_cmp(&entry_guid, cmp_guid) == 0;
|
||||
}
|
||||
|
||||
+/* Compare the entry with the given name */
|
||||
+static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const void *name)
|
||||
+{
|
||||
+ return memcmp(name, entry->name, GPT_ENTRY_NAME_LENGTH) == 0;
|
||||
+}
|
||||
+
|
||||
+/* Compare the entry with the given type */
|
||||
+static bool gpt_entry_cmp_type(const struct gpt_entry_t *entry, const void *type)
|
||||
+{
|
||||
+ const struct efi_guid_t *cmp_type = (const struct efi_guid_t *)type;
|
||||
+ const struct efi_guid_t entry_type = entry->partition_type;
|
||||
+
|
||||
+ return efi_guid_cmp(&entry_type, cmp_type) == 0;
|
||||
+}
|
||||
+
|
||||
/* Read entry with given GUID from given table and return it if found. */
|
||||
static psa_status_t find_gpt_entry(const struct gpt_t *table,
|
||||
gpt_entry_cmp_t compare,
|
||||
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
index 325887fae..d6671f3fc 100644
|
||||
--- a/lib/gpt/unittests/gpt/test_gpt.c
|
||||
+++ b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
@@ -381,3 +381,130 @@ void test_gpt_entry_read_should_failWhenEntryNotExisting(void)
|
||||
register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read(&non_existing, &entry));
|
||||
}
|
||||
+
|
||||
+void test_gpt_entry_read_by_name_should_populateEntry(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Ensure an entry is found, even with repeat names */
|
||||
+ struct partition_entry_t entry;
|
||||
+ struct gpt_entry_t *desired1 = &(test_partition_array[0]);
|
||||
+ struct efi_guid_t test_guid1 = desired1->guid;
|
||||
+ struct efi_guid_t test_type1 = desired1->type;
|
||||
+
|
||||
+ /* Change the name of something else and ensure it is found */
|
||||
+ struct gpt_entry_t *desired2 = &(test_partition_array[1]);
|
||||
+ struct efi_guid_t test_guid2 = desired2->guid;
|
||||
+ struct efi_guid_t test_type2 = desired2->type;
|
||||
+ memcpy(desired2->name, desired1->name, GPT_ENTRY_NAME_LENGTH);
|
||||
+
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_read_by_name(desired1->name, 0, &entry));
|
||||
+
|
||||
+ /* Ensure this is the correct entry */
|
||||
+ TEST_ASSERT_EQUAL(0, efi_guid_cmp(&test_guid1, &(entry.partition_guid)));
|
||||
+ TEST_ASSERT_EQUAL(0, efi_guid_cmp(&test_type1, &(entry.type_guid)));
|
||||
+ TEST_ASSERT_EQUAL(desired1->start, entry.start);
|
||||
+ TEST_ASSERT_EQUAL(desired1->end, entry.start + entry.size - 1);
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL_MEMORY(desired1->name, entry.name, GPT_ENTRY_NAME_LENGTH);
|
||||
+
|
||||
+ /* Do again but the next entry */
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_read_by_name(desired1->name, 1, &entry));
|
||||
+
|
||||
+ /* Ensure this is the correct entry */
|
||||
+ TEST_ASSERT_EQUAL(0, efi_guid_cmp(&test_guid2, &(entry.partition_guid)));
|
||||
+ TEST_ASSERT_EQUAL(0, efi_guid_cmp(&test_type2, &(entry.type_guid)));
|
||||
+ TEST_ASSERT_EQUAL(desired2->start, entry.start);
|
||||
+ TEST_ASSERT_EQUAL(desired2->end, entry.start + entry.size - 1);
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL_MEMORY(desired2->name, entry.name, GPT_ENTRY_NAME_LENGTH);
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_read_by_name_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ /* Start with an empty GPT */
|
||||
+ setup_empty_gpt();
|
||||
+
|
||||
+ /* Try to read something */
|
||||
+ struct partition_entry_t entry;
|
||||
+ char test_name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read_by_name(test_name, 0, &entry));
|
||||
+
|
||||
+ /* Now, have a non-empty GPT but search for a name that won't exist */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Each entry should be read */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read_by_name(test_name, 0, &entry));
|
||||
+
|
||||
+ /* Finally, search for the second entry of a name that appears only once */
|
||||
+ memcpy(test_name, test_partition_array[0].name, GPT_ENTRY_NAME_LENGTH);
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read_by_name(test_name, 1, &entry));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read_by_name(test_name, TEST_DEFAULT_NUM_PARTITIONS, &entry));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_read_by_type_should_populateEntry(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Ensure an entry is found, even with repeat types */
|
||||
+ struct partition_entry_t entry;
|
||||
+ struct gpt_entry_t *desired1 = &(test_partition_array[0]);
|
||||
+ struct efi_guid_t test_guid1 = desired1->guid;
|
||||
+ struct efi_guid_t test_type1 = desired1->type;
|
||||
+
|
||||
+ struct gpt_entry_t *desired2 = &(test_partition_array[1]);
|
||||
+ struct efi_guid_t test_guid2 = desired2->guid;
|
||||
+ struct efi_guid_t test_type2 = test_type1;
|
||||
+ desired2->type = test_type2;
|
||||
+
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_read_by_type(&test_type1, 0, &entry));
|
||||
+
|
||||
+ /* Ensure this is the correct entry */
|
||||
+ TEST_ASSERT_EQUAL(0, efi_guid_cmp(&test_guid1, &(entry.partition_guid)));
|
||||
+ TEST_ASSERT_EQUAL(0, efi_guid_cmp(&test_type1, &(entry.type_guid)));
|
||||
+ TEST_ASSERT_EQUAL(desired1->start, entry.start);
|
||||
+ TEST_ASSERT_EQUAL(desired1->end, entry.start + entry.size - 1);
|
||||
+
|
||||
+ /* Name is unicode */
|
||||
+ TEST_ASSERT_EQUAL_MEMORY(desired1->name, entry.name, GPT_ENTRY_NAME_LENGTH);
|
||||
+
|
||||
+ /* Do again but the next entry */
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_read_by_type(&test_type1, 1, &entry));
|
||||
+
|
||||
+ /* Ensure this is the correct entry */
|
||||
+ TEST_ASSERT_EQUAL(0, efi_guid_cmp(&test_guid2, &(entry.partition_guid)));
|
||||
+ TEST_ASSERT_EQUAL(0, efi_guid_cmp(&test_type2, &(entry.type_guid)));
|
||||
+ TEST_ASSERT_EQUAL(desired2->start, entry.start);
|
||||
+ TEST_ASSERT_EQUAL(desired2->end, entry.start + entry.size - 1);
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL_MEMORY(desired2->name, entry.name, GPT_ENTRY_NAME_LENGTH);
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_read_by_type_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ /* Start with an empty GPT */
|
||||
+ setup_empty_gpt();
|
||||
+
|
||||
+ /* Try to read something */
|
||||
+ struct partition_entry_t entry;
|
||||
+ struct efi_guid_t test_type = MAKE_EFI_GUID(11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read_by_type(&test_type, 0, &entry));
|
||||
+
|
||||
+ /* Now, have a non-empty GPT but search for a type that won't exist */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Each entry should be read */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read_by_type(&test_type, 0, &entry));
|
||||
+
|
||||
+ /* Finally, search for the second entry of a type that appears only once */
|
||||
+ struct efi_guid_t existing_type = test_partition_array[0].type;
|
||||
+ efi_guid_cpy(&existing_type, &test_type);
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read_by_type(&test_type, 1, &entry));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_read_by_type(&test_type, TEST_DEFAULT_NUM_PARTITIONS, &entry));
|
||||
+}
|
||||
+961
@@ -0,0 +1,961 @@
|
||||
From 02ec696240d0225ed6473ea5b01ec6617d897a41 Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Wed, 11 Feb 2026 11:23:31 +0000
|
||||
Subject: [PATCH] lib: gpt: Added operations to modify partitions
|
||||
|
||||
The operations added allow the user to modify existing GPT partitions,
|
||||
apart from moving them; that is, they are all metadata changes. The
|
||||
library also handles updating both primary and backup partition entry
|
||||
arrays and headers after each modification.
|
||||
|
||||
Each modification on an entry is buffered in order to reduce the number
|
||||
of flash erase operations. A simple heuristic of "write on every n
|
||||
operations" is used to prevent infinite buffering. The buffering is most
|
||||
effective when consecutive entries are operated on. If the mapping of
|
||||
LBA to flash sector is not 1:1, the flash driver the platform registers
|
||||
with the library may implement its own buffering for further
|
||||
optimisation.
|
||||
|
||||
Change-Id: Ie358464427d66883e1681497a2630a8ef281e528
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [ab942ecb7cb62bcfe1e5701f3f3adbf1eebd330a]
|
||||
---
|
||||
lib/gpt/CMakeLists.txt | 1 +
|
||||
lib/gpt/inc/gpt.h | 69 +++++
|
||||
lib/gpt/inc/gpt_flash.h | 41 ++-
|
||||
lib/gpt/src/gpt.c | 419 +++++++++++++++++++++++++++++-
|
||||
lib/gpt/unittests/gpt/test_gpt.c | 187 +++++++++++++
|
||||
lib/gpt/unittests/gpt/utcfg.cmake | 2 +
|
||||
6 files changed, 712 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/lib/gpt/CMakeLists.txt b/lib/gpt/CMakeLists.txt
|
||||
index 2b5d6af8f..25d0de9cd 100644
|
||||
--- a/lib/gpt/CMakeLists.txt
|
||||
+++ b/lib/gpt/CMakeLists.txt
|
||||
@@ -37,4 +37,5 @@ target_link_libraries(tfm_gpt
|
||||
tfm_log_headers
|
||||
PRIVATE
|
||||
tfm_efi_guid
|
||||
+ tfm_efi_soft_crc
|
||||
)
|
||||
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
|
||||
index 947a6b341..6e7bb360e 100644
|
||||
--- a/lib/gpt/inc/gpt.h
|
||||
+++ b/lib/gpt/inc/gpt.h
|
||||
@@ -82,6 +82,75 @@ psa_status_t gpt_entry_read_by_type(const struct efi_guid_t *type,
|
||||
const uint32_t index,
|
||||
struct partition_entry_t *partition_entry);
|
||||
|
||||
+/**
|
||||
+ * \brief Renames a partition entry.
|
||||
+ *
|
||||
+ * \param[in] guid Entry to rename.
|
||||
+ * \param[in] name New unicode name for the entry.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GUID.
|
||||
+ * \retval PSA_ERROR_INVALID_ARGUMENT Empty name.
|
||||
+ */
|
||||
+__attribute__((nonnull(1,2)))
|
||||
+psa_status_t gpt_entry_rename(const struct efi_guid_t *guid,
|
||||
+ const char name[GPT_ENTRY_NAME_LENGTH]);
|
||||
+
|
||||
+/**
|
||||
+ * \brief Changes the type of a partition.
|
||||
+ *
|
||||
+ * \param[in] guid Entry to update.
|
||||
+ * \param[in] type New type GUID.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_INVALID_ARGUMENT \p type is the null GUID.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GUID.
|
||||
+ */
|
||||
+__attribute__((nonnull(1,2)))
|
||||
+psa_status_t gpt_entry_change_type(const struct efi_guid_t *guid,
|
||||
+ const struct efi_guid_t *type);
|
||||
+
|
||||
+/**
|
||||
+ * \brief Adds attributes to a partition entry.
|
||||
+ *
|
||||
+ * \param[in] guid Entry to modify.
|
||||
+ * \param[in] attr Attributes to add.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GUID.
|
||||
+ */
|
||||
+__attribute__((nonnull(1)))
|
||||
+psa_status_t gpt_attr_add(const struct efi_guid_t *guid, const uint64_t attr);
|
||||
+
|
||||
+/**
|
||||
+ * \brief Removes attributes from a partition entry.
|
||||
+ *
|
||||
+ * \param[in] guid Entry to modify.
|
||||
+ * \param[in] attr Attributes to remove.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GUID.
|
||||
+ */
|
||||
+__attribute__((nonnull(1)))
|
||||
+psa_status_t gpt_attr_remove(const struct efi_guid_t *guid, const uint64_t attr);
|
||||
+
|
||||
+/**
|
||||
+ * \brief Sets attributes for a partition entry.
|
||||
+ *
|
||||
+ * \param[in] guid Entry to modify.
|
||||
+ * \param[in] attr Attributes to set.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GUID.
|
||||
+ */
|
||||
+__attribute__((nonnull(1)))
|
||||
+psa_status_t gpt_attr_set(const struct efi_guid_t *guid, const uint64_t attr);
|
||||
+
|
||||
/**
|
||||
* \brief Reads the GPT header from the second block (LBA 1).
|
||||
*
|
||||
diff --git a/lib/gpt/inc/gpt_flash.h b/lib/gpt/inc/gpt_flash.h
|
||||
index 2b43390c1..ada5f6932 100644
|
||||
--- a/lib/gpt/inc/gpt_flash.h
|
||||
+++ b/lib/gpt/inc/gpt_flash.h
|
||||
@@ -57,13 +57,48 @@ __attribute__((nonnull(2)))
|
||||
typedef ssize_t (*gpt_flash_read_t)(uint64_t lba,
|
||||
void *buf);
|
||||
|
||||
+/**
|
||||
+ * \brief Function that writes to a logical block address.
|
||||
+ *
|
||||
+ * \param[in] lba Logical block address to write to.
|
||||
+ * \param[in] buf Buffer to write from. Must be at least the size of an LBA.
|
||||
+ *
|
||||
+ * \return Number of bytes written on success or a negative error code on failure.
|
||||
+ * \retval GPT_FLASH_NOT_INIT The flash driver has not been initialised.
|
||||
+ * \retval GPT_FLASH_UNAVAILABLE The flash driver is unavailable.
|
||||
+ * \retval GPT_FLASH_BAD_PARAM \p lba is not a valid address (for example larger than the flash size).
|
||||
+ * \retval GPT_FLASH_GENERIC_ERROR Unspecified error.
|
||||
+ */
|
||||
+__attribute__((nonnull(2)))
|
||||
+typedef ssize_t (*gpt_flash_write_t)(uint64_t lba,
|
||||
+ const void *buf);
|
||||
+
|
||||
+/**
|
||||
+ * \brief Function that erases consecutive logical blocks.
|
||||
+ *
|
||||
+ * \param[in] lba Starting logical block address.
|
||||
+ * \param[in] num_blocks Number of blocks to erase.
|
||||
+ *
|
||||
+ * \return Number of blocks erased on success or a negative error code on failure.
|
||||
+ * \retval GPT_FLASH_NOT_INIT The flash driver has not been initialised.
|
||||
+ * \retval GPT_FLASH_UNAVAILABLE The flash driver is unavailable.
|
||||
+ * \retval GPT_FLASH_BAD_PARAM \p num_blocks is zero.
|
||||
+ * \retval GPT_FLASH_BAD_PARAM One of the requested blocks is invalid (for example,
|
||||
+ * \p lba + \p num_blocks exceeds the flash size).
|
||||
+ * \retval GPT_FLASH_BAD_PARAM \p lba is not a valid address (for example larger than the flash size).
|
||||
+ * \retval GPT_FLASH_GENERIC_ERROR Unspecified error.
|
||||
+ */
|
||||
+typedef ssize_t (*gpt_flash_erase_t)(uint64_t lba, size_t num_blocks);
|
||||
+
|
||||
/**
|
||||
* \brief Interface for interacting with the flash driver.
|
||||
*/
|
||||
struct gpt_flash_driver_t {
|
||||
- gpt_flash_init_t init; /**< Flash initialisation routine. */
|
||||
- gpt_flash_uninit_t uninit; /**< Flash deinitialisation routine. */
|
||||
- gpt_flash_read_t read; /**< Routine used to read a logical block. */
|
||||
+ gpt_flash_init_t init; /**< Flash initialisation routine. */
|
||||
+ gpt_flash_uninit_t uninit; /**< Flash deinitialisation routine. */
|
||||
+ gpt_flash_read_t read; /**< Routine used to read a logical block. */
|
||||
+ gpt_flash_write_t write; /**< Routine used to write a logical block. */
|
||||
+ gpt_flash_erase_t erase; /**< Routine used to erase logical blocks. */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
|
||||
index 1662b81c2..2cfcdff07 100644
|
||||
--- a/lib/gpt/src/gpt.c
|
||||
+++ b/lib/gpt/src/gpt.c
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "gpt_flash.h"
|
||||
#include "tfm_log.h"
|
||||
#include "efi_guid_structs.h"
|
||||
+#include "efi_soft_crc.h"
|
||||
|
||||
/* This needs to be defined by the platform and is used by the GPT library as
|
||||
* the number of bytes in a Logical Block Address (LBA)
|
||||
@@ -146,6 +147,15 @@ typedef bool (*gpt_entry_cmp_t)(const struct gpt_entry_t *, const void *);
|
||||
/* The LBA for the backup table */
|
||||
static uint64_t backup_gpt_lba = 0;
|
||||
|
||||
+/* The LBA for the partition array for the backup table. Rather than storing
|
||||
+ * the entire table header in memory, just this is stored so that updates can
|
||||
+ * be done without reading from flash
|
||||
+ */
|
||||
+static uint64_t backup_gpt_array_lba = 0;
|
||||
+
|
||||
+/* CRC for backup header. Because the LBAs differ, so too will the CRC */
|
||||
+static uint32_t backup_crc32 = 0;
|
||||
+
|
||||
/* The flash driver, used to perform I/O */
|
||||
static struct gpt_flash_driver_t *plat_flash_driver = NULL;
|
||||
|
||||
@@ -165,6 +175,9 @@ static uint8_t lba_buf[TFM_GPT_BLOCK_SIZE] = {0};
|
||||
*/
|
||||
static uint64_t cached_lba = 0;
|
||||
|
||||
+/* True if write was buffered */
|
||||
+static bool write_buffered = false;
|
||||
+
|
||||
/* Helper function prototypes */
|
||||
__attribute__((unused))
|
||||
static void print_guid(struct efi_guid_t guid);
|
||||
@@ -174,7 +187,9 @@ __attribute__((unused))
|
||||
static psa_status_t unicode_to_ascii(const char *unicode, char *ascii);
|
||||
static inline uint64_t partition_entry_lba(const struct gpt_t *table,
|
||||
uint32_t array_index);
|
||||
+static inline uint64_t partition_array_last_lba(const struct gpt_t *table);
|
||||
static inline uint64_t gpt_entry_per_lba_count(void);
|
||||
+static inline void swap_headers(const struct gpt_header_t *src, struct gpt_header_t *dst);
|
||||
static psa_status_t count_used_partitions(const struct gpt_t *table,
|
||||
uint32_t *num_used);
|
||||
static inline void parse_entry(struct gpt_entry_t *entry,
|
||||
@@ -184,6 +199,15 @@ static psa_status_t read_entry_from_flash(const struct gpt_t *table,
|
||||
uint32_t array_index,
|
||||
struct gpt_entry_t *entry);
|
||||
static psa_status_t read_table_from_flash(struct gpt_t *table, bool is_primary);
|
||||
+static psa_status_t flush_lba_buf(void);
|
||||
+static psa_status_t write_to_flash(uint64_t lba);
|
||||
+static psa_status_t write_entries_to_flash(uint32_t lbas_into_array, bool no_header_update);
|
||||
+static psa_status_t write_entry(uint32_t array_index,
|
||||
+ const struct gpt_entry_t *entry,
|
||||
+ bool no_header_update);
|
||||
+static psa_status_t write_header_to_flash(const struct gpt_t *table);
|
||||
+static psa_status_t write_headers_to_flash(void);
|
||||
+static psa_status_t update_header(uint32_t num_partitions);
|
||||
static psa_status_t find_gpt_entry(const struct gpt_t *table,
|
||||
gpt_entry_cmp_t compare,
|
||||
const void *attr,
|
||||
@@ -241,16 +265,146 @@ psa_status_t gpt_entry_read_by_type(const struct efi_guid_t *type,
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
|
||||
+psa_status_t gpt_entry_rename(const struct efi_guid_t *guid, const char name[GPT_ENTRY_NAME_LENGTH])
|
||||
+{
|
||||
+ if (name[0] == '\0') {
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ uint32_t cached_index;
|
||||
+ const psa_status_t ret = find_gpt_entry(
|
||||
+ &primary_gpt,
|
||||
+ gpt_entry_cmp_guid,
|
||||
+ guid,
|
||||
+ 0,
|
||||
+ &cached_entry,
|
||||
+ &cached_index);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* NOOP if no name change. Prevents header update. */
|
||||
+ if (memcmp(cached_entry.name, name, GPT_ENTRY_NAME_LENGTH) == 0) {
|
||||
+ return PSA_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
+ memcpy(cached_entry.name, name, GPT_ENTRY_NAME_LENGTH);
|
||||
+ cached_entry.name[GPT_ENTRY_NAME_LENGTH - 1] = '\0';
|
||||
+ cached_entry.name[GPT_ENTRY_NAME_LENGTH - 2] = '\0';
|
||||
+ return write_entry(cached_index, &cached_entry, false);
|
||||
+}
|
||||
+
|
||||
+psa_status_t gpt_entry_change_type(const struct efi_guid_t *guid, const struct efi_guid_t *type)
|
||||
+{
|
||||
+ struct efi_guid_t null_type = NULL_GUID;
|
||||
+ if (efi_guid_cmp(&null_type, type) == 0) {
|
||||
+ ERROR("Cannot set type to null-GUID; delete instead\n");
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ uint32_t cached_index;
|
||||
+ const psa_status_t ret = find_gpt_entry(
|
||||
+ &primary_gpt,
|
||||
+ gpt_entry_cmp_guid,
|
||||
+ guid,
|
||||
+ 0,
|
||||
+ &cached_entry,
|
||||
+ &cached_index);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ cached_entry.partition_type = *type;
|
||||
+
|
||||
+ return write_entry(cached_index, &cached_entry, false);
|
||||
+}
|
||||
+
|
||||
+psa_status_t gpt_attr_add(const struct efi_guid_t *guid, const uint64_t attr)
|
||||
+{
|
||||
+ /* This quick check prevents I/O from happening for a no-op */
|
||||
+ if (attr == 0) {
|
||||
+ return PSA_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ uint32_t cached_index;
|
||||
+ const psa_status_t ret = find_gpt_entry(
|
||||
+ &primary_gpt,
|
||||
+ gpt_entry_cmp_guid,
|
||||
+ guid,
|
||||
+ 0,
|
||||
+ &cached_entry,
|
||||
+ &cached_index);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ cached_entry.attr |= attr;
|
||||
+
|
||||
+ return write_entry(cached_index, &cached_entry, false);
|
||||
+}
|
||||
+
|
||||
+psa_status_t gpt_attr_remove(const struct efi_guid_t *guid, const uint64_t attr)
|
||||
+{
|
||||
+ /* This quick check prevents I/O from happening for a no-op */
|
||||
+ if (attr == 0) {
|
||||
+ return PSA_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ uint32_t cached_index;
|
||||
+ const psa_status_t ret = find_gpt_entry(
|
||||
+ &primary_gpt,
|
||||
+ gpt_entry_cmp_guid,
|
||||
+ guid,
|
||||
+ 0,
|
||||
+ &cached_entry,
|
||||
+ &cached_index);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ cached_entry.attr &= ~(attr);
|
||||
+
|
||||
+ return write_entry(cached_index, &cached_entry, false);
|
||||
+}
|
||||
+
|
||||
+psa_status_t gpt_attr_set(const struct efi_guid_t *guid, const uint64_t attr)
|
||||
+{
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ uint32_t cached_index;
|
||||
+ const psa_status_t ret = find_gpt_entry(
|
||||
+ &primary_gpt,
|
||||
+ gpt_entry_cmp_guid,
|
||||
+ guid,
|
||||
+ 0,
|
||||
+ &cached_entry,
|
||||
+ &cached_index);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ cached_entry.attr = attr;
|
||||
+
|
||||
+ return write_entry(cached_index, &cached_entry, false);
|
||||
+}
|
||||
+
|
||||
/* Initialises GPT from first block. */
|
||||
psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_partitions)
|
||||
{
|
||||
cached_lba = 0;
|
||||
+ write_buffered = false;
|
||||
if (max_partitions < GPT_MIN_PARTITIONS) {
|
||||
ERROR("Minimum number of partitions is %d\n", GPT_MIN_PARTITIONS);
|
||||
return PSA_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
- if (flash_driver->read == NULL) {
|
||||
+ if (flash_driver->read == NULL ||
|
||||
+ flash_driver->write == NULL ||
|
||||
+ flash_driver->erase == NULL)
|
||||
+ {
|
||||
ERROR("I/O functions must be defined\n");
|
||||
return PSA_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
@@ -303,6 +457,7 @@ psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_part
|
||||
if (ret != PSA_SUCCESS) {
|
||||
goto fail_load;
|
||||
}
|
||||
+ backup_gpt_array_lba = backup_gpt.header.array_lba;
|
||||
} else {
|
||||
WARN("Backup GPT location is unknown!\n");
|
||||
}
|
||||
@@ -314,7 +469,9 @@ fail_load:
|
||||
plat_flash_driver = NULL;
|
||||
plat_max_partitions = 0;
|
||||
backup_gpt_lba = 0;
|
||||
+ backup_gpt_array_lba = 0;
|
||||
cached_lba = 0;
|
||||
+ write_buffered = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -324,6 +481,11 @@ psa_status_t gpt_uninit(void)
|
||||
psa_status_t ret = PSA_SUCCESS;
|
||||
|
||||
if (plat_flash_driver) {
|
||||
+ /* Flush the in-memory buffer */
|
||||
+ if (write_buffered) {
|
||||
+ ret = flush_lba_buf();
|
||||
+ }
|
||||
+
|
||||
/* Uninitialise driver if function provided */
|
||||
if (plat_flash_driver->uninit != NULL) {
|
||||
if (plat_flash_driver->uninit() != 0) {
|
||||
@@ -336,7 +498,9 @@ psa_status_t gpt_uninit(void)
|
||||
plat_flash_driver = NULL;
|
||||
plat_max_partitions = 0;
|
||||
backup_gpt_lba = 0;
|
||||
+ backup_gpt_array_lba = 0;
|
||||
cached_lba = 0;
|
||||
+ write_buffered = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -422,6 +586,47 @@ static psa_status_t find_gpt_entry(const struct gpt_t *table,
|
||||
return io_failure ? PSA_ERROR_STORAGE_FAILURE : PSA_ERROR_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
+/* Updates the header of the GPT based on new number of partitions */
|
||||
+static psa_status_t update_header(uint32_t num_partitions)
|
||||
+{
|
||||
+ primary_gpt.num_used_partitions = num_partitions;
|
||||
+ struct gpt_header_t *header = &(primary_gpt.header);
|
||||
+
|
||||
+ /* Take the CRC of the partition array */
|
||||
+ uint32_t crc = 0;
|
||||
+ for (uint32_t i = 0; i < header->num_partitions; ++i) {
|
||||
+ uint8_t entry_buf[header->entry_size];
|
||||
+ memset(entry_buf, 0, header->entry_size);
|
||||
+ struct gpt_entry_t *entry = (struct gpt_entry_t *)entry_buf;
|
||||
+
|
||||
+ psa_status_t ret = read_entry_from_flash(&primary_gpt, i, entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ crc = efi_soft_crc32_update(crc, entry_buf, header->entry_size);
|
||||
+ }
|
||||
+ header->array_crc = crc;
|
||||
+
|
||||
+ /* Calculate new CRC32 for primary header */
|
||||
+ header->header_crc = 0;
|
||||
+ header->header_crc = efi_soft_crc32_update(0, (uint8_t *)header, GPT_HEADER_SIZE);
|
||||
+
|
||||
+ /* Calculate new CRC32 for backup header */
|
||||
+ struct gpt_header_t backup_header = {0};
|
||||
+ swap_headers(header, &backup_header);
|
||||
+ backup_header.header_crc = 0;
|
||||
+ backup_crc32 = efi_soft_crc32_update(0, (uint8_t *)&backup_header, GPT_HEADER_SIZE);
|
||||
+
|
||||
+ /* Write headers */
|
||||
+ const psa_status_t ret = write_headers_to_flash();
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ ERROR("Unable to write headers to flash\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
/* Load MBR from flash */
|
||||
static psa_status_t mbr_load(struct mbr_t *mbr)
|
||||
{
|
||||
@@ -452,10 +657,16 @@ static psa_status_t mbr_load(struct mbr_t *mbr)
|
||||
*/
|
||||
static psa_status_t read_from_flash(uint64_t required_lba)
|
||||
{
|
||||
- ssize_t ret;
|
||||
-
|
||||
if (required_lba != cached_lba) {
|
||||
- ret = plat_flash_driver->read(required_lba, lba_buf);
|
||||
+ if (write_buffered && cached_lba != 0) {
|
||||
+ psa_status_t ret = flush_lba_buf();
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ write_buffered = false;
|
||||
+
|
||||
+ ssize_t ret = plat_flash_driver->read(required_lba, lba_buf);
|
||||
if (ret != TFM_GPT_BLOCK_SIZE) {
|
||||
ERROR("Unable to read from flash at block 0x%08x%08x\n",
|
||||
(uint32_t)(required_lba >> 32),
|
||||
@@ -477,6 +688,14 @@ static uint64_t inline partition_entry_lba(const struct gpt_t *table,
|
||||
return table->header.array_lba + (array_index / gpt_entry_per_lba_count());
|
||||
}
|
||||
|
||||
+/* Returns the last LBA used by the partition entry array */
|
||||
+static uint64_t inline partition_array_last_lba(const struct gpt_t *table)
|
||||
+{
|
||||
+ return (table->num_used_partitions == 0 ?
|
||||
+ table->header.array_lba :
|
||||
+ partition_entry_lba(table, table->num_used_partitions - 1));
|
||||
+}
|
||||
+
|
||||
/* Returns the number of partition entries used in the array, assuming the
|
||||
* array is not sparse
|
||||
*/
|
||||
@@ -539,6 +758,198 @@ static psa_status_t read_table_from_flash(struct gpt_t *table, bool is_primary)
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
|
||||
+/* Writes the in-memory LBA buffer to flash, taking care of multiple writes if
|
||||
+ * needed.
|
||||
+ */
|
||||
+static psa_status_t flush_lba_buf(void)
|
||||
+{
|
||||
+ /* Prevent recursive calls, as the various writes below may attempt to
|
||||
+ * flush, particularly those making multiple writes
|
||||
+ */
|
||||
+ static bool in_flush = false;
|
||||
+ if (in_flush) {
|
||||
+ return PSA_SUCCESS;
|
||||
+ }
|
||||
+ in_flush = true;
|
||||
+ write_buffered = false;
|
||||
+ psa_status_t ret = PSA_SUCCESS;
|
||||
+
|
||||
+ /* Commit to flash what is in the buffer. Also update the backup table if
|
||||
+ * the cached LBA was part of the primary table (or vise-versa)
|
||||
+ */
|
||||
+ uint64_t array_size = partition_array_last_lba(&primary_gpt) - primary_gpt.header.array_lba + 1;
|
||||
+
|
||||
+ if (cached_lba == PRIMARY_GPT_LBA || (backup_gpt_lba != 0 && cached_lba == backup_gpt_lba)) {
|
||||
+ /* Write both backup and primary headers */
|
||||
+ ret = write_headers_to_flash();
|
||||
+ } else if (PRIMARY_GPT_ARRAY_LBA <= cached_lba &&
|
||||
+ cached_lba <= partition_array_last_lba(&primary_gpt))
|
||||
+ {
|
||||
+ /* Primary array entry. Write to backup and primary array */
|
||||
+ ret = write_entries_to_flash(cached_lba - PRIMARY_GPT_ARRAY_LBA, false);
|
||||
+ } else if (backup_gpt_array_lba != 0 &&
|
||||
+ backup_gpt_array_lba <= cached_lba &&
|
||||
+ cached_lba <= backup_gpt_array_lba + array_size - 1)
|
||||
+ {
|
||||
+ /* Backup array entry. Write to backup and primary array */
|
||||
+ ret = write_entries_to_flash(cached_lba - backup_gpt_array_lba, false);
|
||||
+ } else {
|
||||
+ /* Shouldn't be possible */
|
||||
+ ERROR("Unknown data in LBA cache, discarding\n");
|
||||
+ }
|
||||
+
|
||||
+ in_flush = false;
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/* Write to the flash at the specified LBA */
|
||||
+static psa_status_t write_to_flash(uint64_t lba)
|
||||
+{
|
||||
+ if (plat_flash_driver->erase(lba, 1) != 1) {
|
||||
+ ERROR("Unable to erase flash at LBA 0x%08x%08x\n",
|
||||
+ (uint32_t)(lba >> 32),
|
||||
+ (uint32_t)lba);
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
+ if (plat_flash_driver->write(lba, lba_buf) != TFM_GPT_BLOCK_SIZE) {
|
||||
+ ERROR("Unable to program flash at LBA 0x%08x%08x\n",
|
||||
+ (uint32_t)(lba >> 32),
|
||||
+ (uint32_t)lba);
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+/* Writes the in-memory buffer to both the primary and backup partition arrays.
|
||||
+ * This should only be used when it is certain that the cached lba is part of
|
||||
+ * either the primary or backup partition array
|
||||
+ */
|
||||
+static psa_status_t write_entries_to_flash(uint32_t lbas_into_array, bool no_header_update)
|
||||
+{
|
||||
+ psa_status_t ret;
|
||||
+
|
||||
+ if (backup_gpt_array_lba != 0) {
|
||||
+ ret = write_to_flash(backup_gpt_array_lba + lbas_into_array);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ ERROR("Unable to write entry to backup partition array\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ } else {
|
||||
+ WARN("Backup array LBA unknown!\n");
|
||||
+ }
|
||||
+
|
||||
+ ret = write_to_flash(PRIMARY_GPT_ARRAY_LBA + lbas_into_array);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ ERROR("Unable to write entry to primary partition array\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Update the header unless the user specifies not to, This might be useful
|
||||
+ * if it is known that multiple entries are being written, such as on removal
|
||||
+ * or defragmentation operations
|
||||
+ */
|
||||
+ if (!no_header_update) {
|
||||
+ return update_header(primary_gpt.num_used_partitions);
|
||||
+ }
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+/* Writes a GPT entry to flash or the in-memory buffer. The buffer is flushed
|
||||
+ * to both the primary and backup partition entry arrays ocassionally. When the
|
||||
+ * buffer is flushed, the header is updated unless no_header_update is true.
|
||||
+ */
|
||||
+static psa_status_t write_entry(uint32_t array_index,
|
||||
+ const struct gpt_entry_t *entry,
|
||||
+ bool no_header_update)
|
||||
+{
|
||||
+ /* Use this for a very simple, very dumb buffering heuristic. Flush every
|
||||
+ * time an LBA's worth of entries have been written (flush every nth
|
||||
+ * operation).
|
||||
+ */
|
||||
+ static uint32_t num_writes = 0;
|
||||
+
|
||||
+ /* First, ensure the entry is part of the buffered block. In most cases,
|
||||
+ * this will be a no-op
|
||||
+ */
|
||||
+ psa_status_t ret = read_from_flash(partition_entry_lba(&primary_gpt, array_index));
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Copy into buffer */
|
||||
+ uint32_t index_in_lba = array_index % gpt_entry_per_lba_count();
|
||||
+ memcpy(lba_buf + index_in_lba * primary_gpt.header.entry_size, entry, GPT_ENTRY_SIZE);
|
||||
+
|
||||
+ /* Write on every nth operation. */
|
||||
+ if (++num_writes == gpt_entry_per_lba_count()) {
|
||||
+ /* Write the buffer to flash */
|
||||
+ num_writes = 0;
|
||||
+ write_buffered = false;
|
||||
+
|
||||
+ ret = write_entries_to_flash(cached_lba - PRIMARY_GPT_ARRAY_LBA, no_header_update);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ } else {
|
||||
+ write_buffered = true;
|
||||
+ }
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+/* Writes GPT header to flash. Returns PSA_SUCCESS on success or a PSA error on failure */
|
||||
+static psa_status_t write_header_to_flash(const struct gpt_t *table)
|
||||
+{
|
||||
+ /* Ensure the in-memory LBA buffer has the header. Because the header is
|
||||
+ * also in memory, there is no need to read it again before writing.
|
||||
+ */
|
||||
+ uint8_t temp_buf[GPT_HEADER_SIZE];
|
||||
+ memcpy(temp_buf, lba_buf, GPT_HEADER_SIZE);
|
||||
+ memcpy(lba_buf, &(table->header), GPT_HEADER_SIZE);
|
||||
+ const psa_status_t ret = write_to_flash(table->header.current_lba);
|
||||
+ memcpy(lba_buf, temp_buf, GPT_HEADER_SIZE);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/* Writes GPT headers for backup and primary tables to flash. */
|
||||
+static psa_status_t write_headers_to_flash(void)
|
||||
+{
|
||||
+ /* Backup table first, then primary */
|
||||
+ struct gpt_t backup_gpt;
|
||||
+ swap_headers(&(primary_gpt.header), &(backup_gpt.header));
|
||||
+ backup_gpt.header.header_crc = backup_crc32;
|
||||
+ psa_status_t ret = write_header_to_flash(&backup_gpt);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ ERROR("Unable to write backup GPT header\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = write_header_to_flash(&primary_gpt);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ ERROR("Unable to write primary GPT header\n");
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/* Copies one header to another and swaps the fields referring to self
|
||||
+ * and alternate headers. This is useful for primary to backup copies
|
||||
+ * and vice-versa.
|
||||
+ */
|
||||
+static inline void swap_headers(const struct gpt_header_t *src, struct gpt_header_t *dst)
|
||||
+{
|
||||
+ memcpy(dst, src, GPT_HEADER_SIZE);
|
||||
+ dst->backup_lba = src->current_lba;
|
||||
+ dst->current_lba = src->backup_lba;
|
||||
+ dst->array_lba = (src->current_lba == PRIMARY_GPT_LBA ?
|
||||
+ backup_gpt_array_lba :
|
||||
+ primary_gpt.header.array_lba);
|
||||
+}
|
||||
+
|
||||
/* Converts unicode string to valid ascii */
|
||||
static psa_status_t unicode_to_ascii(const char *unicode, char *ascii)
|
||||
{
|
||||
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
index d6671f3fc..1121f7c73 100644
|
||||
--- a/lib/gpt/unittests/gpt/test_gpt.c
|
||||
+++ b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "unity.h"
|
||||
|
||||
+#include "mock_efi_soft_crc.h"
|
||||
#include "mock_tfm_log.h"
|
||||
#include "mock_tfm_vprintf.h"
|
||||
|
||||
@@ -133,12 +134,16 @@ struct gpt_header_t {
|
||||
|
||||
static void register_mocked_read(void *buf, size_t num_bytes);
|
||||
static ssize_t test_driver_read(uint64_t lba, void *buf);
|
||||
+static ssize_t test_driver_write(uint64_t lba, const void *buf);
|
||||
+static ssize_t test_driver_erase(uint64_t lba, size_t num_blocks);
|
||||
|
||||
/* LBA driver used in test module */
|
||||
static struct gpt_flash_driver_t mock_driver = {
|
||||
.init = NULL,
|
||||
.uninit = NULL,
|
||||
.read = test_driver_read,
|
||||
+ .write = test_driver_write,
|
||||
+ .erase = test_driver_erase,
|
||||
};
|
||||
|
||||
/* Valid MBR. Only signature is required to be valid */
|
||||
@@ -227,6 +232,17 @@ static ssize_t test_driver_read(uint64_t lba, void *buf)
|
||||
return TEST_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
+/* Driver function that always succeeds in writing all data */
|
||||
+static ssize_t test_driver_write(uint64_t lba, const void *buf)
|
||||
+{
|
||||
+ return TEST_BLOCK_SIZE;
|
||||
+}
|
||||
+
|
||||
+static ssize_t test_driver_erase(uint64_t lba, size_t num_blocks)
|
||||
+{
|
||||
+ return num_blocks;
|
||||
+}
|
||||
+
|
||||
/* Creates backup table from test table and registers a read for it */
|
||||
static void setup_backup_gpt(void)
|
||||
{
|
||||
@@ -285,6 +301,9 @@ void setUp(void)
|
||||
|
||||
test_mbr.partitions[0].os_type = TEST_MBR_TYPE_GPT;
|
||||
|
||||
+ /* Any time this is called, return the same number and ignore the arguments */
|
||||
+ efi_soft_crc32_update_IgnoreAndReturn(test_header.header_crc);
|
||||
+
|
||||
/* Ignore all logging calls */
|
||||
tfm_log_Ignore();
|
||||
|
||||
@@ -337,6 +356,174 @@ void test_gpt_init_should_failWhenFlashDriverNotFullyDefined(void)
|
||||
mock_driver.read = NULL;
|
||||
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_init(&mock_driver, TEST_MAX_PARTITIONS));
|
||||
mock_driver.read = read_fn;
|
||||
+
|
||||
+ gpt_flash_write_t write_fn = mock_driver.write;
|
||||
+ mock_driver.write = NULL;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_init(&mock_driver, TEST_MAX_PARTITIONS));
|
||||
+ mock_driver.write = write_fn;
|
||||
+
|
||||
+ gpt_flash_erase_t erase_fn = mock_driver.erase;
|
||||
+ mock_driver.erase = NULL;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_init(&mock_driver, TEST_MAX_PARTITIONS));
|
||||
+ mock_driver.erase = erase_fn;
|
||||
+}
|
||||
+
|
||||
+void test_gpt_attr_set_should_setAttributes(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Entries are read */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t guid = test_partition_array[0].guid;
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_attr_set(&guid, 0x1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_attr_set_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Read every entry */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t non_existing = NULL_GUID;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_attr_set(&non_existing, 0x1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_attr_remove_should_removeAttributes(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[0]);
|
||||
+ uint64_t test_attr = 0x1;
|
||||
+ test_entry->attr = test_attr;
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* First entry is read */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_attr_set(&test_guid, test_attr));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_attr_remove_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Read every entry */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t non_existing = NULL_GUID;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_attr_remove(&non_existing, 0x1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_attr_add_should_addAttributes(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[0]);
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* First entry is read */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_attr_add(&test_guid, 0x1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_attr_add_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Read every entry */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t non_existing = NULL_GUID;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_attr_add(&non_existing, 0x1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_change_type_should_setNewType(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[0]);
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* First entry is read */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+
|
||||
+ /* Type validation is not a function of the library, as this is OS
|
||||
+ * dependent, so anything will do here.
|
||||
+ */
|
||||
+ struct efi_guid_t new_type = MAKE_EFI_GUID(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_change_type(&test_guid, &new_type));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_change_type_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Read every entry */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t non_existing = NULL_GUID;
|
||||
+ struct efi_guid_t new_type = MAKE_EFI_GUID(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_change_type(&non_existing, &new_type));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_change_type_should_failWhenSettingTypeToNullGuid(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[0]);
|
||||
+
|
||||
+ /* First entry is read */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+ struct efi_guid_t new_type = NULL_GUID;
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_change_type(&test_guid, &new_type));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_rename_should_renameEntry(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[0]);
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* First entry is read */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ char new_name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ new_name[0] = 'a';
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_rename(&test_guid, new_name));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_rename_should_failWhenNameIsEmpty(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[0]);
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Try to change name to an empty string */
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+ char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_rename(&test_guid, name));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_rename_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Read every entry */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t non_existing = NULL_GUID;
|
||||
+ char new_name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ new_name[0] = 'a';
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_rename(&non_existing, new_name));
|
||||
}
|
||||
|
||||
void test_gpt_entry_read_should_populateEntry(void)
|
||||
diff --git a/lib/gpt/unittests/gpt/utcfg.cmake b/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
index 37d72d138..620d15b4c 100644
|
||||
--- a/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
+++ b/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
@@ -15,12 +15,14 @@ set(UNIT_TEST_SUITE ${CMAKE_CURRENT_LIST_DIR}/test_gpt.c)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/interface/include)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/efi_guid/inc)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/gpt/inc)
|
||||
+list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/ext/efi_soft_crc/inc)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/tfm_log/inc)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/tfm_vprintf/inc)
|
||||
|
||||
# Headers to be mocked
|
||||
list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/tfm_log/inc/tfm_log.h)
|
||||
list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/tfm_vprintf/inc/tfm_vprintf.h)
|
||||
+list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/ext/efi_soft_crc/inc/efi_soft_crc.h)
|
||||
|
||||
# Compile-time definitions
|
||||
list(APPEND UNIT_TEST_COMPILE_DEFS LOG_LEVEL=LOG_LEVEL_VERBOSE)
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
From 7a453edd736c919eccc40eec567e8e97c0597bba Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Tue, 30 Dec 2025 09:13:07 +0000
|
||||
Subject: [PATCH] lib: gpt: Added operation to move entry
|
||||
|
||||
The operation to move or resize an entry is not just a metadata change
|
||||
and actually moves the data the partition entry points to as well.
|
||||
|
||||
Change-Id: Id6b98dcb3d77366db19e453acfbe4af59697eaf6
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [cbab5fd7757e9c06952c15ac7d30b0809b21406c]
|
||||
---
|
||||
lib/gpt/inc/gpt.h | 19 ++++
|
||||
lib/gpt/src/gpt.c | 156 ++++++++++++++++++++++++++++++-
|
||||
lib/gpt/unittests/gpt/test_gpt.c | 128 +++++++++++++++++++++++++
|
||||
3 files changed, 301 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
|
||||
index 6e7bb360e..85f9bed9c 100644
|
||||
--- a/lib/gpt/inc/gpt.h
|
||||
+++ b/lib/gpt/inc/gpt.h
|
||||
@@ -151,6 +151,25 @@ psa_status_t gpt_attr_remove(const struct efi_guid_t *guid, const uint64_t attr)
|
||||
__attribute__((nonnull(1)))
|
||||
psa_status_t gpt_attr_set(const struct efi_guid_t *guid, const uint64_t attr);
|
||||
|
||||
+/**
|
||||
+ * \brief Moves (or resizes) a partition entry.
|
||||
+ *
|
||||
+ * \param[in] guid Entry to move.
|
||||
+ * \param[in] start New start LBA.
|
||||
+ * \param[in] end New end LBA.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GUID.
|
||||
+ * \retval PSA_ERROR_INVALID_ARGUMENT Move would overlap with an existing partition.
|
||||
+ * \retval PSA_ERROR_INVALID_ARGUMENT \p end is less than \p start.
|
||||
+ * \retval PSA_ERROR_INVALID_ARGUMENT Part of the partition would move off flash.
|
||||
+ */
|
||||
+__attribute__((nonnull(1)))
|
||||
+psa_status_t gpt_entry_move(const struct efi_guid_t *guid,
|
||||
+ const uint64_t start,
|
||||
+ const uint64_t end);
|
||||
+
|
||||
/**
|
||||
* \brief Reads the GPT header from the second block (LBA 1).
|
||||
*
|
||||
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
|
||||
index 2cfcdff07..40b73f3e9 100644
|
||||
--- a/lib/gpt/src/gpt.c
|
||||
+++ b/lib/gpt/src/gpt.c
|
||||
@@ -214,6 +214,10 @@ static psa_status_t find_gpt_entry(const struct gpt_t *table,
|
||||
const uint32_t repeat_index,
|
||||
struct gpt_entry_t *entry,
|
||||
uint32_t *array_index);
|
||||
+static psa_status_t move_lba(const uint64_t old_lba, const uint64_t new_lba);
|
||||
+static psa_status_t move_partition(const uint64_t old_lba,
|
||||
+ const uint64_t new_lba,
|
||||
+ const uint64_t num_blocks);
|
||||
static psa_status_t mbr_load(struct mbr_t *mbr);
|
||||
static bool gpt_entry_cmp_guid(const struct gpt_entry_t *entry, const void *guid);
|
||||
static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const void *name);
|
||||
@@ -391,6 +395,111 @@ psa_status_t gpt_attr_set(const struct efi_guid_t *guid, const uint64_t attr)
|
||||
return write_entry(cached_index, &cached_entry, false);
|
||||
}
|
||||
|
||||
+psa_status_t gpt_entry_move(const struct efi_guid_t *guid,
|
||||
+ const uint64_t start,
|
||||
+ const uint64_t end)
|
||||
+{
|
||||
+ if (end < start) {
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+
|
||||
+ /* Must fit on flash */
|
||||
+ if (start < primary_gpt.header.first_lba ||
|
||||
+ end < primary_gpt.header.first_lba ||
|
||||
+ start > primary_gpt.header.last_lba ||
|
||||
+ end > primary_gpt.header.last_lba)
|
||||
+ {
|
||||
+ ERROR("Requested move would not be on disk\n");
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ uint32_t cached_index;
|
||||
+ psa_status_t ret = find_gpt_entry(
|
||||
+ &primary_gpt,
|
||||
+ gpt_entry_cmp_guid,
|
||||
+ guid,
|
||||
+ 0,
|
||||
+ &cached_entry,
|
||||
+ &cached_index);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Prevent unecessary I/O */
|
||||
+ if (start == cached_entry.start && end == cached_entry.end) {
|
||||
+ return PSA_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
+ /* It is not possible to move a partition such that it overlaps with an
|
||||
+ * existing partition (other than itself). Check the currently cached LBA
|
||||
+ * first, then the others to avoid reading this LBA twice
|
||||
+ */
|
||||
+ struct gpt_entry_t entry;
|
||||
+ const uint64_t checked_lba = cached_lba;
|
||||
+ const uint64_t array_end_lba = partition_array_last_lba(&primary_gpt);
|
||||
+ uint32_t num_entries_in_cached_lba;
|
||||
+ if (cached_lba == array_end_lba) {
|
||||
+ /* If this is 0, then the last LBA is full */
|
||||
+ uint32_t num_entries_in_last_lba = primary_gpt.num_used_partitions % gpt_entry_per_lba_count();
|
||||
+ if (num_entries_in_last_lba == 0) {
|
||||
+ num_entries_in_cached_lba = gpt_entry_per_lba_count();
|
||||
+ } else {
|
||||
+ num_entries_in_cached_lba = num_entries_in_last_lba;
|
||||
+ }
|
||||
+ } else {
|
||||
+ num_entries_in_cached_lba = gpt_entry_per_lba_count();
|
||||
+ }
|
||||
+
|
||||
+ /* Cached LBA */
|
||||
+ for (uint32_t i = 0; i < num_entries_in_cached_lba; ++i) {
|
||||
+ memcpy(&entry, lba_buf + (i * primary_gpt.header.entry_size), GPT_ENTRY_SIZE);
|
||||
+
|
||||
+ const struct efi_guid_t ent_guid = entry.unique_guid;
|
||||
+ if (efi_guid_cmp(&ent_guid, guid) == 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if ((start >= entry.start && start <= entry.end) ||
|
||||
+ (end >= entry.start && end <= entry.end) ||
|
||||
+ (start <= entry.start && end >= entry.end))
|
||||
+ {
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* All the rest */
|
||||
+ for (uint32_t i = 0; i < primary_gpt.num_used_partitions; ++i) {
|
||||
+ if (partition_entry_lba(&primary_gpt, i) == checked_lba) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ret = read_entry_from_flash(&primary_gpt, i, &entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if ((start >= entry.start && start <= entry.end) ||
|
||||
+ (end >= entry.start && end <= entry.end) ||
|
||||
+ (start <= entry.start && end >= entry.end))
|
||||
+ {
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ ret = move_partition(
|
||||
+ cached_entry.start,
|
||||
+ start,
|
||||
+ end - start + 1);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ cached_entry.start = start;
|
||||
+ cached_entry.end = end;
|
||||
+
|
||||
+ return write_entry(cached_index, &cached_entry, false);
|
||||
+}
|
||||
+
|
||||
/* Initialises GPT from first block. */
|
||||
psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_partitions)
|
||||
{
|
||||
@@ -586,6 +695,49 @@ static psa_status_t find_gpt_entry(const struct gpt_t *table,
|
||||
return io_failure ? PSA_ERROR_STORAGE_FAILURE : PSA_ERROR_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
+/* Move a single LBAs data to somewhere else */
|
||||
+static psa_status_t move_lba(const uint64_t old_lba, const uint64_t new_lba)
|
||||
+{
|
||||
+ const psa_status_t ret = read_from_flash(old_lba);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return write_to_flash(new_lba);
|
||||
+}
|
||||
+
|
||||
+/* Moves a partition's data to start from one logical block to another */
|
||||
+static psa_status_t move_partition(const uint64_t old_lba,
|
||||
+ const uint64_t new_lba,
|
||||
+ const uint64_t num_blocks)
|
||||
+{
|
||||
+ if (old_lba == new_lba) {
|
||||
+ return PSA_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
+ if (old_lba < new_lba) {
|
||||
+ /* Move block by block backwards */
|
||||
+ for (uint64_t block = num_blocks; block > 0; --block) {
|
||||
+ const psa_status_t ret = move_lba(old_lba + block - 1, new_lba + block - 1);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ /* Move block by block forwards */
|
||||
+ for (uint64_t block = 0; block < num_blocks; ++block) {
|
||||
+ const psa_status_t ret = move_lba(old_lba + block, new_lba + block);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ write_buffered = false;
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
/* Updates the header of the GPT based on new number of partitions */
|
||||
static psa_status_t update_header(uint32_t num_partitions)
|
||||
{
|
||||
@@ -794,8 +946,8 @@ static psa_status_t flush_lba_buf(void)
|
||||
/* Backup array entry. Write to backup and primary array */
|
||||
ret = write_entries_to_flash(cached_lba - backup_gpt_array_lba, false);
|
||||
} else {
|
||||
- /* Shouldn't be possible */
|
||||
- ERROR("Unknown data in LBA cache, discarding\n");
|
||||
+ /* Some other LBA is cached, possibly data. Write it anyway */
|
||||
+ ret = write_to_flash(cached_lba);
|
||||
}
|
||||
|
||||
in_flush = false;
|
||||
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
index 1121f7c73..251b04ee9 100644
|
||||
--- a/lib/gpt/unittests/gpt/test_gpt.c
|
||||
+++ b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
@@ -368,6 +368,134 @@ void test_gpt_init_should_failWhenFlashDriverNotFullyDefined(void)
|
||||
mock_driver.erase = erase_fn;
|
||||
}
|
||||
|
||||
+void test_gpt_entry_move_should_moveEntry(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ setup_valid_gpt();
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[TEST_DEFAULT_NUM_PARTITIONS - 1]);
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+
|
||||
+ /* First all entries are read to determine for overlap */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ /* Move the partition. Read each block to then write. It doesn't matter what
|
||||
+ * the data is
|
||||
+ */
|
||||
+ char unused_read_data = 'X';
|
||||
+ register_mocked_read(&unused_read_data, sizeof(unused_read_data));
|
||||
+
|
||||
+ /* Header update - reads partition array to calculate crc32 and also then
|
||||
+ * reads the header to modify and write back
|
||||
+ */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ register_mocked_read(&test_header, sizeof(test_header));
|
||||
+
|
||||
+ /* Do a valid move and resize in one */
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_move_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Read every entry */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ struct efi_guid_t non_existing = NULL_GUID;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_move(
|
||||
+ &non_existing,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_move_should_failWhenEndLessThanStart(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ struct efi_guid_t test_guid = test_partition_array[0].guid;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 2,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_move_should_failWhenLbaOverlapping(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Try to move an entry. Each entry is read to determine for overlap */
|
||||
+ size_t test_index = 1;
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[test_index]);
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ /* Try to move the test entry into the middle of the entry just read.
|
||||
+ * Starting at the same LBA
|
||||
+ */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_FIRST_PARTITION_START,
|
||||
+ TEST_GPT_SECOND_PARTITION_END));
|
||||
+
|
||||
+ /* Try to move the test entry into the middle of the entry just read.
|
||||
+ * Starting in the middle
|
||||
+ */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_FIRST_PARTITION_START + 1,
|
||||
+ TEST_GPT_SECOND_PARTITION_END));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_SECOND_PARTITION_START,
|
||||
+ TEST_GPT_THIRD_PARTITION_START));
|
||||
+
|
||||
+ /* Try to move the test entry into the middle of the entry just read.
|
||||
+ * Starting and ending in the middle.
|
||||
+ */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_FIRST_PARTITION_START + 1,
|
||||
+ TEST_GPT_FIRST_PARTITION_START + 1));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_move_should_failWhenLbaOffDisk(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Try to move an entry. */
|
||||
+ size_t test_index = 1;
|
||||
+ struct gpt_entry_t *test_entry = &(test_partition_array[test_index]);
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+
|
||||
+ /* First start on disk, then go off the disk */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ TEST_DISK_NUM_BLOCKS + 1));
|
||||
+
|
||||
+ /* Second, start off the disk entirely */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_DISK_NUM_BLOCKS + 1,
|
||||
+ TEST_DISK_NUM_BLOCKS + 2));
|
||||
+
|
||||
+ /* Third, do the same but in the header area */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_PRIMARY_LBA,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 2));
|
||||
+
|
||||
+ /* Fourth, start in the backup header area */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
|
||||
+ &test_guid,
|
||||
+ TEST_GPT_BACKUP_LBA,
|
||||
+ TEST_GPT_BACKUP_LBA + 1));
|
||||
+}
|
||||
+
|
||||
void test_gpt_attr_set_should_setAttributes(void)
|
||||
{
|
||||
/* Start with a populated GPT */
|
||||
+713
@@ -0,0 +1,713 @@
|
||||
From bec8f44a2732c20e8f69a06841637673af90c37e Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Tue, 30 Dec 2025 10:16:21 +0000
|
||||
Subject: [PATCH] lib: gpt: Added ability to create and remove partitions
|
||||
|
||||
With this change, new GPT partitions can be created and added to the
|
||||
table and old partitions can be removed. Both of these are metadata
|
||||
changes and only make changes to the partition entry array and header
|
||||
(primary and backup).
|
||||
|
||||
New partitions must be created in empty space only. The data that the
|
||||
partition points to is not cleared and if desired this should be done by
|
||||
the caller before or after creation.
|
||||
|
||||
When a partition is removed, the data it pointed to is not cleared so
|
||||
this should be done by the caller if desired. After removal, that data
|
||||
is considered empty space.
|
||||
|
||||
Change-Id: Iecccb814aaf8f48cbdd8e29b3d2b54fb5b58aae8
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [4d69babc99917e3cc2df284a311ec37004c3ee3f]
|
||||
---
|
||||
lib/gpt/CMakeLists.txt | 1 +
|
||||
lib/gpt/inc/gpt.h | 37 +++
|
||||
lib/gpt/src/gpt.c | 249 ++++++++++++++-
|
||||
lib/gpt/unittests/gpt/test_gpt.c | 284 ++++++++++++++++++
|
||||
lib/gpt/unittests/gpt/utcfg.cmake | 2 +
|
||||
.../include/mbedtls/mbedtls_config.h | 18 ++
|
||||
6 files changed, 590 insertions(+), 1 deletion(-)
|
||||
create mode 100644 lib/gpt/unittests/include/mbedtls/mbedtls_config.h
|
||||
|
||||
diff --git a/lib/gpt/CMakeLists.txt b/lib/gpt/CMakeLists.txt
|
||||
index 25d0de9cd..5befc346f 100644
|
||||
--- a/lib/gpt/CMakeLists.txt
|
||||
+++ b/lib/gpt/CMakeLists.txt
|
||||
@@ -23,6 +23,7 @@ target_sources(tfm_gpt
|
||||
target_include_directories(tfm_gpt
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||
+ $<INSTALL_INTERFACE:inc>
|
||||
)
|
||||
|
||||
target_compile_definitions(tfm_gpt
|
||||
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
|
||||
index 85f9bed9c..f04643957 100644
|
||||
--- a/lib/gpt/inc/gpt.h
|
||||
+++ b/lib/gpt/inc/gpt.h
|
||||
@@ -170,6 +170,43 @@ psa_status_t gpt_entry_move(const struct efi_guid_t *guid,
|
||||
const uint64_t start,
|
||||
const uint64_t end);
|
||||
|
||||
+/**
|
||||
+ * \brief Creates a partition entry in the table.
|
||||
+ *
|
||||
+ * \param[in] type Partition type.
|
||||
+ * \param[in] start Starting LBA (0 uses the lowest free LBA possible).
|
||||
+ * \param[in] size Size of the partition in LBAs.
|
||||
+ * \param[in] attr Attributes for the partition.
|
||||
+ * \param[in] name Partition name in unicode.
|
||||
+ * \param[out] guid GUID populated on success for subsequent API calls.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_INSUFFICIENT_STORAGE Maximum number of partitions reached.
|
||||
+ * \retval PSA_ERROR_INVALID_ARGUMENT Partition would extend beyond the flash.
|
||||
+ * \retval PSA_ERROR_INVALID_ARGUMENT New entry would overlap an existing partition, the name is empty,
|
||||
+ * or \p size is zero.
|
||||
+ */
|
||||
+__attribute__((nonnull(1,5,6)))
|
||||
+psa_status_t gpt_entry_create(const struct efi_guid_t *type,
|
||||
+ const uint64_t start,
|
||||
+ const uint64_t size,
|
||||
+ const uint64_t attr,
|
||||
+ const char name[GPT_ENTRY_NAME_LENGTH],
|
||||
+ struct efi_guid_t *guid);
|
||||
+
|
||||
+/**
|
||||
+ * \brief Removes a partition entry from the table.
|
||||
+ *
|
||||
+ * \param[in] guid Entry to remove.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GUID.
|
||||
+ */
|
||||
+__attribute__((nonnull(1)))
|
||||
+psa_status_t gpt_entry_remove(const struct efi_guid_t *guid);
|
||||
+
|
||||
/**
|
||||
* \brief Reads the GPT header from the second block (LBA 1).
|
||||
*
|
||||
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
|
||||
index 40b73f3e9..676f04fd6 100644
|
||||
--- a/lib/gpt/src/gpt.c
|
||||
+++ b/lib/gpt/src/gpt.c
|
||||
@@ -9,11 +9,12 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
-#include "psa/error.h"
|
||||
+#include "psa/crypto.h"
|
||||
#include "gpt.h"
|
||||
#include "gpt_flash.h"
|
||||
#include "tfm_log.h"
|
||||
#include "efi_guid_structs.h"
|
||||
+#include "efi_guid.h"
|
||||
#include "efi_soft_crc.h"
|
||||
|
||||
/* This needs to be defined by the platform and is used by the GPT library as
|
||||
@@ -500,6 +501,252 @@ psa_status_t gpt_entry_move(const struct efi_guid_t *guid,
|
||||
return write_entry(cached_index, &cached_entry, false);
|
||||
}
|
||||
|
||||
+psa_status_t gpt_entry_create(const struct efi_guid_t *type,
|
||||
+ const uint64_t start,
|
||||
+ const uint64_t size,
|
||||
+ const uint64_t attr,
|
||||
+ const char name[GPT_ENTRY_NAME_LENGTH],
|
||||
+ struct efi_guid_t *guid)
|
||||
+{
|
||||
+ /* Using inequlity here handles when reading an initial GPT has more than
|
||||
+ * the maximum defined number of partitions.
|
||||
+ */
|
||||
+ if (primary_gpt.num_used_partitions >= plat_max_partitions) {
|
||||
+ ERROR("Maximum number of partitions reached\n");
|
||||
+ return PSA_ERROR_INSUFFICIENT_STORAGE;
|
||||
+ }
|
||||
+ if (size == 0) {
|
||||
+ ERROR("Cannot create entry of size 0\n");
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+ if (name[0] == '\0') {
|
||||
+ ERROR("Cannot create entry with no name\n");
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+
|
||||
+ uint64_t start_lba = start;
|
||||
+ psa_status_t ret = PSA_SUCCESS;
|
||||
+ if (start_lba == 0) {
|
||||
+ /* Use the lowest free LBA possible. Each partition uses contiguous space,
|
||||
+ * so if there is a gap between partitions, that will be shown by the end
|
||||
+ * and start not being contiguous.
|
||||
+ */
|
||||
+ uint64_t prev_end = primary_gpt.header.first_lba;
|
||||
+ for (uint32_t i = 0; i < primary_gpt.header.num_partitions; ++i) {
|
||||
+ struct gpt_entry_t entry = {0};
|
||||
+ ret = read_entry_from_flash(&primary_gpt, i, &entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if (entry.start - 1 > prev_end) {
|
||||
+ start_lba = prev_end + 1;
|
||||
+ break;
|
||||
+ }
|
||||
+ prev_end = entry.end;
|
||||
+ }
|
||||
+
|
||||
+ if (start_lba != prev_end + 1) {
|
||||
+ /* No free space */
|
||||
+ ERROR("No free space on device!\n");
|
||||
+ return PSA_ERROR_INSUFFICIENT_STORAGE;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Must fit on flash */
|
||||
+ const uint64_t end_lba = start_lba + size - 1;
|
||||
+ if (start_lba < primary_gpt.header.first_lba ||
|
||||
+ end_lba < primary_gpt.header.first_lba ||
|
||||
+ start_lba > primary_gpt.header.last_lba ||
|
||||
+ end_lba > primary_gpt.header.last_lba)
|
||||
+ {
|
||||
+ ERROR("Requested partition would not be on disk\n");
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+
|
||||
+ /* Do not allow overlapping partitions */
|
||||
+ struct gpt_entry_t entry;
|
||||
+ for (uint32_t i = 0; i < primary_gpt.num_used_partitions; ++i) {
|
||||
+ ret = read_entry_from_flash(&primary_gpt, i, &entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if ((start_lba >= entry.start && start_lba <= entry.end) ||
|
||||
+ (end_lba >= entry.start && end_lba <= entry.end) ||
|
||||
+ (start_lba <= entry.start && end_lba >= entry.end))
|
||||
+ {
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Generate the new random GUID */
|
||||
+ if (efi_guid_generate_random(guid) != PSA_SUCCESS) {
|
||||
+ ERROR("Unable to generate GUID\n");
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
+ /* Set new entry's metadata */
|
||||
+ struct gpt_entry_t new_entry = {0};
|
||||
+ new_entry.start = start_lba;
|
||||
+ new_entry.end = end_lba;
|
||||
+ new_entry.attr = attr;
|
||||
+ memcpy(new_entry.name, name, GPT_ENTRY_NAME_LENGTH);
|
||||
+ new_entry.partition_type = *type;
|
||||
+ new_entry.unique_guid = *guid;
|
||||
+
|
||||
+ /* Write the new entry. Skip header update as it is explicitely called
|
||||
+ * below with new number of partitions
|
||||
+ */
|
||||
+ ret = write_entry(primary_gpt.num_used_partitions++, &new_entry, true);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ --primary_gpt.num_used_partitions;
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Flush the buffered LBA if not done so. This will cause the header to be
|
||||
+ * updated
|
||||
+ */
|
||||
+ if (write_buffered) {
|
||||
+ /* flush_lba_buf will update the header */
|
||||
+ ret = flush_lba_buf();
|
||||
+ } else {
|
||||
+ ret = update_header(primary_gpt.num_used_partitions);
|
||||
+ }
|
||||
+
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
|
||||
+{
|
||||
+ struct gpt_entry_t cached_entry;
|
||||
+ uint32_t cached_index;
|
||||
+ psa_status_t ret = find_gpt_entry(
|
||||
+ &primary_gpt,
|
||||
+ gpt_entry_cmp_guid,
|
||||
+ guid,
|
||||
+ 0,
|
||||
+ &cached_entry,
|
||||
+ &cached_index);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Shuffle the remainder of the array up. This will overwrite the
|
||||
+ * most previous entry.
|
||||
+ *
|
||||
+ * The first LBA to potentially modify is in memory. It doesn't need
|
||||
+ * to be modified if the last entry in the array was moved or if it is
|
||||
+ * the only LBA used by the partition array
|
||||
+ */
|
||||
+ if (cached_index != primary_gpt.num_used_partitions - 1 ||
|
||||
+ cached_index < gpt_entry_per_lba_count())
|
||||
+ {
|
||||
+ /* Shuffle up the remainder of the LBA. If it was the last entry
|
||||
+ * in the LBA, there is nothing to do.
|
||||
+ */
|
||||
+ const uint32_t lba_index = cached_index % gpt_entry_per_lba_count();
|
||||
+ if (lba_index + 1 != gpt_entry_per_lba_count()) {
|
||||
+ memmove(
|
||||
+ lba_buf + lba_index * primary_gpt.header.entry_size,
|
||||
+ lba_buf + (lba_index + 1) * primary_gpt.header.entry_size,
|
||||
+ (gpt_entry_per_lba_count() - lba_index - 1) * primary_gpt.header.entry_size);
|
||||
+ }
|
||||
+
|
||||
+ /* If this is not the last LBA, then read the next LBA into memory and
|
||||
+ * place it's first element in the final slot of the currently modified
|
||||
+ * LBA. Repeat this for each LBA read.
|
||||
+ *
|
||||
+ * Use a second buffer to read each consecutive LBA and copy that to
|
||||
+ * the global LBA buffer to then write afterwards.
|
||||
+ */
|
||||
+ const uint64_t array_end_lba = partition_array_last_lba(&primary_gpt);
|
||||
+ for (uint64_t i = partition_entry_lba(&primary_gpt, cached_index) + 1;
|
||||
+ i <= array_end_lba;
|
||||
+ ++i)
|
||||
+ {
|
||||
+ uint8_t array_buf[TFM_GPT_BLOCK_SIZE] = {0};
|
||||
+ int read_ret = plat_flash_driver->read(i, array_buf);
|
||||
+ if (read_ret != TFM_GPT_BLOCK_SIZE) {
|
||||
+ ERROR("Unable to read LBA 0x%08x%08x\n",
|
||||
+ (uint32_t)(i >> 32), (uint32_t)i);
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
+ memcpy(
|
||||
+ lba_buf + primary_gpt.header.entry_size * (gpt_entry_per_lba_count() - 1),
|
||||
+ array_buf,
|
||||
+ GPT_ENTRY_SIZE);
|
||||
+
|
||||
+ /* Write to backup first, then primary partition array */
|
||||
+ if (backup_gpt_array_lba != 0) {
|
||||
+ ret = write_to_flash(backup_gpt_array_lba + i - 1 - PRIMARY_GPT_ARRAY_LBA);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ ret = write_to_flash(i - 1);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ memmove(
|
||||
+ array_buf,
|
||||
+ array_buf + primary_gpt.header.entry_size,
|
||||
+ sizeof(array_buf) - primary_gpt.header.entry_size);
|
||||
+ memcpy(lba_buf, array_buf, TFM_GPT_BLOCK_SIZE);
|
||||
+ }
|
||||
+
|
||||
+ /* What was the final LBA is now cached and may be empty or partially-filled */
|
||||
+ cached_lba = array_end_lba;
|
||||
+ write_buffered = false;
|
||||
+ uint32_t entries_in_last_lba = (--primary_gpt.num_used_partitions) % gpt_entry_per_lba_count();
|
||||
+ if (entries_in_last_lba == 0) {
|
||||
+ /* There's nothing left in this LBA, so zero it all and write it out.
|
||||
+ * There is also no need to do an erase just to zero afterwards.
|
||||
+ */
|
||||
+ memset(lba_buf, 0, TFM_GPT_BLOCK_SIZE);
|
||||
+ if (backup_gpt_array_lba != 0) {
|
||||
+ int write_ret = plat_flash_driver->write(
|
||||
+ backup_gpt_array_lba + array_end_lba - PRIMARY_GPT_ARRAY_LBA,
|
||||
+ lba_buf);
|
||||
+ if (write_ret != TFM_GPT_BLOCK_SIZE) {
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+ }
|
||||
+ int write_ret = plat_flash_driver->write(array_end_lba, lba_buf);
|
||||
+ if (write_ret != TFM_GPT_BLOCK_SIZE) {
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+ } else {
|
||||
+ /* Zero what is not needed anymore */
|
||||
+ memset(
|
||||
+ lba_buf + primary_gpt.header.entry_size * entries_in_last_lba,
|
||||
+ 0,
|
||||
+ (gpt_entry_per_lba_count() - entries_in_last_lba) * primary_gpt.header.entry_size);
|
||||
+ if (backup_gpt_array_lba != 0) {
|
||||
+ ret = write_to_flash(backup_gpt_array_lba + array_end_lba - PRIMARY_GPT_ARRAY_LBA);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ ret = write_to_flash(array_end_lba);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Update the header after flash changes */
|
||||
+ ret = update_header(primary_gpt.num_used_partitions);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
/* Initialises GPT from first block. */
|
||||
psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_partitions)
|
||||
{
|
||||
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
index 251b04ee9..18cf2c8f3 100644
|
||||
--- a/lib/gpt/unittests/gpt/test_gpt.c
|
||||
+++ b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "unity.h"
|
||||
|
||||
+#include "mock_efi_guid.h"
|
||||
#include "mock_efi_soft_crc.h"
|
||||
#include "mock_tfm_log.h"
|
||||
#include "mock_tfm_vprintf.h"
|
||||
@@ -368,6 +369,260 @@ void test_gpt_init_should_failWhenFlashDriverNotFullyDefined(void)
|
||||
mock_driver.erase = erase_fn;
|
||||
}
|
||||
|
||||
+void test_gpt_entry_create_should_createNewEntry(void)
|
||||
+{
|
||||
+ /* Add an entry. It must not overlap with an existing entry and must also
|
||||
+ * fit on the storage device. The GUID should be populated with something.
|
||||
+ */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Each entry will be read in order to check that it doesn't overlap with
|
||||
+ * any of them
|
||||
+ */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ /* Update header. Read each entry for CRC calculation. */
|
||||
+ struct gpt_entry_t new_entry = {
|
||||
+ .type = NULL_GUID,
|
||||
+ .start = TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ .end = TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ .attr = 0,
|
||||
+ .name = "Fourth partition"
|
||||
+ };
|
||||
+
|
||||
+ /* Mock out the call to create a new GUID */
|
||||
+ struct efi_guid_t expected_guid = MAKE_EFI_GUID(5, 5, 5, 5, 5, 6, 7, 8, 9, 10, 11);
|
||||
+ efi_guid_generate_random_ExpectAnyArgsAndReturn(PSA_SUCCESS);
|
||||
+ efi_guid_generate_random_ReturnThruPtr_guid(&expected_guid);
|
||||
+
|
||||
+ /* Ensure also the that a new GUID is assigned */
|
||||
+ struct efi_guid_t new_guid = MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_create(
|
||||
+ &expected_guid,
|
||||
+ new_entry.start,
|
||||
+ new_entry.end - new_entry.start + 1,
|
||||
+ new_entry.attr,
|
||||
+ new_entry.name,
|
||||
+ &new_guid));
|
||||
+ TEST_ASSERT_EQUAL_MEMORY(&expected_guid, &new_guid, sizeof(new_guid));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_create_should_createNewEntryNextToLastEntry(void)
|
||||
+{
|
||||
+ /* Add an entry, allowing the library to choose the start LBA.
|
||||
+ * The GUID should be populated with something.
|
||||
+ */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Each entry will be read in order to check that it doesn't overlap with
|
||||
+ * any of them
|
||||
+ */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ /* Update header. Read each entry for CRC calculation. */
|
||||
+ struct gpt_entry_t new_entry = {
|
||||
+ .type = NULL_GUID,
|
||||
+ .start = TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ .end = TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ .attr = 0,
|
||||
+ .name = "Fourth partition"
|
||||
+ };
|
||||
+
|
||||
+ /* Mock out the call to create a new GUID */
|
||||
+ struct efi_guid_t expected_guid = MAKE_EFI_GUID(5, 5, 5, 5, 5, 6, 7, 8, 9, 10, 11);
|
||||
+ efi_guid_generate_random_ExpectAnyArgsAndReturn(PSA_SUCCESS);
|
||||
+ efi_guid_generate_random_ReturnThruPtr_guid(&expected_guid);
|
||||
+
|
||||
+ /* Ensure also the that a new GUID is assigned */
|
||||
+ struct efi_guid_t new_guid = MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||
+ char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ name[0] = 'a';
|
||||
+
|
||||
+ /* Ensure also the that a new GUID is assigned */
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_create(
|
||||
+ &expected_guid,
|
||||
+ 0,
|
||||
+ 1,
|
||||
+ new_entry.attr,
|
||||
+ name,
|
||||
+ &new_guid));
|
||||
+ TEST_ASSERT_EQUAL_MEMORY(&expected_guid, &new_guid, sizeof(new_guid));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_create_should_failToCreateEntryWhenLowestFreeLbaDoesNotHaveSpace(void)
|
||||
+{
|
||||
+ /* Add an entry, allowing the library to choose the start LBA.
|
||||
+ * The GUID should be populated with something.
|
||||
+ */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Each entry will be read in order to check that it doesn't overlap with
|
||||
+ * any of them
|
||||
+ */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ /* Ensure also the that a new GUID is assigned */
|
||||
+ struct efi_guid_t existing_guid = MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||
+ struct efi_guid_t new_guid;
|
||||
+ char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ name[0] = 'a';
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &(existing_guid),
|
||||
+ 0,
|
||||
+ TEST_DISK_NUM_BLOCKS,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &(new_guid)));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_create_should_failWhenTableFull(void)
|
||||
+{
|
||||
+ /* Start with a full array of entries */
|
||||
+ struct gpt_entry_t new_entry = {
|
||||
+ .type = MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11),
|
||||
+ .start = TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ .end = TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ .attr = 0,
|
||||
+ .guid = MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11),
|
||||
+ .name = "Fourth partition"
|
||||
+ };
|
||||
+ test_partition_array[TEST_MAX_PARTITIONS - 1] = new_entry;
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ struct efi_guid_t type = MAKE_EFI_GUID(5, 5, 5, 5, 5, 6, 7, 8, 9, 10, 11);
|
||||
+ struct efi_guid_t guid;
|
||||
+ char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ name[0] = 'a';
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INSUFFICIENT_STORAGE, gpt_entry_create(
|
||||
+ &type,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 4,
|
||||
+ 1,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &guid));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_create_should_failWhenLbaOffDisk(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* First start on disk, then go off the disk */
|
||||
+ struct efi_guid_t type = NULL_GUID;
|
||||
+ struct efi_guid_t guid;
|
||||
+ char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ name[0] = 'a';
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &type,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ 1000,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &guid));
|
||||
+
|
||||
+ /* Second, start off the disk entirely */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &type,
|
||||
+ TEST_DISK_NUM_BLOCKS + 100,
|
||||
+ 1,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &guid));
|
||||
+
|
||||
+ /* Third, do the same but in the header area */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &type,
|
||||
+ TEST_GPT_PRIMARY_LBA,
|
||||
+ 1,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &guid));
|
||||
+
|
||||
+ /* Fourth, start in the backup header area */
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &type,
|
||||
+ TEST_GPT_BACKUP_LBA,
|
||||
+ 1,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &guid));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_create_should_failWhenOverlapping(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Since the disk is not fragmented by default, there are two test cases:
|
||||
+ * 1. start in the middle of a partition and end in the middle of a partition
|
||||
+ * 2. start in the middle of a partition and end in free space
|
||||
+ */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ struct efi_guid_t type = NULL_GUID;
|
||||
+ struct efi_guid_t guid;
|
||||
+ char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ name[0] = 'a';
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &type,
|
||||
+ TEST_GPT_FIRST_PARTITION_START,
|
||||
+ TEST_GPT_FIRST_PARTITION_END - TEST_GPT_FIRST_PARTITION_START + 1,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &guid));
|
||||
+
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &type,
|
||||
+ TEST_GPT_FIRST_PARTITION_START,
|
||||
+ TEST_GPT_LAST_USABLE_LBA - TEST_GPT_FIRST_PARTITION_START,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &guid));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_create_should_failWhenNameIsEmpty(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ struct efi_guid_t type = NULL_GUID;
|
||||
+ struct gpt_entry_t new_entry = {
|
||||
+ .type = type,
|
||||
+ .start = TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ .end = TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ .attr = 0,
|
||||
+ };
|
||||
+
|
||||
+ /* Make an entry with an empty name */
|
||||
+ char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ struct efi_guid_t new_guid;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &type,
|
||||
+ new_entry.start,
|
||||
+ 1,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &new_guid));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_create_should_failWhenSizeIsZero(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ struct efi_guid_t type = NULL_GUID;
|
||||
+
|
||||
+ /* Make the size zero */
|
||||
+ struct efi_guid_t new_guid;
|
||||
+ char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
|
||||
+ name[0] = 'a';
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
|
||||
+ &type,
|
||||
+ TEST_GPT_THIRD_PARTITION_END + 1,
|
||||
+ 0,
|
||||
+ 0,
|
||||
+ name,
|
||||
+ &new_guid));
|
||||
+}
|
||||
+
|
||||
void test_gpt_entry_move_should_moveEntry(void)
|
||||
{
|
||||
/* Start with a populated GPT */
|
||||
@@ -654,6 +909,35 @@ void test_gpt_entry_rename_should_failWhenEntryNotExisting(void)
|
||||
TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_rename(&non_existing, new_name));
|
||||
}
|
||||
|
||||
+void test_gpt_entry_remove_should_removeEntry(void)
|
||||
+{
|
||||
+ /* Start with a populated GPT */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Each entry is read */
|
||||
+ struct gpt_entry_t *test_entry = &(default_partition_array[TEST_DEFAULT_NUM_PARTITIONS - 1]);
|
||||
+ struct efi_guid_t test_guid = test_entry->guid;
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_remove(&test_guid));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_entry_remove_should_failWhenEntryNotExisting(void)
|
||||
+{
|
||||
+ /* Start by trying to remove from an empty table */
|
||||
+ setup_empty_gpt();
|
||||
+
|
||||
+ struct efi_guid_t non_existing = NULL_GUID;
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_remove(&non_existing));
|
||||
+
|
||||
+ /* Now, have a non-empty GPT but search for a non-existing GUID */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* Each entry should be read. */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_remove(&non_existing));
|
||||
+}
|
||||
+
|
||||
void test_gpt_entry_read_should_populateEntry(void)
|
||||
{
|
||||
/* Start with a populated GPT */
|
||||
diff --git a/lib/gpt/unittests/gpt/utcfg.cmake b/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
index 620d15b4c..f3f4e7dcc 100644
|
||||
--- a/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
+++ b/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
@@ -15,11 +15,13 @@ set(UNIT_TEST_SUITE ${CMAKE_CURRENT_LIST_DIR}/test_gpt.c)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/interface/include)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/efi_guid/inc)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/gpt/inc)
|
||||
+list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/gpt/unittests/include)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/ext/efi_soft_crc/inc)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/tfm_log/inc)
|
||||
list(APPEND UNIT_TEST_INCLUDE_DIRS ${TFM_ROOT_DIR}/lib/tfm_vprintf/inc)
|
||||
|
||||
# Headers to be mocked
|
||||
+list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/efi_guid/inc/efi_guid.h)
|
||||
list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/tfm_log/inc/tfm_log.h)
|
||||
list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/tfm_vprintf/inc/tfm_vprintf.h)
|
||||
list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/ext/efi_soft_crc/inc/efi_soft_crc.h)
|
||||
diff --git a/lib/gpt/unittests/include/mbedtls/mbedtls_config.h b/lib/gpt/unittests/include/mbedtls/mbedtls_config.h
|
||||
new file mode 100644
|
||||
index 000000000..5144daae3
|
||||
--- /dev/null
|
||||
+++ b/lib/gpt/unittests/include/mbedtls/mbedtls_config.h
|
||||
@@ -0,0 +1,18 @@
|
||||
+/*
|
||||
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: BSD-3-Clause
|
||||
+ */
|
||||
+
|
||||
+#ifndef MBEDTLS_CONFIG_H
|
||||
+#define MBEDTLS_CONFIG_H
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+extern "C" {
|
||||
+#endif
|
||||
+
|
||||
+#ifdef __cplusplus
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+#endif
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
From c0037dc1779d181ce7d7dbd5871519c9d218440a Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Tue, 30 Dec 2025 10:29:47 +0000
|
||||
Subject: [PATCH] lib: gpt: Added table validation operations
|
||||
|
||||
Added validate and restore operations. Both of these operate on the
|
||||
entirety of the table and allow the caller to:
|
||||
|
||||
1. determine if the GPT is valid or not.
|
||||
2. restore a GPT from the alternative at the other end of the storage
|
||||
device.
|
||||
|
||||
Both of these operations can be performed on either the primary or
|
||||
backup table to allow full recovery.
|
||||
|
||||
Change-Id: Ie5ac2fdc42858cb439a2dcd08f4203eb181d4e88
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [62a038e80574e094e64617953748633737cd760d]
|
||||
---
|
||||
lib/gpt/inc/gpt.h | 22 ++++
|
||||
lib/gpt/src/gpt.c | 184 +++++++++++++++++++++++++++++++
|
||||
lib/gpt/unittests/gpt/test_gpt.c | 135 +++++++++++++++++++++++
|
||||
3 files changed, 341 insertions(+)
|
||||
|
||||
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
|
||||
index f04643957..0ce4d04ab 100644
|
||||
--- a/lib/gpt/inc/gpt.h
|
||||
+++ b/lib/gpt/inc/gpt.h
|
||||
@@ -207,6 +207,28 @@ psa_status_t gpt_entry_create(const struct efi_guid_t *type,
|
||||
__attribute__((nonnull(1)))
|
||||
psa_status_t gpt_entry_remove(const struct efi_guid_t *guid);
|
||||
|
||||
+/**
|
||||
+ * \brief Validates the GPT.
|
||||
+ *
|
||||
+ * \param[in] is_primary True to validate the primary table, false to validate the backup.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS GPT is valid.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_INVALID_SIGNATURE GPT is invalid.
|
||||
+ */
|
||||
+psa_status_t gpt_validate(bool is_primary);
|
||||
+
|
||||
+/**
|
||||
+ * \brief Restores either the primary or backup GPT from the other copy.
|
||||
+ *
|
||||
+ * \param[in] is_primary True to restore the primary table, false to restore the backup.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS GPT restored.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O error.
|
||||
+ * \retval PSA_ERROR_INVALID_SIGNATURE Restoring GPT invalid; cannot restore.
|
||||
+ */
|
||||
+psa_status_t gpt_restore(bool is_primary);
|
||||
+
|
||||
/**
|
||||
* \brief Reads the GPT header from the second block (LBA 1).
|
||||
*
|
||||
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
|
||||
index 676f04fd6..1bc592bb3 100644
|
||||
--- a/lib/gpt/src/gpt.c
|
||||
+++ b/lib/gpt/src/gpt.c
|
||||
@@ -223,6 +223,8 @@ static psa_status_t mbr_load(struct mbr_t *mbr);
|
||||
static bool gpt_entry_cmp_guid(const struct gpt_entry_t *entry, const void *guid);
|
||||
static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const void *name);
|
||||
static bool gpt_entry_cmp_type(const struct gpt_entry_t *entry, const void *type);
|
||||
+static psa_status_t validate_table(struct gpt_t *table, bool is_primary);
|
||||
+static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary);
|
||||
|
||||
/* PUBLIC API FUNCTIONS */
|
||||
|
||||
@@ -747,6 +749,76 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
|
||||
return ret;
|
||||
}
|
||||
|
||||
+psa_status_t gpt_validate(bool is_primary)
|
||||
+{
|
||||
+ if (!is_primary && (backup_gpt_lba == 0 || backup_gpt_array_lba == 0)) {
|
||||
+ ERROR("Backup GPT location unknown!\n");
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
+ /* Flush and invalidate the in-memory buffer before attempting to validate
|
||||
+ * a table. The in-memory header needs to be updated if the flushed LBA was
|
||||
+ * part of the entry array
|
||||
+ */
|
||||
+ psa_status_t ret;
|
||||
+ if (write_buffered) {
|
||||
+ ret = flush_lba_buf();
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ write_buffered = false;
|
||||
+ }
|
||||
+ cached_lba = 0;
|
||||
+
|
||||
+ if (is_primary) {
|
||||
+ return validate_table(&primary_gpt, true);
|
||||
+ } else {
|
||||
+ struct gpt_t backup_gpt;
|
||||
+ ret = read_table_from_flash(&backup_gpt, false);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ return validate_table(&backup_gpt, false);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+psa_status_t gpt_restore(bool is_primary)
|
||||
+{
|
||||
+ if (!is_primary && (backup_gpt_lba == 0 || backup_gpt_array_lba == 0)) {
|
||||
+ ERROR("Backup GPT location unknown!\n");
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
+ /* Flush and invalidate the in-memory buffer before attempting to restore a
|
||||
+ * table. The in-memory header needs to be updated if the flushed LBA was
|
||||
+ * part of the entry array
|
||||
+ */
|
||||
+ psa_status_t ret;
|
||||
+ if (write_buffered) {
|
||||
+ ret = flush_lba_buf();
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ write_buffered = false;
|
||||
+ }
|
||||
+ cached_lba = 0;
|
||||
+
|
||||
+ if (is_primary) {
|
||||
+ struct gpt_t backup_gpt;
|
||||
+ ret = read_table_from_flash(&backup_gpt, false);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = count_used_partitions(&backup_gpt, &backup_gpt.num_used_partitions);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ return restore_table(&backup_gpt, false);
|
||||
+ } else {
|
||||
+ return restore_table(&primary_gpt, true);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* Initialises GPT from first block. */
|
||||
psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_partitions)
|
||||
{
|
||||
@@ -1349,6 +1421,118 @@ static inline void swap_headers(const struct gpt_header_t *src, struct gpt_heade
|
||||
primary_gpt.header.array_lba);
|
||||
}
|
||||
|
||||
+/* Validates a specific GPT. */
|
||||
+static psa_status_t validate_table(struct gpt_t *table, bool is_primary)
|
||||
+{
|
||||
+ struct gpt_header_t *header = &(table->header);
|
||||
+
|
||||
+ /* Check signature */
|
||||
+ if (strncmp(header->signature, GPT_SIG, GPT_SIG_LEN) != 0) {
|
||||
+ ERROR("Invalid GPT signature\n");
|
||||
+ return PSA_ERROR_INVALID_SIGNATURE;
|
||||
+ }
|
||||
+
|
||||
+ /* Check header CRC */
|
||||
+ uint32_t calc_crc = 0;
|
||||
+ uint32_t old_crc = header->header_crc;
|
||||
+ header->header_crc = 0;
|
||||
+ calc_crc = efi_soft_crc32_update(calc_crc, (uint8_t *)header, GPT_HEADER_SIZE);
|
||||
+ header->header_crc = old_crc;
|
||||
+
|
||||
+ if (old_crc != calc_crc) {
|
||||
+ ERROR("CRC of header does not match, expected 0x%x got 0x%x\n",
|
||||
+ old_crc, calc_crc);
|
||||
+ return PSA_ERROR_INVALID_SIGNATURE;
|
||||
+ }
|
||||
+
|
||||
+ /* Check MyLBA field points to this table */
|
||||
+ const uint64_t table_lba = (is_primary ? PRIMARY_GPT_LBA : backup_gpt_lba);
|
||||
+ if (table_lba != header->current_lba) {
|
||||
+ ERROR("MyLBA not pointing to this GPT, expected 0x%08x%08x, got 0x%08x%08x\n",
|
||||
+ (uint32_t)(table_lba >> 32),
|
||||
+ (uint32_t)table_lba,
|
||||
+ (uint32_t)(header->current_lba >> 32),
|
||||
+ (uint32_t)(header->current_lba));
|
||||
+ return PSA_ERROR_INVALID_SIGNATURE;
|
||||
+ }
|
||||
+
|
||||
+ /* Check the CRC of the partition array */
|
||||
+ calc_crc = 0;
|
||||
+ for (uint32_t i = 0; i < header->num_partitions; ++i) {
|
||||
+ uint8_t entry_buf[header->entry_size];
|
||||
+ memset(entry_buf, 0, header->entry_size);
|
||||
+ struct gpt_entry_t *entry = (struct gpt_entry_t *)entry_buf;
|
||||
+
|
||||
+ psa_status_t ret = read_entry_from_flash(table, i, entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ calc_crc = efi_soft_crc32_update(calc_crc, (uint8_t *)entry, header->entry_size);
|
||||
+ }
|
||||
+
|
||||
+ if (calc_crc != header->array_crc) {
|
||||
+ ERROR("CRC of partition array does not match, expected 0x%x got 0x%x\n",
|
||||
+ calc_crc, header->array_crc);
|
||||
+ return PSA_ERROR_INVALID_SIGNATURE;
|
||||
+ }
|
||||
+
|
||||
+ if (is_primary) {
|
||||
+ /* Any time the primary table is considered valid, cache the backup
|
||||
+ * LBA field
|
||||
+ */
|
||||
+ backup_gpt_lba = header->backup_lba;
|
||||
+ } else {
|
||||
+ /* Any time backup table is considered valid, cache its array LBA
|
||||
+ * field and crc32
|
||||
+ */
|
||||
+ backup_gpt_array_lba = header->array_lba;
|
||||
+ backup_crc32 = header->header_crc;
|
||||
+ }
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+/* Restore a table from another. The second parameter indicates whether the
|
||||
+ * restoring table is the primary GPT or not
|
||||
+ */
|
||||
+static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary)
|
||||
+{
|
||||
+ /* Determine if the restoring GPT is valid */
|
||||
+ psa_status_t ret = validate_table(restore_from, is_primary);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ struct gpt_t restore_to;
|
||||
+ swap_headers(&(restore_from->header), &(restore_to.header));
|
||||
+
|
||||
+ /* Copy the partition array as well */
|
||||
+ ret = move_partition(
|
||||
+ restore_from->header.array_lba,
|
||||
+ restore_to.header.array_lba,
|
||||
+ (restore_from->header.num_partitions +
|
||||
+ gpt_entry_per_lba_count() - 1) / gpt_entry_per_lba_count());
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Write the header */
|
||||
+ ret = write_header_to_flash(&restore_to);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ ERROR("Unable to write %s GPT header\n", is_primary ? "backup" : "primary");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* The primary GPT is cached in memory */
|
||||
+ if (!is_primary) {
|
||||
+ memcpy(&(primary_gpt.header), &(restore_to.header), GPT_HEADER_SIZE);
|
||||
+ primary_gpt.num_used_partitions = restore_from->num_used_partitions;
|
||||
+ }
|
||||
+
|
||||
+ INFO("Successfully restored %s GPT table\n", is_primary ? "backup" : "primary");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/* Converts unicode string to valid ascii */
|
||||
static psa_status_t unicode_to_ascii(const char *unicode, char *ascii)
|
||||
{
|
||||
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
index 18cf2c8f3..f15a0a737 100644
|
||||
--- a/lib/gpt/unittests/gpt/test_gpt.c
|
||||
+++ b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
@@ -369,6 +369,141 @@ void test_gpt_init_should_failWhenFlashDriverNotFullyDefined(void)
|
||||
mock_driver.erase = erase_fn;
|
||||
}
|
||||
|
||||
+void test_gpt_validate_should_validateWhenGptGood(void)
|
||||
+{
|
||||
+ setup_test_gpt();
|
||||
+
|
||||
+ /* Each entry will be read in order to check the partition array CRC */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_validate(true));
|
||||
+
|
||||
+ /* Now do the backup */
|
||||
+ setup_backup_gpt();
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_validate(false));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_validate_should_failWhenGptSigBad(void)
|
||||
+{
|
||||
+ test_header.signature[0] = '\0';
|
||||
+ setup_test_gpt();
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
|
||||
+
|
||||
+ /* Now do the backup */
|
||||
+ setup_backup_gpt();
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_validate_should_failWhenHeaderCrcBad(void)
|
||||
+{
|
||||
+ test_header.header_crc--;
|
||||
+ setup_test_gpt();
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
|
||||
+
|
||||
+ /* Now do the backup */
|
||||
+ struct gpt_header_t backup_header;
|
||||
+ MAKE_BACKUP_HEADER(backup_header, test_header);
|
||||
+ backup_header.header_crc--;
|
||||
+ register_mocked_read(&backup_header, sizeof(backup_header));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_validate_should_failWhenLbaPointerBad(void)
|
||||
+{
|
||||
+ test_header.current_lba = 2;
|
||||
+ test_header.backup_lba = 3;
|
||||
+ setup_test_gpt();
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
|
||||
+
|
||||
+ /* Now set the backup LBA to be something different that what it should be
|
||||
+ * to force a mismatch
|
||||
+ */
|
||||
+ test_header.current_lba = default_header.current_lba;
|
||||
+ test_header.backup_lba = default_header.backup_lba - 1;
|
||||
+
|
||||
+ /* Now do the backup */
|
||||
+ struct gpt_header_t backup_header;
|
||||
+ MAKE_BACKUP_HEADER(backup_header, test_header);
|
||||
+ register_mocked_read(&backup_header, sizeof(backup_header));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_validate_should_failWhenArrayCrcBad(void)
|
||||
+{
|
||||
+ test_header.array_crc--;
|
||||
+ setup_test_gpt();
|
||||
+
|
||||
+ /* Each entry will be read in order to check the partition array CRC */
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
|
||||
+
|
||||
+ /* Now do the backup */
|
||||
+ struct gpt_header_t backup_header;
|
||||
+ MAKE_BACKUP_HEADER(backup_header, test_header);
|
||||
+ backup_header.array_crc--;
|
||||
+ register_mocked_read(&backup_header, sizeof(test_partition_array));
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_restore_should_restorePrimaryFromBackup(void)
|
||||
+{
|
||||
+ /* Start with a valid GPT */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* The backup table is read and checked for validity, including taking
|
||||
+ * CRC32 of partition array
|
||||
+ */
|
||||
+ setup_backup_gpt();
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_restore(true));
|
||||
+}
|
||||
+
|
||||
+void test_gpt_restore_should_failToRestoreWhenBackupIsBad(void)
|
||||
+{
|
||||
+ /* Start with a valid GPT */
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ /* The backup table is read and checked for validity. Corrupt it in
|
||||
+ * various ways
|
||||
+ */
|
||||
+ struct gpt_header_t backup_header;
|
||||
+
|
||||
+ /* Bad signature */
|
||||
+ MAKE_BACKUP_HEADER(backup_header, test_header);
|
||||
+ backup_header.signature[0] = '\0';
|
||||
+ register_mocked_read(&backup_header, sizeof(backup_header));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_restore(true));
|
||||
+
|
||||
+ /* Bad header CRC */
|
||||
+ MAKE_BACKUP_HEADER(backup_header, test_header);
|
||||
+ backup_header.header_crc = 0;
|
||||
+ register_mocked_read(&backup_header, sizeof(backup_header));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_restore(true));
|
||||
+
|
||||
+ /* Bad LBA */
|
||||
+ test_header.backup_lba = 2;
|
||||
+ MAKE_BACKUP_HEADER(backup_header, test_header);
|
||||
+ register_mocked_read(&backup_header, sizeof(backup_header));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_restore(true));
|
||||
+ test_header.backup_lba = default_header.backup_lba;
|
||||
+
|
||||
+ /* Bad array CRC. Will involve reading array entries */
|
||||
+ MAKE_BACKUP_HEADER(backup_header, test_header);
|
||||
+ backup_header.array_crc = 0;
|
||||
+ register_mocked_read(&backup_header, sizeof(backup_header));
|
||||
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
|
||||
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_restore(true));
|
||||
+}
|
||||
+
|
||||
void test_gpt_entry_create_should_createNewEntry(void)
|
||||
{
|
||||
/* Add an entry. It must not overlap with an existing entry and must also
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
From ba38f0f304ad69047ce9830b81a4f9cfbccb8fcb Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Tue, 30 Dec 2025 10:51:57 +0000
|
||||
Subject: [PATCH] lib: gpt: Added defragmentation operation
|
||||
|
||||
Defragmentation moves all of used data of the device to the beginning
|
||||
such that all of the free space is afterwards towards the end. This is
|
||||
less meaningful on flash devices however is still provided as an
|
||||
operation for completeness. This is done by ordering the partition entry
|
||||
array first, so that it is safe to shuffle data in one direction without
|
||||
risk of data loss/overwriting.
|
||||
|
||||
Change-Id: Ic401c79ac8c1fba2489ab2680efc02139c759c66
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [21ff1f85c0b5463c1ea75fd61d872c0029eed902]
|
||||
---
|
||||
lib/gpt/inc/gpt.h | 8 ++
|
||||
lib/gpt/src/gpt.c | 190 +++++++++++++++++++++++++++++++
|
||||
lib/gpt/unittests/gpt/test_gpt.c | 7 ++
|
||||
3 files changed, 205 insertions(+)
|
||||
|
||||
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
|
||||
index 0ce4d04ab..baf4767f9 100644
|
||||
--- a/lib/gpt/inc/gpt.h
|
||||
+++ b/lib/gpt/inc/gpt.h
|
||||
@@ -229,6 +229,14 @@ psa_status_t gpt_validate(bool is_primary);
|
||||
*/
|
||||
psa_status_t gpt_restore(bool is_primary);
|
||||
|
||||
+/**
|
||||
+ * \brief Defragments the GPT, ensuring free space becomes contiguous.
|
||||
+ *
|
||||
+ * \retval PSA_SUCCESS Success.
|
||||
+ * \retval PSA_ERROR_STORAGE_FAILURE I/O failure.
|
||||
+ */
|
||||
+psa_status_t gpt_defragment(void);
|
||||
+
|
||||
/**
|
||||
* \brief Reads the GPT header from the second block (LBA 1).
|
||||
*
|
||||
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
|
||||
index 1bc592bb3..b1d4597cd 100644
|
||||
--- a/lib/gpt/src/gpt.c
|
||||
+++ b/lib/gpt/src/gpt.c
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
+#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
@@ -225,6 +226,7 @@ static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const void *name
|
||||
static bool gpt_entry_cmp_type(const struct gpt_entry_t *entry, const void *type);
|
||||
static psa_status_t validate_table(struct gpt_t *table, bool is_primary);
|
||||
static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary);
|
||||
+static psa_status_t sort_partition_array(struct gpt_t *table);
|
||||
|
||||
/* PUBLIC API FUNCTIONS */
|
||||
|
||||
@@ -819,6 +821,66 @@ psa_status_t gpt_restore(bool is_primary)
|
||||
}
|
||||
}
|
||||
|
||||
+psa_status_t gpt_defragment(void)
|
||||
+{
|
||||
+ /* First, sort the partition array according to start LBA. This means that
|
||||
+ * moving partitions towards the start of the flash sequentially is safe
|
||||
+ * and will not result in lost data.
|
||||
+ */
|
||||
+ psa_status_t ret = sort_partition_array(&primary_gpt);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ WARN("Unable to defragment flash!\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ uint64_t prev_end = primary_gpt.header.first_lba;
|
||||
+ struct gpt_entry_t entry;
|
||||
+
|
||||
+ for (uint32_t i = 0; i < primary_gpt.num_used_partitions; ++i) {
|
||||
+ ret = read_entry_from_flash(&primary_gpt, i, &entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Move to be next to previous entry. Continue if already where it
|
||||
+ * needs to be.
|
||||
+ */
|
||||
+ if (prev_end == entry.start) {
|
||||
+ prev_end = entry.end + 1;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ const uint64_t num_blocks = entry.end - entry.start + 1;
|
||||
+ ret = move_partition(entry.start, prev_end, num_blocks);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Update header information */
|
||||
+ entry.start = prev_end;
|
||||
+ entry.end = entry.start + num_blocks - 1;
|
||||
+ prev_end = entry.end + 1;
|
||||
+
|
||||
+ /* Write the entry change, skipping header update until every entry
|
||||
+ * written
|
||||
+ */
|
||||
+ ret = write_entry(i, &entry, true);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Write everything to flash after defragmentation if not done so already.
|
||||
+ * The previous loop will write the last entry to the LBA buffer, which may
|
||||
+ * or not may not be flushed
|
||||
+ */
|
||||
+ if (write_buffered) {
|
||||
+ return flush_lba_buf();
|
||||
+ }
|
||||
+
|
||||
+ return update_header(primary_gpt.num_used_partitions);
|
||||
+}
|
||||
+
|
||||
/* Initialises GPT from first block. */
|
||||
psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_partitions)
|
||||
{
|
||||
@@ -1533,6 +1595,134 @@ static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/* Comparison function to pass to qsort */
|
||||
+static int cmp_u64(const void *a, const void *b)
|
||||
+{
|
||||
+ const uint64_t *a_u64 = (const uint64_t *)a;
|
||||
+ const uint64_t *b_u64 = (const uint64_t *)b;
|
||||
+ return (*a_u64 > *b_u64) - (*a_u64 < *b_u64);
|
||||
+}
|
||||
+
|
||||
+/* bsearch but returns the index rather than the item */
|
||||
+static int64_t bsearch_index(uint64_t arr[], uint32_t len, uint64_t key)
|
||||
+{
|
||||
+ uint32_t l = 0;
|
||||
+ uint32_t r = len;
|
||||
+
|
||||
+ while (l < r) {
|
||||
+ uint32_t m = l + (r - l) / 2;
|
||||
+ uint64_t item = arr[m];
|
||||
+
|
||||
+ if (item < key) {
|
||||
+ l = m + 1;
|
||||
+ } else if (item > key) {
|
||||
+ r = m;
|
||||
+ } else {
|
||||
+ return (int64_t)m;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+/* Sorts the partition array for the given table by the start LBA for each
|
||||
+ * partition. This makes defragmentation easier.
|
||||
+ */
|
||||
+static psa_status_t sort_partition_array(struct gpt_t *table)
|
||||
+{
|
||||
+ /* To avoid as much I/O as possible, the LBA's for each entry are sorted in
|
||||
+ * memory and then the entries rearranged on flash after
|
||||
+ */
|
||||
+ uint64_t lba_arr[table->num_used_partitions];
|
||||
+ psa_status_t ret;
|
||||
+ for (uint32_t i = 0; i < table->num_used_partitions; ++i) {
|
||||
+ struct gpt_entry_t entry;
|
||||
+ ret = read_entry_from_flash(table, i, &entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ lba_arr[i] = entry.start;
|
||||
+ }
|
||||
+
|
||||
+ qsort(lba_arr, table->num_used_partitions, sizeof(uint64_t), cmp_u64);
|
||||
+
|
||||
+ /* Now read and place the entries in the correct spot, starting with the
|
||||
+ * first. Each entry is dealt with as it is encountered. When an entry is
|
||||
+ * found and already in the correct spot, the next smallest index not yet
|
||||
+ * handled becomes the next.
|
||||
+ */
|
||||
+ struct gpt_entry_t saved_entry = {0};
|
||||
+ struct gpt_entry_t curr_entry;
|
||||
+ uint8_t handled_indices[table->num_used_partitions];
|
||||
+ memset(handled_indices, 0, table->num_used_partitions);
|
||||
+
|
||||
+ ret = read_entry_from_flash(table, 0, &curr_entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ for (uint32_t i = 0; i < table->num_used_partitions; ++i) {
|
||||
+ const int64_t new_index = bsearch_index(
|
||||
+ lba_arr,
|
||||
+ table->num_used_partitions,
|
||||
+ curr_entry.start);
|
||||
+ if (new_index < 0) {
|
||||
+ ERROR("Encountered unknown partition entry!\n");
|
||||
+ return PSA_ERROR_STORAGE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
+ /* For final entry, just write it out */
|
||||
+ if (i == table->num_used_partitions - 1) {
|
||||
+ ret = write_entry((uint32_t)new_index, &curr_entry, false);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Replace the entry in the new_index place with the current entry */
|
||||
+ ret = read_entry_from_flash(table, (uint32_t)new_index, &saved_entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ struct efi_guid_t saved_guid = saved_entry.unique_guid;
|
||||
+ struct efi_guid_t curr_guid = curr_entry.unique_guid;
|
||||
+ if (efi_guid_cmp(&saved_guid, &curr_guid) == 0) {
|
||||
+ /* This entry is already where it needs to be, so try the smallest
|
||||
+ * index not yet handled next
|
||||
+ */
|
||||
+ handled_indices[new_index] = 1;
|
||||
+ uint32_t next_entry = 0;
|
||||
+ while(next_entry < table->num_used_partitions && handled_indices[next_entry]) {
|
||||
+ ++next_entry;
|
||||
+ }
|
||||
+
|
||||
+ if (next_entry == table->num_used_partitions) {
|
||||
+ /* Done everything */
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ ret = read_entry_from_flash(table, next_entry, &saved_entry);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ } else {
|
||||
+ /* Write, skipping header update until very end */
|
||||
+ ret = write_entry((uint32_t)new_index, &curr_entry, true);
|
||||
+ if (ret != PSA_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Ready up for the next loop */
|
||||
+ curr_entry = saved_entry;
|
||||
+ handled_indices[new_index] = 1;
|
||||
+ }
|
||||
+
|
||||
+ return PSA_SUCCESS;
|
||||
+}
|
||||
+
|
||||
/* Converts unicode string to valid ascii */
|
||||
static psa_status_t unicode_to_ascii(const char *unicode, char *ascii)
|
||||
{
|
||||
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
index f15a0a737..2f05a6b4a 100644
|
||||
--- a/lib/gpt/unittests/gpt/test_gpt.c
|
||||
+++ b/lib/gpt/unittests/gpt/test_gpt.c
|
||||
@@ -504,6 +504,13 @@ void test_gpt_restore_should_failToRestoreWhenBackupIsBad(void)
|
||||
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_restore(true));
|
||||
}
|
||||
|
||||
+void test_gpt_defragment_should_succeedWhenNoIOFailure(void)
|
||||
+{
|
||||
+ setup_valid_gpt();
|
||||
+
|
||||
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_defragment());
|
||||
+}
|
||||
+
|
||||
void test_gpt_entry_create_should_createNewEntry(void)
|
||||
{
|
||||
/* Add an entry. It must not overlap with an existing entry and must also
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
From f9e4d1f48a4553278dc4de82b0d2f4d82b1e5193 Mon Sep 17 00:00:00 2001
|
||||
From: Antonio de Angelis <antonio.deangelis@arm.com>
|
||||
Date: Wed, 4 Mar 2026 13:05:51 +0000
|
||||
Subject: [PATCH] lib: GPT: Fix cppcheck warnings
|
||||
|
||||
Change-Id: Ia9e99dc59cbf869b804a24ba029f19f7170860b4
|
||||
Signed-off-by: Antonio de Angelis <antonio.deangelis@arm.com>
|
||||
Upstream-Status: Backport [92d5b6b7d296c174364e684508089c0a0678e3bb]
|
||||
---
|
||||
lib/gpt/src/gpt.c | 15 ++++++++++-----
|
||||
1 file changed, 10 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
|
||||
index b1d4597cd..a9dbb918e 100644
|
||||
--- a/lib/gpt/src/gpt.c
|
||||
+++ b/lib/gpt/src/gpt.c
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
+#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -194,7 +195,7 @@ static inline uint64_t gpt_entry_per_lba_count(void);
|
||||
static inline void swap_headers(const struct gpt_header_t *src, struct gpt_header_t *dst);
|
||||
static psa_status_t count_used_partitions(const struct gpt_t *table,
|
||||
uint32_t *num_used);
|
||||
-static inline void parse_entry(struct gpt_entry_t *entry,
|
||||
+static inline void parse_entry(const struct gpt_entry_t *entry,
|
||||
struct partition_entry_t *partition_entry);
|
||||
static psa_status_t read_from_flash(uint64_t required_lba);
|
||||
static psa_status_t read_entry_from_flash(const struct gpt_t *table,
|
||||
@@ -226,7 +227,7 @@ static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const void *name
|
||||
static bool gpt_entry_cmp_type(const struct gpt_entry_t *entry, const void *type);
|
||||
static psa_status_t validate_table(struct gpt_t *table, bool is_primary);
|
||||
static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary);
|
||||
-static psa_status_t sort_partition_array(struct gpt_t *table);
|
||||
+static psa_status_t sort_partition_array(const struct gpt_t *table);
|
||||
|
||||
/* PUBLIC API FUNCTIONS */
|
||||
|
||||
@@ -1006,7 +1007,7 @@ static inline uint64_t gpt_entry_per_lba_count(void)
|
||||
}
|
||||
|
||||
/* Copies information from the entry to the user visible structure */
|
||||
-static inline void parse_entry(struct gpt_entry_t *entry,
|
||||
+static inline void parse_entry(const struct gpt_entry_t *entry,
|
||||
struct partition_entry_t *partition_entry)
|
||||
{
|
||||
partition_entry->start = entry->start;
|
||||
@@ -1604,7 +1605,7 @@ static int cmp_u64(const void *a, const void *b)
|
||||
}
|
||||
|
||||
/* bsearch but returns the index rather than the item */
|
||||
-static int64_t bsearch_index(uint64_t arr[], uint32_t len, uint64_t key)
|
||||
+static int64_t bsearch_index(const uint64_t arr[], uint32_t len, uint64_t key)
|
||||
{
|
||||
uint32_t l = 0;
|
||||
uint32_t r = len;
|
||||
@@ -1628,8 +1629,12 @@ static int64_t bsearch_index(uint64_t arr[], uint32_t len, uint64_t key)
|
||||
/* Sorts the partition array for the given table by the start LBA for each
|
||||
* partition. This makes defragmentation easier.
|
||||
*/
|
||||
-static psa_status_t sort_partition_array(struct gpt_t *table)
|
||||
+static psa_status_t sort_partition_array(const struct gpt_t *table)
|
||||
{
|
||||
+ if (table->num_used_partitions == 0) {
|
||||
+ assert(table->num_used_partitions > 0);
|
||||
+ return PSA_ERROR_INVALID_ARGUMENT;
|
||||
+ }
|
||||
/* To avoid as much I/O as possible, the LBA's for each entry are sorted in
|
||||
* memory and then the entries rearranged on flash after
|
||||
*/
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
From 31cd6d9eb82792efd5af7d99802581eace13a083 Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Fri, 13 Mar 2026 17:10:25 +0000
|
||||
Subject: [PATCH] lib: efi_guid: Remove unecessary include folder
|
||||
|
||||
There are no header files in the src folder, so there is nothing to
|
||||
include. This line was redundant.
|
||||
|
||||
Change-Id: I5289932119629fc1ef6a71f0a0ceb63e5a466c96
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [e74a1f467317c16487b9d41f43f147dd41985cb9]
|
||||
---
|
||||
lib/efi_guid/CMakeLists.txt | 2 --
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
diff --git a/lib/efi_guid/CMakeLists.txt b/lib/efi_guid/CMakeLists.txt
|
||||
index 656eb72ea..9c4771cba 100644
|
||||
--- a/lib/efi_guid/CMakeLists.txt
|
||||
+++ b/lib/efi_guid/CMakeLists.txt
|
||||
@@ -15,8 +15,6 @@ target_sources(tfm_efi_guid
|
||||
target_include_directories(tfm_efi_guid
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||
- PRIVATE
|
||||
- ${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(tfm_efi_guid
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
From 0b6a2e681b0f76c425896990ebc4afcbc65419d3 Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Fri, 13 Mar 2026 17:11:05 +0000
|
||||
Subject: [PATCH] lib: efi_guid: Correct included folder
|
||||
|
||||
The use of the variables is unecessary and has no real effect. This
|
||||
change is therefore simplified.
|
||||
|
||||
Change-Id: I0811b4768502d15b62f1af3ef90a56db3e9df525
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [7bb11e81cd69718df3b11a3f8b42a3b5edf4c42d]
|
||||
---
|
||||
lib/efi_guid/CMakeLists.txt | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/efi_guid/CMakeLists.txt b/lib/efi_guid/CMakeLists.txt
|
||||
index 9c4771cba..95d7b8f1e 100644
|
||||
--- a/lib/efi_guid/CMakeLists.txt
|
||||
+++ b/lib/efi_guid/CMakeLists.txt
|
||||
@@ -14,7 +14,7 @@ target_sources(tfm_efi_guid
|
||||
|
||||
target_include_directories(tfm_efi_guid
|
||||
PUBLIC
|
||||
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||
+ inc
|
||||
)
|
||||
|
||||
target_link_libraries(tfm_efi_guid
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
From f1862245d39a726c8e37f58498dd433c6fd99d1b Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Fri, 13 Mar 2026 17:13:07 +0000
|
||||
Subject: [PATCH] lib: efi_soft_crc: Correct include directory
|
||||
|
||||
The variables had no real effect, so this is therefore simplified.
|
||||
|
||||
Change-Id: Ifa03bc23e0419f9d6d598e7753f23dfde57c7bf6
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [3856d55117b727c6c21bb821fa4236b113fb08ef]
|
||||
---
|
||||
lib/ext/efi_soft_crc/CMakeLists.txt | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/ext/efi_soft_crc/CMakeLists.txt b/lib/ext/efi_soft_crc/CMakeLists.txt
|
||||
index 47fd2d507..d77310167 100644
|
||||
--- a/lib/ext/efi_soft_crc/CMakeLists.txt
|
||||
+++ b/lib/ext/efi_soft_crc/CMakeLists.txt
|
||||
@@ -14,5 +14,5 @@ target_sources(tfm_efi_soft_crc
|
||||
|
||||
target_include_directories(tfm_efi_soft_crc
|
||||
PUBLIC
|
||||
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||
+ inc
|
||||
)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
From 343bba6c20802555dfb92c3e2b22a7e07c10a57a Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Fri, 13 Mar 2026 17:16:00 +0000
|
||||
Subject: [PATCH] lib: gpt: Add missing link library
|
||||
|
||||
tfm_log requires tfm_vprintf. When building for a platform with logging
|
||||
enabled, these headers are therefore required.
|
||||
|
||||
Change-Id: I63b35d5af818ea5ad7b1fe76200250ad98584a63
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [92a3738634fe121766705599d66e4a8b772ce06e]
|
||||
---
|
||||
lib/gpt/CMakeLists.txt | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/lib/gpt/CMakeLists.txt b/lib/gpt/CMakeLists.txt
|
||||
index 5befc346f..ed31adfbe 100644
|
||||
--- a/lib/gpt/CMakeLists.txt
|
||||
+++ b/lib/gpt/CMakeLists.txt
|
||||
@@ -36,6 +36,7 @@ target_compile_definitions(tfm_gpt
|
||||
target_link_libraries(tfm_gpt
|
||||
PUBLIC
|
||||
tfm_log_headers
|
||||
+ tfm_vprintf_headers
|
||||
PRIVATE
|
||||
tfm_efi_guid
|
||||
tfm_efi_soft_crc
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
From 1ae97df8db8babcaa999e47d9641c83c4ee8424a Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Fri, 13 Mar 2026 17:17:33 +0000
|
||||
Subject: [PATCH] lib: gpt: Correct variable name used
|
||||
|
||||
Change-Id: I86d616b3c86cf0a1fd053b732565477278030d89
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [bc9522f5c47c7e3f3a92189073166e73323010e9]
|
||||
---
|
||||
lib/gpt/unittests/gpt/utcfg.cmake | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/gpt/unittests/gpt/utcfg.cmake b/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
index f3f4e7dcc..69a1d10bd 100644
|
||||
--- a/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
+++ b/lib/gpt/unittests/gpt/utcfg.cmake
|
||||
@@ -27,6 +27,6 @@ list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/tfm_vprintf/inc/tfm_vprintf.h)
|
||||
list(APPEND MOCK_HEADERS ${TFM_ROOT_DIR}/lib/ext/efi_soft_crc/inc/efi_soft_crc.h)
|
||||
|
||||
# Compile-time definitions
|
||||
-list(APPEND UNIT_TEST_COMPILE_DEFS LOG_LEVEL=LOG_LEVEL_VERBOSE)
|
||||
+list(APPEND UNIT_TEST_COMPILE_DEFS GPT_LOG_LEVEL=LOG_LEVEL_VERBOSE)
|
||||
list(APPEND UNIT_TEST_COMPILE_DEFS TFM_GPT_BLOCK_SIZE=512)
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
From eb934a532bbc8385cd9b062fc80cece61350f086 Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Fri, 13 Mar 2026 17:19:50 +0000
|
||||
Subject: [PATCH] lib: gpt: Correct include directory
|
||||
|
||||
The variables have no real effect, so therefore this is simplified.
|
||||
|
||||
Change-Id: Ie912941f8eafd9cd5bf4dd88927640ad70d0676a
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [369c897be0b9102cec8141c53b3d3fe6abb56b6e]
|
||||
---
|
||||
lib/gpt/CMakeLists.txt | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/gpt/CMakeLists.txt b/lib/gpt/CMakeLists.txt
|
||||
index ed31adfbe..19cfe5bc2 100644
|
||||
--- a/lib/gpt/CMakeLists.txt
|
||||
+++ b/lib/gpt/CMakeLists.txt
|
||||
@@ -22,7 +22,7 @@ target_sources(tfm_gpt
|
||||
|
||||
target_include_directories(tfm_gpt
|
||||
PUBLIC
|
||||
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||
+ inc
|
||||
$<INSTALL_INTERFACE:inc>
|
||||
)
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
From ce3a4f3dd8900c068f2bfe951d55b895e3c10d43 Mon Sep 17 00:00:00 2001
|
||||
From: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Date: Fri, 13 Mar 2026 17:29:04 +0000
|
||||
Subject: [PATCH] lib: gpt: Move contents of CMake config file
|
||||
|
||||
A config.cmake used in a library can be a little confusing, especially
|
||||
considering that such files are used for platform configurations. The
|
||||
contents of the file are placed directly in the CMakeLists.txt that was
|
||||
including it.
|
||||
|
||||
Change-Id: I8a88971feebaf37498828af5854de94e74e16f2c
|
||||
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
|
||||
Upstream-Status: Backport [4b569b02b98a577e45e833f4dd9886e54df71a7d]
|
||||
---
|
||||
lib/gpt/CMakeLists.txt | 6 +++---
|
||||
lib/gpt/config.cmake | 8 --------
|
||||
2 files changed, 3 insertions(+), 11 deletions(-)
|
||||
delete mode 100644 lib/gpt/config.cmake
|
||||
|
||||
diff --git a/lib/gpt/CMakeLists.txt b/lib/gpt/CMakeLists.txt
|
||||
index 19cfe5bc2..4a15173fd 100644
|
||||
--- a/lib/gpt/CMakeLists.txt
|
||||
+++ b/lib/gpt/CMakeLists.txt
|
||||
@@ -9,10 +9,10 @@ cmake_minimum_required(VERSION 3.21)
|
||||
|
||||
add_library(tfm_gpt STATIC)
|
||||
|
||||
-include(./config.cmake)
|
||||
+set(GPT_LOG_LEVEL LOG_LEVEL_INFO CACHE STRING "Set default log level for the GPT library")
|
||||
|
||||
-if(NOT DEFINED TFM_GPT_BLOCK_SIZE OR NOT DEFINED GPT_LOG_LEVEL)
|
||||
- message(FATAL_ERROR "TFM_GPT_BLOCK_SIZE and GPT_LOG_LEVEL must be defined to use GPT library")
|
||||
+if(NOT DEFINED TFM_GPT_BLOCK_SIZE)
|
||||
+ message(FATAL_ERROR "TFM_GPT_BLOCK_SIZE must be defined to use GPT library")
|
||||
endif()
|
||||
|
||||
target_sources(tfm_gpt
|
||||
diff --git a/lib/gpt/config.cmake b/lib/gpt/config.cmake
|
||||
deleted file mode 100644
|
||||
index 9575aa8a8..000000000
|
||||
--- a/lib/gpt/config.cmake
|
||||
+++ /dev/null
|
||||
@@ -1,8 +0,0 @@
|
||||
-#-------------------------------------------------------------------------------
|
||||
-# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
|
||||
-#
|
||||
-# SPDX-License-Identifier: BSD-3-Clause
|
||||
-#
|
||||
-#-------------------------------------------------------------------------------
|
||||
-
|
||||
-set(GPT_LOG_LEVEL LOG_LEVEL_INFO CACHE STRING "Set default log level for the GPT library")
|
||||
@@ -48,6 +48,23 @@ SRC_URI:append:corstone1000 = " \
|
||||
file://0014-Workaround-compile-errors-in-AES.patch \
|
||||
file://0015-CC312-Add-barrier-before-first-AO-lock-write.patch \
|
||||
file://0016-Platform-CS1K-make-mutlicore-support-platform-generi.patch \
|
||||
file://0017-lib-efi_guid-Added-EFI-GUID-library.patch \
|
||||
file://0018-lib-efi_soft_crc-Added-EFI-CRC-library.patch \
|
||||
file://0019-lib-gpt-Implemented-generic-GPT-parser-for-flash.patch \
|
||||
file://0020-lib-gpt-Expanded-how-GPT-partition-can-be-identified.patch \
|
||||
file://0021-lib-gpt-Added-operations-to-modify-partitions.patch \
|
||||
file://0022-lib-gpt-Added-operation-to-move-entry.patch \
|
||||
file://0023-lib-gpt-Added-ability-to-create-and-remove-partition.patch \
|
||||
file://0024-lib-gpt-Added-table-validation-operations.patch \
|
||||
file://0025-lib-gpt-Added-defragmentation-operation.patch \
|
||||
file://0026-lib-GPT-Fix-cppcheck-warnings.patch \
|
||||
file://0027-lib-efi_guid-Remove-unecessary-include-folder.patch \
|
||||
file://0028-lib-efi_guid-Correct-included-folder.patch \
|
||||
file://0029-lib-efi_soft_crc-Correct-include-directory.patch \
|
||||
file://0030-lib-gpt-Add-missing-link-library.patch \
|
||||
file://0031-lib-gpt-Correct-variable-name-used.patch \
|
||||
file://0032-lib-gpt-Correct-include-directory.patch \
|
||||
file://0033-lib-gpt-Move-contents-of-CMake-config-file.patch \
|
||||
"
|
||||
|
||||
SRCREV_tfm-psa-adac:corstone1000 = "f2809ae231be33a1afcd7714f40756c67d846c88"
|
||||
|
||||
Reference in New Issue
Block a user