[PATCH 2/3] arm-bsp/trusted-firmware-m:cs1k: Add extra GPT library operations
Frazer Carsley <[email protected]> Fri, 24 Apr 2026 16:36:55 +0100
| Newsgroups | org.yoctoproject.lists.meta-arm |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- ...ent-of-GUIDs-in-unittests-more-clear.patch | 216 ++++++ ...Provide-macro-identifying-free-space.patch | 157 ++++ ...t-Add-operation-to-duplicate-entries.patch | 255 +++++++ ...ively-erase-blocks-when-moving-parti.patch | 240 ++++++ .../0055-lib-gpt-Clarify-API-operation.patch | 38 + ...gpt-Add-metadata-only-API-operations.patch | 701 ++++++++++++++++++ .../trusted-firmware-m-corstone1000.inc | 6 + 7 files changed, 1613 insertions(+) create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/cor= stone1000/0051-lib-gpt-Show-intent-of-GUIDs-in-unittests-more-clear.patch create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/cor= stone1000/0052-lib-gpt-Provide-macro-identifying-free-space.patch create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/cor= stone1000/0053-lib-gpt-Add-operation-to-duplicate-entries.patch create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/cor= stone1000/0054-lib-gpt-Consecutively-erase-blocks-when-moving-parti.patch create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/cor= stone1000/0055-lib-gpt-Clarify-API-operation.patch create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/cor= stone1000/0056-lib-gpt-Add-metadata-only-API-operations.patch diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone10= 00/0051-lib-gpt-Show-intent-of-GUIDs-in-unittests-more-clear.patch b/meta= -arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0051-lib-gpt-S= how-intent-of-GUIDs-in-unittests-more-clear.patch new file mode 100644 index 00000000..1279db9d --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0051= -lib-gpt-Show-intent-of-GUIDs-in-unittests-more-clear.patch @@ -0,0 +1,216 @@ +From ca0d50fc1abbfe165941dc0bd674bb117f236f87 Mon Sep 17 00:00:00 2001 +From: Frazer Carsley <[email protected]> +Date: Mon, 30 Mar 2026 14:06:20 +0100 +Subject: [PATCH] lib: gpt: Show intent of GUIDs in unittests more clearl= y + +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 <[email protected]> +Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/tr= usted-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/te= st_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 =3D TEST_GPT_BACKUP_ARRAY_LBA; \ + } while (0) +=20 ++/* 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_PART= ITIONS] =3D { + { +- .type =3D MAKE_EFI_GUID(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), +- .guid =3D MAKE_EFI_GUID(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), ++ .type =3D TEST_GPT_VALID_TYPE(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11= ), ++ .guid =3D TEST_GPT_VALID_GUID(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11= ), + .start =3D TEST_GPT_FIRST_PARTITION_START, + .end =3D TEST_GPT_FIRST_PARTITION_END, + .attr =3D 0, + .name =3D "First partition" + }, + { +- .type =3D MAKE_EFI_GUID(2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), +- .guid =3D MAKE_EFI_GUID(2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), ++ .type =3D TEST_GPT_VALID_TYPE(2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11= ), ++ .guid =3D TEST_GPT_VALID_GUID(2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11= ), + .start =3D TEST_GPT_SECOND_PARTITION_START, + .end =3D TEST_GPT_SECOND_PARTITION_END, + .attr =3D 0, + .name =3D "Second partition" + }, + { +- .type =3D MAKE_EFI_GUID(3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11), +- .guid =3D MAKE_EFI_GUID(3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11), ++ .type =3D TEST_GPT_VALID_TYPE(3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11= ), ++ .guid =3D TEST_GPT_VALID_GUID(3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11= ), + .start =3D TEST_GPT_THIRD_PARTITION_START, + .end =3D TEST_GPT_THIRD_PARTITION_END, + .attr =3D 0, +@@ -333,7 +340,7 @@ void test_gpt_init_should_overwriteOldGpt(void) + gpt_uninit(); +=20 + /* Use a different disk GUID */ +- const struct efi_guid_t new_guid =3D MAKE_EFI_GUID(1, 1, 3, 4, 5, 6= ,7 ,8, 9, 10, 11); ++ const struct efi_guid_t new_guid =3D TEST_GPT_VALID_GUID(1, 1, 3, 4= , 5, 6 ,7 ,8, 9, 10, 11); + test_header.disk_guid =3D new_guid; +=20 + setup_valid_gpt(); +@@ -744,7 +751,7 @@ void test_gpt_entry_create_should_createNewEntry(voi= d) +=20 + /* Update header. Read each entry for CRC calculation. */ + struct gpt_entry_t new_entry =3D { +- .type =3D NULL_GUID, ++ .type =3D TEST_GPT_DUMMY_TYPE, + .start =3D TEST_GPT_THIRD_PARTITION_END + 1, + .end =3D TEST_GPT_THIRD_PARTITION_END + 1, + .attr =3D 0, +@@ -752,12 +759,12 @@ void test_gpt_entry_create_should_createNewEntry(v= oid) + }; +=20 + /* Mock out the call to create a new GUID */ +- struct efi_guid_t expected_guid =3D MAKE_EFI_GUID(5, 5, 5, 5, 5, 6,= 7, 8, 9, 10, 11); ++ struct efi_guid_t expected_guid =3D 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); +=20 + /* Ensure also the that a new GUID is assigned */ +- struct efi_guid_t new_guid =3D MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8= , 9, 10, 11); ++ struct efi_guid_t new_guid =3D 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_createNewEntryNext= ToLastEntry(void) +=20 + /* Update header. Read each entry for CRC calculation. */ + struct gpt_entry_t new_entry =3D { +- .type =3D NULL_GUID, ++ .type =3D TEST_GPT_DUMMY_TYPE, + .start =3D TEST_GPT_THIRD_PARTITION_END + 1, + .end =3D TEST_GPT_THIRD_PARTITION_END + 1, + .attr =3D 0, +@@ -790,12 +797,12 @@ void test_gpt_entry_create_should_createNewEntryNe= xtToLastEntry(void) + }; +=20 + /* Mock out the call to create a new GUID */ +- struct efi_guid_t expected_guid =3D MAKE_EFI_GUID(5, 5, 5, 5, 5, 6,= 7, 8, 9, 10, 11); ++ struct efi_guid_t expected_guid =3D 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); +=20 + /* Ensure also the that a new GUID is assigned */ +- struct efi_guid_t new_guid =3D MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8= , 9, 10, 11); ++ struct efi_guid_t new_guid =3D TEST_GPT_DUMMY_GUID; + char name[GPT_ENTRY_NAME_LENGTH] =3D {'\0'}; + name[0] =3D 'a'; +=20 +@@ -823,7 +830,7 @@ void test_gpt_entry_create_should_failToCreateEntryW= henLowestFreeLbaDoesNotHaveS + register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); +=20 + /* Ensure also the that a new GUID is assigned */ +- struct efi_guid_t existing_guid =3D MAKE_EFI_GUID(4, 4, 4, 4, 5, 6,= 7, 8, 9, 10, 11); ++ struct efi_guid_t existing_guid =3D 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] =3D {'\0'}; + name[0] =3D 'a'; +@@ -840,17 +847,17 @@ void test_gpt_entry_create_should_failWhenTableFul= l(void) + { + /* Start with a full array of entries */ + struct gpt_entry_t new_entry =3D { +- .type =3D MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11), ++ .type =3D TEST_GPT_VALID_TYPE(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11= ), + .start =3D TEST_GPT_THIRD_PARTITION_END + 1, + .end =3D TEST_GPT_THIRD_PARTITION_END + 1, + .attr =3D 0, +- .guid =3D MAKE_EFI_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11), ++ .guid =3D TEST_GPT_VALID_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11= ), + .name =3D "Fourth partition" + }; + test_partition_array[TEST_MAX_PARTITIONS - 1] =3D new_entry; + setup_valid_gpt(); +=20 +- struct efi_guid_t type =3D MAKE_EFI_GUID(5, 5, 5, 5, 5, 6, 7, 8, 9,= 10, 11); ++ struct efi_guid_t type =3D 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] =3D {'\0'}; + name[0] =3D 'a'; +@@ -868,7 +875,7 @@ void test_gpt_entry_create_should_failWhenLbaOffDisk= (void) + setup_valid_gpt(); +=20 + /* First start on disk, then go off the disk */ +- struct efi_guid_t type =3D NULL_GUID; ++ struct efi_guid_t type =3D TEST_GPT_DUMMY_TYPE; + struct efi_guid_t guid; + char name[GPT_ENTRY_NAME_LENGTH] =3D {'\0'}; + name[0] =3D 'a'; +@@ -917,7 +924,7 @@ void test_gpt_entry_create_should_failWhenOverlappin= g(void) + * 2. start in the middle of a partition and end in free space + */ + register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); +- struct efi_guid_t type =3D NULL_GUID; ++ struct efi_guid_t type =3D TEST_GPT_DUMMY_TYPE; + struct efi_guid_t guid; + char name[GPT_ENTRY_NAME_LENGTH] =3D {'\0'}; + name[0] =3D 'a'; +@@ -944,7 +951,7 @@ void test_gpt_entry_create_should_failWhenNameIsEmpt= y(void) + /* Start with a populated GPT */ + setup_valid_gpt(); +=20 +- struct efi_guid_t type =3D NULL_GUID; ++ struct efi_guid_t type =3D TEST_GPT_DUMMY_TYPE; + struct gpt_entry_t new_entry =3D { + .type =3D type, + .start =3D 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(); +=20 +- struct efi_guid_t type =3D NULL_GUID; ++ struct efi_guid_t type =3D TEST_GPT_DUMMY_TYPE; +=20 + /* 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 =3D MAKE_EFI_GUID(1, 2, 3, 4, 5, 6, 7, 8= , 9, 10, 11); ++ struct efi_guid_t new_type =3D 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, &n= ew_type)); + } +=20 +@@ -1211,7 +1218,7 @@ void test_gpt_entry_change_type_should_failWhenEnt= ryNotExisting(void) + register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); +=20 + struct efi_guid_t non_existing =3D NULL_GUID; +- struct efi_guid_t new_type =3D MAKE_EFI_GUID(1, 2, 3, 4, 5, 6, 7, 8= , 9, 10, 11); ++ struct efi_guid_t new_type =3D 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)); + } +=20 +@@ -1452,7 +1459,7 @@ void test_gpt_entry_read_by_type_should_failWhenEn= tryNotExisting(void) +=20 + /* Try to read something */ + struct partition_entry_t entry; +- struct efi_guid_t test_type =3D MAKE_EFI_GUID(11, 10, 9, 8, 7, 6, 5= , 4, 3, 2, 1); ++ struct efi_guid_t test_type =3D 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)); +=20 + /* Now, have a non-empty GPT but search for a type that won't exist= */ diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone10= 00/0052-lib-gpt-Provide-macro-identifying-free-space.patch b/meta-arm-bsp= /recipes-bsp/trusted-firmware-m/files/corstone1000/0052-lib-gpt-Provide-m= acro-identifying-free-space.patch new file mode 100644 index 00000000..3deaf93a --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0052= -lib-gpt-Provide-macro-identifying-free-space.patch @@ -0,0 +1,157 @@ +From 229313778bae6ca16d6e3b25437c8e87eddf3084 Mon Sep 17 00:00:00 2001 +From: Frazer Carsley <[email protected]> +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 <[email protected]> +Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/tr= usted-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/te= st_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) +=20 + /* Populates a backup header from a primary header and calculates the n= ew CRC32 */ + #define MAKE_BACKUP_HEADER(backup, primary) \ +@@ -752,8 +753,8 @@ void test_gpt_entry_create_should_createNewEntry(voi= d) + /* Update header. Read each entry for CRC calculation. */ + struct gpt_entry_t new_entry =3D { + .type =3D TEST_GPT_DUMMY_TYPE, +- .start =3D TEST_GPT_THIRD_PARTITION_END + 1, +- .end =3D TEST_GPT_THIRD_PARTITION_END + 1, ++ .start =3D TEST_GPT_DISK_FREE_SPACE_START, ++ .end =3D TEST_GPT_DISK_FREE_SPACE_START, + .attr =3D 0, + .name =3D "Fourth partition" + }; +@@ -790,8 +791,8 @@ void test_gpt_entry_create_should_createNewEntryNext= ToLastEntry(void) + /* Update header. Read each entry for CRC calculation. */ + struct gpt_entry_t new_entry =3D { + .type =3D TEST_GPT_DUMMY_TYPE, +- .start =3D TEST_GPT_THIRD_PARTITION_END + 1, +- .end =3D TEST_GPT_THIRD_PARTITION_END + 1, ++ .start =3D TEST_GPT_DISK_FREE_SPACE_START, ++ .end =3D TEST_GPT_DISK_FREE_SPACE_START, + .attr =3D 0, + .name =3D "Fourth partition" + }; +@@ -846,10 +847,11 @@ void test_gpt_entry_create_should_failToCreateEntr= yWhenLowestFreeLbaDoesNotHaveS + void test_gpt_entry_create_should_failWhenTableFull(void) + { + /* Start with a full array of entries */ ++ const uint64_t new_entry_end =3D TEST_GPT_DISK_FREE_SPACE_START; + struct gpt_entry_t new_entry =3D { + .type =3D TEST_GPT_VALID_TYPE(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11= ), +- .start =3D TEST_GPT_THIRD_PARTITION_END + 1, +- .end =3D TEST_GPT_THIRD_PARTITION_END + 1, ++ .start =3D new_entry_end, ++ .end =3D new_entry_end, + .attr =3D 0, + .guid =3D TEST_GPT_VALID_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11= ), + .name =3D "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] =3D {'\0'}; + name[0] =3D 'a'; ++ const uint64_t new_free_space =3D 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] =3D '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_failWhenNameIsEmpt= y(void) + struct efi_guid_t type =3D TEST_GPT_DUMMY_TYPE; + struct gpt_entry_t new_entry =3D { + .type =3D type, +- .start =3D TEST_GPT_THIRD_PARTITION_END + 1, +- .end =3D TEST_GPT_THIRD_PARTITION_END + 1, ++ .start =3D TEST_GPT_DISK_FREE_SPACE_START, ++ .end =3D TEST_GPT_DISK_FREE_SPACE_START, + .attr =3D 0, + }; +=20 +@@ -984,7 +988,7 @@ void test_gpt_entry_create_should_failWhenSizeIsZero= (void) + name[0] =3D '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)); + } +=20 + void test_gpt_entry_move_should_failWhenEntryNotExisting(void) +@@ -1030,8 +1034,8 @@ void test_gpt_entry_move_should_failWhenEntryNotEx= isting(void) + struct efi_guid_t non_existing =3D 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)); + } +=20 + void test_gpt_entry_move_should_failWhenEndLessThanStart(void) +@@ -1041,8 +1045,8 @@ void test_gpt_entry_move_should_failWhenEndLessTha= nStart(void) + struct efi_guid_t test_guid =3D 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)); + } +=20 + 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)); +=20 + /* 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)); +=20 + /* Fourth, start in the backup header area */ + TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move( diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone10= 00/0053-lib-gpt-Add-operation-to-duplicate-entries.patch b/meta-arm-bsp/r= ecipes-bsp/trusted-firmware-m/files/corstone1000/0053-lib-gpt-Add-operati= on-to-duplicate-entries.patch new file mode 100644 index 00000000..09fcb39a --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0053= -lib-gpt-Add-operation-to-duplicate-entries.patch @@ -0,0 +1,255 @@ +From 38daa61f876a6becb3968f1360d403f496634131 Mon Sep 17 00:00:00 2001 +From: Frazer Carsley <[email protected]> +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 <[email protected]> +Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/tr= usted-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); +=20 ++/** ++ * \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 possib= le). ++ * \param[out] new_guid GUID populated on success for subsequent API ca= lls. ++ * ++ * \retval PSA_SUCCESS Success. ++ * \retval PSA_ERROR_STORAGE_FAILURE I/O error. ++ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GU= ID. ++ * \retval PSA_ERROR_INSUFFICIENT_STORAGE Maximum number of partitions = reached. ++ * \retval PSA_ERROR_INVALID_ARGUMENT New entry would overlap with an e= xisting partition. ++ * \retval PSA_ERROR_INVALID_ARGUMENT Part of the partition would be of= f 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); + } +=20 ++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 =3D gpt_entry_read(old_guid, &old_entry); ++ if (ret !=3D PSA_SUCCESS) { ++ return ret; ++ } ++ ++ ret =3D gpt_entry_create(&(old_entry.type_guid), ++ start, ++ old_entry.size, ++ old_entry.attr, ++ old_entry.name, ++ new_guid); ++ if (ret !=3D 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/te= st_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_succeedWhenNoIOFai= lure(void) + TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_defragment()); + } +=20 ++void test_gpt_entry_duplicate_should_DuplicateOldEntry(void) ++{ ++ /* Duplicate an entry. It must not overlap with an existing entry a= nd must also ++ * fit on the storage device. The GUID should be populated with som= ething. ++ */ ++ setup_valid_gpt(); ++ struct gpt_entry_t *old_entry =3D &(test_partition_array[0]); ++ struct efi_guid_t old_guid =3D old_entry->guid; ++ ++ /* Each entry will be read to find the entry to be duplicated. */ ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ ++ /* The partition data is moved: this means reading each block then = writing. ++ * It doesn't matter what the data is ++ */ ++ char unused_read_data =3D '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 =3D 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 a= nd therefore ++ * pointless ++ */ ++ struct efi_guid_t new_guid =3D 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 =3D &(test_partition_array[0]); ++ struct efi_guid_t old_guid =3D old_entry->guid; ++ ++ /* Each entry will be read to find the entry to be duplicated. */ ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ ++ /* The partition data is moved: this means reading each block then = writing. ++ * It doesn't matter what the data is ++ */ ++ char unused_read_data =3D '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 =3D 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 a= nd therefore ++ * pointless ++ */ ++ struct efi_guid_t new_guid =3D 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_failToCreateEntryWhenLowestFreeLba= DoesNotHaveSpace(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 d= uplicating ++ * it won't be possible. ++ */ ++ struct gpt_entry_t *old_entry =3D &(test_partition_array[TEST_DEFAU= LT_NUM_PARTITIONS - 1]); ++ old_entry->end =3D TEST_GPT_THIRD_PARTITION_START + (TEST_DISK_NUM_= BLOCKS / 2 ) + 1; ++ struct efi_guid_t old_guid =3D 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_a= rray)); ++ ++ 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 =3D NULL_GUID; ++ ++ /* Each entry will be read to find the entry to be duplicated. */ ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ ++ 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 =3D &(test_partition_array[0]); ++ struct efi_guid_t old_guid =3D 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_a= rray)); ++ ++ /* 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 =3D TEST_GPT_DISK_FREE_SPACE_START; ++ struct gpt_entry_t new_entry =3D { ++ .type =3D TEST_GPT_VALID_TYPE(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11= ), ++ .start =3D new_entry_end, ++ .end =3D new_entry_end, ++ .attr =3D 0, ++ .guid =3D TEST_GPT_VALID_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11= ), ++ .name =3D "Fourth partition" ++ }; ++ test_partition_array[TEST_MAX_PARTITIONS - 1] =3D new_entry; ++ setup_valid_gpt(); ++ ++ struct gpt_entry_t *old_entry =3D &(test_partition_array[0]); ++ struct efi_guid_t old_guid =3D 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_a= rray)); ++ ++ TEST_ASSERT_EQUAL(PSA_ERROR_INSUFFICIENT_STORAGE, gpt_entry_duplica= te( ++ &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 mus= t also diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone10= 00/0054-lib-gpt-Consecutively-erase-blocks-when-moving-parti.patch b/meta= -arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0054-lib-gpt-C= onsecutively-erase-blocks-when-moving-parti.patch new file mode 100644 index 00000000..53eabd6e --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0054= -lib-gpt-Consecutively-erase-blocks-when-moving-parti.patch @@ -0,0 +1,240 @@ +From 0dcbef3a0800a2a610b32935a54762d4b42203f1 Mon Sep 17 00:00:00 2001 +From: Frazer Carsley <[email protected]> +Date: Tue, 17 Mar 2026 11:44:14 +0000 +Subject: [PATCH] lib: gpt: Consecutively erase blocks when moving partit= ions + +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 <[email protected]> +Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/tr= usted-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 stru= ct 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, bo= ol 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_inde= x, + 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_gui= d_t *guid) +=20 + /* Write to backup first, then primary partition array */ + if (backup_gpt_array_lba !=3D 0) { +- ret =3D write_to_flash(backup_gpt_array_lba + i - 1 - P= RIMARY_GPT_ARRAY_LBA); ++ ret =3D write_to_flash(backup_gpt_array_lba + i - 1 - P= RIMARY_GPT_ARRAY_LBA, false); + if (ret !=3D PSA_SUCCESS) { + return ret; + } + } +- ret =3D write_to_flash(i - 1); ++ ret =3D write_to_flash(i - 1, false); + if (ret !=3D PSA_SUCCESS) { + return ret; + } +@@ -750,16 +750,16 @@ psa_status_t gpt_entry_remove(const struct efi_gui= d_t *guid) + */ + memset(lba_buf, 0, TFM_GPT_BLOCK_SIZE); + if (backup_gpt_array_lba !=3D 0) { +- int write_ret =3D plat_flash_driver->write( ++ ret =3D write_to_flash( + backup_gpt_array_lba + array_end_lba - PRIMARY_GPT_= ARRAY_LBA, +- lba_buf); +- if (write_ret !=3D TFM_GPT_BLOCK_SIZE) { +- return PSA_ERROR_STORAGE_FAILURE; ++ true); ++ if (ret !=3D PSA_SUCCESS) { ++ return ret; + } + } +- int write_ret =3D plat_flash_driver->write(array_end_lba, lba_b= uf); +- if (write_ret !=3D TFM_GPT_BLOCK_SIZE) { +- return PSA_ERROR_STORAGE_FAILURE; ++ ret =3D write_to_flash(array_end_lba, true); ++ if (ret !=3D PSA_SUCCESS) { ++ return ret; + } + } else { + /* Zero what is not needed anymore */ +@@ -768,12 +768,12 @@ psa_status_t gpt_entry_remove(const struct efi_gui= d_t *guid) + 0, + (gpt_entry_per_lba_count() - entries_in_last_lba) * GPT= _ENTRY_SIZE); + if (backup_gpt_array_lba !=3D 0) { +- ret =3D write_to_flash(backup_gpt_array_lba + array_end_lba= - PRIMARY_GPT_ARRAY_LBA); ++ ret =3D write_to_flash(array_end_lba, false); + if (ret !=3D PSA_SUCCESS) { + return ret; + } + } +- ret =3D write_to_flash(array_end_lba); ++ ret =3D write_to_flash(array_end_lba, false); + if (ret !=3D PSA_SUCCESS) { + return ret; + } +@@ -1121,14 +1121,18 @@ static psa_status_t find_gpt_entry(const struct = gpt_t *table, + } +=20 + /* 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 =3D read_from_flash(old_lba); + if (ret !=3D PSA_SUCCESS) { + return ret; + } +=20 +- return write_to_flash(new_lba); ++ return write_to_flash(new_lba, skip_erase); + } +=20 + /* 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; + } +=20 ++ /* 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 w= here the ++ * partition is now and where it will be does not overlap, then era= se all ++ * blocks in the new area. If there is overlap, erase only those wh= ich are ++ * not within the old area ++ */ + if (old_lba < new_lba) { ++ /* Attempt consecutive erase */ ++ uint64_t non_overlap_blocks =3D ++ (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_ove= rlap_blocks, ++ (uint32_t)(new_lba >> 32), (uint32_t)new_lba); ++ ++ const ssize_t erase_ret =3D plat_flash_driver->erase( ++ new_lba + (num_blocks - non_overlap_blocks), ++ (size_t)non_overlap_blocks); ++ if (erase_ret !=3D (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 =3D 0; ++ } ++ + /* Move block by block backwards */ + for (uint64_t block =3D num_blocks; block > 0; --block) { +- const psa_status_t ret =3D move_lba(old_lba + block - 1, ne= w_lba + block - 1); ++ const bool skip_erase =3D (block < non_overlap_blocks); ++ const psa_status_t ret =3D move_lba(old_lba + block - 1, ne= w_lba + block - 1, skip_erase); + if (ret !=3D PSA_SUCCESS) { + return ret; + } + } + } else { ++ /* Attempt consecutive erase */ ++ uint64_t non_overlap_blocks =3D ++ (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_ove= rlap_blocks, ++ (uint32_t)(new_lba >> 32), (uint32_t)new_lba); ++ ++ const ssize_t erase_ret =3D plat_flash_driver->erase(new_lba, (= size_t)non_overlap_blocks); ++ if (erase_ret !=3D (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 =3D 0; ++ } ++ + /* Move block by block forwards */ + for (uint64_t block =3D 0; block < num_blocks; ++block) { +- const psa_status_t ret =3D move_lba(old_lba + block, new_lb= a + block); ++ const bool skip_erase =3D (block < non_overlap_blocks); ++ const psa_status_t ret =3D move_lba(old_lba + block, new_lb= a + block, skip_erase); + if (ret !=3D PSA_SUCCESS) { + return ret; + } +@@ -1372,7 +1418,7 @@ static psa_status_t flush_lba_buf(void) + ret =3D write_entries_to_flash(cached_lba - backup_gpt_array_lb= a, false); + } else { + /* Some other LBA is cached, possibly data. Write it anyway */ +- ret =3D write_to_flash(cached_lba); ++ ret =3D write_to_flash(cached_lba, false); + } +=20 + in_flush =3D false; +@@ -1380,13 +1426,15 @@ static psa_status_t flush_lba_buf(void) + } +=20 + /* 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) !=3D 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) !=3D 1) { ++ ERROR("Unable to erase flash at LBA 0x%08x%08x\n", ++ (uint32_t)(lba >> 32), ++ (uint32_t)lba); ++ return PSA_ERROR_STORAGE_FAILURE; ++ } + } +=20 + if (plat_flash_driver->write(lba, lba_buf) !=3D 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; +=20 + if (backup_gpt_array_lba !=3D 0) { +- ret =3D write_to_flash(backup_gpt_array_lba + lbas_into_array); ++ ret =3D write_to_flash(backup_gpt_array_lba + lbas_into_array, = false); + if (ret !=3D 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"); + } +=20 +- ret =3D write_to_flash(PRIMARY_GPT_ARRAY_LBA + lbas_into_array); ++ ret =3D write_to_flash(PRIMARY_GPT_ARRAY_LBA + lbas_into_array, fal= se); + if (ret !=3D 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 st= ruct 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 =3D write_to_flash(table->header.current_lba= ); ++ const psa_status_t ret =3D write_to_flash(table->header.current_lba= , false); + memcpy(lba_buf, temp_buf, GPT_HEADER_SIZE); +=20 + return ret; diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone10= 00/0055-lib-gpt-Clarify-API-operation.patch b/meta-arm-bsp/recipes-bsp/tr= usted-firmware-m/files/corstone1000/0055-lib-gpt-Clarify-API-operation.pa= tch new file mode 100644 index 00000000..29986c43 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0055= -lib-gpt-Clarify-API-operation.patch @@ -0,0 +1,38 @@ +From 7e2ae2fc4f8ae8a16a24b87d0650c6b4b28fc870 Mon Sep 17 00:00:00 2001 +From: Frazer Carsley <[email protected]> +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 <[email protected]> +Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/tr= usted-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); +=20 + /** +- * \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); +=20 + /** +- * \brief Duplicates an existing partition entry into new space. ++ * \brief Duplicates an existing partition entry into new space, includ= ing the ++ * partition data. + * + * \param[in] old_guid Entry to duplicate. + * \param[in] start Starting LBA (0 uses the lowest free LBA possib= le). diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone10= 00/0056-lib-gpt-Add-metadata-only-API-operations.patch b/meta-arm-bsp/rec= ipes-bsp/trusted-firmware-m/files/corstone1000/0056-lib-gpt-Add-metadata-= only-API-operations.patch new file mode 100644 index 00000000..e3320caf --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0056= -lib-gpt-Add-metadata-only-API-operations.patch @@ -0,0 +1,701 @@ +From f9badce3570bbc89b141c86a6b8a988d90d81f0c Mon Sep 17 00:00:00 2001 +From: Frazer Carsley <[email protected]> +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 <[email protected]> +Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/tr= usted-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); +=20 ++/** ++ * \brief Moves (or resizes) a partition entry, without moving the part= ition 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 GU= ID. ++ * \retval PSA_ERROR_INVALID_ARGUMENT Move would overlap with an existi= ng 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, includ= ing the + * partition data. +@@ -190,6 +209,26 @@ psa_status_t gpt_entry_duplicate(const struct efi_g= uid_t *old_guid, + const uint64_t start, + struct efi_guid_t *new_guid); +=20 ++/** ++ * \brief Duplicates an existing partition entry into new space, withou= t copying ++ * the partition data. ++ * ++ * \param[in] old_guid Entry to duplicate. ++ * \param[in] start Starting LBA (0 uses the lowest free LBA possib= le). ++ * \param[out] new_guid GUID populated on success for subsequent API ca= lls. ++ * ++ * \retval PSA_SUCCESS Success. ++ * \retval PSA_ERROR_STORAGE_FAILURE I/O error. ++ * \retval PSA_ERROR_DOES_NOT_EXIST No entry found with the provided GU= ID. ++ * \retval PSA_ERROR_INSUFFICIENT_STORAGE Maximum number of partitions = reached. ++ * \retval PSA_ERROR_INVALID_ARGUMENT New entry would overlap with an e= xisting partition. ++ * \retval PSA_ERROR_INVALID_ARGUMENT Part of the partition would be of= f flash. ++ */ ++__attribute__((nonnull(1,3))) ++psa_status_t gpt_entry_duplicate_no_copy(const struct efi_guid_t *old_g= uid, ++ const uint64_t start= , ++ struct efi_guid_t *new_g= uid); ++ + /** + * \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_gu= id, ++ const uint64_t start, ++ const bool no_cop= y, ++ struct efi_guid_t *new_gu= id); + static psa_status_t mbr_load(struct mbr_t *mbr); + static bool gpt_entry_cmp_guid(const struct gpt_entry_t *entry, const v= oid *guid); + static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const v= oid *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 =3D find_gpt_entry( +- &primary_gpt, +- gpt_entry_cmp_guid, +- guid, +- 0, +- &cached_entry, +- &cached_index); +- if (ret !=3D PSA_SUCCESS) { +- return ret; +- } +- +- /* Prevent unecessary I/O */ +- if (start =3D=3D cached_entry.start && end =3D=3D cached_entry.end)= { +- return PSA_SUCCESS; +- } +- +- /* It is not possible to move a partition such that it overlaps wit= h an +- * existing partition (other than itself). Check the currently cach= ed LBA +- * first, then the others to avoid reading this LBA twice +- */ +- struct gpt_entry_t entry; +- const uint64_t checked_lba =3D cached_lba; +- const uint64_t array_end_lba =3D partition_array_last_lba(&primary_= gpt); +- uint32_t num_entries_in_cached_lba; +- if (cached_lba =3D=3D array_end_lba) { +- /* If this is 0, then the last LBA is full */ +- uint32_t num_entries_in_last_lba =3D primary_gpt.num_used_parti= tions % gpt_entry_per_lba_count(); +- if (num_entries_in_last_lba =3D=3D 0) { +- num_entries_in_cached_lba =3D gpt_entry_per_lba_count(); +- } else { +- num_entries_in_cached_lba =3D num_entries_in_last_lba; +- } +- } else { +- num_entries_in_cached_lba =3D gpt_entry_per_lba_count(); +- } +- +- /* Cached LBA */ +- for (uint32_t i =3D 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 =3D entry.unique_guid; +- if (efi_guid_cmp(&ent_guid, guid) =3D=3D 0) { +- continue; +- } +- +- if ((start >=3D entry.start && start <=3D entry.end) || +- (end >=3D entry.start && end <=3D entry.end) || +- (start <=3D entry.start && end >=3D entry.end)) +- { +- return PSA_ERROR_INVALID_ARGUMENT; +- } +- } +- +- /* All the rest */ +- for (uint32_t i =3D 0; i < primary_gpt.num_used_partitions; ++i) { +- if (partition_entry_lba(&primary_gpt, i) =3D=3D checked_lba) { +- continue; +- } +- +- ret =3D read_entry_from_flash(&primary_gpt, i, &entry); +- if (ret !=3D PSA_SUCCESS) { +- return ret; +- } +- +- if ((start >=3D entry.start && start <=3D entry.end) || +- (end >=3D entry.start && end <=3D entry.end) || +- (start <=3D entry.start && end >=3D entry.end)) +- { +- return PSA_ERROR_INVALID_ARGUMENT; +- } +- } +- +- ret =3D move_partition( +- cached_entry.start, +- start, +- end - start + 1); +- if (ret !=3D PSA_SUCCESS) { +- return ret; +- } +- cached_entry.start =3D start; +- cached_entry.end =3D end; ++ return move_partition(guid, start, end, false); ++} +=20 +- 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); + } +=20 + 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 =3D gpt_entry_read(old_guid, &old_entry); +- if (ret !=3D PSA_SUCCESS) { +- return ret; +- } +- +- ret =3D gpt_entry_create(&(old_entry.type_guid), +- start, +- old_entry.size, +- old_entry.attr, +- old_entry.name, +- new_guid); +- if (ret !=3D PSA_SUCCESS) { +- return ret; +- } ++ return duplicate_partition(old_guid, start, false, new_guid); ++} +=20 +- return move_partition(old_entry.start, start, old_entry.size); ++psa_status_t gpt_entry_duplicate_no_copy(const struct efi_guid_t *old_g= uid, ++ const uint64_t start= , ++ struct efi_guid_t *new_g= uid) ++{ ++ return duplicate_partition(old_guid, start, true, new_guid); + } +=20 + psa_status_t gpt_entry_create(const struct efi_guid_t *type, +@@ -885,7 +793,7 @@ psa_status_t gpt_defragment(void) + } +=20 + const uint64_t num_blocks =3D entry.end - entry.start + 1; +- ret =3D move_partition(entry.start, prev_end, num_blocks); ++ ret =3D move_partition_data(entry.start, prev_end, num_blocks); + if (ret !=3D 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; + } +=20 ++/* 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_gu= id, ++ const uint64_t start, ++ const bool no_cop= y, ++ struct efi_guid_t *new_gu= id) ++{ ++ struct partition_entry_t old_entry; ++ psa_status_t ret =3D gpt_entry_read(old_guid, &old_entry); ++ if (ret !=3D PSA_SUCCESS) { ++ return ret; ++ } ++ ++ ret =3D gpt_entry_create(&(old_entry.type_guid), ++ start, ++ old_entry.size, ++ old_entry.attr, ++ old_entry.name, ++ new_guid); ++ if (ret !=3D 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 updat= ing 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 =3D find_gpt_entry( ++ &primary_gpt, ++ gpt_entry_cmp_guid, ++ guid, ++ 0, ++ &cached_entry, ++ &cached_index); ++ if (ret !=3D PSA_SUCCESS) { ++ return ret; ++ } ++ ++ /* Prevent unecessary I/O */ ++ if (start =3D=3D cached_entry.start && end =3D=3D cached_entry.end)= { ++ return PSA_SUCCESS; ++ } ++ ++ /* It is not possible to move a partition such that it overlaps wit= h an ++ * existing partition (other than itself). Check the currently cach= ed LBA ++ * first, then the others to avoid reading this LBA twice ++ */ ++ struct gpt_entry_t entry; ++ const uint64_t checked_lba =3D cached_lba; ++ const uint64_t array_end_lba =3D partition_array_last_lba(&primary_= gpt); ++ uint32_t num_entries_in_cached_lba; ++ if (cached_lba =3D=3D array_end_lba) { ++ /* If this is 0, then the last LBA is full */ ++ uint32_t num_entries_in_last_lba =3D primary_gpt.num_used_parti= tions % gpt_entry_per_lba_count(); ++ if (num_entries_in_last_lba =3D=3D 0) { ++ num_entries_in_cached_lba =3D gpt_entry_per_lba_count(); ++ } else { ++ num_entries_in_cached_lba =3D num_entries_in_last_lba; ++ } ++ } else { ++ num_entries_in_cached_lba =3D gpt_entry_per_lba_count(); ++ } ++ ++ /* Cached LBA */ ++ for (uint32_t i =3D 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 =3D entry.unique_guid; ++ if (efi_guid_cmp(&ent_guid, guid) =3D=3D 0) { ++ continue; ++ } ++ ++ if ((start >=3D entry.start && start <=3D entry.end) || ++ (end >=3D entry.start && end <=3D entry.end) || ++ (start <=3D entry.start && end >=3D entry.end)) ++ { ++ return PSA_ERROR_INVALID_ARGUMENT; ++ } ++ } ++ ++ /* All the rest */ ++ for (uint32_t i =3D 0; i < primary_gpt.num_used_partitions; ++i) { ++ if (partition_entry_lba(&primary_gpt, i) =3D=3D checked_lba) { ++ continue; ++ } ++ ++ ret =3D read_entry_from_flash(&primary_gpt, i, &entry); ++ if (ret !=3D PSA_SUCCESS) { ++ return ret; ++ } ++ ++ if ((start >=3D entry.start && start <=3D entry.end) || ++ (end >=3D entry.start && end <=3D entry.end) || ++ (start <=3D entry.start && end >=3D entry.end)) ++ { ++ return PSA_ERROR_INVALID_ARGUMENT; ++ } ++ } ++ ++ if (!no_copy) { ++ ret =3D move_partition_data( ++ cached_entry.start, ++ start, ++ end - start + 1); ++ if (ret !=3D PSA_SUCCESS) { ++ return ret; ++ } ++ } ++ ++ cached_entry.start =3D start; ++ cached_entry.end =3D 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_lb= a, const uint64_t new_lba, con + } +=20 + /* 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 *re= store_from, bool is_primary) + swap_headers(&(restore_from->header), &(restore_to.header)); +=20 + /* Copy the partition array as well */ +- ret =3D move_partition( ++ ret =3D 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/te= st_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_failWhenTable= Full(void) + &new_guid)); + } +=20 ++void test_gpt_entry_duplicate_no_copy_should_DuplicateOldEntry(void) ++{ ++ /* Duplicate an entry. It must not overlap with an existing entry a= nd must also ++ * fit on the storage device. The GUID should be populated with som= ething. ++ */ ++ setup_valid_gpt(); ++ struct gpt_entry_t *old_entry =3D &(test_partition_array[0]); ++ struct efi_guid_t old_guid =3D old_entry->guid; ++ ++ /* Each entry will be read to find the entry to be duplicated. */ ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ ++ /* Mock out the call to create a new GUID */ ++ struct efi_guid_t expected_guid =3D 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 a= nd therefore ++ * pointless ++ */ ++ struct efi_guid_t new_guid =3D 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_createNewEntryNextToLastEn= try(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 =3D &(test_partition_array[0]); ++ struct efi_guid_t old_guid =3D old_entry->guid; ++ ++ /* Each entry will be read to find the entry to be duplicated. */ ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ ++ /* Mock out the call to create a new GUID */ ++ struct efi_guid_t expected_guid =3D 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 a= nd therefore ++ * pointless ++ */ ++ struct efi_guid_t new_guid =3D 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_failToCreateEntryWhenLowes= tFreeLbaDoesNotHaveSpace(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 d= uplicating ++ * it won't be possible. ++ */ ++ struct gpt_entry_t *old_entry =3D &(test_partition_array[TEST_DEFAU= LT_NUM_PARTITIONS - 1]); ++ old_entry->end =3D TEST_GPT_THIRD_PARTITION_START + (TEST_DISK_NUM_= BLOCKS / 2 ) + 1; ++ struct efi_guid_t old_guid =3D 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_a= rray)); ++ ++ struct efi_guid_t new_guid; ++ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_duplicate_n= o_copy( ++ &old_guid, ++ 0, ++ &new_guid)); ++} ++ ++void test_gpt_entry_duplicate_no_copy_should_failWhenEntryNotExisting(v= oid) ++{ ++ setup_valid_gpt(); ++ struct efi_guid_t old_guid =3D NULL_GUID; ++ ++ /* Each entry will be read to find the entry to be duplicated. */ ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ ++ 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(vo= id) ++{ ++ setup_valid_gpt(); ++ struct gpt_entry_t *old_entry =3D &(test_partition_array[0]); ++ struct efi_guid_t old_guid =3D 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_a= rray)); ++ ++ /* 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_n= o_copy( ++ &old_guid, ++ TEST_GPT_FIRST_PARTITION_START + 1, ++ &new_guid)); ++ ++ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_duplicate_n= o_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 =3D TEST_GPT_DISK_FREE_SPACE_START; ++ struct gpt_entry_t new_entry =3D { ++ .type =3D TEST_GPT_VALID_TYPE(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11= ), ++ .start =3D new_entry_end, ++ .end =3D new_entry_end, ++ .attr =3D 0, ++ .guid =3D TEST_GPT_VALID_GUID(4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11= ), ++ .name =3D "Fourth partition" ++ }; ++ test_partition_array[TEST_MAX_PARTITIONS - 1] =3D new_entry; ++ setup_valid_gpt(); ++ ++ struct gpt_entry_t *old_entry =3D &(test_partition_array[0]); ++ struct efi_guid_t old_guid =3D 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_a= rray)); ++ ++ TEST_ASSERT_EQUAL(PSA_ERROR_INSUFFICIENT_STORAGE, gpt_entry_duplica= te_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 mus= t also +@@ -1282,6 +1429,128 @@ void test_gpt_entry_move_should_failWhenLbaOffDi= sk(void) + TEST_GPT_BACKUP_LBA + 1)); + } +=20 ++void test_gpt_entry_move_no_copy_should_moveEntry(void) ++{ ++ /* Start with a populated GPT */ ++ setup_valid_gpt(); ++ struct gpt_entry_t *test_entry =3D &(test_partition_array[TEST_DEFA= ULT_NUM_PARTITIONS - 1]); ++ struct efi_guid_t test_guid =3D test_entry->guid; ++ ++ /* First all entries are read to determine for overlap */ ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ ++ /* Header update - reads partition array to calculate crc32 and als= o then ++ * reads the header to modify and write back ++ */ ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ 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_a= rray)); ++ ++ struct efi_guid_t non_existing =3D 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 =3D test_partition_array[0].guid; ++ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_cop= y( ++ &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 overla= p */ ++ size_t test_index =3D 1; ++ struct gpt_entry_t *test_entry =3D &(test_partition_array[test_inde= x]); ++ struct efi_guid_t test_guid =3D test_entry->guid; ++ register_mocked_read(&test_partition_array, sizeof(test_partition_a= rray)); ++ ++ /* Try to move the test entry into the middle of the entry just rea= d. ++ * Starting at the same LBA ++ */ ++ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_cop= y( ++ &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 rea= d. ++ * Starting in the middle ++ */ ++ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_cop= y( ++ &test_guid, ++ TEST_GPT_FIRST_PARTITION_START + 1, ++ TEST_GPT_SECOND_PARTITION_END)); ++ ++ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_cop= y( ++ &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 rea= d. ++ * Starting and ending in the middle. ++ */ ++ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_cop= y( ++ &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 =3D 1; ++ struct gpt_entry_t *test_entry =3D &(test_partition_array[test_inde= x]); ++ struct efi_guid_t test_guid =3D test_entry->guid; ++ ++ /* First start on disk, then go off the disk */ ++ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_ARGUMENT, gpt_entry_move_no_cop= y( ++ &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_cop= y( ++ &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_cop= y( ++ &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_cop= y( ++ &test_guid, ++ TEST_GPT_BACKUP_LBA, ++ TEST_GPT_BACKUP_LBA + 1)); ++} ++ + void test_gpt_attr_set_should_setAttributes(void) + { + /* Start with a populated GPT */ diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware= -m-corstone1000.inc b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted= -firmware-m-corstone1000.inc index 2bfea84b..4a8ec99b 100644 --- a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-cors= tone1000.inc +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-cors= tone1000.inc @@ -82,6 +82,12 @@ SRC_URI:append:corstone1000 =3D " \ 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.pat= ch \ + 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.pat= ch \ + file://0055-lib-gpt-Clarify-API-operation.patch \ + file://0056-lib-gpt-Add-metadata-only-API-operations.patch \ " =20 SRCREV_tfm-psa-adac:corstone1000 =3D "f2809ae231be33a1afcd7714f40756c67d= 846c88"