1
0
mirror of https://git.yoctoproject.org/meta-arm synced 2026-05-07 04:58:57 +00:00

arm-bsp/trusted-firmware-m:cs1k: Add extra GPT library operations

These patches add functionality to duplicate GPT partition entries. This
combines a "create-write" into a single step, letting the GPT library
handle it, useful for the Corstone1000 firmware update process.

Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Signed-off-by: Jon Mason <jon.mason@arm.com>
This commit is contained in:
Frazer Carsley
2026-04-24 16:36:55 +01:00
committed by Jon Mason
parent 9af92ed09e
commit 1eaa431ff3
7 changed files with 1613 additions and 0 deletions
@@ -0,0 +1,216 @@
From ca0d50fc1abbfe165941dc0bd674bb117f236f87 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Mon, 30 Mar 2026 14:06:20 +0100
Subject: [PATCH] lib: gpt: Show intent of GUIDs in unittests more clearly
The standard EFI_GUID macros used in the unittests do not convey the
full meaning of the why that particular GUID is used. The new macros can
be used to make it clear when a valid GUID is being used or when the
value is just a dummy and has no meaning.
Change-Id: I1cf041703cbc40e60072e0662baebaef3239d3d1
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/50233/1]
---
lib/gpt/unittests/gpt/test_gpt.c | 55 ++++++++++++++++++--------------
1 file changed, 31 insertions(+), 24 deletions(-)
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
index bd161ec74..32dfb8fb2 100644
--- a/lib/gpt/unittests/gpt/test_gpt.c
+++ b/lib/gpt/unittests/gpt/test_gpt.c
@@ -69,6 +69,13 @@
backup.array_lba = TEST_GPT_BACKUP_ARRAY_LBA; \
} while (0)
+/* These macros make it clearer in the tests what is happening */
+#define TEST_GPT_VALID_GUID(...) MAKE_EFI_GUID(__VA_ARGS__)
+#define TEST_GPT_DUMMY_GUID NULL_GUID
+
+#define TEST_GPT_VALID_TYPE(...) MAKE_EFI_GUID(__VA_ARGS__)
+#define TEST_GPT_DUMMY_TYPE NULL_GUID
+
/* MBR partition entry */
struct mbr_entry_t {
/* Indicates if bootable */
@@ -176,24 +183,24 @@ static struct gpt_header_t test_header;
/* Default entry array. This is valid, though fragmented. */
static struct gpt_entry_t default_partition_array[TEST_DEFAULT_NUM_PARTITIONS] = {
{
- .type = MAKE_EFI_GUID(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
- .guid = MAKE_EFI_GUID(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
+ .type = TEST_GPT_VALID_TYPE(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
+ .guid = TEST_GPT_VALID_GUID(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
.start = TEST_GPT_FIRST_PARTITION_START,
.end = TEST_GPT_FIRST_PARTITION_END,
.attr = 0,
.name = "First partition"
},
{
- .type = MAKE_EFI_GUID(2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
- .guid = MAKE_EFI_GUID(2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
+ .type = TEST_GPT_VALID_TYPE(2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
+ .guid = TEST_GPT_VALID_GUID(2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
.start = TEST_GPT_SECOND_PARTITION_START,
.end = TEST_GPT_SECOND_PARTITION_END,
.attr = 0,
.name = "Second partition"
},
{
- .type = MAKE_EFI_GUID(3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11),
- .guid = MAKE_EFI_GUID(3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11),
+ .type = TEST_GPT_VALID_TYPE(3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11),
+ .guid = TEST_GPT_VALID_GUID(3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11),
.start = TEST_GPT_THIRD_PARTITION_START,
.end = TEST_GPT_THIRD_PARTITION_END,
.attr = 0,
@@ -333,7 +340,7 @@ void test_gpt_init_should_overwriteOldGpt(void)
gpt_uninit();
/* Use a different disk GUID */
- const struct efi_guid_t new_guid = MAKE_EFI_GUID(1, 1, 3, 4, 5, 6 ,7 ,8, 9, 10, 11);
+ const struct efi_guid_t new_guid = TEST_GPT_VALID_GUID(1, 1, 3, 4, 5, 6 ,7 ,8, 9, 10, 11);
test_header.disk_guid = new_guid;
setup_valid_gpt();
@@ -744,7 +751,7 @@ void test_gpt_entry_create_should_createNewEntry(void)
/* Update header. Read each entry for CRC calculation. */
struct gpt_entry_t new_entry = {
- .type = NULL_GUID,
+ .type = TEST_GPT_DUMMY_TYPE,
.start = TEST_GPT_THIRD_PARTITION_END + 1,
.end = TEST_GPT_THIRD_PARTITION_END + 1,
.attr = 0,
@@ -752,12 +759,12 @@ void test_gpt_entry_create_should_createNewEntry(void)
};
/* 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);
+ struct efi_guid_t expected_guid = TEST_GPT_VALID_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);
+ struct efi_guid_t new_guid = TEST_GPT_DUMMY_GUID;
TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_create(
&expected_guid,
new_entry.start,
@@ -782,7 +789,7 @@ void test_gpt_entry_create_should_createNewEntryNextToLastEntry(void)
/* Update header. Read each entry for CRC calculation. */
struct gpt_entry_t new_entry = {
- .type = NULL_GUID,
+ .type = TEST_GPT_DUMMY_TYPE,
.start = TEST_GPT_THIRD_PARTITION_END + 1,
.end = TEST_GPT_THIRD_PARTITION_END + 1,
.attr = 0,
@@ -790,12 +797,12 @@ void test_gpt_entry_create_should_createNewEntryNextToLastEntry(void)
};
/* 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);
+ struct efi_guid_t expected_guid = TEST_GPT_VALID_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);
+ struct efi_guid_t new_guid = TEST_GPT_DUMMY_GUID;
char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
name[0] = 'a';
@@ -823,7 +830,7 @@ void test_gpt_entry_create_should_failToCreateEntryWhenLowestFreeLbaDoesNotHaveS
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 existing_guid = TEST_GPT_VALID_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';
@@ -840,17 +847,17 @@ 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),
+ .type = TEST_GPT_VALID_TYPE(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),
+ .guid = TEST_GPT_VALID_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 type = TEST_GPT_VALID_TYPE(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';
@@ -868,7 +875,7 @@ 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 type = TEST_GPT_DUMMY_TYPE;
struct efi_guid_t guid;
char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
name[0] = 'a';
@@ -917,7 +924,7 @@ void test_gpt_entry_create_should_failWhenOverlapping(void)
* 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 type = TEST_GPT_DUMMY_TYPE;
struct efi_guid_t guid;
char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
name[0] = 'a';
@@ -944,7 +951,7 @@ void test_gpt_entry_create_should_failWhenNameIsEmpty(void)
/* Start with a populated GPT */
setup_valid_gpt();
- struct efi_guid_t type = NULL_GUID;
+ struct efi_guid_t type = TEST_GPT_DUMMY_TYPE;
struct gpt_entry_t new_entry = {
.type = type,
.start = TEST_GPT_THIRD_PARTITION_END + 1,
@@ -969,7 +976,7 @@ void test_gpt_entry_create_should_failWhenSizeIsZero(void)
/* Start with a populated GPT */
setup_valid_gpt();
- struct efi_guid_t type = NULL_GUID;
+ struct efi_guid_t type = TEST_GPT_DUMMY_TYPE;
/* Make the size zero */
struct efi_guid_t new_guid;
@@ -1199,7 +1206,7 @@ void test_gpt_entry_change_type_should_setNewType(void)
/* 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);
+ struct efi_guid_t new_type = TEST_GPT_VALID_TYPE(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_change_type(&test_guid, &new_type));
}
@@ -1211,7 +1218,7 @@ void test_gpt_entry_change_type_should_failWhenEntryNotExisting(void)
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);
+ struct efi_guid_t new_type = TEST_GPT_VALID_TYPE(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));
}
@@ -1452,7 +1459,7 @@ void test_gpt_entry_read_by_type_should_failWhenEntryNotExisting(void)
/* 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);
+ struct efi_guid_t test_type = TEST_GPT_VALID_TYPE(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 */
@@ -0,0 +1,157 @@
From 229313778bae6ca16d6e3b25437c8e87eddf3084 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Mon, 30 Mar 2026 14:35:45 +0100
Subject: [PATCH] lib: gpt: Provide macro identifying free space
In the unit tests, it is often required to know where free space on the
mocked disk is in order to determine where it is valid to create or move
a new partition. The macro makes it clearer when this is being done.
Change-Id: I147faf516efa3e5c7fdd49775d0efa878890b771
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/50234/1]
---
lib/gpt/unittests/gpt/test_gpt.c | 42 +++++++++++++++++---------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
index 32dfb8fb2..0ae660336 100644
--- a/lib/gpt/unittests/gpt/test_gpt.c
+++ b/lib/gpt/unittests/gpt/test_gpt.c
@@ -58,6 +58,7 @@
#define TEST_GPT_SECOND_PARTITION_END (TEST_GPT_SECOND_PARTITION_START + 50)
#define TEST_GPT_THIRD_PARTITION_START (TEST_GPT_SECOND_PARTITION_END + 1)
#define TEST_GPT_THIRD_PARTITION_END (TEST_GPT_THIRD_PARTITION_START + 1)
+#define TEST_GPT_DISK_FREE_SPACE_START (TEST_GPT_THIRD_PARTITION_END + 1)
/* Populates a backup header from a primary header and calculates the new CRC32 */
#define MAKE_BACKUP_HEADER(backup, primary) \
@@ -752,8 +753,8 @@ void test_gpt_entry_create_should_createNewEntry(void)
/* Update header. Read each entry for CRC calculation. */
struct gpt_entry_t new_entry = {
.type = TEST_GPT_DUMMY_TYPE,
- .start = TEST_GPT_THIRD_PARTITION_END + 1,
- .end = TEST_GPT_THIRD_PARTITION_END + 1,
+ .start = TEST_GPT_DISK_FREE_SPACE_START,
+ .end = TEST_GPT_DISK_FREE_SPACE_START,
.attr = 0,
.name = "Fourth partition"
};
@@ -790,8 +791,8 @@ void test_gpt_entry_create_should_createNewEntryNextToLastEntry(void)
/* Update header. Read each entry for CRC calculation. */
struct gpt_entry_t new_entry = {
.type = TEST_GPT_DUMMY_TYPE,
- .start = TEST_GPT_THIRD_PARTITION_END + 1,
- .end = TEST_GPT_THIRD_PARTITION_END + 1,
+ .start = TEST_GPT_DISK_FREE_SPACE_START,
+ .end = TEST_GPT_DISK_FREE_SPACE_START,
.attr = 0,
.name = "Fourth partition"
};
@@ -846,10 +847,11 @@ void test_gpt_entry_create_should_failToCreateEntryWhenLowestFreeLbaDoesNotHaveS
void test_gpt_entry_create_should_failWhenTableFull(void)
{
/* Start with a full array of entries */
+ const uint64_t new_entry_end = TEST_GPT_DISK_FREE_SPACE_START;
struct gpt_entry_t new_entry = {
.type = TEST_GPT_VALID_TYPE(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,
+ .start = new_entry_end,
+ .end = new_entry_end,
.attr = 0,
.guid = TEST_GPT_VALID_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11),
.name = "Fourth partition"
@@ -861,9 +863,11 @@ void test_gpt_entry_create_should_failWhenTableFull(void)
struct efi_guid_t guid;
char name[GPT_ENTRY_NAME_LENGTH] = {'\0'};
name[0] = 'a';
+ const uint64_t new_free_space = new_entry_end + 1;
+
TEST_ASSERT_EQUAL(PSA_ERROR_INSUFFICIENT_STORAGE, gpt_entry_create(
&type,
- TEST_GPT_THIRD_PARTITION_END + 4,
+ new_free_space,
1,
0,
name,
@@ -881,7 +885,7 @@ void test_gpt_entry_create_should_failWhenLbaOffDisk(void)
name[0] = 'a';
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
&type,
- TEST_GPT_THIRD_PARTITION_END + 1,
+ TEST_GPT_DISK_FREE_SPACE_START,
1000,
0,
name,
@@ -954,8 +958,8 @@ void test_gpt_entry_create_should_failWhenNameIsEmpty(void)
struct efi_guid_t type = TEST_GPT_DUMMY_TYPE;
struct gpt_entry_t new_entry = {
.type = type,
- .start = TEST_GPT_THIRD_PARTITION_END + 1,
- .end = TEST_GPT_THIRD_PARTITION_END + 1,
+ .start = TEST_GPT_DISK_FREE_SPACE_START,
+ .end = TEST_GPT_DISK_FREE_SPACE_START,
.attr = 0,
};
@@ -984,7 +988,7 @@ void test_gpt_entry_create_should_failWhenSizeIsZero(void)
name[0] = 'a';
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_create(
&type,
- TEST_GPT_THIRD_PARTITION_END + 1,
+ TEST_GPT_DISK_FREE_SPACE_START,
0,
0,
name,
@@ -1016,8 +1020,8 @@ void test_gpt_entry_move_should_moveEntry(void)
/* 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));
+ TEST_GPT_DISK_FREE_SPACE_START,
+ TEST_GPT_DISK_FREE_SPACE_START));
}
void test_gpt_entry_move_should_failWhenEntryNotExisting(void)
@@ -1030,8 +1034,8 @@ void test_gpt_entry_move_should_failWhenEntryNotExisting(void)
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));
+ TEST_GPT_DISK_FREE_SPACE_START,
+ TEST_GPT_DISK_FREE_SPACE_START));
}
void test_gpt_entry_move_should_failWhenEndLessThanStart(void)
@@ -1041,8 +1045,8 @@ void test_gpt_entry_move_should_failWhenEndLessThanStart(void)
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));
+ TEST_GPT_DISK_FREE_SPACE_START + 1,
+ TEST_GPT_DISK_FREE_SPACE_START));
}
void test_gpt_entry_move_should_failWhenLbaOverlapping(void)
@@ -1097,7 +1101,7 @@ void test_gpt_entry_move_should_failWhenLbaOffDisk(void)
/* 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_GPT_DISK_FREE_SPACE_START,
TEST_DISK_NUM_BLOCKS + 1));
/* Second, start off the disk entirely */
@@ -1110,7 +1114,7 @@ void test_gpt_entry_move_should_failWhenLbaOffDisk(void)
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
&test_guid,
TEST_GPT_PRIMARY_LBA,
- TEST_GPT_THIRD_PARTITION_END + 2));
+ TEST_GPT_DISK_FREE_SPACE_START + 1));
/* Fourth, start in the backup header area */
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move(
@@ -0,0 +1,255 @@
From 38daa61f876a6becb3968f1360d403f496634131 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Mon, 16 Mar 2026 16:46:43 +0000
Subject: [PATCH] lib: gpt: Add operation to duplicate entries
Without this new function, callers of the library would have to first
read the entry they want to duplicate, then attempt to create a new
entry, then manually, with their driver, copy the data of the partition
across. This new function streamlines the latter half of that process
into a single API call which creates, with the same size size, name and
attributes, and copies the data across block by block.
Change-Id: Ia30700cf6463f0e07e76d4b56ec015b51770459d
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/50235/1]
---
lib/gpt/inc/gpt.h | 19 ++++
lib/gpt/src/gpt.c | 23 +++++
lib/gpt/unittests/gpt/test_gpt.c | 159 +++++++++++++++++++++++++++++++
3 files changed, 201 insertions(+)
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
index 34ce67580..334a08f41 100644
--- a/lib/gpt/inc/gpt.h
+++ b/lib/gpt/inc/gpt.h
@@ -170,6 +170,25 @@ psa_status_t gpt_entry_move(const struct efi_guid_t *guid,
const uint64_t start,
const uint64_t end);
+/**
+ * \brief Duplicates an existing partition entry into new space.
+ *
+ * \param[in] old_guid Entry to duplicate.
+ * \param[in] start Starting LBA (0 uses the lowest free LBA possible).
+ * \param[out] new_guid GUID populated on success for subsequent API calls.
+ *
+ * \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_INSUFFICIENT_STORAGE Maximum number of partitions reached.
+ * \retval PSA_ERROR_INVALID_ARGUMENT New entry would overlap with an existing partition.
+ * \retval PSA_ERROR_INVALID_ARGUMENT Part of the partition would be off flash.
+ */
+__attribute__((nonnull(1,3)))
+psa_status_t gpt_entry_duplicate(const struct efi_guid_t *old_guid,
+ const uint64_t start,
+ struct efi_guid_t *new_guid);
+
/**
* \brief Creates a partition entry in the table.
*
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
index 0335befa7..920c4ccca 100644
--- a/lib/gpt/src/gpt.c
+++ b/lib/gpt/src/gpt.c
@@ -516,6 +516,29 @@ 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_duplicate(const struct efi_guid_t *old_guid,
+ const uint64_t start,
+ struct efi_guid_t *new_guid)
+{
+ struct partition_entry_t old_entry;
+ psa_status_t ret = gpt_entry_read(old_guid, &old_entry);
+ if (ret != PSA_SUCCESS) {
+ return ret;
+ }
+
+ ret = gpt_entry_create(&(old_entry.type_guid),
+ start,
+ old_entry.size,
+ old_entry.attr,
+ old_entry.name,
+ new_guid);
+ if (ret != PSA_SUCCESS) {
+ return ret;
+ }
+
+ return move_partition(old_entry.start, start, old_entry.size);
+}
+
psa_status_t gpt_entry_create(const struct efi_guid_t *type,
const uint64_t start,
const uint64_t size,
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
index 0ae660336..5d2c4243f 100644
--- a/lib/gpt/unittests/gpt/test_gpt.c
+++ b/lib/gpt/unittests/gpt/test_gpt.c
@@ -738,6 +738,165 @@ void test_gpt_defragment_should_succeedWhenNoIOFailure(void)
TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_defragment());
}
+void test_gpt_entry_duplicate_should_DuplicateOldEntry(void)
+{
+ /* Duplicate 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();
+ struct gpt_entry_t *old_entry = &(test_partition_array[0]);
+ struct efi_guid_t old_guid = old_entry->guid;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ /* The partition data is moved: this means reading each block then writing.
+ * It doesn't matter what the data is
+ */
+ char unused_read_data = 'X';
+ register_mocked_read(&unused_read_data, sizeof(unused_read_data));
+
+ /* Mock out the call to create a new GUID */
+ struct efi_guid_t expected_guid = TEST_GPT_VALID_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 that a new GUID is assigned. To test the duplication was successful
+ * would require reading from flash, which would be mocked anyway and therefore
+ * pointless
+ */
+ struct efi_guid_t new_guid = TEST_GPT_DUMMY_GUID;
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_duplicate(
+ &old_guid,
+ TEST_GPT_DISK_FREE_SPACE_START,
+ &new_guid));
+ TEST_ASSERT_EQUAL_MEMORY(&expected_guid, &new_guid, sizeof(new_guid));
+}
+
+void test_gpt_entry_duplicate_should_createNewEntryNextToLastEntry(void)
+{
+ /* Duplicate an entry, allowing the library to choose the start LBA. The
+ * GUID should be populated with something
+ */
+ setup_valid_gpt();
+ struct gpt_entry_t *old_entry = &(test_partition_array[0]);
+ struct efi_guid_t old_guid = old_entry->guid;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ /* The partition data is moved: this means reading each block then writing.
+ * It doesn't matter what the data is
+ */
+ char unused_read_data = 'X';
+ register_mocked_read(&unused_read_data, sizeof(unused_read_data));
+
+ /* Mock out the call to create a new GUID */
+ struct efi_guid_t expected_guid = TEST_GPT_VALID_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 that a new GUID is assigned. To test the duplication was successful
+ * would require reading from flash, which would be mocked anyway and therefore
+ * pointless
+ */
+ struct efi_guid_t new_guid = TEST_GPT_DUMMY_GUID;
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_duplicate(
+ &old_guid,
+ 0,
+ &new_guid));
+ TEST_ASSERT_EQUAL_MEMORY(&expected_guid, &new_guid, sizeof(new_guid));
+}
+
+void test_gpt_entry_duplicate_should_failToCreateEntryWhenLowestFreeLbaDoesNotHaveSpace(void)
+{
+ /* Duplicate an entry, allowing the library to choose the start LBA. Resize
+ * the last partition to consume over half of the disk, such that duplicating
+ * it won't be possible.
+ */
+ struct gpt_entry_t *old_entry = &(test_partition_array[TEST_DEFAULT_NUM_PARTITIONS - 1]);
+ old_entry->end = TEST_GPT_THIRD_PARTITION_START + (TEST_DISK_NUM_BLOCKS / 2 ) + 1;
+ struct efi_guid_t old_guid = old_entry->guid;
+ setup_valid_gpt();
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ struct efi_guid_t new_guid;
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_duplicate(
+ &old_guid,
+ 0,
+ &new_guid));
+}
+
+void test_gpt_entry_duplicate_should_failWhenEntryNotExisting(void)
+{
+ setup_valid_gpt();
+ struct efi_guid_t old_guid = NULL_GUID;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ struct efi_guid_t new_guid;
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_duplicate(
+ &old_guid,
+ TEST_GPT_DISK_FREE_SPACE_START,
+ &new_guid));
+}
+
+void test_gpt_entry_duplicate_should_failNewEntryOverlapping(void)
+{
+ setup_valid_gpt();
+ struct gpt_entry_t *old_entry = &(test_partition_array[0]);
+ struct efi_guid_t old_guid = old_entry->guid;
+ struct efi_guid_t new_guid;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ /* 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
+ */
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_duplicate(
+ &old_guid,
+ TEST_GPT_FIRST_PARTITION_START + 1,
+ &new_guid));
+
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_duplicate(
+ &old_guid,
+ TEST_GPT_THIRD_PARTITION_START + 1,
+ &new_guid));
+}
+
+void test_gpt_entry_duplicate_should_failWhenTableFull(void)
+{
+ /* Start with a full array of entries */
+ const uint64_t new_entry_end = TEST_GPT_DISK_FREE_SPACE_START;
+ struct gpt_entry_t new_entry = {
+ .type = TEST_GPT_VALID_TYPE(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11),
+ .start = new_entry_end,
+ .end = new_entry_end,
+ .attr = 0,
+ .guid = TEST_GPT_VALID_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 gpt_entry_t *old_entry = &(test_partition_array[0]);
+ struct efi_guid_t old_guid = old_entry->guid;
+ struct efi_guid_t new_guid;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ TEST_ASSERT_EQUAL(PSA_ERROR_INSUFFICIENT_STORAGE, gpt_entry_duplicate(
+ &old_guid,
+ new_entry_end + 1,
+ &new_guid));
+}
+
void test_gpt_entry_create_should_createNewEntry(void)
{
/* Add an entry. It must not overlap with an existing entry and must also
@@ -0,0 +1,240 @@
From 0dcbef3a0800a2a610b32935a54762d4b42203f1 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Tue, 17 Mar 2026 11:44:14 +0000
Subject: [PATCH] lib: gpt: Consecutively erase blocks when moving partitions
An LBA is typically smaller than a flash sector size, so it becomes
inefficient to erase block by block and also erases the same sector
multiple times. Consecutively erasing blocks allows the platform
implementation of the driver the opportunity to save on erase cycles.
As a result, the write_to_flash function has had an extra parameter
added to indicate whether an erase is required or not. Under normal
circumstances, it should be false, however there are some reasons where
it is not required, such as if it has been erased before already.
Change-Id: I80c6d73565ab9bfcb0b286aaa215798dd09725f2
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/50236/1]
---
lib/gpt/src/gpt.c | 102 ++++++++++++++++++++++++++++++++++------------
1 file changed, 75 insertions(+), 27 deletions(-)
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
index 920c4ccca..984c8f821 100644
--- a/lib/gpt/src/gpt.c
+++ b/lib/gpt/src/gpt.c
@@ -207,7 +207,7 @@ static psa_status_t read_entry_from_flash(const struct gpt_t *table,
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_to_flash(uint64_t lba, bool skip_erase);
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,
@@ -221,7 +221,7 @@ 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_lba(const uint64_t old_lba, const uint64_t new_lba, const bool skip_erase);
static psa_status_t move_partition(const uint64_t old_lba,
const uint64_t new_lba,
const uint64_t num_blocks);
@@ -722,12 +722,12 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
/* 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);
+ ret = write_to_flash(backup_gpt_array_lba + i - 1 - PRIMARY_GPT_ARRAY_LBA, false);
if (ret != PSA_SUCCESS) {
return ret;
}
}
- ret = write_to_flash(i - 1);
+ ret = write_to_flash(i - 1, false);
if (ret != PSA_SUCCESS) {
return ret;
}
@@ -750,16 +750,16 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
*/
memset(lba_buf, 0, TFM_GPT_BLOCK_SIZE);
if (backup_gpt_array_lba != 0) {
- int write_ret = plat_flash_driver->write(
+ ret = write_to_flash(
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;
+ true);
+ if (ret != PSA_SUCCESS) {
+ return ret;
}
}
- int write_ret = plat_flash_driver->write(array_end_lba, lba_buf);
- if (write_ret != TFM_GPT_BLOCK_SIZE) {
- return PSA_ERROR_STORAGE_FAILURE;
+ ret = write_to_flash(array_end_lba, true);
+ if (ret != PSA_SUCCESS) {
+ return ret;
}
} else {
/* Zero what is not needed anymore */
@@ -768,12 +768,12 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
0,
(gpt_entry_per_lba_count() - entries_in_last_lba) * GPT_ENTRY_SIZE);
if (backup_gpt_array_lba != 0) {
- ret = write_to_flash(backup_gpt_array_lba + array_end_lba - PRIMARY_GPT_ARRAY_LBA);
+ ret = write_to_flash(array_end_lba, false);
if (ret != PSA_SUCCESS) {
return ret;
}
}
- ret = write_to_flash(array_end_lba);
+ ret = write_to_flash(array_end_lba, false);
if (ret != PSA_SUCCESS) {
return ret;
}
@@ -1121,14 +1121,18 @@ static psa_status_t find_gpt_entry(const struct gpt_t *table,
}
/* Move a single LBAs data to somewhere else */
-static psa_status_t move_lba(const uint64_t old_lba, const uint64_t new_lba)
+static psa_status_t move_lba(const uint64_t old_lba, const uint64_t new_lba, const bool skip_erase)
{
+ VERBOSE("Moving from 0x%x%x to 0x%x%x %s erase\n",
+ (uint32_t)(old_lba >> 32), (uint32_t)old_lba,
+ (uint32_t)(new_lba >> 32), (uint32_t)new_lba,
+ skip_erase ? "without" : "with");
const psa_status_t ret = read_from_flash(old_lba);
if (ret != PSA_SUCCESS) {
return ret;
}
- return write_to_flash(new_lba);
+ return write_to_flash(new_lba, skip_erase);
}
/* Moves a partition's data to start from one logical block to another */
@@ -1140,18 +1144,60 @@ static psa_status_t move_partition(const uint64_t old_lba,
return PSA_SUCCESS;
}
+ /* If possible, erase all of the LBAs that the data is going to be read
+ * to, so that, in the case an LBA is smaller than a flash sector, the
+ * number of flash erase cycles is reduced. Ignore any errors when erasing,
+ * as the "write" will perform erase anyway. If the areas between where the
+ * partition is now and where it will be does not overlap, then erase all
+ * blocks in the new area. If there is overlap, erase only those which are
+ * not within the old area
+ */
if (old_lba < new_lba) {
+ /* Attempt consecutive erase */
+ uint64_t non_overlap_blocks =
+ (old_lba + num_blocks - 1 < new_lba ? num_blocks : new_lba - old_lba);
+
+ VERBOSE("Erasing 0x%x%x blocks from LBA 0x%x%x\n",
+ (uint32_t)(non_overlap_blocks >> 32), (uint32_t)non_overlap_blocks,
+ (uint32_t)(new_lba >> 32), (uint32_t)new_lba);
+
+ const ssize_t erase_ret = plat_flash_driver->erase(
+ new_lba + (num_blocks - non_overlap_blocks),
+ (size_t)non_overlap_blocks);
+ if (erase_ret != (ssize_t)non_overlap_blocks) {
+ WARN("Failed to erase all blocks consecutively, only erased %ld. "
+ "Continuing to erase on a per-block basis\n", erase_ret);
+ non_overlap_blocks = 0;
+ }
+
/* 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);
+ const bool skip_erase = (block < non_overlap_blocks);
+ const psa_status_t ret = move_lba(old_lba + block - 1, new_lba + block - 1, skip_erase);
if (ret != PSA_SUCCESS) {
return ret;
}
}
} else {
+ /* Attempt consecutive erase */
+ uint64_t non_overlap_blocks =
+ (new_lba + num_blocks - 1 < old_lba ? num_blocks : old_lba - new_lba);
+
+ VERBOSE("Erasing 0x%x%x blocks from LBA 0x%x%x\n",
+ (uint32_t)(non_overlap_blocks >> 32), (uint32_t)non_overlap_blocks,
+ (uint32_t)(new_lba >> 32), (uint32_t)new_lba);
+
+ const ssize_t erase_ret = plat_flash_driver->erase(new_lba, (size_t)non_overlap_blocks);
+ if (erase_ret != (ssize_t)non_overlap_blocks) {
+ WARN("Failed to erase all blocks consecutively, only erased %ld. "
+ "Continuing to erase on a per-block basis\n", erase_ret);
+ non_overlap_blocks = 0;
+ }
+
/* 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);
+ const bool skip_erase = (block < non_overlap_blocks);
+ const psa_status_t ret = move_lba(old_lba + block, new_lba + block, skip_erase);
if (ret != PSA_SUCCESS) {
return ret;
}
@@ -1372,7 +1418,7 @@ static psa_status_t flush_lba_buf(void)
ret = write_entries_to_flash(cached_lba - backup_gpt_array_lba, false);
} else {
/* Some other LBA is cached, possibly data. Write it anyway */
- ret = write_to_flash(cached_lba);
+ ret = write_to_flash(cached_lba, false);
}
in_flush = false;
@@ -1380,13 +1426,15 @@ static psa_status_t flush_lba_buf(void)
}
/* Write to the flash at the specified LBA */
-static psa_status_t write_to_flash(uint64_t lba)
+static psa_status_t write_to_flash(uint64_t lba, bool skip_erase)
{
- 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 (!skip_erase) {
+ 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) {
@@ -1408,7 +1456,7 @@ static psa_status_t write_entries_to_flash(uint32_t lbas_into_array, bool no_hea
psa_status_t ret;
if (backup_gpt_array_lba != 0) {
- ret = write_to_flash(backup_gpt_array_lba + lbas_into_array);
+ ret = write_to_flash(backup_gpt_array_lba + lbas_into_array, false);
if (ret != PSA_SUCCESS) {
ERROR("Unable to write entry to backup partition array\n");
return ret;
@@ -1417,7 +1465,7 @@ static psa_status_t write_entries_to_flash(uint32_t lbas_into_array, bool no_hea
WARN("Backup array LBA unknown!\n");
}
- ret = write_to_flash(PRIMARY_GPT_ARRAY_LBA + lbas_into_array);
+ ret = write_to_flash(PRIMARY_GPT_ARRAY_LBA + lbas_into_array, false);
if (ret != PSA_SUCCESS) {
ERROR("Unable to write entry to primary partition array\n");
return ret;
@@ -1486,7 +1534,7 @@ static psa_status_t write_header_to_flash(const struct gpt_t *table)
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);
+ const psa_status_t ret = write_to_flash(table->header.current_lba, false);
memcpy(lba_buf, temp_buf, GPT_HEADER_SIZE);
return ret;
@@ -0,0 +1,38 @@
From 7e2ae2fc4f8ae8a16a24b87d0650c6b4b28fc870 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Wed, 8 Apr 2026 17:39:13 +0100
Subject: [PATCH] lib: gpt: Clarify API operation
The move and duplicate operations both also move or copy (respectively)
the partition data, which is not immediately obvious.
Change-Id: If4a0c4a87d30bfceb2534c9d01ba763688e0cc9e
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/50237/1]
---
lib/gpt/inc/gpt.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
index 334a08f41..c11ecbff2 100644
--- a/lib/gpt/inc/gpt.h
+++ b/lib/gpt/inc/gpt.h
@@ -152,7 +152,7 @@ __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.
+ * \brief Moves (or resizes) a partition entry, including the partition data.
*
* \param[in] guid Entry to move.
* \param[in] start New start LBA.
@@ -171,7 +171,8 @@ psa_status_t gpt_entry_move(const struct efi_guid_t *guid,
const uint64_t end);
/**
- * \brief Duplicates an existing partition entry into new space.
+ * \brief Duplicates an existing partition entry into new space, including the
+ * partition data.
*
* \param[in] old_guid Entry to duplicate.
* \param[in] start Starting LBA (0 uses the lowest free LBA possible).
@@ -0,0 +1,701 @@
From f9badce3570bbc89b141c86a6b8a988d90d81f0c Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Wed, 8 Apr 2026 17:51:18 +0100
Subject: [PATCH] lib: gpt: Add metadata-only API operations
Both move and duplicate functions also move or copy (respectively) the
partition data. This is not always required, for example if the
new location or partition will simply be erased or overwritten anyway.
Change-Id: I180f8790335444e9925c405616aa5a3c9a4290a8
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/50238/1]
---
lib/gpt/inc/gpt.h | 39 +++++
lib/gpt/src/gpt.c | 285 ++++++++++++++++++-------------
lib/gpt/unittests/gpt/test_gpt.c | 269 +++++++++++++++++++++++++++++
3 files changed, 473 insertions(+), 120 deletions(-)
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
index c11ecbff2..c5bddb470 100644
--- a/lib/gpt/inc/gpt.h
+++ b/lib/gpt/inc/gpt.h
@@ -170,6 +170,25 @@ psa_status_t gpt_entry_move(const struct efi_guid_t *guid,
const uint64_t start,
const uint64_t end);
+/**
+ * \brief Moves (or resizes) a partition entry, without moving the partition data.
+ *
+ * \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_no_copy(const struct efi_guid_t *guid,
+ const uint64_t start,
+ const uint64_t end);
+
/**
* \brief Duplicates an existing partition entry into new space, including the
* partition data.
@@ -190,6 +209,26 @@ psa_status_t gpt_entry_duplicate(const struct efi_guid_t *old_guid,
const uint64_t start,
struct efi_guid_t *new_guid);
+/**
+ * \brief Duplicates an existing partition entry into new space, without copying
+ * the partition data.
+ *
+ * \param[in] old_guid Entry to duplicate.
+ * \param[in] start Starting LBA (0 uses the lowest free LBA possible).
+ * \param[out] new_guid GUID populated on success for subsequent API calls.
+ *
+ * \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_INSUFFICIENT_STORAGE Maximum number of partitions reached.
+ * \retval PSA_ERROR_INVALID_ARGUMENT New entry would overlap with an existing partition.
+ * \retval PSA_ERROR_INVALID_ARGUMENT Part of the partition would be off flash.
+ */
+__attribute__((nonnull(1,3)))
+psa_status_t gpt_entry_duplicate_no_copy(const struct efi_guid_t *old_guid,
+ const uint64_t start,
+ struct efi_guid_t *new_guid);
+
/**
* \brief Creates a partition entry in the table.
*
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
index 984c8f821..d6528f6a5 100644
--- a/lib/gpt/src/gpt.c
+++ b/lib/gpt/src/gpt.c
@@ -222,9 +222,17 @@ static psa_status_t find_gpt_entry(const struct gpt_t *table,
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, const bool skip_erase);
-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 move_partition_data(const uint64_t old_lba,
+ const uint64_t new_lba,
+ const uint64_t num_blocks);
+static psa_status_t move_partition(const struct efi_guid_t *guid,
+ const uint64_t start,
+ const uint64_t end,
+ const bool no_copy);
+static psa_status_t duplicate_partition(const struct efi_guid_t *old_guid,
+ const uint64_t start,
+ const bool no_copy,
+ struct efi_guid_t *new_guid);
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);
@@ -415,128 +423,28 @@ 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 * GPT_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 move_partition(guid, start, end, false);
+}
- return write_entry(cached_index, &cached_entry, false);
+psa_status_t gpt_entry_move_no_copy(const struct efi_guid_t *guid,
+ const uint64_t start,
+ const uint64_t end)
+{
+ return move_partition(guid, start, end, true);
}
psa_status_t gpt_entry_duplicate(const struct efi_guid_t *old_guid,
const uint64_t start,
struct efi_guid_t *new_guid)
{
- struct partition_entry_t old_entry;
- psa_status_t ret = gpt_entry_read(old_guid, &old_entry);
- if (ret != PSA_SUCCESS) {
- return ret;
- }
-
- ret = gpt_entry_create(&(old_entry.type_guid),
- start,
- old_entry.size,
- old_entry.attr,
- old_entry.name,
- new_guid);
- if (ret != PSA_SUCCESS) {
- return ret;
- }
+ return duplicate_partition(old_guid, start, false, new_guid);
+}
- return move_partition(old_entry.start, start, old_entry.size);
+psa_status_t gpt_entry_duplicate_no_copy(const struct efi_guid_t *old_guid,
+ const uint64_t start,
+ struct efi_guid_t *new_guid)
+{
+ return duplicate_partition(old_guid, start, true, new_guid);
}
psa_status_t gpt_entry_create(const struct efi_guid_t *type,
@@ -885,7 +793,7 @@ psa_status_t gpt_defragment(void)
}
const uint64_t num_blocks = entry.end - entry.start + 1;
- ret = move_partition(entry.start, prev_end, num_blocks);
+ ret = move_partition_data(entry.start, prev_end, num_blocks);
if (ret != PSA_SUCCESS) {
return ret;
}
@@ -1120,6 +1028,143 @@ static psa_status_t find_gpt_entry(const struct gpt_t *table,
return io_failure ? PSA_ERROR_STORAGE_FAILURE : PSA_ERROR_DOES_NOT_EXIST;
}
+/* Duplicate a partition, potentially also copying its data but always updating
+ * the header
+ */
+static psa_status_t duplicate_partition(const struct efi_guid_t *old_guid,
+ const uint64_t start,
+ const bool no_copy,
+ struct efi_guid_t *new_guid)
+{
+ struct partition_entry_t old_entry;
+ psa_status_t ret = gpt_entry_read(old_guid, &old_entry);
+ if (ret != PSA_SUCCESS) {
+ return ret;
+ }
+
+ ret = gpt_entry_create(&(old_entry.type_guid),
+ start,
+ old_entry.size,
+ old_entry.attr,
+ old_entry.name,
+ new_guid);
+ if (ret != PSA_SUCCESS || no_copy) {
+ return ret;
+ }
+
+ return move_partition_data(old_entry.start, start, old_entry.size);
+}
+
+/* Move a partition, potentially also copying its data but always updating the header */
+static psa_status_t move_partition(const struct efi_guid_t *guid,
+ const uint64_t start,
+ const uint64_t end,
+ const bool no_copy)
+{
+ 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 * GPT_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;
+ }
+ }
+
+ if (!no_copy) {
+ ret = move_partition_data(
+ 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);
+}
+
/* 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 bool skip_erase)
{
@@ -1136,7 +1181,7 @@ static psa_status_t move_lba(const uint64_t old_lba, const uint64_t new_lba, con
}
/* Moves a partition's data to start from one logical block to another */
-static psa_status_t move_partition(const uint64_t old_lba,
+static psa_status_t move_partition_data(const uint64_t old_lba,
const uint64_t new_lba,
const uint64_t num_blocks)
{
@@ -1780,7 +1825,7 @@ static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary)
swap_headers(&(restore_from->header), &(restore_to.header));
/* Copy the partition array as well */
- ret = move_partition(
+ ret = move_partition_data(
restore_from->header.array_lba,
restore_to.header.array_lba,
(restore_from->header.num_partitions +
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
index 5d2c4243f..bd9eb8ba1 100644
--- a/lib/gpt/unittests/gpt/test_gpt.c
+++ b/lib/gpt/unittests/gpt/test_gpt.c
@@ -897,6 +897,153 @@ void test_gpt_entry_duplicate_should_failWhenTableFull(void)
&new_guid));
}
+void test_gpt_entry_duplicate_no_copy_should_DuplicateOldEntry(void)
+{
+ /* Duplicate 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();
+ struct gpt_entry_t *old_entry = &(test_partition_array[0]);
+ struct efi_guid_t old_guid = old_entry->guid;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ /* Mock out the call to create a new GUID */
+ struct efi_guid_t expected_guid = TEST_GPT_VALID_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 that a new GUID is assigned. To test the duplication was successful
+ * would require reading from flash, which would be mocked anyway and therefore
+ * pointless
+ */
+ struct efi_guid_t new_guid = TEST_GPT_DUMMY_GUID;
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_duplicate_no_copy(
+ &old_guid,
+ TEST_GPT_DISK_FREE_SPACE_START,
+ &new_guid));
+ TEST_ASSERT_EQUAL_MEMORY(&expected_guid, &new_guid, sizeof(new_guid));
+}
+
+void test_gpt_entry_duplicate_no_copy_should_createNewEntryNextToLastEntry(void)
+{
+ /* Duplicate an entry, allowing the library to choose the start LBA. The
+ * GUID should be populated with something
+ */
+ setup_valid_gpt();
+ struct gpt_entry_t *old_entry = &(test_partition_array[0]);
+ struct efi_guid_t old_guid = old_entry->guid;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ /* Mock out the call to create a new GUID */
+ struct efi_guid_t expected_guid = TEST_GPT_VALID_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 that a new GUID is assigned. To test the duplication was successful
+ * would require reading from flash, which would be mocked anyway and therefore
+ * pointless
+ */
+ struct efi_guid_t new_guid = TEST_GPT_DUMMY_GUID;
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_entry_duplicate_no_copy(
+ &old_guid,
+ 0,
+ &new_guid));
+ TEST_ASSERT_EQUAL_MEMORY(&expected_guid, &new_guid, sizeof(new_guid));
+}
+
+void test_gpt_entry_duplicate_no_copy_should_failToCreateEntryWhenLowestFreeLbaDoesNotHaveSpace(void)
+{
+ /* Duplicate an entry, allowing the library to choose the start LBA. Resize
+ * the last partition to consume over half of the disk, such that duplicating
+ * it won't be possible.
+ */
+ struct gpt_entry_t *old_entry = &(test_partition_array[TEST_DEFAULT_NUM_PARTITIONS - 1]);
+ old_entry->end = TEST_GPT_THIRD_PARTITION_START + (TEST_DISK_NUM_BLOCKS / 2 ) + 1;
+ struct efi_guid_t old_guid = old_entry->guid;
+ setup_valid_gpt();
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ struct efi_guid_t new_guid;
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_duplicate_no_copy(
+ &old_guid,
+ 0,
+ &new_guid));
+}
+
+void test_gpt_entry_duplicate_no_copy_should_failWhenEntryNotExisting(void)
+{
+ setup_valid_gpt();
+ struct efi_guid_t old_guid = NULL_GUID;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ struct efi_guid_t new_guid;
+ TEST_ASSERT_EQUAL(PSA_ERROR_DOES_NOT_EXIST, gpt_entry_duplicate_no_copy(
+ &old_guid,
+ TEST_GPT_DISK_FREE_SPACE_START,
+ &new_guid));
+}
+
+void test_gpt_entry_duplicate_no_copy_should_failNewEntryOverlapping(void)
+{
+ setup_valid_gpt();
+ struct gpt_entry_t *old_entry = &(test_partition_array[0]);
+ struct efi_guid_t old_guid = old_entry->guid;
+ struct efi_guid_t new_guid;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ /* 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
+ */
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_duplicate_no_copy(
+ &old_guid,
+ TEST_GPT_FIRST_PARTITION_START + 1,
+ &new_guid));
+
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_duplicate_no_copy(
+ &old_guid,
+ TEST_GPT_THIRD_PARTITION_START + 1,
+ &new_guid));
+}
+
+void test_gpt_entry_duplicate_no_copy_should_failWhenTableFull(void)
+{
+ /* Start with a full array of entries */
+ const uint64_t new_entry_end = TEST_GPT_DISK_FREE_SPACE_START;
+ struct gpt_entry_t new_entry = {
+ .type = TEST_GPT_VALID_TYPE(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11),
+ .start = new_entry_end,
+ .end = new_entry_end,
+ .attr = 0,
+ .guid = TEST_GPT_VALID_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 gpt_entry_t *old_entry = &(test_partition_array[0]);
+ struct efi_guid_t old_guid = old_entry->guid;
+ struct efi_guid_t new_guid;
+
+ /* Each entry will be read to find the entry to be duplicated. */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ TEST_ASSERT_EQUAL(PSA_ERROR_INSUFFICIENT_STORAGE, gpt_entry_duplicate_no_copy(
+ &old_guid,
+ new_entry_end + 1,
+ &new_guid));
+}
+
void test_gpt_entry_create_should_createNewEntry(void)
{
/* Add an entry. It must not overlap with an existing entry and must also
@@ -1282,6 +1429,128 @@ void test_gpt_entry_move_should_failWhenLbaOffDisk(void)
TEST_GPT_BACKUP_LBA + 1));
}
+void test_gpt_entry_move_no_copy_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));
+
+ /* 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_no_copy(
+ &test_guid,
+ TEST_GPT_DISK_FREE_SPACE_START,
+ TEST_GPT_DISK_FREE_SPACE_START));
+}
+
+void test_gpt_entry_move_no_copy_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_no_copy(
+ &non_existing,
+ TEST_GPT_DISK_FREE_SPACE_START,
+ TEST_GPT_DISK_FREE_SPACE_START));
+}
+
+void test_gpt_entry_move_no_copy_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_no_copy(
+ &test_guid,
+ TEST_GPT_DISK_FREE_SPACE_START + 1,
+ TEST_GPT_DISK_FREE_SPACE_START));
+}
+
+void test_gpt_entry_move_no_copy_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_no_copy(
+ &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_no_copy(
+ &test_guid,
+ TEST_GPT_FIRST_PARTITION_START + 1,
+ TEST_GPT_SECOND_PARTITION_END));
+
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_copy(
+ &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_no_copy(
+ &test_guid,
+ TEST_GPT_FIRST_PARTITION_START + 1,
+ TEST_GPT_FIRST_PARTITION_START + 1));
+}
+
+void test_gpt_entry_move_no_copy_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_no_copy(
+ &test_guid,
+ TEST_GPT_DISK_FREE_SPACE_START,
+ TEST_DISK_NUM_BLOCKS + 1));
+
+ /* Second, start off the disk entirely */
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_copy(
+ &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_no_copy(
+ &test_guid,
+ TEST_GPT_PRIMARY_LBA,
+ TEST_GPT_DISK_FREE_SPACE_START + 1));
+
+ /* Fourth, start in the backup header area */
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_copy(
+ &test_guid,
+ TEST_GPT_BACKUP_LBA,
+ TEST_GPT_BACKUP_LBA + 1));
+}
+
void test_gpt_attr_set_should_setAttributes(void)
{
/* Start with a populated GPT */
@@ -82,6 +82,12 @@ SRC_URI:append:corstone1000 = " \
file://0048-lib-gpt-Enforce-entry-size-of-128-bytes.patch \
file://0049-lib-gpt-Ensure-block-size-complies-with-spec.patch \
file://0050-lib-gpt-Expand-table-validation.patch \
file://0051-lib-gpt-Show-intent-of-GUIDs-in-unittests-more-clear.patch \
file://0052-lib-gpt-Provide-macro-identifying-free-space.patch \
file://0053-lib-gpt-Add-operation-to-duplicate-entries.patch \
file://0054-lib-gpt-Consecutively-erase-blocks-when-moving-parti.patch \
file://0055-lib-gpt-Clarify-API-operation.patch \
file://0056-lib-gpt-Add-metadata-only-API-operations.patch \
"
SRCREV_tfm-psa-adac:corstone1000 = "f2809ae231be33a1afcd7714f40756c67d846c88"