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

arm-bsp/trusted-firmware-m:cs1k: Add fixes for GPT library

These patches backport bug fixes for the GPT library in TF-M.

Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Signed-off-by: Jon Mason <jon.mason@arm.com>
This commit is contained in:
Frazer Carsley
2026-04-24 16:36:54 +01:00
committed by Jon Mason
parent 4526637250
commit 9af92ed09e
6 changed files with 814 additions and 0 deletions
@@ -0,0 +1,114 @@
From 4f11567a0152f1ecd98159ca555d8663ee8e5ce0 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Fri, 10 Apr 2026 17:20:39 +0100
Subject: [PATCH] lib: gpt: Fix final entry not being removed
The final entry would not be removed due to the removals being hidden
behind a check on whether the removed entry was not the final entry,
causing it to be a no-op. Removal operation is no longer hidden behind
that condition.
Change-Id: Ib264d988d57cb39098f2783c4a001fcf3b004270
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Backport [f3cf6bda1534b8893620c7b7c0d9ff647b44603e]
---
lib/gpt/src/gpt.c | 64 +++++++++++++++++++++++------------------------
1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
index a9dbb918e..e4e7fde32 100644
--- a/lib/gpt/src/gpt.c
+++ b/lib/gpt/src/gpt.c
@@ -648,6 +648,7 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
* to be modified if the last entry in the array was moved or if it is
* the only LBA used by the partition array
*/
+ const uint64_t array_end_lba = partition_array_last_lba(&primary_gpt);;
if (cached_index != primary_gpt.num_used_partitions - 1 ||
cached_index < gpt_entry_per_lba_count())
{
@@ -669,7 +670,6 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
* Use a second buffer to read each consecutive LBA and copy that to
* the global LBA buffer to then write afterwards.
*/
- const uint64_t array_end_lba = partition_array_last_lba(&primary_gpt);
for (uint64_t i = partition_entry_lba(&primary_gpt, cached_index) + 1;
i <= array_end_lba;
++i)
@@ -705,45 +705,45 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
sizeof(array_buf) - primary_gpt.header.entry_size);
memcpy(lba_buf, array_buf, TFM_GPT_BLOCK_SIZE);
}
+ }
- /* What was the final LBA is now cached and may be empty or partially-filled */
- cached_lba = array_end_lba;
- write_buffered = false;
- uint32_t entries_in_last_lba = (--primary_gpt.num_used_partitions) % gpt_entry_per_lba_count();
- if (entries_in_last_lba == 0) {
- /* There's nothing left in this LBA, so zero it all and write it out.
- * There is also no need to do an erase just to zero afterwards.
- */
- memset(lba_buf, 0, TFM_GPT_BLOCK_SIZE);
- if (backup_gpt_array_lba != 0) {
- int write_ret = plat_flash_driver->write(
- backup_gpt_array_lba + array_end_lba - PRIMARY_GPT_ARRAY_LBA,
- lba_buf);
- if (write_ret != TFM_GPT_BLOCK_SIZE) {
- return PSA_ERROR_STORAGE_FAILURE;
- }
- }
- int write_ret = plat_flash_driver->write(array_end_lba, lba_buf);
+ /* What was the final LBA is now cached and may be empty or partially-filled */
+ cached_lba = array_end_lba;
+ write_buffered = false;
+ uint32_t entries_in_last_lba = (--primary_gpt.num_used_partitions) % gpt_entry_per_lba_count();
+ if (entries_in_last_lba == 0) {
+ /* There's nothing left in this LBA, so zero it all and write it out.
+ * There is also no need to do an erase just to zero afterwards.
+ */
+ memset(lba_buf, 0, TFM_GPT_BLOCK_SIZE);
+ if (backup_gpt_array_lba != 0) {
+ int write_ret = plat_flash_driver->write(
+ backup_gpt_array_lba + array_end_lba - PRIMARY_GPT_ARRAY_LBA,
+ lba_buf);
if (write_ret != TFM_GPT_BLOCK_SIZE) {
return PSA_ERROR_STORAGE_FAILURE;
}
- } else {
- /* Zero what is not needed anymore */
- memset(
- lba_buf + primary_gpt.header.entry_size * entries_in_last_lba,
- 0,
- (gpt_entry_per_lba_count() - entries_in_last_lba) * primary_gpt.header.entry_size);
- if (backup_gpt_array_lba != 0) {
- ret = write_to_flash(backup_gpt_array_lba + array_end_lba - PRIMARY_GPT_ARRAY_LBA);
- if (ret != PSA_SUCCESS) {
- return ret;
- }
- }
- ret = write_to_flash(array_end_lba);
+ }
+ int write_ret = plat_flash_driver->write(array_end_lba, lba_buf);
+ if (write_ret != TFM_GPT_BLOCK_SIZE) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ } else {
+ /* Zero what is not needed anymore */
+ memset(
+ lba_buf + primary_gpt.header.entry_size * entries_in_last_lba,
+ 0,
+ (gpt_entry_per_lba_count() - entries_in_last_lba) * primary_gpt.header.entry_size);
+ if (backup_gpt_array_lba != 0) {
+ ret = write_to_flash(backup_gpt_array_lba + array_end_lba - PRIMARY_GPT_ARRAY_LBA);
if (ret != PSA_SUCCESS) {
return ret;
}
}
+ ret = write_to_flash(array_end_lba);
+ if (ret != PSA_SUCCESS) {
+ return ret;
+ }
}
/* Update the header after flash changes */
@@ -0,0 +1,37 @@
From 68874e58811c5d4004492f15b3ac46d2cca186c0 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Thu, 16 Apr 2026 09:54:55 +0100
Subject: [PATCH] lib: gpt: Replace warnings with errors
These warnings return errors, so it makes more sense to output an error
message.
Change-Id: I589e24ba8aba2502a3fc86d2aeb648d125c022bd
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Backport [68874e58811c5d4004492f15b3ac46d2cca186c0]
---
lib/gpt/src/gpt.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
index 16bda8aba..32c7277bd 100644
--- a/lib/gpt/src/gpt.c
+++ b/lib/gpt/src/gpt.c
@@ -830,7 +830,7 @@ psa_status_t gpt_defragment(void)
*/
psa_status_t ret = sort_partition_array(&primary_gpt);
if (ret != PSA_SUCCESS) {
- WARN("Unable to defragment flash!\n");
+ ERROR("Unable to defragment flash!\n");
return ret;
}
@@ -926,7 +926,7 @@ psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_part
if (mbr.partitions[0].os_type == MBR_TYPE_GPT) {
ret = read_table_from_flash(&primary_gpt, true);
} else {
- WARN("Unsupported legacy MBR in use\n");
+ ERROR("Unsupported legacy MBR in use\n");
ret = PSA_ERROR_NOT_SUPPORTED;
}
@@ -0,0 +1,268 @@
From ff08a9d998c545a6152789f8ce55bd4200a937cf Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Wed, 15 Apr 2026 10:59:28 +0100
Subject: [PATCH] lib: gpt: Enforce entry size of 128 bytes
Previous to this, the entry size could have been set to any number, even
those deemed illegal by the UEFI spec 2.10 [1], as the library did not
validate it. 128 bytes is the minimum size for a partition entry and
most standard tools that create GPTs will use this.
[1] https://uefi.org/specs/UEFI/2.10/05_GUID_Partition_Table_Format.html
Change-Id: Ib6c436353a4b8e00e6c6e63b933a49f11c0a7340
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Backport [ff08a9d998c545a6152789f8ce55bd4200a937cf]
---
lib/gpt/inc/gpt.h | 2 +
lib/gpt/src/gpt.c | 65 +++++++++++++++++++++-----------
lib/gpt/unittests/gpt/test_gpt.c | 40 ++++++++++++++++++++
3 files changed, 85 insertions(+), 22 deletions(-)
diff --git a/lib/gpt/inc/gpt.h b/lib/gpt/inc/gpt.h
index baf4767f9..34ce67580 100644
--- a/lib/gpt/inc/gpt.h
+++ b/lib/gpt/inc/gpt.h
@@ -214,6 +214,7 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid);
*
* \retval PSA_SUCCESS GPT is valid.
* \retval PSA_ERROR_STORAGE_FAILURE I/O error.
+ * \retval PSA_ERROR_NOT_SUPPORTED Entry size is not 128 bytes
* \retval PSA_ERROR_INVALID_SIGNATURE GPT is invalid.
*/
psa_status_t gpt_validate(bool is_primary);
@@ -249,6 +250,7 @@ psa_status_t gpt_defragment(void);
* functions defined by \p flash_driver is NULL. The init
* and uninit functions may be NULL if not required.
* \retval PSA_ERROR_NOT_SUPPORTED Legacy MBR is used and not GPT.
+ * \retval PSA_ERROR_NOT_SUPPORTED Entry size is not 128 bytes
*/
__attribute__((nonnull(1)))
psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver,
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
index 32c7277bd..200e21599 100644
--- a/lib/gpt/src/gpt.c
+++ b/lib/gpt/src/gpt.c
@@ -459,7 +459,7 @@ psa_status_t gpt_entry_move(const struct efi_guid_t *guid,
/* Cached LBA */
for (uint32_t i = 0; i < num_entries_in_cached_lba; ++i) {
- memcpy(&entry, lba_buf + (i * primary_gpt.header.entry_size), GPT_ENTRY_SIZE);
+ 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) {
@@ -658,9 +658,9 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
const uint32_t lba_index = cached_index % gpt_entry_per_lba_count();
if (lba_index + 1 != gpt_entry_per_lba_count()) {
memmove(
- lba_buf + lba_index * primary_gpt.header.entry_size,
- lba_buf + (lba_index + 1) * primary_gpt.header.entry_size,
- (gpt_entry_per_lba_count() - lba_index - 1) * primary_gpt.header.entry_size);
+ lba_buf + lba_index * GPT_ENTRY_SIZE,
+ lba_buf + (lba_index + 1) * GPT_ENTRY_SIZE,
+ (gpt_entry_per_lba_count() - lba_index - 1) * GPT_ENTRY_SIZE);
}
/* If this is not the last LBA, then read the next LBA into memory and
@@ -683,7 +683,7 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
}
memcpy(
- lba_buf + primary_gpt.header.entry_size * (gpt_entry_per_lba_count() - 1),
+ lba_buf + GPT_ENTRY_SIZE * (gpt_entry_per_lba_count() - 1),
array_buf,
GPT_ENTRY_SIZE);
@@ -701,8 +701,8 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
memmove(
array_buf,
- array_buf + primary_gpt.header.entry_size,
- sizeof(array_buf) - primary_gpt.header.entry_size);
+ array_buf + GPT_ENTRY_SIZE,
+ sizeof(array_buf) - GPT_ENTRY_SIZE);
memcpy(lba_buf, array_buf, TFM_GPT_BLOCK_SIZE);
}
}
@@ -731,9 +731,9 @@ psa_status_t gpt_entry_remove(const struct efi_guid_t *guid)
} else {
/* Zero what is not needed anymore */
memset(
- lba_buf + primary_gpt.header.entry_size * entries_in_last_lba,
+ lba_buf + GPT_ENTRY_SIZE * entries_in_last_lba,
0,
- (gpt_entry_per_lba_count() - entries_in_last_lba) * primary_gpt.header.entry_size);
+ (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);
if (ret != PSA_SUCCESS) {
@@ -934,6 +934,14 @@ psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_part
goto fail_load;
}
+ /* Ensure entry size is supported. */
+ if (primary_gpt.header.entry_size != GPT_ENTRY_SIZE) {
+ ERROR("Unsupported entry size 0x%08x, must be 0x%08x\n",
+ primary_gpt.header.entry_size, GPT_ENTRY_SIZE);
+ ret = PSA_ERROR_NOT_SUPPORTED;
+ goto fail_load;
+ }
+
/* Count the number of used entries, assuming the array is not sparese */
ret = count_used_partitions(&primary_gpt, &primary_gpt.num_used_partitions);
if (ret != PSA_SUCCESS) {
@@ -948,6 +956,12 @@ psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_part
if (ret != PSA_SUCCESS) {
goto fail_load;
}
+ if (backup_gpt.header.entry_size != GPT_ENTRY_SIZE) {
+ ERROR("Unsupported entry size 0x%08x, must be 0x%08x\n",
+ backup_gpt.header.entry_size, GPT_ENTRY_SIZE);
+ ret = PSA_ERROR_NOT_SUPPORTED;
+ goto fail_load;
+ }
backup_gpt_array_lba = backup_gpt.header.array_lba;
} else {
WARN("Backup GPT location is unknown!\n");
@@ -999,11 +1013,7 @@ psa_status_t gpt_uninit(void)
/* Returns the number of partition entries in each LBA */
static inline uint64_t gpt_entry_per_lba_count(void)
{
- static uint64_t num_entries = 0;
- if (num_entries == 0) {
- num_entries = TFM_GPT_BLOCK_SIZE / primary_gpt.header.entry_size;
- }
- return num_entries;
+ return TFM_GPT_BLOCK_SIZE / GPT_ENTRY_SIZE;
}
/* Copies information from the entry to the user visible structure */
@@ -1129,15 +1139,15 @@ static psa_status_t update_header(uint32_t num_partitions)
/* Take the CRC of the partition array */
uint32_t crc = 0;
for (uint32_t i = 0; i < header->num_partitions; ++i) {
- uint8_t entry_buf[header->entry_size];
- memset(entry_buf, 0, header->entry_size);
+ uint8_t entry_buf[GPT_ENTRY_SIZE];
+ memset(entry_buf, 0, GPT_ENTRY_SIZE);
struct gpt_entry_t *entry = (struct gpt_entry_t *)entry_buf;
psa_status_t ret = read_entry_from_flash(&primary_gpt, i, entry);
if (ret != PSA_SUCCESS) {
return ret;
}
- crc = efi_soft_crc32_update(crc, entry_buf, header->entry_size);
+ crc = efi_soft_crc32_update(crc, entry_buf, GPT_ENTRY_SIZE);
}
header->array_crc = crc;
@@ -1268,7 +1278,7 @@ static psa_status_t read_entry_from_flash(const struct gpt_t *table,
memcpy(
entry,
- lba_buf + ((array_index % gpt_entry_per_lba_count()) * table->header.entry_size),
+ lba_buf + ((array_index % gpt_entry_per_lba_count()) * GPT_ENTRY_SIZE),
GPT_ENTRY_SIZE);
return PSA_SUCCESS;
@@ -1415,7 +1425,7 @@ static psa_status_t write_entry(uint32_t array_index,
/* Copy into buffer */
uint32_t index_in_lba = array_index % gpt_entry_per_lba_count();
- memcpy(lba_buf + index_in_lba * primary_gpt.header.entry_size, entry, GPT_ENTRY_SIZE);
+ memcpy(lba_buf + index_in_lba * GPT_ENTRY_SIZE, entry, GPT_ENTRY_SIZE);
/* Write on every nth operation. */
if (++num_writes == gpt_entry_per_lba_count()) {
@@ -1519,18 +1529,29 @@ static psa_status_t validate_table(struct gpt_t *table, bool is_primary)
return PSA_ERROR_INVALID_SIGNATURE;
}
+ /* Check the entry size. This is not a part of the spec but ensures the
+ * library only supports entry sizes equal to 128. Otherwise, the backup
+ * could be used to restore the primary with an entry size that is different
+ * and break that assumption, or vise-versa
+ */
+ if (header->entry_size != GPT_ENTRY_SIZE) {
+ ERROR("Unsupported entry size 0x%08x, must be 0x%08x\n",
+ header->entry_size, GPT_ENTRY_SIZE);
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
/* Check the CRC of the partition array */
calc_crc = 0;
for (uint32_t i = 0; i < header->num_partitions; ++i) {
- uint8_t entry_buf[header->entry_size];
- memset(entry_buf, 0, header->entry_size);
+ uint8_t entry_buf[GPT_ENTRY_SIZE];
+ memset(entry_buf, 0, GPT_ENTRY_SIZE);
struct gpt_entry_t *entry = (struct gpt_entry_t *)entry_buf;
psa_status_t ret = read_entry_from_flash(table, i, entry);
if (ret != PSA_SUCCESS) {
return ret;
}
- calc_crc = efi_soft_crc32_update(calc_crc, (uint8_t *)entry, header->entry_size);
+ calc_crc = efi_soft_crc32_update(calc_crc, (uint8_t *)entry, GPT_ENTRY_SIZE);
}
if (calc_crc != header->array_crc) {
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
index 2f05a6b4a..db897b967 100644
--- a/lib/gpt/unittests/gpt/test_gpt.c
+++ b/lib/gpt/unittests/gpt/test_gpt.c
@@ -351,6 +351,30 @@ void test_gpt_init_should_failWhenMbrTypeInvalid(void)
TEST_ASSERT_EQUAL(PSA_ERROR_NOT_SUPPORTED, setup_test_gpt());
}
+void test_gpt_init_should_failWhenEntrySizeBad(void)
+{
+ test_header.entry_size--;
+ /* Expect first a valid MBR read */
+ register_mocked_read(&test_mbr, sizeof(test_mbr));
+
+ /* Expect a GPT header read second */
+ register_mocked_read(&test_header, sizeof(test_header));
+
+ TEST_ASSERT_EQUAL(PSA_ERROR_NOT_SUPPORTED, gpt_init(&mock_driver, TEST_MAX_PARTITIONS));
+ test_header.entry_size = default_header.entry_size;
+
+ /* Now do the backup. */
+ register_mocked_read(&test_mbr, sizeof(test_mbr));
+ register_mocked_read(&test_header, sizeof(test_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ /* Expect fourth the backup to be read. Make the entry size bad */
+ test_header.entry_size = 0;
+ setup_backup_gpt();
+
+ TEST_ASSERT_EQUAL(PSA_ERROR_NOT_SUPPORTED, gpt_init(&mock_driver, TEST_MAX_PARTITIONS));
+}
+
void test_gpt_init_should_failWhenFlashDriverNotFullyDefined(void)
{
gpt_flash_read_t read_fn = mock_driver.read;
@@ -433,6 +457,22 @@ void test_gpt_validate_should_failWhenLbaPointerBad(void)
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
}
+void test_gpt_validate_should_failWhenBackupEntrySizeInvalid(void)
+{
+ /* The entry size for the primary GPT is validated on gpt_init and kept
+ * in memory. Therefore, the entry size can only be validated on gpt_validate
+ * for the backup table, which is read
+ */
+ setup_test_gpt();
+ struct gpt_header_t backup_header;
+ MAKE_BACKUP_HEADER(backup_header, test_header);
+ backup_header.entry_size--;
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+
+ TEST_ASSERT_EQUAL(PSA_ERROR_NOT_SUPPORTED, gpt_validate(false));
+}
+
void test_gpt_validate_should_failWhenArrayCrcBad(void)
{
test_header.array_crc--;
@@ -0,0 +1,38 @@
From c07da31be4551ee9b3ce546a1f6adccb19bc3b59 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Wed, 15 Apr 2026 11:17:25 +0100
Subject: [PATCH] lib: gpt: Ensure block size complies with spec
The UEFI spec 2.10 [1] implicitly requires a block size of at least 512
in order to fit a legacy Master Boot Record.
[1] https://uefi.org/specs/UEFI/2.10/05_GUID_Partition_Table_Format.html#legacy-master-boot-record-mbr
Change-Id: I8218ef3d883b51d8fa14835e0ed884139fdebc7d
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Backport [c07da31be4551ee9b3ce546a1f6adccb19bc3b59]
---
lib/gpt/src/gpt.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
index 200e21599..cda9fe358 100644
--- a/lib/gpt/src/gpt.c
+++ b/lib/gpt/src/gpt.c
@@ -20,11 +20,15 @@
#include "efi_soft_crc.h"
/* This needs to be defined by the platform and is used by the GPT library as
- * the number of bytes in a Logical Block Address (LBA)
+ * the number of bytes in a Logical Block Address (LBA). It also must be at least
+ * 512.
*/
#ifndef TFM_GPT_BLOCK_SIZE
#error "TFM_GPT_BLOCK_SIZE must be defined if using GPT library!"
#endif
+#if TFM_GPT_BLOCK_SIZE < 512
+#error "TFM_GPT_BLOCK_SIZE must be at least 512!"
+#endif
/* Where Master Boot Record (MBR) is on flash */
#define MBR_LBA 0ULL
@@ -0,0 +1,352 @@
From bcce0ce881817b36ad52df550bcbf41b4f0d4938 Mon Sep 17 00:00:00 2001
From: Frazer Carsley <frazer.carsley@arm.com>
Date: Wed, 15 Apr 2026 15:09:03 +0100
Subject: [PATCH] lib: gpt: Expand table validation
It is implied by the UEFI spec 2.10 [1] that the backup GPT must be
located at the end of storage. To this effect, ensure that it is the
largest known LBA value.
Additionally, it is also implied that the partition entry arrays, both
primary and backup, must be before or after usable space respectively.
[1] https://uefi.org/specs/UEFI/2.10/05_GUID_Partition_Table_Format.html
Change-Id: I5042836822f990a21fbf48b19422879384a844aa
Signed-off-by: Frazer Carsley <frazer.carsley@arm.com>
Upstream-Status: Backport [bcce0ce881817b36ad52df550bcbf41b4f0d4938]
---
lib/gpt/src/gpt.c | 115 ++++++++++++++++++++
lib/gpt/unittests/gpt/test_gpt.c | 179 +++++++++++++++++++++++++++++++
2 files changed, 294 insertions(+)
diff --git a/lib/gpt/src/gpt.c b/lib/gpt/src/gpt.c
index cda9fe358..0335befa7 100644
--- a/lib/gpt/src/gpt.c
+++ b/lib/gpt/src/gpt.c
@@ -229,6 +229,12 @@ static psa_status_t mbr_load(struct mbr_t *mbr);
static bool gpt_entry_cmp_guid(const struct gpt_entry_t *entry, const void *guid);
static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const void *name);
static bool gpt_entry_cmp_type(const struct gpt_entry_t *entry, const void *type);
+static psa_status_t validate_backup_gpt_lba(const uint64_t backup_lba,
+ const uint64_t primary_lba,
+ const uint64_t partition_array_end,
+ const struct gpt_header_t *header);
+static psa_status_t validate_array_lba(const uint64_t partition_array_end,
+ const uint64_t usable_lba_start);
static psa_status_t validate_table(struct gpt_t *table, bool is_primary);
static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary);
static psa_status_t sort_partition_array(const struct gpt_t *table);
@@ -1498,6 +1504,73 @@ static inline void swap_headers(const struct gpt_header_t *src, struct gpt_heade
primary_gpt.header.array_lba);
}
+/* Validate that the backup GPT LBA is greater than all other LBAs in the header
+ */
+static psa_status_t validate_backup_gpt_lba(const uint64_t backup_lba,
+ const uint64_t primary_lba,
+ const uint64_t partition_array_end,
+ const struct gpt_header_t *header)
+{
+ if (backup_lba <= primary_lba) {
+ ERROR("Backup LBA (0x%08x%08x) must be final LBA on flash, "
+ "primary LBA at 0x%08x%08x\n",
+ (uint32_t)(backup_lba >> 32),
+ (uint32_t)(backup_lba),
+ (uint32_t)(primary_lba >> 32),
+ (uint32_t)(primary_lba));
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+ if (backup_lba <= header->first_lba) {
+ ERROR("Backup LBA (0x%08x%08x) must be final LBA on flash, "
+ "first usable LBA at 0x%08x%08x\n",
+ (uint32_t)(backup_lba >> 32),
+ (uint32_t)(backup_lba),
+ (uint32_t)(header->first_lba >> 32),
+ (uint32_t)(header->first_lba));
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+ if (backup_lba <= header->last_lba) {
+ ERROR("Backup LBA (0x%08x%08x) must be final LBA on flash, "
+ "last usable LBA at 0x%08x%08x\n",
+ (uint32_t)(backup_lba >> 32),
+ (uint32_t)(backup_lba),
+ (uint32_t)(header->last_lba >> 32),
+ (uint32_t)(header->last_lba));
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+ if (backup_lba <= partition_array_end) {
+ ERROR("Backup LBA (0x%08x%08x) must be final LBA on flash, "
+ "partition array ends at LBA at 0x%08x%08x\n",
+ (uint32_t)(backup_lba >> 32),
+ (uint32_t)(backup_lba),
+ (uint32_t)(partition_array_end >> 32),
+ (uint32_t)(partition_array_end));
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+ return PSA_SUCCESS;
+}
+
+/* Validate partition array is outside the area of usable flash */
+static psa_status_t validate_array_lba(const uint64_t partition_array_end,
+ const uint64_t usable_lba_start)
+{
+ if (partition_array_end >= usable_lba_start) {
+ ERROR("GPT partition array must not be in usable space: "
+ "0x%08x%08x >= 0x%08x%08x\n",
+ (uint32_t)(partition_array_end >> 32),
+ (uint32_t)partition_array_end,
+ (uint32_t)(usable_lba_start >> 32),
+ (uint32_t)usable_lba_start);
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+ return PSA_SUCCESS;
+}
+
/* Validates a specific GPT. */
static psa_status_t validate_table(struct gpt_t *table, bool is_primary)
{
@@ -1564,6 +1637,48 @@ static psa_status_t validate_table(struct gpt_t *table, bool is_primary)
return PSA_ERROR_INVALID_SIGNATURE;
}
+ /* Check the backup LBA is greater than all other LBAs. Check also
+ * the partition array cannot be overritten by data by ensuring
+ * that it is not between the first and last usable LBAs
+ */
+ if (is_primary) {
+ psa_status_t ret = validate_backup_gpt_lba(
+ header->backup_lba,
+ header->current_lba,
+ partition_entry_lba(table, header->num_partitions - 1),
+ header);
+ if (ret != PSA_SUCCESS) {
+ return ret;
+ }
+
+ /* Go to the final LBA of the partition array, including unused entries */
+ ret = validate_array_lba(
+ partition_entry_lba(table, header->num_partitions - 1),
+ header->first_lba);
+ if (ret != PSA_SUCCESS) {
+ return ret;
+ }
+ } else {
+ psa_status_t ret = validate_backup_gpt_lba(
+ header->current_lba,
+ header->backup_lba,
+ partition_entry_lba(table, header->num_partitions - 1),
+ header);
+ if (ret != PSA_SUCCESS) {
+ return ret;
+ }
+
+ /* To flip the condition, negate the parameters passed: it becomes
+ * -array_lba >= -last_lba (equivalent to) array_lba < last_lba
+ * (equivalent to) last_lba >= array_lba. This is because the backup array is
+ * after the last_lba
+ */
+ ret = validate_array_lba(~(header->array_lba), ~(header->last_lba));
+ if (ret != PSA_SUCCESS) {
+ return ret;
+ }
+ }
+
if (is_primary) {
/* Any time the primary table is considered valid, cache the backup
* LBA field
diff --git a/lib/gpt/unittests/gpt/test_gpt.c b/lib/gpt/unittests/gpt/test_gpt.c
index db897b967..bd161ec74 100644
--- a/lib/gpt/unittests/gpt/test_gpt.c
+++ b/lib/gpt/unittests/gpt/test_gpt.c
@@ -493,6 +493,185 @@ void test_gpt_validate_should_failWhenArrayCrcBad(void)
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
}
+void test_gpt_validate_should_failWhenBackupLbaNotAtEndOfDisk(void)
+{
+ /* First test when the backup lba is before usable disk */
+ test_header.backup_lba = test_header.first_lba - 1;
+ setup_test_gpt();
+
+ /* Each entry will be read in order to check the partition array CRC */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ /* Then test when the backup is before in usable disk space */
+ test_header.backup_lba = test_header.first_lba;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ test_header.backup_lba = test_header.first_lba + 1;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ test_header.backup_lba = test_header.last_lba - 1;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ test_header.backup_lba = test_header.last_lba;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ /* Finally, test when the backup is before the end of the partition entry array */
+ test_header.backup_lba = test_header.array_lba - 1;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ /* For this scenario, manually setup the backup header so that the array LBA
+ * (also the backup header LBA) is valid on init and can then be validated
+ * with gpt_validate
+ */
+ test_header.backup_lba = test_header.array_lba;
+
+ /* Expect first a valid MBR read */
+ register_mocked_read(&test_mbr, sizeof(test_mbr));
+
+ /* Expect a GPT header read second */
+ register_mocked_read(&test_header, sizeof(test_header));
+
+ /* Expect third each partition is read to find the number in use. This is
+ * also the backup header, which will be cached
+ */
+ setup_backup_gpt();
+
+ TEST_ASSERT_EQUAL(PSA_SUCCESS, gpt_init(&mock_driver, TEST_MAX_PARTITIONS));
+
+ /* Backup partition array read for crc */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ /* Now do the backup gpt header */
+ struct gpt_header_t backup_header;
+ test_header.backup_lba = test_header.first_lba - 1;
+ MAKE_BACKUP_HEADER(backup_header, test_header);
+
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ test_header.backup_lba = backup_header.first_lba;
+ backup_header.current_lba = backup_header.first_lba;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ test_header.backup_lba = backup_header.first_lba + 1;
+ backup_header.current_lba = backup_header.first_lba + 1;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ test_header.backup_lba = backup_header.last_lba - 1;
+ backup_header.current_lba = backup_header.last_lba - 1;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ test_header.backup_lba = backup_header.last_lba;
+ backup_header.current_lba = backup_header.last_lba;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ test_header.backup_lba = backup_header.array_lba - 1;
+ backup_header.current_lba = backup_header.array_lba - 1;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ test_header.backup_lba = backup_header.array_lba;
+ backup_header.current_lba = backup_header.array_lba;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+}
+
+void test_gpt_validate_should_failWhenPartitionArrayInUsableDiskSpace(void)
+{
+ /* First test when the primary partition array is in usable disk space */
+ test_header.array_lba = test_header.first_lba;
+ setup_test_gpt();
+
+ /* Each entry will be read in order to check the partition array CRC */
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ test_header.array_lba = test_header.first_lba + 1;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ test_header.array_lba = test_header.last_lba - 1;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ test_header.array_lba = test_header.last_lba;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ /* Then test when the primary partition array is after usable disk space */
+ test_header.array_lba = test_header.last_lba + 1;
+ setup_test_gpt();
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(true));
+
+ /* Now do the backup gpt header, ensuring it is always after usable space */
+ struct gpt_header_t backup_header;
+ MAKE_BACKUP_HEADER(backup_header, test_header);
+
+ backup_header.array_lba = test_header.first_lba - 1;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ /* Then test that the backup partition array is after usable disk space */
+ backup_header.array_lba = test_header.first_lba;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ backup_header.array_lba = test_header.first_lba + 1;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ backup_header.array_lba = test_header.last_lba - 1;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+
+ backup_header.array_lba = test_header.last_lba;
+ setup_test_gpt();
+ register_mocked_read(&backup_header, sizeof(backup_header));
+ register_mocked_read(&test_partition_array, sizeof(test_partition_array));
+ TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, gpt_validate(false));
+}
+
void test_gpt_restore_should_restorePrimaryFromBackup(void)
{
/* Start with a valid GPT */
@@ -77,6 +77,11 @@ SRC_URI:append:corstone1000 = " \
file://0043-plat-cs1k-Create-and-remove-FWU-image-partitions.patch \
file://0044-plat-cs1k-Derive-host-base-addresses-from-offsets.patch \
file://0045-plat-cs1k-Drive-NPU-via-external-system-reset-contro.patch \
file://0046-lib-gpt-Fix-final-entry-not-being-removed.patch \
file://0047-lib-gpt-Replace-warnings-with-errors.patch \
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 \
"
SRCREV_tfm-psa-adac:corstone1000 = "f2809ae231be33a1afcd7714f40756c67d846c88"