diff mbox series

[2/3] arm-bsp/trusted-firmware-m:cs1k: Add extra GPT library operations

Message ID 20260424153656.774555-3-frazer.carsley@arm.com
State New
Headers show
Series Add GPT library fixes and duplicate features | expand

Commit Message

Frazer Carsley April 24, 2026, 3:36 p.m. UTC
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>
---
 ...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/corstone1000/0051-lib-gpt-Show-intent-of-GUIDs-in-unittests-more-clear.patch
 create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0052-lib-gpt-Provide-macro-identifying-free-space.patch
 create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0053-lib-gpt-Add-operation-to-duplicate-entries.patch
 create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0054-lib-gpt-Consecutively-erase-blocks-when-moving-parti.patch
 create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0055-lib-gpt-Clarify-API-operation.patch
 create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0056-lib-gpt-Add-metadata-only-API-operations.patch
diff mbox series

Patch

diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/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-Show-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 <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 */
diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0052-lib-gpt-Provide-macro-identifying-free-space.patch b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0052-lib-gpt-Provide-macro-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 <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(
diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0053-lib-gpt-Add-operation-to-duplicate-entries.patch b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0053-lib-gpt-Add-operation-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 <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
diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0054-lib-gpt-Consecutively-erase-blocks-when-moving-parti.patch b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0054-lib-gpt-Consecutively-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 <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;
diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0055-lib-gpt-Clarify-API-operation.patch b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0055-lib-gpt-Clarify-API-operation.patch
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 <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).
diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0056-lib-gpt-Add-metadata-only-API-operations.patch b/meta-arm-bsp/recipes-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 <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 */
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-corstone1000.inc
+++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc
@@ -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"