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:
committed by
Jon Mason
parent
9af92ed09e
commit
1eaa431ff3
+216
@@ -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 */
|
||||
+157
@@ -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(
|
||||
+255
@@ -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
|
||||
+240
@@ -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;
|
||||
+38
@@ -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).
|
||||
+701
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user