| From c4eaf83548eed4ed6194ff9e1368d6ae65f4ebf9 Mon Sep 17 00:00:00 2001 |
| From: Julian Hall <julian.hall@arm.com> |
| Date: Thu, 2 Dec 2021 17:27:55 +0000 |
| Subject: [PATCH] Add UEFI variable support for QueryVariableInfo |
| |
| Adds support for the UEFI QueryVariableInfo operation. The total |
| store size currently relies on pre-configured values, set for a |
| particular deployment. Ideally, this information would be read |
| from the storage backend. This facility is not however yet |
| supported by the storage backend interface or by any PSA |
| storage backend storage providers. |
| |
| Signed-off-by: Julian Hall <julian.hall@arm.com> |
| Change-Id: I971252831f7e478914d736c672d184a371e64502 |
| |
| Upstream-Status: Pending [Not submitted to upstream yet] |
| Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> |
| |
| |
| --- |
| .../backend/test/variable_store_tests.cpp | 89 +++++++- |
| .../backend/uefi_variable_store.c | 213 ++++++++++++++---- |
| .../backend/uefi_variable_store.h | 39 +++- |
| .../client/cpp/smm_variable_client.cpp | 66 ++++++ |
| .../client/cpp/smm_variable_client.h | 7 + |
| .../provider/smm_variable_provider.c | 31 ++- |
| .../service/smm_variable_service_tests.cpp | 55 ++++- |
| 7 files changed, 445 insertions(+), 55 deletions(-) |
| |
| diff --git a/components/service/smm_variable/backend/test/variable_store_tests.cpp b/components/service/smm_variable/backend/test/variable_store_tests.cpp |
| index 578f118f..e90c1067 100644 |
| --- a/components/service/smm_variable/backend/test/variable_store_tests.cpp |
| +++ b/components/service/smm_variable/backend/test/variable_store_tests.cpp |
| @@ -27,6 +27,18 @@ TEST_GROUP(UefiVariableStoreTests) |
| |
| UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status); |
| |
| + uefi_variable_store_set_storage_limits( |
| + &m_uefi_variable_store, |
| + EFI_VARIABLE_NON_VOLATILE, |
| + STORE_CAPACITY, |
| + MAX_VARIABLE_SIZE); |
| + |
| + uefi_variable_store_set_storage_limits( |
| + &m_uefi_variable_store, |
| + 0, |
| + STORE_CAPACITY, |
| + MAX_VARIABLE_SIZE); |
| + |
| setup_common_guid(); |
| } |
| |
| @@ -152,6 +164,33 @@ TEST_GROUP(UefiVariableStoreTests) |
| return status; |
| } |
| |
| + efi_status_t query_variable_info( |
| + uint32_t attributes, |
| + size_t *max_variable_storage_size, |
| + size_t *remaining_variable_storage_size, |
| + size_t *max_variable_size) |
| + { |
| + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO query; |
| + |
| + query.MaximumVariableStorageSize = 0; |
| + query.RemainingVariableStorageSize = 0; |
| + query.MaximumVariableSize = 0; |
| + query.Attributes = attributes; |
| + |
| + efi_status_t status = uefi_variable_store_query_variable_info( |
| + &m_uefi_variable_store, |
| + &query); |
| + |
| + if (status == EFI_SUCCESS) { |
| + |
| + *max_variable_storage_size = query.MaximumVariableStorageSize; |
| + *remaining_variable_storage_size = query.RemainingVariableStorageSize; |
| + *max_variable_size = query.MaximumVariableSize; |
| + } |
| + |
| + return status; |
| + } |
| + |
| efi_status_t set_check_var_property( |
| const std::wstring &name, |
| const VAR_CHECK_VARIABLE_PROPERTY &check_property) |
| @@ -195,7 +234,8 @@ TEST_GROUP(UefiVariableStoreTests) |
| |
| if (info && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE)) { |
| |
| - struct storage_backend *storage_backend = m_uefi_variable_store.persistent_store; |
| + struct storage_backend *storage_backend = |
| + m_uefi_variable_store.persistent_store.storage_backend; |
| |
| storage_backend->interface->remove( |
| storage_backend->context, |
| @@ -220,9 +260,24 @@ TEST_GROUP(UefiVariableStoreTests) |
| m_volatile_backend); |
| |
| UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status); |
| + |
| + uefi_variable_store_set_storage_limits( |
| + &m_uefi_variable_store, |
| + EFI_VARIABLE_NON_VOLATILE, |
| + STORE_CAPACITY, |
| + MAX_VARIABLE_SIZE); |
| + |
| + uefi_variable_store_set_storage_limits( |
| + &m_uefi_variable_store, |
| + 0, |
| + STORE_CAPACITY, |
| + MAX_VARIABLE_SIZE); |
| } |
| |
| static const size_t MAX_VARIABLES = 10; |
| + static const size_t MAX_VARIABLE_SIZE = 100; |
| + static const size_t STORE_CAPACITY = 1000; |
| + |
| static const uint32_t OWNER_ID = 100; |
| static const size_t VARIABLE_BUFFER_SIZE = 1024; |
| |
| @@ -265,6 +320,22 @@ TEST(UefiVariableStoreTests, setGetRoundtrip) |
| /* Expect the append write operation to have extended the variable */ |
| UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size()); |
| LONGS_EQUAL(0, expected_output.compare(output_data)); |
| + |
| + /* Expect query_variable_info to return consistent values */ |
| + size_t max_variable_storage_size = 0; |
| + size_t remaining_variable_storage_size = 0; |
| + size_t max_variable_size = 0; |
| + |
| + status = query_variable_info( |
| + 0, |
| + &max_variable_storage_size, |
| + &remaining_variable_storage_size, |
| + &max_variable_size); |
| + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status); |
| + |
| + UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY, max_variable_storage_size); |
| + UNSIGNED_LONGLONGS_EQUAL(MAX_VARIABLE_SIZE, max_variable_size); |
| + UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY - expected_output.size(), remaining_variable_storage_size); |
| } |
| |
| TEST(UefiVariableStoreTests, persistentSetGet) |
| @@ -311,6 +382,22 @@ TEST(UefiVariableStoreTests, persistentSetGet) |
| /* Still expect got variable data to be the same as the set value */ |
| UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size()); |
| LONGS_EQUAL(0, expected_output.compare(output_data)); |
| + |
| + /* Expect query_variable_info to return consistent values */ |
| + size_t max_variable_storage_size = 0; |
| + size_t remaining_variable_storage_size = 0; |
| + size_t max_variable_size = 0; |
| + |
| + status = query_variable_info( |
| + EFI_VARIABLE_NON_VOLATILE, |
| + &max_variable_storage_size, |
| + &remaining_variable_storage_size, |
| + &max_variable_size); |
| + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status); |
| + |
| + UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY, max_variable_storage_size); |
| + UNSIGNED_LONGLONGS_EQUAL(MAX_VARIABLE_SIZE, max_variable_size); |
| + UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY - expected_output.size(), remaining_variable_storage_size); |
| } |
| |
| TEST(UefiVariableStoreTests, removeVolatile) |
| diff --git a/components/service/smm_variable/backend/uefi_variable_store.c b/components/service/smm_variable/backend/uefi_variable_store.c |
| index bcb85995..ed50eaf9 100644 |
| --- a/components/service/smm_variable/backend/uefi_variable_store.c |
| +++ b/components/service/smm_variable/backend/uefi_variable_store.c |
| @@ -46,8 +46,15 @@ static efi_status_t load_variable_data( |
| SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var, |
| size_t max_data_len); |
| |
| -static psa_status_t append_write( |
| - struct storage_backend *storage_backend, |
| +static psa_status_t store_overwrite( |
| + struct delegate_variable_store *delegate_store, |
| + uint32_t client_id, |
| + uint64_t uid, |
| + size_t data_length, |
| + const void *data); |
| + |
| +static psa_status_t store_append_write( |
| + struct delegate_variable_store *delegate_store, |
| uint32_t client_id, |
| uint64_t uid, |
| size_t data_length, |
| @@ -56,6 +63,15 @@ static psa_status_t append_write( |
| static void purge_orphan_index_entries( |
| struct uefi_variable_store *context); |
| |
| +static struct delegate_variable_store *select_delegate_store( |
| + struct uefi_variable_store *context, |
| + uint32_t attributes); |
| + |
| +static size_t space_used( |
| + struct uefi_variable_store *context, |
| + uint32_t attributes, |
| + struct storage_backend *storage_backend); |
| + |
| static efi_status_t psa_to_efi_storage_status( |
| psa_status_t psa_status); |
| |
| @@ -66,6 +82,10 @@ static efi_status_t check_name_terminator( |
| /* Private UID for storing the variable index */ |
| #define VARIABLE_INDEX_STORAGE_UID (1) |
| |
| +/* Default maximum variable size - |
| + * may be overridden using uefi_variable_store_set_storage_limits() |
| + */ |
| +#define DEFAULT_MAX_VARIABLE_SIZE (2048) |
| |
| efi_status_t uefi_variable_store_init( |
| struct uefi_variable_store *context, |
| @@ -76,8 +96,17 @@ efi_status_t uefi_variable_store_init( |
| { |
| efi_status_t status = EFI_SUCCESS; |
| |
| - context->persistent_store = persistent_store; |
| - context->volatile_store = volatile_store; |
| + /* Initialise persistent store defaults */ |
| + context->persistent_store.is_nv = true; |
| + context->persistent_store.max_variable_size = DEFAULT_MAX_VARIABLE_SIZE; |
| + context->persistent_store.total_capacity = DEFAULT_MAX_VARIABLE_SIZE * max_variables; |
| + context->persistent_store.storage_backend = persistent_store; |
| + |
| + /* Initialise volatile store defaults */ |
| + context->volatile_store.is_nv = false; |
| + context->volatile_store.max_variable_size = DEFAULT_MAX_VARIABLE_SIZE; |
| + context->volatile_store.total_capacity = DEFAULT_MAX_VARIABLE_SIZE * max_variables; |
| + context->volatile_store.storage_backend = volatile_store; |
| |
| context->owner_id = owner_id; |
| context->is_boot_service = true; |
| @@ -116,6 +145,20 @@ void uefi_variable_store_deinit( |
| context->index_sync_buffer = NULL; |
| } |
| |
| +void uefi_variable_store_set_storage_limits( |
| + struct uefi_variable_store *context, |
| + uint32_t attributes, |
| + size_t total_capacity, |
| + size_t max_variable_size) |
| +{ |
| + struct delegate_variable_store *delegate_store = select_delegate_store( |
| + context, |
| + attributes); |
| + |
| + delegate_store->total_capacity = total_capacity; |
| + delegate_store->max_variable_size = max_variable_size; |
| +} |
| + |
| efi_status_t uefi_variable_store_set_variable( |
| struct uefi_variable_store *context, |
| const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var) |
| @@ -284,12 +327,24 @@ efi_status_t uefi_variable_store_get_next_variable_name( |
| |
| efi_status_t uefi_variable_store_query_variable_info( |
| struct uefi_variable_store *context, |
| - SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *cur) |
| + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *var_info) |
| { |
| - efi_status_t status = EFI_UNSUPPORTED; |
| + struct delegate_variable_store *delegate_store = select_delegate_store( |
| + context, |
| + var_info->Attributes); |
| |
| + size_t total_used = space_used( |
| + context, |
| + var_info->Attributes, |
| + delegate_store->storage_backend); |
| |
| - return status; |
| + var_info->MaximumVariableSize = delegate_store->max_variable_size; |
| + var_info->MaximumVariableStorageSize = delegate_store->total_capacity; |
| + var_info->RemainingVariableStorageSize = (total_used < delegate_store->total_capacity) ? |
| + delegate_store->total_capacity - total_used : |
| + 0; |
| + |
| + return EFI_SUCCESS; |
| } |
| |
| efi_status_t uefi_variable_store_exit_boot_service( |
| @@ -375,7 +430,7 @@ efi_status_t uefi_variable_store_get_var_check_property( |
| static void load_variable_index( |
| struct uefi_variable_store *context) |
| { |
| - struct storage_backend *persistent_store = context->persistent_store; |
| + struct storage_backend *persistent_store = context->persistent_store.storage_backend; |
| |
| if (persistent_store) { |
| |
| @@ -413,7 +468,7 @@ static efi_status_t sync_variable_index( |
| |
| if (is_dirty) { |
| |
| - struct storage_backend *persistent_store = context->persistent_store; |
| + struct storage_backend *persistent_store = context->persistent_store.storage_backend; |
| |
| if (persistent_store) { |
| |
| @@ -501,30 +556,27 @@ static efi_status_t store_variable_data( |
| const uint8_t *data = (const uint8_t*)var + |
| SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var); |
| |
| - bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE); |
| - |
| - struct storage_backend *storage_backend = (is_nv) ? |
| - context->persistent_store : |
| - context->volatile_store; |
| + struct delegate_variable_store *delegate_store = select_delegate_store( |
| + context, |
| + info->metadata.attributes); |
| |
| - if (storage_backend) { |
| + if (delegate_store->storage_backend) { |
| |
| if (!(var->Attributes & EFI_VARIABLE_APPEND_WRITE)) { |
| |
| /* Create or overwrite variable data */ |
| - psa_status = storage_backend->interface->set( |
| - storage_backend->context, |
| + psa_status = store_overwrite( |
| + delegate_store, |
| context->owner_id, |
| info->metadata.uid, |
| data_len, |
| - data, |
| - PSA_STORAGE_FLAG_NONE); |
| + data); |
| } |
| else { |
| |
| /* Append new data to existing variable data */ |
| - psa_status = append_write( |
| - storage_backend, |
| + psa_status = store_append_write( |
| + delegate_store, |
| context->owner_id, |
| info->metadata.uid, |
| data_len, |
| @@ -532,7 +584,7 @@ static efi_status_t store_variable_data( |
| } |
| } |
| |
| - if ((psa_status != PSA_SUCCESS) && is_nv) { |
| + if ((psa_status != PSA_SUCCESS) && delegate_store->is_nv) { |
| |
| /* A storage failure has occurred so attempt to fix any |
| * mismatch between the variable index and stored NV variables. |
| @@ -551,16 +603,14 @@ static efi_status_t remove_variable_data( |
| |
| if (info->is_variable_set) { |
| |
| - bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE); |
| + struct delegate_variable_store *delegate_store = select_delegate_store( |
| + context, |
| + info->metadata.attributes); |
| |
| - struct storage_backend *storage_backend = (is_nv) ? |
| - context->persistent_store : |
| - context->volatile_store; |
| + if (delegate_store->storage_backend) { |
| |
| - if (storage_backend) { |
| - |
| - psa_status = storage_backend->interface->remove( |
| - storage_backend->context, |
| + psa_status = delegate_store->storage_backend->interface->remove( |
| + delegate_store->storage_backend->context, |
| context->owner_id, |
| info->metadata.uid); |
| } |
| @@ -580,16 +630,14 @@ static efi_status_t load_variable_data( |
| uint8_t *data = (uint8_t*)var + |
| SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var); |
| |
| - bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE); |
| + struct delegate_variable_store *delegate_store = select_delegate_store( |
| + context, |
| + info->metadata.attributes); |
| |
| - struct storage_backend *storage_backend = (is_nv) ? |
| - context->persistent_store : |
| - context->volatile_store; |
| + if (delegate_store->storage_backend) { |
| |
| - if (storage_backend) { |
| - |
| - psa_status = storage_backend->interface->get( |
| - storage_backend->context, |
| + psa_status = delegate_store->storage_backend->interface->get( |
| + delegate_store->storage_backend->context, |
| context->owner_id, |
| info->metadata.uid, |
| 0, |
| @@ -603,8 +651,29 @@ static efi_status_t load_variable_data( |
| return psa_to_efi_storage_status(psa_status); |
| } |
| |
| -static psa_status_t append_write( |
| - struct storage_backend *storage_backend, |
| +static psa_status_t store_overwrite( |
| + struct delegate_variable_store *delegate_store, |
| + uint32_t client_id, |
| + uint64_t uid, |
| + size_t data_length, |
| + const void *data) |
| +{ |
| + /* Police maximum variable size limit */ |
| + if (data_length > delegate_store->max_variable_size) return PSA_ERROR_INVALID_ARGUMENT; |
| + |
| + psa_status_t psa_status = delegate_store->storage_backend->interface->set( |
| + delegate_store->storage_backend->context, |
| + client_id, |
| + uid, |
| + data_length, |
| + data, |
| + PSA_STORAGE_FLAG_NONE); |
| + |
| + return psa_status; |
| +} |
| + |
| +static psa_status_t store_append_write( |
| + struct delegate_variable_store *delegate_store, |
| uint32_t client_id, |
| uint64_t uid, |
| size_t data_length, |
| @@ -614,8 +683,8 @@ static psa_status_t append_write( |
| |
| if (data_length == 0) return PSA_SUCCESS; |
| |
| - psa_status_t psa_status = storage_backend->interface->get_info( |
| - storage_backend->context, |
| + psa_status_t psa_status = delegate_store->storage_backend->interface->get_info( |
| + delegate_store->storage_backend->context, |
| client_id, |
| uid, |
| &storage_info); |
| @@ -628,6 +697,9 @@ static psa_status_t append_write( |
| /* Defend against integer overflow */ |
| if (new_size < storage_info.size) return PSA_ERROR_INVALID_ARGUMENT; |
| |
| + /* Police maximum variable size limit */ |
| + if (new_size > delegate_store->max_variable_size) return PSA_ERROR_INVALID_ARGUMENT; |
| + |
| /* Storage backend doesn't support an append operation so we need |
| * need to read the current variable data, extend it and write it back. |
| */ |
| @@ -635,8 +707,8 @@ static psa_status_t append_write( |
| if (!rw_buf) return PSA_ERROR_INSUFFICIENT_MEMORY; |
| |
| size_t old_size = 0; |
| - psa_status = storage_backend->interface->get( |
| - storage_backend->context, |
| + psa_status = delegate_store->storage_backend->interface->get( |
| + delegate_store->storage_backend->context, |
| client_id, |
| uid, |
| 0, |
| @@ -651,8 +723,8 @@ static psa_status_t append_write( |
| /* Extend the variable data */ |
| memcpy(&rw_buf[old_size], data, data_length); |
| |
| - psa_status = storage_backend->interface->set( |
| - storage_backend->context, |
| + psa_status = delegate_store->storage_backend->interface->set( |
| + delegate_store->storage_backend->context, |
| client_id, |
| uid, |
| old_size + data_length, |
| @@ -692,7 +764,7 @@ static void purge_orphan_index_entries( |
| if (info->is_variable_set && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE)) { |
| |
| struct psa_storage_info_t storage_info; |
| - struct storage_backend *storage_backend = context->persistent_store; |
| + struct storage_backend *storage_backend = context->persistent_store.storage_backend; |
| |
| psa_status_t psa_status = storage_backend->interface->get_info( |
| storage_backend->context, |
| @@ -714,6 +786,53 @@ static void purge_orphan_index_entries( |
| if (any_orphans) sync_variable_index(context); |
| } |
| |
| +static struct delegate_variable_store *select_delegate_store( |
| + struct uefi_variable_store *context, |
| + uint32_t attributes) |
| +{ |
| + bool is_nv = (attributes & EFI_VARIABLE_NON_VOLATILE); |
| + |
| + return (is_nv) ? |
| + &context->persistent_store : |
| + &context->volatile_store; |
| +} |
| + |
| +static size_t space_used( |
| + struct uefi_variable_store *context, |
| + uint32_t attributes, |
| + struct storage_backend *storage_backend) |
| +{ |
| + if (!storage_backend) return 0; |
| + |
| + size_t total_used = 0; |
| + struct variable_index_iterator iter; |
| + variable_index_iterator_first(&iter, &context->variable_index); |
| + |
| + while (!variable_index_iterator_is_done(&iter)) { |
| + |
| + struct variable_info *info = variable_index_iterator_current(&iter); |
| + |
| + if (info->is_variable_set && |
| + ((info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE) == |
| + (attributes & EFI_VARIABLE_NON_VOLATILE))) { |
| + |
| + struct psa_storage_info_t storage_info; |
| + |
| + psa_status_t psa_status = storage_backend->interface->get_info( |
| + storage_backend->context, |
| + context->owner_id, |
| + info->metadata.uid, |
| + &storage_info); |
| + |
| + if (psa_status == PSA_SUCCESS) total_used += storage_info.size; |
| + } |
| + |
| + variable_index_iterator_next(&iter); |
| + } |
| + |
| + return total_used; |
| +} |
| + |
| static efi_status_t psa_to_efi_storage_status( |
| psa_status_t psa_status) |
| { |
| diff --git a/components/service/smm_variable/backend/uefi_variable_store.h b/components/service/smm_variable/backend/uefi_variable_store.h |
| index fe0f24af..cc992067 100644 |
| --- a/components/service/smm_variable/backend/uefi_variable_store.h |
| +++ b/components/service/smm_variable/backend/uefi_variable_store.h |
| @@ -20,6 +20,20 @@ |
| extern "C" { |
| #endif |
| |
| +/** |
| + * \brief delegate_variable_store structure definition |
| + * |
| + * A delegate_variable_store combines an association with a concrete |
| + * storage backend and a set of limits parameters. |
| + */ |
| +struct delegate_variable_store |
| +{ |
| + bool is_nv; |
| + size_t total_capacity; |
| + size_t max_variable_size; |
| + struct storage_backend *storage_backend; |
| +}; |
| + |
| /** |
| * \brief uefi_variable_store structure definition |
| * |
| @@ -35,8 +49,8 @@ struct uefi_variable_store |
| uint8_t *index_sync_buffer; |
| size_t index_sync_buffer_size; |
| struct variable_index variable_index; |
| - struct storage_backend *persistent_store; |
| - struct storage_backend *volatile_store; |
| + struct delegate_variable_store persistent_store; |
| + struct delegate_variable_store volatile_store; |
| }; |
| |
| /** |
| @@ -69,6 +83,23 @@ efi_status_t uefi_variable_store_init( |
| void uefi_variable_store_deinit( |
| struct uefi_variable_store *context); |
| |
| +/** |
| + * @brief Set storage limits |
| + * |
| + * Overrides the default limits for the specified storage space. These |
| + * values are reflected in the values returned by QueryVariableInfo. |
| + * |
| + * @param[in] context uefi_variable_store instance |
| + * @param[in] attributes EFI_VARIABLE_NON_VOLATILE or 0 |
| + * @param[in] total_capacity The total storage capacity in bytes |
| + * @param[in] max_variable_size Variable size limit |
| + */ |
| +void uefi_variable_store_set_storage_limits( |
| + struct uefi_variable_store *context, |
| + uint32_t attributes, |
| + size_t total_capacity, |
| + size_t max_variable_size); |
| + |
| /** |
| * @brief Set variable |
| * |
| @@ -123,13 +154,13 @@ efi_status_t uefi_variable_store_get_next_variable_name( |
| * @brief Query for variable info |
| * |
| * @param[in] context uefi_variable_store instance |
| - * @param[out] info Returns info |
| + * @param[inout] var_info Returns info |
| * |
| * @return EFI_SUCCESS if succesful |
| */ |
| efi_status_t uefi_variable_store_query_variable_info( |
| struct uefi_variable_store *context, |
| - SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *cur); |
| + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *var_info); |
| |
| /** |
| * @brief Exit boot service |
| diff --git a/components/service/smm_variable/client/cpp/smm_variable_client.cpp b/components/service/smm_variable/client/cpp/smm_variable_client.cpp |
| index a68b7ace..8438285b 100644 |
| --- a/components/service/smm_variable/client/cpp/smm_variable_client.cpp |
| +++ b/components/service/smm_variable/client/cpp/smm_variable_client.cpp |
| @@ -219,6 +219,72 @@ efi_status_t smm_variable_client::get_next_variable_name( |
| 0); |
| } |
| |
| +efi_status_t smm_variable_client::query_variable_info( |
| + uint32_t attributes, |
| + size_t *max_variable_storage_size, |
| + size_t *remaining_variable_storage_size, |
| + size_t *max_variable_size) |
| +{ |
| + efi_status_t efi_status = EFI_NOT_READY; |
| + |
| + size_t req_len = sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO); |
| + rpc_call_handle call_handle; |
| + uint8_t *req_buf; |
| + |
| + call_handle = rpc_caller_begin(m_caller, &req_buf, req_len); |
| + |
| + if (call_handle) { |
| + |
| + uint8_t *resp_buf; |
| + size_t resp_len; |
| + rpc_opstatus_t opstatus; |
| + |
| + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *query = |
| + (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)req_buf; |
| + |
| + query->Attributes = attributes; |
| + query->MaximumVariableSize = 0; |
| + query->MaximumVariableStorageSize = 0; |
| + query->RemainingVariableStorageSize = 0; |
| + |
| + m_err_rpc_status = rpc_caller_invoke(m_caller, call_handle, |
| + SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, &opstatus, &resp_buf, &resp_len); |
| + |
| + if (m_err_rpc_status == TS_RPC_CALL_ACCEPTED) { |
| + |
| + efi_status = opstatus; |
| + |
| + if (efi_status == EFI_SUCCESS) { |
| + |
| + if (resp_len >= sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) { |
| + |
| + query = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)resp_buf; |
| + |
| + *max_variable_storage_size = query->MaximumVariableStorageSize; |
| + *remaining_variable_storage_size = query->RemainingVariableStorageSize; |
| + *max_variable_size = query->MaximumVariableSize; |
| + } |
| + else { |
| + |
| + efi_status = EFI_PROTOCOL_ERROR; |
| + } |
| + } |
| + else { |
| + |
| + efi_status = EFI_PROTOCOL_ERROR; |
| + } |
| + } |
| + else { |
| + |
| + efi_status = rpc_to_efi_status(); |
| + } |
| + |
| + rpc_caller_end(m_caller, call_handle); |
| + } |
| + |
| + return efi_status; |
| +} |
| + |
| efi_status_t smm_variable_client::get_next_variable_name( |
| EFI_GUID &guid, |
| std::wstring &name, |
| diff --git a/components/service/smm_variable/client/cpp/smm_variable_client.h b/components/service/smm_variable/client/cpp/smm_variable_client.h |
| index 9c36c4eb..c7973916 100644 |
| --- a/components/service/smm_variable/client/cpp/smm_variable_client.h |
| +++ b/components/service/smm_variable/client/cpp/smm_variable_client.h |
| @@ -63,6 +63,13 @@ public: |
| const EFI_GUID &guid, |
| const std::wstring &name); |
| |
| + /* Query variable info */ |
| + efi_status_t query_variable_info( |
| + uint32_t attributes, |
| + size_t *max_variable_storage_size, |
| + size_t *remaining_variable_storage_size, |
| + size_t *max_variable_size); |
| + |
| /* Get the next variable name - for enumerating store contents */ |
| efi_status_t get_next_variable_name( |
| EFI_GUID &guid, |
| diff --git a/components/service/smm_variable/provider/smm_variable_provider.c b/components/service/smm_variable/provider/smm_variable_provider.c |
| index d239a428..52e68d09 100644 |
| --- a/components/service/smm_variable/provider/smm_variable_provider.c |
| +++ b/components/service/smm_variable/provider/smm_variable_provider.c |
| @@ -252,11 +252,38 @@ static rpc_status_t set_variable_handler(void *context, struct call_req* req) |
| |
| static rpc_status_t query_variable_info_handler(void *context, struct call_req* req) |
| { |
| + efi_status_t efi_status = EFI_INVALID_PARAMETER; |
| struct smm_variable_provider *this_instance = (struct smm_variable_provider*)context; |
| |
| - /* todo */ |
| + const struct call_param_buf *req_buf = call_req_get_req_buf(req); |
| + |
| + if (req_buf->data_len >= sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) { |
| + |
| + struct call_param_buf *resp_buf = call_req_get_resp_buf(req); |
| + |
| + if (resp_buf->size >= req_buf->data_len) { |
| |
| - return TS_RPC_ERROR_NOT_READY; |
| + memmove(resp_buf->data, req_buf->data, req_buf->data_len); |
| + |
| + efi_status = uefi_variable_store_query_variable_info( |
| + &this_instance->variable_store, |
| + (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)resp_buf->data); |
| + |
| + if (efi_status == EFI_SUCCESS) { |
| + |
| + resp_buf->data_len = sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO); |
| + } |
| + } |
| + else { |
| + |
| + /* Reponse buffer not big enough */ |
| + efi_status = EFI_BAD_BUFFER_SIZE; |
| + } |
| + } |
| + |
| + call_req_set_opstatus(req, efi_status); |
| + |
| + return TS_RPC_CALL_ACCEPTED; |
| } |
| |
| static rpc_status_t exit_boot_service_handler(void *context, struct call_req* req) |
| diff --git a/components/service/smm_variable/test/service/smm_variable_service_tests.cpp b/components/service/smm_variable/test/service/smm_variable_service_tests.cpp |
| index 088940a8..15556e9d 100644 |
| --- a/components/service/smm_variable/test/service/smm_variable_service_tests.cpp |
| +++ b/components/service/smm_variable/test/service/smm_variable_service_tests.cpp |
| @@ -335,12 +335,38 @@ TEST(SmmVariableServiceTests, setAndGetNv) |
| TEST(SmmVariableServiceTests, enumerateStoreContents) |
| { |
| efi_status_t efi_status = EFI_SUCCESS; |
| + |
| + /* Query information about the empty variable store */ |
| + size_t nv_max_variable_storage_size = 0; |
| + size_t nv_max_variable_size = 0; |
| + size_t nv_remaining_variable_storage_size = 0; |
| + |
| + efi_status = m_client->query_variable_info( |
| + EFI_VARIABLE_NON_VOLATILE, |
| + &nv_max_variable_storage_size, |
| + &nv_remaining_variable_storage_size, |
| + &nv_max_variable_size); |
| + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| + UNSIGNED_LONGLONGS_EQUAL(nv_max_variable_storage_size, nv_remaining_variable_storage_size); |
| + |
| + size_t v_max_variable_storage_size = 0; |
| + size_t v_max_variable_size = 0; |
| + size_t v_remaining_variable_storage_size = 0; |
| + |
| + efi_status = m_client->query_variable_info( |
| + 0, |
| + &v_max_variable_storage_size, |
| + &v_remaining_variable_storage_size, |
| + &v_max_variable_size); |
| + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| + UNSIGNED_LONGLONGS_EQUAL(v_max_variable_storage_size, v_remaining_variable_storage_size); |
| + |
| + /* Add some variables to the store */ |
| std::wstring var_name_1 = L"varibale_1"; |
| std::wstring var_name_2 = L"varibale_2"; |
| std::wstring var_name_3 = L"varibale_3"; |
| std::string set_data = "Some variable data"; |
| |
| - /* Add some variables to the store */ |
| efi_status = m_client->set_variable( |
| m_common_guid, |
| var_name_1, |
| @@ -365,6 +391,33 @@ TEST(SmmVariableServiceTests, enumerateStoreContents) |
| |
| UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| |
| + /* Query variable info again and check it's as expected */ |
| + size_t max_variable_storage_size = 0; |
| + size_t max_variable_size = 0; |
| + size_t remaining_variable_storage_size = 0; |
| + |
| + /* Check non-volatile - two variables have been added */ |
| + efi_status = m_client->query_variable_info( |
| + EFI_VARIABLE_NON_VOLATILE, |
| + &max_variable_storage_size, |
| + &remaining_variable_storage_size, |
| + &max_variable_size); |
| + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| + UNSIGNED_LONGLONGS_EQUAL( |
| + (nv_remaining_variable_storage_size - set_data.size() * 2), |
| + remaining_variable_storage_size); |
| + |
| + /* Check volatile - one variables have been added */ |
| + efi_status = m_client->query_variable_info( |
| + 0, |
| + &max_variable_storage_size, |
| + &remaining_variable_storage_size, |
| + &max_variable_size); |
| + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| + UNSIGNED_LONGLONGS_EQUAL( |
| + (v_remaining_variable_storage_size - set_data.size() * 1), |
| + remaining_variable_storage_size); |
| + |
| /* Enumerate store contents - expect the values we added */ |
| std::wstring var_name; |
| EFI_GUID guid = {0}; |