Upstream-Status: Pending [Not submitted to upstream yet]
Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com>

From 7cc8eaa66c6ecfd66a27317efd9b03725564a43a 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

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 578f118..e90c106 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 bcb8599..ed50eaf 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 fe0f24a..cc99206 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 a68b7ac..8438285 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 9c36c4e..c797391 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 d239a42..52e68d0 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 088940a..15556e9 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};
-- 
2.25.1

