blob: cf7357e84c91380f96a3eeb548006cf1f8234ba7 [file] [log] [blame]
Brad Bishopbec4ebc2022-08-03 09:55:16 -04001From 254f564c76320478e7b509faf279c0c493470657 Mon Sep 17 00:00:00 2001
2From: Julian Hall <julian.hall@arm.com>
3Date: Thu, 2 Dec 2021 10:15:54 +0000
4Subject: [PATCH] Add uefi variable append write support
5
6Adds support for extending UEFI variable data handled by the
7smm_variable service provider using the EFI_VARIABLE_APPEND_WRITE
8attribute.
9
10Signed-off-by: Julian Hall <julian.hall@arm.com>
11Change-Id: I7a6562327bc0a5ce5cd0e85276325227b83e9f9e
12
13Upstream-Status: Pending [Not submitted to upstream yet]
14Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com>
15
16
17---
18 .../backend/test/variable_index_tests.cpp | 90 +++---
19 .../backend/test/variable_store_tests.cpp | 40 ++-
20 .../backend/uefi_variable_store.c | 263 +++++++++++-------
21 .../smm_variable/backend/variable_index.c | 95 +++----
22 .../smm_variable/backend/variable_index.h | 58 ++--
23 .../backend/variable_index_iterator.c | 4 +-
24 .../backend/variable_index_iterator.h | 2 +-
25 .../service/smm_variable_service_tests.cpp | 48 ++++
26 protocols/service/smm_variable/parameters.h | 3 +
27 9 files changed, 364 insertions(+), 239 deletions(-)
28
29diff --git a/components/service/smm_variable/backend/test/variable_index_tests.cpp b/components/service/smm_variable/backend/test/variable_index_tests.cpp
30index c8bacf97..8edc0e70 100644
31--- a/components/service/smm_variable/backend/test/variable_index_tests.cpp
32+++ b/components/service/smm_variable/backend/test/variable_index_tests.cpp
33@@ -69,34 +69,37 @@ TEST_GROUP(UefiVariableIndexTests)
34
35 void create_variables()
36 {
37- const struct variable_info *info = NULL;
38+ struct variable_info *info = NULL;
39
40- info = variable_index_add_variable(
41+ info = variable_index_add_entry(
42 &m_variable_index,
43 &guid_1,
44 name_1.size() * sizeof(int16_t),
45- name_1.data(),
46- EFI_VARIABLE_BOOTSERVICE_ACCESS);
47-
48+ name_1.data());
49 CHECK_TRUE(info);
50+ variable_index_set_variable(
51+ info,
52+ EFI_VARIABLE_BOOTSERVICE_ACCESS);
53
54- info = variable_index_add_variable(
55+ info = variable_index_add_entry(
56 &m_variable_index,
57 &guid_2,
58 name_2.size() * sizeof(int16_t),
59- name_2.data(),
60- EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS);
61-
62+ name_2.data());
63 CHECK_TRUE(info);
64+ variable_index_set_variable(
65+ info,
66+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS);
67
68- info = variable_index_add_variable(
69+ info = variable_index_add_entry(
70 &m_variable_index,
71 &guid_1,
72 name_3.size() * sizeof(int16_t),
73- name_3.data(),
74- EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS);
75-
76+ name_3.data());
77 CHECK_TRUE(info);
78+ variable_index_set_variable(
79+ info,
80+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS);
81 }
82
83 static const size_t MAX_VARIABLES = 10;
84@@ -111,7 +114,7 @@ TEST_GROUP(UefiVariableIndexTests)
85
86 TEST(UefiVariableIndexTests, emptyIndexOperations)
87 {
88- const struct variable_info *info = NULL;
89+ struct variable_info *info = NULL;
90
91 /* Expect not to find a variable */
92 info = variable_index_find(
93@@ -130,36 +133,34 @@ TEST(UefiVariableIndexTests, emptyIndexOperations)
94 POINTERS_EQUAL(NULL, info);
95
96 /* Remove should silently return */
97- variable_index_remove_variable(
98+ variable_index_clear_variable(
99 &m_variable_index,
100 info);
101 }
102
103 TEST(UefiVariableIndexTests, addWithOversizedName)
104 {
105- const struct variable_info *info = NULL;
106+ struct variable_info *info = NULL;
107 std::vector<int16_t> name;
108
109 name = to_variable_name(L"a long variable name that exceeds the length limit");
110
111- info = variable_index_add_variable(
112+ info = variable_index_add_entry(
113 &m_variable_index,
114 &guid_1,
115 name.size() * sizeof(int16_t),
116- name.data(),
117- EFI_VARIABLE_BOOTSERVICE_ACCESS);
118+ name.data());
119
120 /* Expect the add to fail because of an oversized name */
121 POINTERS_EQUAL(NULL, info);
122
123 name = to_variable_name(L"a long variable name that fits!");
124
125- info = variable_index_add_variable(
126+ info = variable_index_add_entry(
127 &m_variable_index,
128 &guid_1,
129 name.size() * sizeof(int16_t),
130- name.data(),
131- EFI_VARIABLE_BOOTSERVICE_ACCESS);
132+ name.data());
133
134 /* Expect the add succeed */
135 CHECK_TRUE(info);
136@@ -167,18 +168,17 @@ TEST(UefiVariableIndexTests, addWithOversizedName)
137
138 TEST(UefiVariableIndexTests, variableIndexFull)
139 {
140- const struct variable_info *info = NULL;
141+ struct variable_info *info = NULL;
142 EFI_GUID guid = guid_1;
143
144 /* Expect to be able to fill the index */
145 for (size_t i = 0; i < MAX_VARIABLES; ++i) {
146
147- info = variable_index_add_variable(
148+ info = variable_index_add_entry(
149 &m_variable_index,
150 &guid,
151 name_1.size() * sizeof(int16_t),
152- name_1.data(),
153- EFI_VARIABLE_BOOTSERVICE_ACCESS);
154+ name_1.data());
155
156 CHECK_TRUE(info);
157
158@@ -187,12 +187,11 @@ TEST(UefiVariableIndexTests, variableIndexFull)
159 }
160
161 /* Variable index should now be full */
162- info = variable_index_add_variable(
163+ info = variable_index_add_entry(
164 &m_variable_index,
165 &guid,
166 name_1.size() * sizeof(int16_t),
167- name_1.data(),
168- EFI_VARIABLE_BOOTSERVICE_ACCESS);
169+ name_1.data());
170
171 POINTERS_EQUAL(NULL, info);
172 }
173@@ -323,7 +322,7 @@ TEST(UefiVariableIndexTests, dumpBufferTooSmall)
174 TEST(UefiVariableIndexTests, removeVariable)
175 {
176 uint8_t buffer[MAX_VARIABLES * sizeof(struct variable_metadata)];
177- const struct variable_info *info = NULL;
178+ struct variable_info *info = NULL;
179
180 create_variables();
181
182@@ -334,7 +333,7 @@ TEST(UefiVariableIndexTests, removeVariable)
183 name_2.size() * sizeof(int16_t),
184 name_2.data());
185
186- variable_index_remove_variable(
187+ variable_index_clear_variable(
188 &m_variable_index,
189 info);
190
191@@ -352,7 +351,7 @@ TEST(UefiVariableIndexTests, removeVariable)
192 name_1.size() * sizeof(int16_t),
193 name_1.data());
194
195- variable_index_remove_variable(
196+ variable_index_clear_variable(
197 &m_variable_index,
198 info);
199
200@@ -370,7 +369,7 @@ TEST(UefiVariableIndexTests, removeVariable)
201 name_3.size() * sizeof(int16_t),
202 name_3.data());
203
204- variable_index_remove_variable(
205+ variable_index_clear_variable(
206 &m_variable_index,
207 info);
208
209@@ -395,7 +394,7 @@ TEST(UefiVariableIndexTests, removeVariable)
210
211 TEST(UefiVariableIndexTests, checkIterator)
212 {
213- const struct variable_info *info = NULL;
214+ struct variable_info *info = NULL;
215
216 create_variables();
217
218@@ -419,7 +418,7 @@ TEST(UefiVariableIndexTests, checkIterator)
219 UNSIGNED_LONGS_EQUAL(name_2.size() * sizeof(int16_t), info->metadata.name_size);
220 MEMCMP_EQUAL(name_2.data(), info->metadata.name, info->metadata.name_size);
221
222- const struct variable_info *info_to_remove = info;
223+ struct variable_info *info_to_remove = info;
224
225 variable_index_iterator_next(&iter);
226 CHECK_FALSE(variable_index_iterator_is_done(&iter));
227@@ -435,7 +434,8 @@ TEST(UefiVariableIndexTests, checkIterator)
228 CHECK_TRUE(variable_index_iterator_is_done(&iter));
229
230 /* Now remove the middle entry */
231- variable_index_remove_variable(&m_variable_index, info_to_remove);
232+ variable_index_clear_variable(&m_variable_index, info_to_remove);
233+ variable_index_remove_unused_entry(&m_variable_index, info_to_remove);
234
235 /* Iterate again but this time there should only be two entries */
236 variable_index_iterator_first(&iter, &m_variable_index);
237@@ -478,7 +478,7 @@ TEST(UefiVariableIndexTests, setCheckConstraintsExistingVar)
238 constraints.max_size = 100;
239
240 /* Set check constraints on one of the variables */
241- const struct variable_info *info = variable_index_find(
242+ struct variable_info *info = variable_index_find(
243 &m_variable_index,
244 &guid_2,
245 name_2.size() * sizeof(int16_t),
246@@ -488,7 +488,7 @@ TEST(UefiVariableIndexTests, setCheckConstraintsExistingVar)
247 CHECK_TRUE(info->is_variable_set);
248 CHECK_FALSE(info->is_constraints_set);
249
250- variable_index_update_constraints(info, &constraints);
251+ variable_index_set_constraints(info, &constraints);
252
253 CHECK_TRUE(info->is_constraints_set);
254 CHECK_TRUE(info->is_variable_set);
255@@ -496,7 +496,7 @@ TEST(UefiVariableIndexTests, setCheckConstraintsExistingVar)
256 /* Remove the variable but still expect the variable to be indexed
257 * because of the set constraints.
258 */
259- variable_index_remove_variable(
260+ variable_index_clear_variable(
261 &m_variable_index,
262 info);
263
264@@ -588,7 +588,7 @@ TEST(UefiVariableIndexTests, setCheckConstraintsNonExistingVar)
265 constraints.max_size = 100;
266
267 /* Initially expect no variable_info */
268- const struct variable_info *info = variable_index_find(
269+ struct variable_info *info = variable_index_find(
270 &m_variable_index,
271 &guid_2,
272 name_2.size() * sizeof(int16_t),
273@@ -597,19 +597,19 @@ TEST(UefiVariableIndexTests, setCheckConstraintsNonExistingVar)
274 CHECK_FALSE(info);
275
276 /* Adding the check constraints should result in an entry being added */
277- info = variable_index_add_constraints(
278+ info = variable_index_add_entry(
279 &m_variable_index,
280 &guid_2,
281 name_2.size() * sizeof(int16_t),
282- name_2.data(),
283- &constraints);
284-
285+ name_2.data());
286 CHECK_TRUE(info);
287+
288+ variable_index_set_constraints(info, &constraints);
289 CHECK_FALSE(info->is_variable_set);
290 CHECK_TRUE(info->is_constraints_set);
291
292 /* Updating the variable should cause the variable to be marked as set */
293- variable_index_update_variable(info, EFI_VARIABLE_RUNTIME_ACCESS);
294+ variable_index_set_variable(info, EFI_VARIABLE_RUNTIME_ACCESS);
295
296 CHECK_TRUE(info->is_variable_set);
297 CHECK_TRUE(info->is_constraints_set);
298diff --git a/components/service/smm_variable/backend/test/variable_store_tests.cpp b/components/service/smm_variable/backend/test/variable_store_tests.cpp
299index f6aba13a..578f118f 100644
300--- a/components/service/smm_variable/backend/test/variable_store_tests.cpp
301+++ b/components/service/smm_variable/backend/test/variable_store_tests.cpp
302@@ -250,6 +250,21 @@ TEST(UefiVariableStoreTests, setGetRoundtrip)
303 /* Expect got variable data to be the same as the set value */
304 UNSIGNED_LONGLONGS_EQUAL(input_data.size(), output_data.size());
305 LONGS_EQUAL(0, input_data.compare(output_data));
306+
307+ /* Extend the variable using an append write */
308+ std::string input_data2 = " jumps over the lazy dog";
309+
310+ status = set_variable(var_name, input_data2, EFI_VARIABLE_APPEND_WRITE);
311+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
312+
313+ status = get_variable(var_name, output_data);
314+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
315+
316+ std::string expected_output = input_data + input_data2;
317+
318+ /* Expect the append write operation to have extended the variable */
319+ UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size());
320+ LONGS_EQUAL(0, expected_output.compare(output_data));
321 }
322
323 TEST(UefiVariableStoreTests, persistentSetGet)
324@@ -259,7 +274,8 @@ TEST(UefiVariableStoreTests, persistentSetGet)
325 std::string input_data = "quick brown fox";
326 std::string output_data;
327
328- status = set_variable(var_name, input_data, EFI_VARIABLE_NON_VOLATILE);
329+ status = set_variable(var_name, input_data,
330+ EFI_VARIABLE_NON_VOLATILE);
331 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
332
333 status = get_variable(var_name, output_data);
334@@ -269,6 +285,22 @@ TEST(UefiVariableStoreTests, persistentSetGet)
335 UNSIGNED_LONGLONGS_EQUAL(input_data.size(), output_data.size());
336 LONGS_EQUAL(0, input_data.compare(output_data));
337
338+ /* Extend the variable using an append write */
339+ std::string input_data2 = " jumps over the lazy dog";
340+
341+ status = set_variable(var_name, input_data2,
342+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_APPEND_WRITE);
343+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
344+
345+ status = get_variable(var_name, output_data);
346+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
347+
348+ std::string expected_output = input_data + input_data2;
349+
350+ /* Expect the append write operation to have extended the variable */
351+ UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size());
352+ LONGS_EQUAL(0, expected_output.compare(output_data));
353+
354 /* Expect the variable to survive a power cycle */
355 power_cycle();
356
357@@ -277,8 +309,8 @@ TEST(UefiVariableStoreTests, persistentSetGet)
358 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
359
360 /* Still expect got variable data to be the same as the set value */
361- UNSIGNED_LONGLONGS_EQUAL(input_data.size(), output_data.size());
362- LONGS_EQUAL(0, input_data.compare(output_data));
363+ UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size());
364+ LONGS_EQUAL(0, expected_output.compare(output_data));
365 }
366
367 TEST(UefiVariableStoreTests, removeVolatile)
368@@ -317,7 +349,7 @@ TEST(UefiVariableStoreTests, removePersistent)
369 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
370
371 /* Remove by setting with zero data length */
372- status = set_variable(var_name, std::string(), 0);
373+ status = set_variable(var_name, std::string(), EFI_VARIABLE_NON_VOLATILE);
374 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status);
375
376 /* Expect variable to no loger exist */
377diff --git a/components/service/smm_variable/backend/uefi_variable_store.c b/components/service/smm_variable/backend/uefi_variable_store.c
378index b7091d75..bcb85995 100644
379--- a/components/service/smm_variable/backend/uefi_variable_store.c
380+++ b/components/service/smm_variable/backend/uefi_variable_store.c
381@@ -46,6 +46,13 @@ static efi_status_t load_variable_data(
382 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var,
383 size_t max_data_len);
384
385+static psa_status_t append_write(
386+ struct storage_backend *storage_backend,
387+ uint32_t client_id,
388+ uint64_t uid,
389+ size_t data_length,
390+ const void *data);
391+
392 static void purge_orphan_index_entries(
393 struct uefi_variable_store *context);
394
395@@ -113,40 +120,45 @@ efi_status_t uefi_variable_store_set_variable(
396 struct uefi_variable_store *context,
397 const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var)
398 {
399+ bool should_sync_index = false;
400+
401+ /* Validate incoming request */
402 efi_status_t status = check_name_terminator(var->Name, var->NameSize);
403 if (status != EFI_SUCCESS) return status;
404
405 status = check_capabilities(var);
406- bool should_sync_index = false;
407-
408 if (status != EFI_SUCCESS) return status;
409
410- /* Find in index */
411- const struct variable_info *info = variable_index_find(
412+ /* Find an existing entry in the variable index or add a new one */
413+ struct variable_info *info = variable_index_find(
414 &context->variable_index,
415 &var->Guid,
416 var->NameSize,
417 var->Name);
418
419- if (info) {
420+ if (!info) {
421
422- /* Variable info already exists */
423- status = check_access_permitted_on_set(context, info, var);
424+ info = variable_index_add_entry(
425+ &context->variable_index,
426+ &var->Guid,
427+ var->NameSize,
428+ var->Name);
429
430- if (status == EFI_SUCCESS) {
431+ if (!info) return EFI_OUT_OF_RESOURCES;
432+ }
433
434- should_sync_index =
435- (var->Attributes & EFI_VARIABLE_NON_VOLATILE) ||
436- (info->is_variable_set && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE));
437+ /* Control access */
438+ status = check_access_permitted_on_set(context, info, var);
439
440- if (var->DataSize) {
441+ if (status == EFI_SUCCESS) {
442
443- /* It's a set rather than a remove operation */
444- variable_index_update_variable(
445- info,
446- var->Attributes);
447- }
448- else {
449+ /* Access permitted */
450+ if (info->is_variable_set) {
451+
452+ /* It's a request to update to an existing variable */
453+ if (!(var->Attributes &
454+ (EFI_VARIABLE_APPEND_WRITE | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS_MASK)) &&
455+ !var->DataSize) {
456
457 /* It's a remove operation - for a remove, the variable
458 * data must be removed from the storage backend before
459@@ -155,30 +167,29 @@ efi_status_t uefi_variable_store_set_variable(
460 * the storage backend without a corresponding index entry.
461 */
462 remove_variable_data(context, info);
463- variable_index_remove_variable(&context->variable_index, info);
464+ variable_index_clear_variable(&context->variable_index, info);
465
466- /* Variable info no longer valid */
467- info = NULL;
468+ should_sync_index = (var->Attributes & EFI_VARIABLE_NON_VOLATILE);
469+ }
470+ else {
471+
472+ /* It's a set operation where variable data is potentially
473+ * being overwritten or extended.
474+ */
475+ if ((var->Attributes & ~EFI_VARIABLE_APPEND_WRITE) != info->metadata.attributes) {
476+
477+ /* Modifying attributes is forbidden */
478+ return EFI_INVALID_PARAMETER;
479+ }
480 }
481 }
482 else {
483
484- /* Access forbidden */
485- info = NULL;
486- }
487- }
488- else if (var->DataSize) {
489+ /* It's a request to create a new variable */
490+ variable_index_set_variable(info, var->Attributes);
491
492- /* It's a new variable */
493- info = variable_index_add_variable(
494- &context->variable_index,
495- &var->Guid,
496- var->NameSize,
497- var->Name,
498- var->Attributes);
499-
500- if (!info) status = EFI_OUT_OF_RESOURCES;
501- should_sync_index = info && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE);
502+ should_sync_index = (var->Attributes & EFI_VARIABLE_NON_VOLATILE);
503+ }
504 }
505
506 /* The order of these operations is important. For an update
507@@ -195,11 +206,13 @@ efi_status_t uefi_variable_store_set_variable(
508 }
509
510 /* Store any variable data to the storage backend */
511- if (info && (status == EFI_SUCCESS)) {
512+ if (info->is_variable_set && (status == EFI_SUCCESS)) {
513
514 status = store_variable_data(context, info, var);
515 }
516
517+ variable_index_remove_unused_entry(&context->variable_index, info);
518+
519 return status;
520 }
521
522@@ -293,54 +306,42 @@ efi_status_t uefi_variable_store_set_var_check_property(
523 efi_status_t status = check_name_terminator(property->Name, property->NameSize);
524 if (status != EFI_SUCCESS) return status;
525
526- /* Find in index */
527- const struct variable_info *info = variable_index_find(
528+ /* Find in index or create a new entry */
529+ struct variable_info *info = variable_index_find(
530 &context->variable_index,
531 &property->Guid,
532 property->NameSize,
533 property->Name);
534
535- if (info) {
536+ if (!info) {
537
538- /* Applying check constraints to an existing variable that may have
539- * constraints already set. These could constrain the setting of
540- * the constraints.
541- */
542- struct variable_constraints constraints = info->check_constraints;
543-
544- status = variable_checker_set_constraints(
545- &constraints,
546- info->is_constraints_set,
547- &property->VariableProperty);
548-
549- if (status == EFI_SUCCESS) {
550+ info = variable_index_add_entry(
551+ &context->variable_index,
552+ &property->Guid,
553+ property->NameSize,
554+ property->Name);
555
556- variable_index_update_constraints(info, &constraints);
557- }
558+ if (!info) return EFI_OUT_OF_RESOURCES;
559 }
560- else {
561-
562- /* Applying check constraints for a new variable */
563- struct variable_constraints constraints;
564
565- status = variable_checker_set_constraints(
566- &constraints,
567- false,
568- &property->VariableProperty);
569+ /* Applying check constraints to an existing variable that may have
570+ * constraints already set. These could constrain the setting of
571+ * the constraints.
572+ */
573+ struct variable_constraints constraints = info->check_constraints;
574
575- if (status == EFI_SUCCESS) {
576+ status = variable_checker_set_constraints(
577+ &constraints,
578+ info->is_constraints_set,
579+ &property->VariableProperty);
580
581- info = variable_index_add_constraints(
582- &context->variable_index,
583- &property->Guid,
584- property->NameSize,
585- property->Name,
586- &constraints);
587+ if (status == EFI_SUCCESS) {
588
589- if (!info) status = EFI_OUT_OF_RESOURCES;
590- }
591+ variable_index_set_constraints(info, &constraints);
592 }
593
594+ variable_index_remove_unused_entry(&context->variable_index, info);
595+
596 return status;
597 }
598
599@@ -440,7 +441,8 @@ static efi_status_t check_capabilities(
600 if (var->Attributes & ~(
601 EFI_VARIABLE_NON_VOLATILE |
602 EFI_VARIABLE_BOOTSERVICE_ACCESS |
603- EFI_VARIABLE_RUNTIME_ACCESS)) {
604+ EFI_VARIABLE_RUNTIME_ACCESS |
605+ EFI_VARIABLE_APPEND_WRITE)) {
606
607 /* An unsupported attribute has been requested */
608 status = EFI_UNSUPPORTED;
609@@ -486,17 +488,6 @@ static efi_status_t check_access_permitted_on_set(
610 var->DataSize);
611 }
612
613- if ((status == EFI_SUCCESS) && var->DataSize) {
614-
615- /* Restrict which attributes can be modified for an existing variable */
616- if ((var->Attributes & EFI_VARIABLE_NON_VOLATILE) !=
617- (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE)) {
618-
619- /* Don't permit change of storage class */
620- status = EFI_INVALID_PARAMETER;
621- }
622- }
623-
624 return status;
625 }
626
627@@ -518,20 +509,34 @@ static efi_status_t store_variable_data(
628
629 if (storage_backend) {
630
631- psa_status = storage_backend->interface->set(
632- storage_backend->context,
633- context->owner_id,
634- info->metadata.uid,
635- data_len,
636- data,
637- PSA_STORAGE_FLAG_NONE);
638+ if (!(var->Attributes & EFI_VARIABLE_APPEND_WRITE)) {
639+
640+ /* Create or overwrite variable data */
641+ psa_status = storage_backend->interface->set(
642+ storage_backend->context,
643+ context->owner_id,
644+ info->metadata.uid,
645+ data_len,
646+ data,
647+ PSA_STORAGE_FLAG_NONE);
648+ }
649+ else {
650+
651+ /* Append new data to existing variable data */
652+ psa_status = append_write(
653+ storage_backend,
654+ context->owner_id,
655+ info->metadata.uid,
656+ data_len,
657+ data);
658+ }
659 }
660
661 if ((psa_status != PSA_SUCCESS) && is_nv) {
662
663 /* A storage failure has occurred so attempt to fix any
664- * mismatch between the variable index and stored NV variables.
665- */
666+ * mismatch between the variable index and stored NV variables.
667+ */
668 purge_orphan_index_entries(context);
669 }
670
671@@ -598,6 +603,76 @@ static efi_status_t load_variable_data(
672 return psa_to_efi_storage_status(psa_status);
673 }
674
675+static psa_status_t append_write(
676+ struct storage_backend *storage_backend,
677+ uint32_t client_id,
678+ uint64_t uid,
679+ size_t data_length,
680+ const void *data)
681+{
682+ struct psa_storage_info_t storage_info;
683+
684+ if (data_length == 0) return PSA_SUCCESS;
685+
686+ psa_status_t psa_status = storage_backend->interface->get_info(
687+ storage_backend->context,
688+ client_id,
689+ uid,
690+ &storage_info);
691+
692+ if (psa_status != PSA_SUCCESS) return psa_status;
693+
694+ /* Determine size of appended variable */
695+ size_t new_size = storage_info.size + data_length;
696+
697+ /* Defend against integer overflow */
698+ if (new_size < storage_info.size) return PSA_ERROR_INVALID_ARGUMENT;
699+
700+ /* Storage backend doesn't support an append operation so we need
701+ * need to read the current variable data, extend it and write it back.
702+ */
703+ uint8_t *rw_buf = malloc(new_size);
704+ if (!rw_buf) return PSA_ERROR_INSUFFICIENT_MEMORY;
705+
706+ size_t old_size = 0;
707+ psa_status = storage_backend->interface->get(
708+ storage_backend->context,
709+ client_id,
710+ uid,
711+ 0,
712+ new_size,
713+ rw_buf,
714+ &old_size);
715+
716+ if (psa_status == PSA_SUCCESS) {
717+
718+ if ((old_size + data_length) <= new_size) {
719+
720+ /* Extend the variable data */
721+ memcpy(&rw_buf[old_size], data, data_length);
722+
723+ psa_status = storage_backend->interface->set(
724+ storage_backend->context,
725+ client_id,
726+ uid,
727+ old_size + data_length,
728+ rw_buf,
729+ storage_info.flags);
730+ }
731+ else {
732+
733+ /* There's a mismatch between the length obtained from
734+ * get_info() and the subsequent length returned by get().
735+ */
736+ psa_status = PSA_ERROR_STORAGE_FAILURE;
737+ }
738+ }
739+
740+ free(rw_buf);
741+
742+ return psa_status;
743+}
744+
745 static void purge_orphan_index_entries(
746 struct uefi_variable_store *context)
747 {
748@@ -612,7 +687,7 @@ static void purge_orphan_index_entries(
749 */
750 while (!variable_index_iterator_is_done(&iter)) {
751
752- const struct variable_info *info = variable_index_iterator_current(&iter);
753+ struct variable_info *info = variable_index_iterator_current(&iter);
754
755 if (info->is_variable_set && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE)) {
756
757@@ -628,7 +703,7 @@ static void purge_orphan_index_entries(
758 if (psa_status != PSA_SUCCESS) {
759
760 /* Detected a mismatch between the index and storage */
761- variable_index_remove_variable(&context->variable_index, info);
762+ variable_index_clear_variable(&context->variable_index, info);
763 any_orphans = true;
764 }
765 }
766diff --git a/components/service/smm_variable/backend/variable_index.c b/components/service/smm_variable/backend/variable_index.c
767index 99d7c97a..a8a55753 100644
768--- a/components/service/smm_variable/backend/variable_index.c
769+++ b/components/service/smm_variable/backend/variable_index.c
770@@ -132,13 +132,13 @@ size_t variable_index_max_dump_size(
771 return sizeof(struct variable_metadata) * context->max_variables;
772 }
773
774-const struct variable_info *variable_index_find(
775- const struct variable_index *context,
776+struct variable_info *variable_index_find(
777+ struct variable_index *context,
778 const EFI_GUID *guid,
779 size_t name_size,
780 const int16_t *name)
781 {
782- const struct variable_info *result = NULL;
783+ struct variable_info *result = NULL;
784 int pos = find_variable(context, guid, name_size, name);
785
786 if (pos >= 0) {
787@@ -149,13 +149,13 @@ const struct variable_info *variable_index_find(
788 return result;
789 }
790
791-const struct variable_info *variable_index_find_next(
792+struct variable_info *variable_index_find_next(
793 const struct variable_index *context,
794 const EFI_GUID *guid,
795 size_t name_size,
796 const int16_t *name)
797 {
798- const struct variable_info *result = NULL;
799+ struct variable_info *result = NULL;
800
801 if (name_size >= sizeof(int16_t)) {
802
803@@ -263,12 +263,11 @@ static struct variable_entry *add_entry(
804 return entry;
805 }
806
807-const struct variable_info *variable_index_add_variable(
808+struct variable_info *variable_index_add_entry(
809 struct variable_index *context,
810 const EFI_GUID *guid,
811 size_t name_size,
812- const int16_t *name,
813- uint32_t attributes)
814+ const int16_t *name)
815 {
816 struct variable_info *info = NULL;
817 struct variable_entry *entry = add_entry(context, guid, name_size, name);
818@@ -276,40 +275,41 @@ const struct variable_info *variable_index_add_variable(
819 if (entry) {
820
821 info = &entry->info;
822-
823- info->metadata.attributes = attributes;
824- info->is_variable_set = true;
825-
826- mark_dirty(entry);
827 }
828
829 return info;
830 }
831
832-const struct variable_info *variable_index_add_constraints(
833+void variable_index_remove_unused_entry(
834 struct variable_index *context,
835- const EFI_GUID *guid,
836- size_t name_size,
837- const int16_t *name,
838- const struct variable_constraints *constraints)
839+ struct variable_info *info)
840 {
841- struct variable_info *info = NULL;
842- struct variable_entry *entry = add_entry(context, guid, name_size, name);
843-
844- if (entry) {
845+ if (info &&
846+ !info->is_constraints_set &&
847+ !info->is_variable_set) {
848
849- info = &entry->info;
850+ struct variable_entry *entry = containing_entry(info);
851+ entry->in_use = false;
852
853- info->check_constraints = *constraints;
854- info->is_constraints_set = true;
855+ memset(info, 0, sizeof(struct variable_info));
856 }
857+}
858
859- return info;
860+void variable_index_set_variable(
861+ struct variable_info *info,
862+ uint32_t attributes)
863+{
864+ struct variable_entry *entry = containing_entry(info);
865+
866+ info->metadata.attributes = attributes;
867+ info->is_variable_set = true;
868+
869+ mark_dirty(entry);
870 }
871
872-void variable_index_remove_variable(
873+void variable_index_clear_variable(
874 struct variable_index *context,
875- const struct variable_info *info)
876+ struct variable_info *info)
877 {
878 if (info) {
879
880@@ -318,48 +318,17 @@ void variable_index_remove_variable(
881
882 /* Mark variable as no longer set */
883 entry->info.is_variable_set = false;
884-
885- /* Entry may still be needed if check constraints were set */
886- entry->in_use = info->is_constraints_set;
887-
888- if (!entry->in_use) {
889-
890- /* Entry not needed so wipe */
891- memset(&entry->info, 0, sizeof(struct variable_info));
892- }
893 }
894 }
895
896-void variable_index_update_variable(
897- const struct variable_info *info,
898- uint32_t attributes)
899-{
900- if (info) {
901-
902- struct variable_info *modified_info = (struct variable_info*)info;
903- struct variable_entry *entry = containing_entry(modified_info);
904-
905- if (!modified_info->is_variable_set ||
906- (attributes != modified_info->metadata.attributes)) {
907-
908- /* The update changes the variable_info state */
909- modified_info->is_variable_set = true;
910- modified_info->metadata.attributes = attributes;
911- mark_dirty(entry);
912- }
913- }
914-}
915-
916-void variable_index_update_constraints(
917- const struct variable_info *info,
918+void variable_index_set_constraints(
919+ struct variable_info *info,
920 const struct variable_constraints *constraints)
921 {
922 if (info) {
923
924- struct variable_info *modified_info = (struct variable_info*)info;
925-
926- modified_info->check_constraints = *constraints;
927- modified_info->is_constraints_set = true;
928+ info->check_constraints = *constraints;
929+ info->is_constraints_set = true;
930 }
931 }
932
933diff --git a/components/service/smm_variable/backend/variable_index.h b/components/service/smm_variable/backend/variable_index.h
934index e109d0d1..63f42ab6 100644
935--- a/components/service/smm_variable/backend/variable_index.h
936+++ b/components/service/smm_variable/backend/variable_index.h
937@@ -119,8 +119,8 @@ size_t variable_index_max_dump_size(
938 *
939 * @return Pointer to variable_info or NULL
940 */
941-const struct variable_info *variable_index_find(
942- const struct variable_index *context,
943+struct variable_info *variable_index_find(
944+ struct variable_index *context,
945 const EFI_GUID *guid,
946 size_t name_size,
947 const int16_t *name);
948@@ -135,78 +135,76 @@ const struct variable_info *variable_index_find(
949 *
950 * @return Pointer to variable_info or NULL
951 */
952-const struct variable_info *variable_index_find_next(
953+struct variable_info *variable_index_find_next(
954 const struct variable_index *context,
955 const EFI_GUID *guid,
956 size_t name_size,
957 const int16_t *name);
958
959 /**
960- * @brief Add a new variable to the index
961+ * @brief Add a new entry to the index
962+ *
963+ * An entry is needed either when a new variable is created or
964+ * when variable constraints are set for a variable that doesn't
965+ * yet exist.
966 *
967 * @param[in] context variable_index
968 * @param[in] guid The variable's guid
969 * @param[in] name_size The name parameter's size
970 * @param[in] name The variable's name
971- * @param[in] attributes The variable's attributes
972 *
973 * @return Pointer to variable_info or NULL
974 */
975-const struct variable_info *variable_index_add_variable(
976+struct variable_info *variable_index_add_entry(
977 struct variable_index *context,
978 const EFI_GUID *guid,
979 size_t name_size,
980- const int16_t *name,
981- uint32_t attributes);
982+ const int16_t *name);
983
984 /**
985- * @brief Remove a variable from the index
986+ * @brief Remove an unused entry from the index
987 *
988- * Removes a variable from the index if it exists.
989+ * Removes an entry if it is not in use.
990 *
991 * @param[in] context variable_index
992 * @param[in] info The variable info corresponding to the entry to remove
993 */
994-void variable_index_remove_variable(
995+void variable_index_remove_unused_entry(
996 struct variable_index *context,
997- const struct variable_info *info);
998+ struct variable_info *info);
999
1000 /**
1001- * @brief Update a variable that's already in the index
1002+ * @brief Set a variable to the index
1003+ *
1004+ * An entry for the variable must already exist.
1005 *
1006 * @param[in] info variable info
1007 * @param[in] attributes The variable's attributes
1008 */
1009-void variable_index_update_variable(
1010- const struct variable_info *info,
1011+void variable_index_set_variable(
1012+ struct variable_info *info,
1013 uint32_t attributes);
1014
1015 /**
1016- * @brief Add a new check constraints object to the index
1017+ * @brief Clear a variable from the index
1018 *
1019- * @param[in] context variable_index
1020- * @param[in] guid The variable's guid
1021- * @param[in] name_size The name parameter's size
1022- * @param[in] name The variable's name
1023- * @param[in] constraints The check constraints
1024+ * Clears a variable from the index
1025 *
1026- * @return Pointer to variable_info or NULL
1027+ * @param[in] context variable_index
1028+ * @param[in] info The variable info corresponding to the variable to clear
1029 */
1030-const struct variable_info *variable_index_add_constraints(
1031+void variable_index_clear_variable(
1032 struct variable_index *context,
1033- const EFI_GUID *guid,
1034- size_t name_size,
1035- const int16_t *name,
1036- const struct variable_constraints *constraints);
1037+ struct variable_info *info);
1038
1039 /**
1040- * @brief Update variable constraints that are already in the index
1041+ * @brief Set a check constraints object associated with a variavle
1042 *
1043 * @param[in] info variable info
1044 * @param[in] constraints The check constraints
1045 */
1046-void variable_index_update_constraints(
1047- const struct variable_info *info,
1048+void variable_index_set_constraints(
1049+ struct variable_info *info,
1050 const struct variable_constraints *constraints);
1051
1052 /**
1053diff --git a/components/service/smm_variable/backend/variable_index_iterator.c b/components/service/smm_variable/backend/variable_index_iterator.c
1054index 7cc6dc7a..8f8fc741 100644
1055--- a/components/service/smm_variable/backend/variable_index_iterator.c
1056+++ b/components/service/smm_variable/backend/variable_index_iterator.c
1057@@ -31,10 +31,10 @@ bool variable_index_iterator_is_done(
1058 return iter->current_pos >= iter->variable_index->max_variables;
1059 }
1060
1061-const struct variable_info *variable_index_iterator_current(
1062+struct variable_info *variable_index_iterator_current(
1063 const struct variable_index_iterator *iter)
1064 {
1065- const struct variable_info *current = NULL;
1066+ struct variable_info *current = NULL;
1067
1068 if (!variable_index_iterator_is_done(iter)) {
1069
1070diff --git a/components/service/smm_variable/backend/variable_index_iterator.h b/components/service/smm_variable/backend/variable_index_iterator.h
1071index f64a2c49..7ff77c50 100644
1072--- a/components/service/smm_variable/backend/variable_index_iterator.h
1073+++ b/components/service/smm_variable/backend/variable_index_iterator.h
1074@@ -54,7 +54,7 @@ bool variable_index_iterator_is_done(
1075 *
1076 * @return Pointer to variable_info or NULL
1077 */
1078-const struct variable_info *variable_index_iterator_current(
1079+struct variable_info *variable_index_iterator_current(
1080 const struct variable_index_iterator *iter);
1081
1082 /**
1083diff --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
1084index d76d9cce..088940a8 100644
1085--- a/components/service/smm_variable/test/service/smm_variable_service_tests.cpp
1086+++ b/components/service/smm_variable/test/service/smm_variable_service_tests.cpp
1087@@ -249,6 +249,30 @@ TEST(SmmVariableServiceTests, setAndGet)
1088 UNSIGNED_LONGS_EQUAL(set_data.size(), get_data.size());
1089 LONGS_EQUAL(0, get_data.compare(set_data));
1090
1091+ /* Extend the variable using an append write */
1092+ std::string append_data = " values added with append write";
1093+
1094+ efi_status = m_client->set_variable(
1095+ m_common_guid,
1096+ var_name,
1097+ append_data,
1098+ EFI_VARIABLE_APPEND_WRITE);
1099+
1100+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
1101+
1102+ efi_status = m_client->get_variable(
1103+ m_common_guid,
1104+ var_name,
1105+ get_data);
1106+
1107+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
1108+
1109+ std::string appended_data = set_data + append_data;
1110+
1111+ /* Expect the append write operation to have extended the variable */
1112+ UNSIGNED_LONGLONGS_EQUAL(appended_data.size(), get_data.size());
1113+ LONGS_EQUAL(0, appended_data.compare(get_data));
1114+
1115 /* Expect remove to be permitted */
1116 efi_status = m_client->remove_variable(m_common_guid, var_name);
1117 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
1118@@ -279,6 +303,30 @@ TEST(SmmVariableServiceTests, setAndGetNv)
1119 UNSIGNED_LONGS_EQUAL(set_data.size(), get_data.size());
1120 LONGS_EQUAL(0, get_data.compare(set_data));
1121
1122+ /* Extend the variable using an append write */
1123+ std::string append_data = " values added with append write";
1124+
1125+ efi_status = m_client->set_variable(
1126+ m_common_guid,
1127+ var_name,
1128+ append_data,
1129+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_APPEND_WRITE);
1130+
1131+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
1132+
1133+ efi_status = m_client->get_variable(
1134+ m_common_guid,
1135+ var_name,
1136+ get_data);
1137+
1138+ UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
1139+
1140+ std::string appended_data = set_data + append_data;
1141+
1142+ /* Expect the append write operation to have extended the variable */
1143+ UNSIGNED_LONGLONGS_EQUAL(appended_data.size(), get_data.size());
1144+ LONGS_EQUAL(0, appended_data.compare(get_data));
1145+
1146 /* Expect remove to be permitted */
1147 efi_status = m_client->remove_variable(m_common_guid, var_name);
1148 UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status);
1149diff --git a/protocols/service/smm_variable/parameters.h b/protocols/service/smm_variable/parameters.h
1150index 1f795a9b..233f301b 100644
1151--- a/protocols/service/smm_variable/parameters.h
1152+++ b/protocols/service/smm_variable/parameters.h
1153@@ -47,6 +47,9 @@ typedef struct {
1154 EFI_VARIABLE_HARDWARE_ERROR_RECORD | \
1155 EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
1156 EFI_VARIABLE_APPEND_WRITE)
1157+#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS_MASK \
1158+ (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
1159+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
1160
1161 /**
1162 * Parameter structure for SetVariable and GetVariable.