blob: 978600dd94ea542f7d140cbdd895e71b27b280bd [file] [log] [blame]
Brad Bishopbec4ebc2022-08-03 09:55:16 -04001From c4eaf83548eed4ed6194ff9e1368d6ae65f4ebf9 Mon Sep 17 00:00:00 2001
2From: Julian Hall <julian.hall@arm.com>
3Date: Thu, 2 Dec 2021 17:27:55 +0000
4Subject: [PATCH] Add UEFI variable support for QueryVariableInfo
5
6Adds support for the UEFI QueryVariableInfo operation. The total
7store size currently relies on pre-configured values, set for a
8particular deployment. Ideally, this information would be read
9from the storage backend. This facility is not however yet
10supported by the storage backend interface or by any PSA
11storage backend storage providers.
12
13Signed-off-by: Julian Hall <julian.hall@arm.com>
14Change-Id: I971252831f7e478914d736c672d184a371e64502
15
16Upstream-Status: Pending [Not submitted to upstream yet]
17Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com>
18
19
20---
21 .../backend/test/variable_store_tests.cpp | 89 +++++++-
22 .../backend/uefi_variable_store.c | 213 ++++++++++++++----
23 .../backend/uefi_variable_store.h | 39 +++-
24 .../client/cpp/smm_variable_client.cpp | 66 ++++++
25 .../client/cpp/smm_variable_client.h | 7 +
26 .../provider/smm_variable_provider.c | 31 ++-
27 .../service/smm_variable_service_tests.cpp | 55 ++++-
28 7 files changed, 445 insertions(+), 55 deletions(-)
29
30diff --git a/components/service/smm_variable/backend/test/variable_store_tests.cpp b/components/service/smm_variable/backend/test/variable_store_tests.cpp
31index 578f118f..e90c1067 100644
32--- a/components/service/smm_variable/backend/test/variable_store_tests.cpp
33+++ b/components/service/smm_variable/backend/test/variable_store_tests.cpp
34@@ -27,6 +27,18 @@ TEST_GROUP(UefiVariableStoreTests)
35
36 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
37
38+ uefi_variable_store_set_storage_limits(
39+ &m_uefi_variable_store,
40+ EFI_VARIABLE_NON_VOLATILE,
41+ STORE_CAPACITY,
42+ MAX_VARIABLE_SIZE);
43+
44+ uefi_variable_store_set_storage_limits(
45+ &m_uefi_variable_store,
46+ 0,
47+ STORE_CAPACITY,
48+ MAX_VARIABLE_SIZE);
49+
50 setup_common_guid();
51 }
52
53@@ -152,6 +164,33 @@ TEST_GROUP(UefiVariableStoreTests)
54 return status;
55 }
56
57+ efi_status_t query_variable_info(
58+ uint32_t attributes,
59+ size_t *max_variable_storage_size,
60+ size_t *remaining_variable_storage_size,
61+ size_t *max_variable_size)
62+ {
63+ SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO query;
64+
65+ query.MaximumVariableStorageSize = 0;
66+ query.RemainingVariableStorageSize = 0;
67+ query.MaximumVariableSize = 0;
68+ query.Attributes = attributes;
69+
70+ efi_status_t status = uefi_variable_store_query_variable_info(
71+ &m_uefi_variable_store,
72+ &query);
73+
74+ if (status == EFI_SUCCESS) {
75+
76+ *max_variable_storage_size = query.MaximumVariableStorageSize;
77+ *remaining_variable_storage_size = query.RemainingVariableStorageSize;
78+ *max_variable_size = query.MaximumVariableSize;
79+ }
80+
81+ return status;
82+ }
83+
84 efi_status_t set_check_var_property(
85 const std::wstring &name,
86 const VAR_CHECK_VARIABLE_PROPERTY &check_property)
87@@ -195,7 +234,8 @@ TEST_GROUP(UefiVariableStoreTests)
88
89 if (info && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE)) {
90
91- struct storage_backend *storage_backend = m_uefi_variable_store.persistent_store;
92+ struct storage_backend *storage_backend =
93+ m_uefi_variable_store.persistent_store.storage_backend;
94
95 storage_backend->interface->remove(
96 storage_backend->context,
97@@ -220,9 +260,24 @@ TEST_GROUP(UefiVariableStoreTests)
98 m_volatile_backend);
99
100 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
101+
102+ uefi_variable_store_set_storage_limits(
103+ &m_uefi_variable_store,
104+ EFI_VARIABLE_NON_VOLATILE,
105+ STORE_CAPACITY,
106+ MAX_VARIABLE_SIZE);
107+
108+ uefi_variable_store_set_storage_limits(
109+ &m_uefi_variable_store,
110+ 0,
111+ STORE_CAPACITY,
112+ MAX_VARIABLE_SIZE);
113 }
114
115 static const size_t MAX_VARIABLES = 10;
116+ static const size_t MAX_VARIABLE_SIZE = 100;
117+ static const size_t STORE_CAPACITY = 1000;
118+
119 static const uint32_t OWNER_ID = 100;
120 static const size_t VARIABLE_BUFFER_SIZE = 1024;
121
122@@ -265,6 +320,22 @@ TEST(UefiVariableStoreTests, setGetRoundtrip)
123 /* Expect the append write operation to have extended the variable */
124 UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size());
125 LONGS_EQUAL(0, expected_output.compare(output_data));
126+
127+ /* Expect query_variable_info to return consistent values */
128+ size_t max_variable_storage_size = 0;
129+ size_t remaining_variable_storage_size = 0;
130+ size_t max_variable_size = 0;
131+
132+ status = query_variable_info(
133+ 0,
134+ &max_variable_storage_size,
135+ &remaining_variable_storage_size,
136+ &max_variable_size);
137+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
138+
139+ UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY, max_variable_storage_size);
140+ UNSIGNED_LONGLONGS_EQUAL(MAX_VARIABLE_SIZE, max_variable_size);
141+ UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY - expected_output.size(), remaining_variable_storage_size);
142 }
143
144 TEST(UefiVariableStoreTests, persistentSetGet)
145@@ -311,6 +382,22 @@ TEST(UefiVariableStoreTests, persistentSetGet)
146 /* Still expect got variable data to be the same as the set value */
147 UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size());
148 LONGS_EQUAL(0, expected_output.compare(output_data));
149+
150+ /* Expect query_variable_info to return consistent values */
151+ size_t max_variable_storage_size = 0;
152+ size_t remaining_variable_storage_size = 0;
153+ size_t max_variable_size = 0;
154+
155+ status = query_variable_info(
156+ EFI_VARIABLE_NON_VOLATILE,
157+ &max_variable_storage_size,
158+ &remaining_variable_storage_size,
159+ &max_variable_size);
160+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
161+
162+ UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY, max_variable_storage_size);
163+ UNSIGNED_LONGLONGS_EQUAL(MAX_VARIABLE_SIZE, max_variable_size);
164+ UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY - expected_output.size(), remaining_variable_storage_size);
165 }
166
167 TEST(UefiVariableStoreTests, removeVolatile)
168diff --git a/components/service/smm_variable/backend/uefi_variable_store.c b/components/service/smm_variable/backend/uefi_variable_store.c
169index bcb85995..ed50eaf9 100644
170--- a/components/service/smm_variable/backend/uefi_variable_store.c
171+++ b/components/service/smm_variable/backend/uefi_variable_store.c
172@@ -46,8 +46,15 @@ static efi_status_t load_variable_data(
173 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var,
174 size_t max_data_len);
175
176-static psa_status_t append_write(
177- struct storage_backend *storage_backend,
178+static psa_status_t store_overwrite(
179+ struct delegate_variable_store *delegate_store,
180+ uint32_t client_id,
181+ uint64_t uid,
182+ size_t data_length,
183+ const void *data);
184+
185+static psa_status_t store_append_write(
186+ struct delegate_variable_store *delegate_store,
187 uint32_t client_id,
188 uint64_t uid,
189 size_t data_length,
190@@ -56,6 +63,15 @@ static psa_status_t append_write(
191 static void purge_orphan_index_entries(
192 struct uefi_variable_store *context);
193
194+static struct delegate_variable_store *select_delegate_store(
195+ struct uefi_variable_store *context,
196+ uint32_t attributes);
197+
198+static size_t space_used(
199+ struct uefi_variable_store *context,
200+ uint32_t attributes,
201+ struct storage_backend *storage_backend);
202+
203 static efi_status_t psa_to_efi_storage_status(
204 psa_status_t psa_status);
205
206@@ -66,6 +82,10 @@ static efi_status_t check_name_terminator(
207 /* Private UID for storing the variable index */
208 #define VARIABLE_INDEX_STORAGE_UID (1)
209
210+/* Default maximum variable size -
211+ * may be overridden using uefi_variable_store_set_storage_limits()
212+ */
213+#define DEFAULT_MAX_VARIABLE_SIZE (2048)
214
215 efi_status_t uefi_variable_store_init(
216 struct uefi_variable_store *context,
217@@ -76,8 +96,17 @@ efi_status_t uefi_variable_store_init(
218 {
219 efi_status_t status = EFI_SUCCESS;
220
221- context->persistent_store = persistent_store;
222- context->volatile_store = volatile_store;
223+ /* Initialise persistent store defaults */
224+ context->persistent_store.is_nv = true;
225+ context->persistent_store.max_variable_size = DEFAULT_MAX_VARIABLE_SIZE;
226+ context->persistent_store.total_capacity = DEFAULT_MAX_VARIABLE_SIZE * max_variables;
227+ context->persistent_store.storage_backend = persistent_store;
228+
229+ /* Initialise volatile store defaults */
230+ context->volatile_store.is_nv = false;
231+ context->volatile_store.max_variable_size = DEFAULT_MAX_VARIABLE_SIZE;
232+ context->volatile_store.total_capacity = DEFAULT_MAX_VARIABLE_SIZE * max_variables;
233+ context->volatile_store.storage_backend = volatile_store;
234
235 context->owner_id = owner_id;
236 context->is_boot_service = true;
237@@ -116,6 +145,20 @@ void uefi_variable_store_deinit(
238 context->index_sync_buffer = NULL;
239 }
240
241+void uefi_variable_store_set_storage_limits(
242+ struct uefi_variable_store *context,
243+ uint32_t attributes,
244+ size_t total_capacity,
245+ size_t max_variable_size)
246+{
247+ struct delegate_variable_store *delegate_store = select_delegate_store(
248+ context,
249+ attributes);
250+
251+ delegate_store->total_capacity = total_capacity;
252+ delegate_store->max_variable_size = max_variable_size;
253+}
254+
255 efi_status_t uefi_variable_store_set_variable(
256 struct uefi_variable_store *context,
257 const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var)
258@@ -284,12 +327,24 @@ efi_status_t uefi_variable_store_get_next_variable_name(
259
260 efi_status_t uefi_variable_store_query_variable_info(
261 struct uefi_variable_store *context,
262- SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *cur)
263+ SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *var_info)
264 {
265- efi_status_t status = EFI_UNSUPPORTED;
266+ struct delegate_variable_store *delegate_store = select_delegate_store(
267+ context,
268+ var_info->Attributes);
269
270+ size_t total_used = space_used(
271+ context,
272+ var_info->Attributes,
273+ delegate_store->storage_backend);
274
275- return status;
276+ var_info->MaximumVariableSize = delegate_store->max_variable_size;
277+ var_info->MaximumVariableStorageSize = delegate_store->total_capacity;
278+ var_info->RemainingVariableStorageSize = (total_used < delegate_store->total_capacity) ?
279+ delegate_store->total_capacity - total_used :
280+ 0;
281+
282+ return EFI_SUCCESS;
283 }
284
285 efi_status_t uefi_variable_store_exit_boot_service(
286@@ -375,7 +430,7 @@ efi_status_t uefi_variable_store_get_var_check_property(
287 static void load_variable_index(
288 struct uefi_variable_store *context)
289 {
290- struct storage_backend *persistent_store = context->persistent_store;
291+ struct storage_backend *persistent_store = context->persistent_store.storage_backend;
292
293 if (persistent_store) {
294
295@@ -413,7 +468,7 @@ static efi_status_t sync_variable_index(
296
297 if (is_dirty) {
298
299- struct storage_backend *persistent_store = context->persistent_store;
300+ struct storage_backend *persistent_store = context->persistent_store.storage_backend;
301
302 if (persistent_store) {
303
304@@ -501,30 +556,27 @@ static efi_status_t store_variable_data(
305 const uint8_t *data = (const uint8_t*)var +
306 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var);
307
308- bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE);
309-
310- struct storage_backend *storage_backend = (is_nv) ?
311- context->persistent_store :
312- context->volatile_store;
313+ struct delegate_variable_store *delegate_store = select_delegate_store(
314+ context,
315+ info->metadata.attributes);
316
317- if (storage_backend) {
318+ if (delegate_store->storage_backend) {
319
320 if (!(var->Attributes & EFI_VARIABLE_APPEND_WRITE)) {
321
322 /* Create or overwrite variable data */
323- psa_status = storage_backend->interface->set(
324- storage_backend->context,
325+ psa_status = store_overwrite(
326+ delegate_store,
327 context->owner_id,
328 info->metadata.uid,
329 data_len,
330- data,
331- PSA_STORAGE_FLAG_NONE);
332+ data);
333 }
334 else {
335
336 /* Append new data to existing variable data */
337- psa_status = append_write(
338- storage_backend,
339+ psa_status = store_append_write(
340+ delegate_store,
341 context->owner_id,
342 info->metadata.uid,
343 data_len,
344@@ -532,7 +584,7 @@ static efi_status_t store_variable_data(
345 }
346 }
347
348- if ((psa_status != PSA_SUCCESS) && is_nv) {
349+ if ((psa_status != PSA_SUCCESS) && delegate_store->is_nv) {
350
351 /* A storage failure has occurred so attempt to fix any
352 * mismatch between the variable index and stored NV variables.
353@@ -551,16 +603,14 @@ static efi_status_t remove_variable_data(
354
355 if (info->is_variable_set) {
356
357- bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE);
358+ struct delegate_variable_store *delegate_store = select_delegate_store(
359+ context,
360+ info->metadata.attributes);
361
362- struct storage_backend *storage_backend = (is_nv) ?
363- context->persistent_store :
364- context->volatile_store;
365+ if (delegate_store->storage_backend) {
366
367- if (storage_backend) {
368-
369- psa_status = storage_backend->interface->remove(
370- storage_backend->context,
371+ psa_status = delegate_store->storage_backend->interface->remove(
372+ delegate_store->storage_backend->context,
373 context->owner_id,
374 info->metadata.uid);
375 }
376@@ -580,16 +630,14 @@ static efi_status_t load_variable_data(
377 uint8_t *data = (uint8_t*)var +
378 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var);
379
380- bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE);
381+ struct delegate_variable_store *delegate_store = select_delegate_store(
382+ context,
383+ info->metadata.attributes);
384
385- struct storage_backend *storage_backend = (is_nv) ?
386- context->persistent_store :
387- context->volatile_store;
388+ if (delegate_store->storage_backend) {
389
390- if (storage_backend) {
391-
392- psa_status = storage_backend->interface->get(
393- storage_backend->context,
394+ psa_status = delegate_store->storage_backend->interface->get(
395+ delegate_store->storage_backend->context,
396 context->owner_id,
397 info->metadata.uid,
398 0,
399@@ -603,8 +651,29 @@ static efi_status_t load_variable_data(
400 return psa_to_efi_storage_status(psa_status);
401 }
402
403-static psa_status_t append_write(
404- struct storage_backend *storage_backend,
405+static psa_status_t store_overwrite(
406+ struct delegate_variable_store *delegate_store,
407+ uint32_t client_id,
408+ uint64_t uid,
409+ size_t data_length,
410+ const void *data)
411+{
412+ /* Police maximum variable size limit */
413+ if (data_length > delegate_store->max_variable_size) return PSA_ERROR_INVALID_ARGUMENT;
414+
415+ psa_status_t psa_status = delegate_store->storage_backend->interface->set(
416+ delegate_store->storage_backend->context,
417+ client_id,
418+ uid,
419+ data_length,
420+ data,
421+ PSA_STORAGE_FLAG_NONE);
422+
423+ return psa_status;
424+}
425+
426+static psa_status_t store_append_write(
427+ struct delegate_variable_store *delegate_store,
428 uint32_t client_id,
429 uint64_t uid,
430 size_t data_length,
431@@ -614,8 +683,8 @@ static psa_status_t append_write(
432
433 if (data_length == 0) return PSA_SUCCESS;
434
435- psa_status_t psa_status = storage_backend->interface->get_info(
436- storage_backend->context,
437+ psa_status_t psa_status = delegate_store->storage_backend->interface->get_info(
438+ delegate_store->storage_backend->context,
439 client_id,
440 uid,
441 &storage_info);
442@@ -628,6 +697,9 @@ static psa_status_t append_write(
443 /* Defend against integer overflow */
444 if (new_size < storage_info.size) return PSA_ERROR_INVALID_ARGUMENT;
445
446+ /* Police maximum variable size limit */
447+ if (new_size > delegate_store->max_variable_size) return PSA_ERROR_INVALID_ARGUMENT;
448+
449 /* Storage backend doesn't support an append operation so we need
450 * need to read the current variable data, extend it and write it back.
451 */
452@@ -635,8 +707,8 @@ static psa_status_t append_write(
453 if (!rw_buf) return PSA_ERROR_INSUFFICIENT_MEMORY;
454
455 size_t old_size = 0;
456- psa_status = storage_backend->interface->get(
457- storage_backend->context,
458+ psa_status = delegate_store->storage_backend->interface->get(
459+ delegate_store->storage_backend->context,
460 client_id,
461 uid,
462 0,
463@@ -651,8 +723,8 @@ static psa_status_t append_write(
464 /* Extend the variable data */
465 memcpy(&rw_buf[old_size], data, data_length);
466
467- psa_status = storage_backend->interface->set(
468- storage_backend->context,
469+ psa_status = delegate_store->storage_backend->interface->set(
470+ delegate_store->storage_backend->context,
471 client_id,
472 uid,
473 old_size + data_length,
474@@ -692,7 +764,7 @@ static void purge_orphan_index_entries(
475 if (info->is_variable_set && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE)) {
476
477 struct psa_storage_info_t storage_info;
478- struct storage_backend *storage_backend = context->persistent_store;
479+ struct storage_backend *storage_backend = context->persistent_store.storage_backend;
480
481 psa_status_t psa_status = storage_backend->interface->get_info(
482 storage_backend->context,
483@@ -714,6 +786,53 @@ static void purge_orphan_index_entries(
484 if (any_orphans) sync_variable_index(context);
485 }
486
487+static struct delegate_variable_store *select_delegate_store(
488+ struct uefi_variable_store *context,
489+ uint32_t attributes)
490+{
491+ bool is_nv = (attributes & EFI_VARIABLE_NON_VOLATILE);
492+
493+ return (is_nv) ?
494+ &context->persistent_store :
495+ &context->volatile_store;
496+}
497+
498+static size_t space_used(
499+ struct uefi_variable_store *context,
500+ uint32_t attributes,
501+ struct storage_backend *storage_backend)
502+{
503+ if (!storage_backend) return 0;
504+
505+ size_t total_used = 0;
506+ struct variable_index_iterator iter;
507+ variable_index_iterator_first(&iter, &context->variable_index);
508+
509+ while (!variable_index_iterator_is_done(&iter)) {
510+
511+ struct variable_info *info = variable_index_iterator_current(&iter);
512+
513+ if (info->is_variable_set &&
514+ ((info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE) ==
515+ (attributes & EFI_VARIABLE_NON_VOLATILE))) {
516+
517+ struct psa_storage_info_t storage_info;
518+
519+ psa_status_t psa_status = storage_backend->interface->get_info(
520+ storage_backend->context,
521+ context->owner_id,
522+ info->metadata.uid,
523+ &storage_info);
524+
525+ if (psa_status == PSA_SUCCESS) total_used += storage_info.size;
526+ }
527+
528+ variable_index_iterator_next(&iter);
529+ }
530+
531+ return total_used;
532+}
533+
534 static efi_status_t psa_to_efi_storage_status(
535 psa_status_t psa_status)
536 {
537diff --git a/components/service/smm_variable/backend/uefi_variable_store.h b/components/service/smm_variable/backend/uefi_variable_store.h
538index fe0f24af..cc992067 100644
539--- a/components/service/smm_variable/backend/uefi_variable_store.h
540+++ b/components/service/smm_variable/backend/uefi_variable_store.h
541@@ -20,6 +20,20 @@
542 extern "C" {
543 #endif
544
545+/**
546+ * \brief delegate_variable_store structure definition
547+ *
548+ * A delegate_variable_store combines an association with a concrete
549+ * storage backend and a set of limits parameters.
550+ */
551+struct delegate_variable_store
552+{
553+ bool is_nv;
554+ size_t total_capacity;
555+ size_t max_variable_size;
556+ struct storage_backend *storage_backend;
557+};
558+
559 /**
560 * \brief uefi_variable_store structure definition
561 *
562@@ -35,8 +49,8 @@ struct uefi_variable_store
563 uint8_t *index_sync_buffer;
564 size_t index_sync_buffer_size;
565 struct variable_index variable_index;
566- struct storage_backend *persistent_store;
567- struct storage_backend *volatile_store;
568+ struct delegate_variable_store persistent_store;
569+ struct delegate_variable_store volatile_store;
570 };
571
572 /**
573@@ -69,6 +83,23 @@ efi_status_t uefi_variable_store_init(
574 void uefi_variable_store_deinit(
575 struct uefi_variable_store *context);
576
577+/**
578+ * @brief Set storage limits
579+ *
580+ * Overrides the default limits for the specified storage space. These
581+ * values are reflected in the values returned by QueryVariableInfo.
582+ *
583+ * @param[in] context uefi_variable_store instance
584+ * @param[in] attributes EFI_VARIABLE_NON_VOLATILE or 0
585+ * @param[in] total_capacity The total storage capacity in bytes
586+ * @param[in] max_variable_size Variable size limit
587+ */
588+void uefi_variable_store_set_storage_limits(
589+ struct uefi_variable_store *context,
590+ uint32_t attributes,
591+ size_t total_capacity,
592+ size_t max_variable_size);
593+
594 /**
595 * @brief Set variable
596 *
597@@ -123,13 +154,13 @@ efi_status_t uefi_variable_store_get_next_variable_name(
598 * @brief Query for variable info
599 *
600 * @param[in] context uefi_variable_store instance
601- * @param[out] info Returns info
602+ * @param[inout] var_info Returns info
603 *
604 * @return EFI_SUCCESS if succesful
605 */
606 efi_status_t uefi_variable_store_query_variable_info(
607 struct uefi_variable_store *context,
608- SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *cur);
609+ SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *var_info);
610
611 /**
612 * @brief Exit boot service
613diff --git a/components/service/smm_variable/client/cpp/smm_variable_client.cpp b/components/service/smm_variable/client/cpp/smm_variable_client.cpp
614index a68b7ace..8438285b 100644
615--- a/components/service/smm_variable/client/cpp/smm_variable_client.cpp
616+++ b/components/service/smm_variable/client/cpp/smm_variable_client.cpp
617@@ -219,6 +219,72 @@ efi_status_t smm_variable_client::get_next_variable_name(
618 0);
619 }
620
621+efi_status_t smm_variable_client::query_variable_info(
622+ uint32_t attributes,
623+ size_t *max_variable_storage_size,
624+ size_t *remaining_variable_storage_size,
625+ size_t *max_variable_size)
626+{
627+ efi_status_t efi_status = EFI_NOT_READY;
628+
629+ size_t req_len = sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO);
630+ rpc_call_handle call_handle;
631+ uint8_t *req_buf;
632+
633+ call_handle = rpc_caller_begin(m_caller, &req_buf, req_len);
634+
635+ if (call_handle) {
636+
637+ uint8_t *resp_buf;
638+ size_t resp_len;
639+ rpc_opstatus_t opstatus;
640+
641+ SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *query =
642+ (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)req_buf;
643+
644+ query->Attributes = attributes;
645+ query->MaximumVariableSize = 0;
646+ query->MaximumVariableStorageSize = 0;
647+ query->RemainingVariableStorageSize = 0;
648+
649+ m_err_rpc_status = rpc_caller_invoke(m_caller, call_handle,
650+ SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, &opstatus, &resp_buf, &resp_len);
651+
652+ if (m_err_rpc_status == TS_RPC_CALL_ACCEPTED) {
653+
654+ efi_status = opstatus;
655+
656+ if (efi_status == EFI_SUCCESS) {
657+
658+ if (resp_len >= sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) {
659+
660+ query = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)resp_buf;
661+
662+ *max_variable_storage_size = query->MaximumVariableStorageSize;
663+ *remaining_variable_storage_size = query->RemainingVariableStorageSize;
664+ *max_variable_size = query->MaximumVariableSize;
665+ }
666+ else {
667+
668+ efi_status = EFI_PROTOCOL_ERROR;
669+ }
670+ }
671+ else {
672+
673+ efi_status = EFI_PROTOCOL_ERROR;
674+ }
675+ }
676+ else {
677+
678+ efi_status = rpc_to_efi_status();
679+ }
680+
681+ rpc_caller_end(m_caller, call_handle);
682+ }
683+
684+ return efi_status;
685+}
686+
687 efi_status_t smm_variable_client::get_next_variable_name(
688 EFI_GUID &guid,
689 std::wstring &name,
690diff --git a/components/service/smm_variable/client/cpp/smm_variable_client.h b/components/service/smm_variable/client/cpp/smm_variable_client.h
691index 9c36c4eb..c7973916 100644
692--- a/components/service/smm_variable/client/cpp/smm_variable_client.h
693+++ b/components/service/smm_variable/client/cpp/smm_variable_client.h
694@@ -63,6 +63,13 @@ public:
695 const EFI_GUID &guid,
696 const std::wstring &name);
697
698+ /* Query variable info */
699+ efi_status_t query_variable_info(
700+ uint32_t attributes,
701+ size_t *max_variable_storage_size,
702+ size_t *remaining_variable_storage_size,
703+ size_t *max_variable_size);
704+
705 /* Get the next variable name - for enumerating store contents */
706 efi_status_t get_next_variable_name(
707 EFI_GUID &guid,
708diff --git a/components/service/smm_variable/provider/smm_variable_provider.c b/components/service/smm_variable/provider/smm_variable_provider.c
709index d239a428..52e68d09 100644
710--- a/components/service/smm_variable/provider/smm_variable_provider.c
711+++ b/components/service/smm_variable/provider/smm_variable_provider.c
712@@ -252,11 +252,38 @@ static rpc_status_t set_variable_handler(void *context, struct call_req* req)
713
714 static rpc_status_t query_variable_info_handler(void *context, struct call_req* req)
715 {
716+ efi_status_t efi_status = EFI_INVALID_PARAMETER;
717 struct smm_variable_provider *this_instance = (struct smm_variable_provider*)context;
718
719- /* todo */
720+ const struct call_param_buf *req_buf = call_req_get_req_buf(req);
721+
722+ if (req_buf->data_len >= sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) {
723+
724+ struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
725+
726+ if (resp_buf->size >= req_buf->data_len) {
727
728- return TS_RPC_ERROR_NOT_READY;
729+ memmove(resp_buf->data, req_buf->data, req_buf->data_len);
730+
731+ efi_status = uefi_variable_store_query_variable_info(
732+ &this_instance->variable_store,
733+ (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)resp_buf->data);
734+
735+ if (efi_status == EFI_SUCCESS) {
736+
737+ resp_buf->data_len = sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO);
738+ }
739+ }
740+ else {
741+
742+ /* Reponse buffer not big enough */
743+ efi_status = EFI_BAD_BUFFER_SIZE;
744+ }
745+ }
746+
747+ call_req_set_opstatus(req, efi_status);
748+
749+ return TS_RPC_CALL_ACCEPTED;
750 }
751
752 static rpc_status_t exit_boot_service_handler(void *context, struct call_req* req)
753diff --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
754index 088940a8..15556e9d 100644
755--- a/components/service/smm_variable/test/service/smm_variable_service_tests.cpp
756+++ b/components/service/smm_variable/test/service/smm_variable_service_tests.cpp
757@@ -335,12 +335,38 @@ TEST(SmmVariableServiceTests, setAndGetNv)
758 TEST(SmmVariableServiceTests, enumerateStoreContents)
759 {
760 efi_status_t efi_status = EFI_SUCCESS;
761+
762+ /* Query information about the empty variable store */
763+ size_t nv_max_variable_storage_size = 0;
764+ size_t nv_max_variable_size = 0;
765+ size_t nv_remaining_variable_storage_size = 0;
766+
767+ efi_status = m_client->query_variable_info(
768+ EFI_VARIABLE_NON_VOLATILE,
769+ &nv_max_variable_storage_size,
770+ &nv_remaining_variable_storage_size,
771+ &nv_max_variable_size);
772+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
773+ UNSIGNED_LONGLONGS_EQUAL(nv_max_variable_storage_size, nv_remaining_variable_storage_size);
774+
775+ size_t v_max_variable_storage_size = 0;
776+ size_t v_max_variable_size = 0;
777+ size_t v_remaining_variable_storage_size = 0;
778+
779+ efi_status = m_client->query_variable_info(
780+ 0,
781+ &v_max_variable_storage_size,
782+ &v_remaining_variable_storage_size,
783+ &v_max_variable_size);
784+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
785+ UNSIGNED_LONGLONGS_EQUAL(v_max_variable_storage_size, v_remaining_variable_storage_size);
786+
787+ /* Add some variables to the store */
788 std::wstring var_name_1 = L"varibale_1";
789 std::wstring var_name_2 = L"varibale_2";
790 std::wstring var_name_3 = L"varibale_3";
791 std::string set_data = "Some variable data";
792
793- /* Add some variables to the store */
794 efi_status = m_client->set_variable(
795 m_common_guid,
796 var_name_1,
797@@ -365,6 +391,33 @@ TEST(SmmVariableServiceTests, enumerateStoreContents)
798
799 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
800
801+ /* Query variable info again and check it's as expected */
802+ size_t max_variable_storage_size = 0;
803+ size_t max_variable_size = 0;
804+ size_t remaining_variable_storage_size = 0;
805+
806+ /* Check non-volatile - two variables have been added */
807+ efi_status = m_client->query_variable_info(
808+ EFI_VARIABLE_NON_VOLATILE,
809+ &max_variable_storage_size,
810+ &remaining_variable_storage_size,
811+ &max_variable_size);
812+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
813+ UNSIGNED_LONGLONGS_EQUAL(
814+ (nv_remaining_variable_storage_size - set_data.size() * 2),
815+ remaining_variable_storage_size);
816+
817+ /* Check volatile - one variables have been added */
818+ efi_status = m_client->query_variable_info(
819+ 0,
820+ &max_variable_storage_size,
821+ &remaining_variable_storage_size,
822+ &max_variable_size);
823+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
824+ UNSIGNED_LONGLONGS_EQUAL(
825+ (v_remaining_variable_storage_size - set_data.size() * 1),
826+ remaining_variable_storage_size);
827+
828 /* Enumerate store contents - expect the values we added */
829 std::wstring var_name;
830 EFI_GUID guid = {0};