Make variable index usage robust with redundancy
If there is a single variable index if a reset happends between the
removal and rewriting of the data, the index will be lost. To solve
this issue there will be two indexes (A,B) and the new data will
always be written to the older one. This way in case of an unlucky
reset, the store will be able to keep working and only the the data
of the last write will be lost.
Change-Id: I7beaca9f3be6ac1431308e309797270134af540b
Signed-off-by: Gabor Toth <gabor.toth2@arm.com>
diff --git a/components/service/uefi/smm_variable/backend/test/variable_index_tests.cpp b/components/service/uefi/smm_variable/backend/test/variable_index_tests.cpp
index a52cfbf..cf0f6a1 100644
--- a/components/service/uefi/smm_variable/backend/test/variable_index_tests.cpp
+++ b/components/service/uefi/smm_variable/backend/test/variable_index_tests.cpp
@@ -208,7 +208,8 @@
TEST(UefiVariableIndexTests, dumpLoadRoadtrip)
{
- uint8_t buffer[MAX_VARIABLES * sizeof(struct variable_metadata)];
+ uint8_t buffer[sizeof(uint32_t) +
+ MAX_VARIABLES * (sizeof(struct variable_metadata) + sizeof(bool))];
create_variables();
@@ -222,7 +223,13 @@
CHECK_TRUE(is_dirty);
UNSIGNED_LONGS_EQUAL(EFI_SUCCESS, status);
- UNSIGNED_LONGS_EQUAL(((sizeof(struct variable_metadata) + sizeof(bool)) * 2), dump_len);
+ /*
+ * Variable index counter is at the beginning, which is followed by metadata and
+ * constraint status byte of both NV variables
+ */
+ UNSIGNED_LONGS_EQUAL(sizeof(uint32_t) +
+ ((sizeof(struct variable_metadata) + sizeof(bool)) * 2),
+ dump_len);
/* Expect no records to be dirty when the dump is repeated */
dump_len = 0;
@@ -231,7 +238,9 @@
UNSIGNED_LONGS_EQUAL(EFI_SUCCESS, status);
CHECK_FALSE(is_dirty);
- UNSIGNED_LONGS_EQUAL(((sizeof(struct variable_metadata) + sizeof(bool)) * 2), dump_len);
+ UNSIGNED_LONGS_EQUAL(sizeof(uint32_t) +
+ ((sizeof(struct variable_metadata) + sizeof(bool)) * 2),
+ dump_len);
/* Tear down and reinitialize to simulate a reboot */
variable_index_deinit(&m_variable_index);
@@ -270,7 +279,8 @@
TEST(UefiVariableIndexTests, dumpLoadConstrainedVariable)
{
- uint8_t buffer[MAX_VARIABLES * sizeof(struct variable_metadata)];
+ uint8_t buffer[sizeof(uint32_t) +
+ MAX_VARIABLES * (sizeof(struct variable_metadata) + sizeof(bool))];
create_variables();
@@ -304,8 +314,13 @@
UNSIGNED_LONGS_EQUAL(EFI_SUCCESS, status);
CHECK_TRUE(is_dirty);
- /* metadata and constraint status byte are stored for both NV variables, but only one of them has constraints */
- UNSIGNED_LONGS_EQUAL((sizeof(struct variable_metadata) + sizeof(bool)) * 2 +
+ /*
+ * Variable index counter is at the beginning, which is followed by metadata and
+ * constraint status byte of both NV variables, but only one of them has
+ * constraints
+ */
+ UNSIGNED_LONGS_EQUAL(sizeof(uint32_t) +
+ (sizeof(struct variable_metadata) + sizeof(bool)) * 2 +
sizeof(struct variable_constraints),
dump_len);
@@ -316,7 +331,11 @@
TEST(UefiVariableIndexTests, dumpBufferTooSmall)
{
- uint8_t buffer[1 * sizeof(struct variable_metadata) + 1];
+ /*
+ * Enough to fit the variable index counter and the metadata and constraint
+ * status of a single variable
+ */
+ uint8_t buffer[sizeof(uint32_t) + sizeof(struct variable_metadata) + sizeof(bool)];
create_variables();
@@ -338,7 +357,8 @@
TEST(UefiVariableIndexTests, removeVariable)
{
- uint8_t buffer[MAX_VARIABLES * sizeof(struct variable_metadata)];
+ uint8_t buffer[sizeof(uint32_t) +
+ MAX_VARIABLES * (sizeof(struct variable_metadata) + sizeof(bool))];
struct variable_info *info = NULL;
create_variables();
@@ -358,7 +378,12 @@
CHECK_TRUE(is_dirty);
UNSIGNED_LONGS_EQUAL(EFI_SUCCESS, status);
- UNSIGNED_LONGS_EQUAL(sizeof(struct variable_metadata) + sizeof(bool), dump_len);
+ /*
+ * Dump to now contains the variable index counter and metadata,
+ * constraint status data of a variable
+ */
+ UNSIGNED_LONGS_EQUAL(sizeof(uint32_t) + sizeof(struct variable_metadata) + sizeof(bool),
+ dump_len);
/* Remove the volatile variable */
info = variable_index_find(&m_variable_index, &guid_1, string_get_size_in_bytes(name_1),
@@ -373,7 +398,8 @@
CHECK_FALSE(is_dirty);
UNSIGNED_LONGS_EQUAL(EFI_SUCCESS, status);
- UNSIGNED_LONGS_EQUAL(sizeof(struct variable_metadata) + sizeof(bool), dump_len);
+ UNSIGNED_LONGS_EQUAL(sizeof(uint32_t) + sizeof(struct variable_metadata) + sizeof(bool),
+ dump_len);
/* Remove the remaining NV variable */
info = variable_index_find(&m_variable_index, &guid_1, string_get_size_in_bytes(name_3),
@@ -381,14 +407,14 @@
variable_index_clear_variable(&m_variable_index, info);
- /* Expect index to be dirty and dump to now be empty */
+ /* Expect index to be dirty and dump to now contains only the variable index counter */
dump_len = 0;
status = variable_index_dump(&m_variable_index, sizeof(buffer), buffer, &dump_len,
&is_dirty);
CHECK_TRUE(is_dirty);
UNSIGNED_LONGS_EQUAL(EFI_SUCCESS, status);
- UNSIGNED_LONGS_EQUAL(0, dump_len);
+ UNSIGNED_LONGS_EQUAL(sizeof(uint32_t), dump_len);
/* Enumerate and now expect an empty index */
info = NULL;
diff --git a/components/service/uefi/smm_variable/backend/test/variable_store_tests.cpp b/components/service/uefi/smm_variable/backend/test/variable_store_tests.cpp
index d684069..c811d9e 100644
--- a/components/service/uefi/smm_variable/backend/test/variable_store_tests.cpp
+++ b/components/service/uefi/smm_variable/backend/test/variable_store_tests.cpp
@@ -5,6 +5,7 @@
*/
#include <CppUTest/TestHarness.h>
+#include <limits>
#include <service/secure_storage/backend/mock_store/mock_store.h>
#include <service/uefi/smm_variable/backend/uefi_variable_store.h>
#include <string.h>
@@ -269,9 +270,17 @@
static const size_t MAX_VARIABLES = 5;
static const size_t MAX_VARIABLE_SIZE = 3000;
static const size_t STORE_CAPACITY = MAX_VARIABLES * MAX_VARIABLE_SIZE;
+ static const size_t VARIABLE_INDEX_MAX_SIZE =
+ sizeof(uint32_t) +
+ MAX_VARIABLES * (sizeof(struct variable_metadata) +
+ sizeof(struct variable_constraints) + sizeof(bool));
static const uint32_t OWNER_ID = 100;
+ /* Synchronize these with the variables with the store */
+ uint64_t DEFAULT_VARIABLE_INDEX_STORAGE_A_UID = 1;
+ uint64_t DEFAULT_VARIABLE_INDEX_STORAGE_B_UID = 2;
+
/*
* Make sure the variable buffer in the test is way above the limit
* so the buffer problems will be handled by the component
@@ -864,3 +873,159 @@
LONGS_EQUAL(0, input_data.compare(output_data));
}
}
+
+TEST(UefiVariableStoreTests, variableIndexCounterOverflow)
+{
+ efi_status_t efi_status = EFI_SUCCESS;
+ psa_status_t psa_status = PSA_SUCCESS;
+ std::u16string var_name = u"var";
+ std::string input_data = "a";
+ uint32_t attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+ /* There are no variables set in the index, only the counter is there */
+ uint8_t buffer[sizeof(uint32_t)] = { 0 };
+
+ mock_store_reset(&m_persistent_store);
+
+ /* Counter of index A is 0 */
+ psa_status = m_persistent_store.backend.interface->set(
+ m_persistent_store.backend.context, OWNER_ID, DEFAULT_VARIABLE_INDEX_STORAGE_A_UID,
+ sizeof(buffer), &buffer, PSA_STORAGE_FLAG_NONE);
+ UNSIGNED_LONGLONGS_EQUAL(PSA_SUCCESS, psa_status);
+
+ /* Set max counter value */
+ buffer[0] = 0xFF;
+ buffer[1] = 0xFF;
+ buffer[2] = 0xFF;
+ buffer[3] = 0xFF;
+
+ /* Counter of index B is max value */
+ psa_status = m_persistent_store.backend.interface->set(
+ m_persistent_store.backend.context, OWNER_ID, DEFAULT_VARIABLE_INDEX_STORAGE_B_UID,
+ sizeof(buffer), &buffer, PSA_STORAGE_FLAG_NONE);
+ UNSIGNED_LONGLONGS_EQUAL(PSA_SUCCESS, psa_status);
+
+ /* At next initialization of the store index A should be the latest index with counter value 0 */
+ uefi_variable_store_deinit(&m_uefi_variable_store);
+
+ efi_status = uefi_variable_store_init(&m_uefi_variable_store, OWNER_ID, MAX_VARIABLES,
+ m_persistent_backend, m_volatile_backend);
+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
+
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.active_variable_index_uid,
+ DEFAULT_VARIABLE_INDEX_STORAGE_A_UID);
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.variable_index.counter, 0);
+
+ /* After setting a variable to trigger sync and rebooting index B should be the latest index with counter value 1*/
+ efi_status = set_variable(var_name, input_data, attributes);
+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
+
+ power_cycle();
+
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.active_variable_index_uid,
+ DEFAULT_VARIABLE_INDEX_STORAGE_B_UID);
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.variable_index.counter, 1);
+}
+
+TEST(UefiVariableStoreTests, oneEmptyVariableIndexExists)
+{
+ psa_status_t status = PSA_SUCCESS;
+
+ /* Only, variable index A exists, but it is empty */
+ mock_store_reset(&m_persistent_store);
+
+ status = m_persistent_store.backend.interface->create(m_persistent_store.backend.context,
+ OWNER_ID,
+ DEFAULT_VARIABLE_INDEX_STORAGE_A_UID,
+ 100, PSA_STORAGE_FLAG_NONE);
+ UNSIGNED_LONGLONGS_EQUAL(PSA_SUCCESS, status);
+
+ power_cycle();
+
+ /* Empty index is considered non-existing so default index (A) is selected */
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.active_variable_index_uid,
+ DEFAULT_VARIABLE_INDEX_STORAGE_A_UID);
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.variable_index.counter, 0);
+
+ /* Only, variable index B exists, but it is empty*/
+ mock_store_reset(&m_persistent_store);
+
+ status = m_persistent_store.backend.interface->create(m_persistent_store.backend.context,
+ OWNER_ID,
+ DEFAULT_VARIABLE_INDEX_STORAGE_B_UID,
+ 100, PSA_STORAGE_FLAG_NONE);
+ UNSIGNED_LONGLONGS_EQUAL(PSA_SUCCESS, status);
+
+ power_cycle();
+
+ /* Empty index is considered non-existing so default index (A) is selected */
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.active_variable_index_uid,
+ DEFAULT_VARIABLE_INDEX_STORAGE_A_UID);
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.variable_index.counter, 0);
+}
+
+TEST(UefiVariableStoreTests, oneVariableIndexAlreadySet)
+{
+ efi_status_t status = EFI_SUCCESS;
+ /* Empty variable index with zero counter value */
+ uint8_t buffer[VARIABLE_INDEX_MAX_SIZE] = { 0 };
+
+ /* Set index A in the store with some data, so it will be found as the currently active index */
+ mock_store_reset(&m_persistent_store);
+
+ status = m_persistent_store.backend.interface->set(
+ m_persistent_store.backend.context, OWNER_ID, DEFAULT_VARIABLE_INDEX_STORAGE_A_UID,
+ sizeof(buffer), &buffer, PSA_STORAGE_FLAG_NONE);
+ UNSIGNED_LONGLONGS_EQUAL(PSA_SUCCESS, status);
+
+ power_cycle();
+
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.active_variable_index_uid,
+ DEFAULT_VARIABLE_INDEX_STORAGE_A_UID);
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.variable_index.counter, 0);
+
+ /* Set index B in the store with some data, so it will be found as the currently active index */
+ mock_store_reset(&m_persistent_store);
+
+ status = m_persistent_store.backend.interface->set(
+ m_persistent_store.backend.context, OWNER_ID, DEFAULT_VARIABLE_INDEX_STORAGE_B_UID,
+ sizeof(buffer), &buffer, PSA_STORAGE_FLAG_NONE);
+ UNSIGNED_LONGLONGS_EQUAL(PSA_SUCCESS, status);
+
+ power_cycle();
+
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.active_variable_index_uid,
+ DEFAULT_VARIABLE_INDEX_STORAGE_B_UID);
+ UNSIGNED_LONGLONGS_EQUAL(m_uefi_variable_store.variable_index.counter, 0);
+}
+
+TEST(UefiVariableStoreTests, variableIndexesWithSameData)
+{
+ psa_status_t psa_status = PSA_SUCCESS;
+ efi_status_t efi_status = EFI_SUCCESS;
+ /* Empty variable index with zero counter value */
+ uint8_t buffer[VARIABLE_INDEX_MAX_SIZE] = { 0 };
+
+ /* Set both indexes to the same data and counter value */
+ mock_store_reset(&m_persistent_store);
+
+ psa_status = m_persistent_store.backend.interface->set(
+ m_persistent_store.backend.context, OWNER_ID, DEFAULT_VARIABLE_INDEX_STORAGE_A_UID,
+ sizeof(buffer), &buffer, PSA_STORAGE_FLAG_NONE);
+ UNSIGNED_LONGLONGS_EQUAL(PSA_SUCCESS, psa_status);
+
+ psa_status = m_persistent_store.backend.interface->set(
+ m_persistent_store.backend.context, OWNER_ID, DEFAULT_VARIABLE_INDEX_STORAGE_B_UID,
+ sizeof(buffer), &buffer, PSA_STORAGE_FLAG_NONE);
+ UNSIGNED_LONGLONGS_EQUAL(PSA_SUCCESS, psa_status);
+
+ /*
+ * Initializing the store should fail, because if there are two indexes with the same counter it cannot be decided
+ * which has the valid data.
+ */
+ uefi_variable_store_deinit(&m_uefi_variable_store);
+
+ efi_status = uefi_variable_store_init(&m_uefi_variable_store, OWNER_ID, MAX_VARIABLES,
+ m_persistent_backend, m_volatile_backend);
+ UNSIGNED_LONGLONGS_EQUAL(EFI_LOAD_ERROR, efi_status);
+}
diff --git a/components/service/uefi/smm_variable/backend/uefi_variable_store.c b/components/service/uefi/smm_variable/backend/uefi_variable_store.c
index 0d89156..42dd575 100644
--- a/components/service/uefi/smm_variable/backend/uefi_variable_store.c
+++ b/components/service/uefi/smm_variable/backend/uefi_variable_store.c
@@ -28,9 +28,12 @@
#include "service/crypto/client/psa/crypto_client.h"
#endif
+static psa_status_t get_active_variable_uid(struct uefi_variable_store *context,
+ uint64_t *active_index_uid, uint32_t *counter);
+
static efi_status_t load_variable_index(struct uefi_variable_store *context);
-static efi_status_t sync_variable_index(const struct uefi_variable_store *context);
+static efi_status_t sync_variable_index(struct uefi_variable_store *context);
static efi_status_t check_capabilities(const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var);
@@ -137,10 +140,18 @@
#endif
/* Private UID for storing the variable index - may be overridden at build-time */
-#ifndef SMM_VARIABLE_INDEX_STORAGE_UID
-#define SMM_VARIABLE_INDEX_STORAGE_UID (1)
+#ifndef SMM_VARIABLE_INDEX_STORAGE_A_UID
+#define SMM_VARIABLE_INDEX_STORAGE_A_UID (1)
#endif
+#ifndef SMM_VARIABLE_INDEX_STORAGE_B_UID
+#define SMM_VARIABLE_INDEX_STORAGE_B_UID (2)
+#endif
+
+_Static_assert(SMM_VARIABLE_INDEX_STORAGE_A_UID != SMM_VARIABLE_INDEX_STORAGE_B_UID,
+ "SMM_VARIABLE_INDEX_STORAGE_A_UID must not be the same value as "
+ "SMM_VARIABLE_INDEX_STORAGE_B_UID");
+
/* Default maximum variable size -
* may be overridden using uefi_variable_store_set_storage_limits()
*/
@@ -393,7 +404,7 @@
* index entry.
*/
if (should_sync_index)
- status = sync_variable_index(context);
+ status = sync_variable_index((struct uefi_variable_store *)context);
/* Store any variable data to the storage backend with the updated metadata */
if (info->is_variable_set && (status == EFI_SUCCESS)) {
@@ -615,40 +626,148 @@
return status;
}
+/* Checks which index contains the latest data, which shall be loaded */
+static psa_status_t get_active_variable_uid(struct uefi_variable_store *context,
+ uint64_t *active_index_uid, uint32_t *counter)
+{
+ uint32_t counter_A = 0;
+ uint32_t counter_B = 0;
+ size_t data_len = 0;
+ psa_status_t psa_status_A = PSA_SUCCESS;
+ psa_status_t psa_status_B = PSA_SUCCESS;
+ struct storage_backend *persistent_store = context->persistent_store.storage_backend;
+
+ /* Set default value for the case when the index does not exist yet */
+ *active_index_uid = SMM_VARIABLE_INDEX_STORAGE_A_UID;
+ *counter = 0;
+
+ if (persistent_store) {
+ psa_status_A = persistent_store->interface->get(persistent_store->context,
+ context->owner_id,
+ SMM_VARIABLE_INDEX_STORAGE_A_UID, 0,
+ sizeof(counter_A), &counter_A,
+ &data_len);
+
+ if (psa_status_A == PSA_SUCCESS && data_len == 0) {
+ psa_status_A = persistent_store->interface->remove(
+ persistent_store->context, context->owner_id,
+ SMM_VARIABLE_INDEX_STORAGE_A_UID);
+
+ if (psa_status_A == PSA_SUCCESS)
+ psa_status_A = PSA_ERROR_DOES_NOT_EXIST;
+ else {
+ EMSG("Erronous state of variable index");
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ }
+
+ psa_status_B = persistent_store->interface->get(persistent_store->context,
+ context->owner_id,
+ SMM_VARIABLE_INDEX_STORAGE_B_UID, 0,
+ sizeof(counter_B), &counter_B,
+ &data_len);
+
+ if (psa_status_B == PSA_SUCCESS && data_len == 0) {
+ psa_status_B = persistent_store->interface->remove(
+ persistent_store->context, context->owner_id,
+ SMM_VARIABLE_INDEX_STORAGE_B_UID);
+
+ if (psa_status_B == PSA_SUCCESS)
+ psa_status_B = PSA_ERROR_DOES_NOT_EXIST;
+ else {
+ EMSG("Erronous state of variable index");
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ }
+
+ if ((psa_status_A != PSA_SUCCESS && psa_status_A != PSA_ERROR_DOES_NOT_EXIST) ||
+ (psa_status_B != PSA_SUCCESS && psa_status_B != PSA_ERROR_DOES_NOT_EXIST))
+ return PSA_ERROR_STORAGE_FAILURE;
+
+ if (psa_status_A == PSA_ERROR_DOES_NOT_EXIST) {
+ if (psa_status_B == PSA_ERROR_DOES_NOT_EXIST)
+ return PSA_ERROR_DOES_NOT_EXIST;
+
+ *active_index_uid = SMM_VARIABLE_INDEX_STORAGE_B_UID;
+ *counter = counter_B;
+
+ return PSA_SUCCESS;
+ } else if (psa_status_B == PSA_ERROR_DOES_NOT_EXIST) {
+ *active_index_uid = SMM_VARIABLE_INDEX_STORAGE_A_UID;
+ *counter = counter_A;
+
+ return PSA_SUCCESS;
+ }
+
+ if (counter_A + 1 == counter_B) {
+ *active_index_uid = SMM_VARIABLE_INDEX_STORAGE_B_UID;
+ *counter = counter_B;
+ return PSA_SUCCESS;
+ } else if (counter_B + 1 == counter_A) {
+ *active_index_uid = SMM_VARIABLE_INDEX_STORAGE_A_UID;
+ *counter = counter_A;
+ return PSA_SUCCESS;
+ } else {
+ EMSG("UEFI metadata variable index is invalid.");
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ } else {
+ EMSG("Store backend is not accessible");
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+ return PSA_ERROR_STORAGE_FAILURE;
+}
+
static efi_status_t load_variable_index(struct uefi_variable_store *context)
{
struct storage_backend *persistent_store = context->persistent_store.storage_backend;
+ psa_status_t psa_status = PSA_SUCCESS;
if (persistent_store) {
size_t data_len = 0;
size_t data_offset = 0;
+ struct psa_storage_info_t variable_index_info = { 0 };
+
+ psa_status = get_active_variable_uid(context, &context->active_variable_index_uid,
+ &context->variable_index.counter);
+ switch (psa_status) {
+ case PSA_SUCCESS:
+ break;
+
+ case PSA_ERROR_DOES_NOT_EXIST:
+ IMSG("Variable index does not exist in NV store, continuing with empty index");
+ return EFI_SUCCESS;
+
+ default:
+ EMSG("Loading variable index failed: %d", psa_status);
+ return EFI_LOAD_ERROR;
+ }
+
+ /* Make sure the variable index fits the buffer */
+ persistent_store->interface->get_info(persistent_store->context, context->owner_id,
+ context->active_variable_index_uid,
+ &variable_index_info);
+
+ if (variable_index_info.size > context->index_sync_buffer_size) {
+ EMSG("Variable index cannot fit the sync buffer");
+ return EFI_LOAD_ERROR;
+ }
do {
- psa_status_t psa_status = persistent_store->interface->get(
+ psa_status = persistent_store->interface->get(
persistent_store->context, context->owner_id,
- SMM_VARIABLE_INDEX_STORAGE_UID, data_offset,
+ context->active_variable_index_uid, data_offset,
RPC_CALLER_SESSION_SHARED_MEMORY_SIZE,
context->index_sync_buffer + data_offset, &data_len);
- switch (psa_status) {
- case PSA_SUCCESS:
- data_offset += data_len;
-
- if (data_offset > context->index_sync_buffer_size) {
- EMSG("Variable index cannot fit the sync buffer");
- return EFI_LOAD_ERROR;
- }
-
- break;
-
- case PSA_ERROR_DOES_NOT_EXIST:
- IMSG("Index variable does not exist in NV store, continuing with empty index");
- return EFI_SUCCESS;
-
- default:
+ if (psa_status != PSA_SUCCESS) {
EMSG("Loading variable index failed: %d", psa_status);
return EFI_LOAD_ERROR;
}
+
+ data_offset += data_len;
+
} while (data_len == RPC_CALLER_SESSION_SHARED_MEMORY_SIZE);
variable_index_restore(&context->variable_index, data_offset,
@@ -661,7 +780,7 @@
return EFI_SUCCESS;
}
-static efi_status_t sync_variable_index(const struct uefi_variable_store *context)
+static efi_status_t sync_variable_index(struct uefi_variable_store *context)
{
efi_status_t status = EFI_SUCCESS;
psa_status_t psa_status = PSA_SUCCESS;
@@ -681,19 +800,24 @@
if (persistent_store) {
size_t data_offset = 0;
+ uint64_t next_index_uid = 0;
+
+ /* Write the older one */
+ next_index_uid = (context->active_variable_index_uid ==
+ SMM_VARIABLE_INDEX_STORAGE_A_UID ?
+ SMM_VARIABLE_INDEX_STORAGE_B_UID :
+ SMM_VARIABLE_INDEX_STORAGE_A_UID);
psa_status = persistent_store->interface->remove(
- persistent_store->context, context->owner_id,
- SMM_VARIABLE_INDEX_STORAGE_UID);
+ persistent_store->context, context->owner_id, next_index_uid);
if (psa_status != PSA_SUCCESS && psa_status != PSA_ERROR_DOES_NOT_EXIST)
goto end;
/* Check if the index exists and create if not yet */
psa_status = persistent_store->interface->create(
- persistent_store->context, context->owner_id,
- SMM_VARIABLE_INDEX_STORAGE_UID, remaining_data_len,
- PSA_STORAGE_FLAG_NONE);
+ persistent_store->context, context->owner_id, next_index_uid,
+ remaining_data_len, PSA_STORAGE_FLAG_NONE);
if (psa_status != PSA_SUCCESS)
goto end;
@@ -704,8 +828,7 @@
psa_status = persistent_store->interface->set_extended(
persistent_store->context, context->owner_id,
- SMM_VARIABLE_INDEX_STORAGE_UID, data_offset,
- data_of_this_iteration,
+ next_index_uid, data_offset, data_of_this_iteration,
context->index_sync_buffer + data_offset);
if (psa_status != PSA_SUCCESS)
@@ -1717,7 +1840,7 @@
}
if (any_orphans)
- sync_variable_index(context);
+ sync_variable_index((struct uefi_variable_store *)context);
}
static struct delegate_variable_store *
diff --git a/components/service/uefi/smm_variable/backend/uefi_variable_store.h b/components/service/uefi/smm_variable/backend/uefi_variable_store.h
index 2493ff6..9f2c4a0 100644
--- a/components/service/uefi/smm_variable/backend/uefi_variable_store.h
+++ b/components/service/uefi/smm_variable/backend/uefi_variable_store.h
@@ -52,6 +52,7 @@
uint32_t owner_id;
uint8_t *index_sync_buffer;
size_t index_sync_buffer_size;
+ uint64_t active_variable_index_uid;
struct variable_index variable_index;
struct delegate_variable_store persistent_store;
struct delegate_variable_store volatile_store;
diff --git a/components/service/uefi/smm_variable/backend/variable_index.c b/components/service/uefi/smm_variable/backend/variable_index.c
index 0fbb091..c521a19 100644
--- a/components/service/uefi/smm_variable/backend/variable_index.c
+++ b/components/service/uefi/smm_variable/backend/variable_index.c
@@ -95,6 +95,7 @@
efi_status_t variable_index_init(struct variable_index *context, size_t max_variables)
{
context->max_variables = max_variables;
+ context->counter = 0;
context->entries =
(struct variable_entry *)malloc(sizeof(struct variable_entry) * max_variables);
@@ -112,9 +113,9 @@
size_t variable_index_max_dump_size(struct variable_index *context)
{
- return (sizeof(struct variable_metadata) + sizeof(bool) +
- sizeof(struct variable_constraints)) *
- context->max_variables;
+ return sizeof(context->counter) + (sizeof(struct variable_metadata) + sizeof(bool) +
+ sizeof(struct variable_constraints)) *
+ context->max_variables;
}
struct variable_info *variable_index_find(const struct variable_index *context,
@@ -288,6 +289,16 @@
*data_len = 0;
*any_dirty = false;
+ /*
+ * Intentionally letting the counter overflow.
+ * The buffer (index_sync_buffer) is provided by malloc, which allocates memory to a boundary
+ * suitable for any default data type of the system (e.g uint32_t)
+ */
+ *((uint32_t *)dump_pos) = context->counter + 1;
+ bytes_dumped += sizeof(context->counter);
+ dump_pos += sizeof(context->counter);
+
+ /* Store variables */
for (size_t pos = 0; pos < context->max_variables; pos++) {
struct variable_entry *entry = &context->entries[pos];
struct variable_metadata *metadata = &entry->info.metadata;
@@ -334,13 +345,19 @@
return EFI_SUCCESS;
}
-size_t variable_index_restore(const struct variable_index *context, size_t data_len,
+size_t variable_index_restore(struct variable_index *context, size_t data_len,
const uint8_t *buffer)
{
size_t bytes_loaded = 0;
const uint8_t *load_pos = buffer;
int pos = 0;
+ if (data_len >= sizeof(context->counter)) {
+ context->counter = *((uint32_t *)load_pos);
+ bytes_loaded += sizeof(context->counter);
+ load_pos += sizeof(context->counter);
+ }
+
while (bytes_loaded < data_len) {
struct variable_entry *entry = &context->entries[pos];
diff --git a/components/service/uefi/smm_variable/backend/variable_index.h b/components/service/uefi/smm_variable/backend/variable_index.h
index e2717d6..720cbed 100644
--- a/components/service/uefi/smm_variable/backend/variable_index.h
+++ b/components/service/uefi/smm_variable/backend/variable_index.h
@@ -75,6 +75,7 @@
*/
struct variable_index {
size_t max_variables;
+ uint32_t counter;
struct variable_entry *entries;
};
@@ -220,7 +221,7 @@
*
* @return Number of bytes loaded
*/
-size_t variable_index_restore(const struct variable_index *context, size_t data_len,
+size_t variable_index_restore(struct variable_index *context, size_t data_len,
const uint8_t *buffer);
#ifdef __cplusplus