1 // SPDX-License-Identifier: BSD-2-Clause-Patent
3 * mock-variables.c - a mock GetVariable/SetVariable/GNVN/etc
4 * implementation for testing.
5 * Copyright Peter Jones <pjones@redhat.com>
8 #include "mock-variables.h"
11 #include <efivar/efivar.h>
16 #include <sys/types.h>
19 #pragma GCC diagnostic ignored "-Wunused-parameter"
20 #pragma GCC diagnostic ignored "-Wunused-function"
22 list_t mock_default_variable_limits
;
23 list_t
*mock_qvi_limits
= &mock_default_variable_limits
;
24 list_t
*mock_sv_limits
= &mock_default_variable_limits
;
26 list_t mock_variables
= LIST_HEAD_INIT(mock_variables
);
28 mock_sort_policy_t mock_variable_sort_policy
= MOCK_SORT_APPEND
;
29 mock_sort_policy_t mock_config_table_sort_policy
= MOCK_SORT_APPEND
;
31 UINT32 mock_variable_delete_attr_policy
;
33 mock_set_variable_pre_hook_t
*mock_set_variable_pre_hook
= NULL
;
34 mock_set_variable_post_hook_t
*mock_set_variable_post_hook
= NULL
;
35 mock_get_variable_pre_hook_t
*mock_get_variable_pre_hook
= NULL
;
36 mock_get_variable_post_hook_t
*mock_get_variable_post_hook
= NULL
;
37 mock_get_next_variable_name_pre_hook_t
*mock_get_next_variable_name_pre_hook
= NULL
;
38 mock_get_next_variable_name_post_hook_t
*mock_get_next_variable_name_post_hook
= NULL
;
39 mock_query_variable_info_pre_hook_t
*mock_query_variable_info_pre_hook
= NULL
;
40 mock_query_variable_info_post_hook_t
*mock_query_variable_info_post_hook
= NULL
;
43 mock_sv_pre_hook(CHAR16
*name
, EFI_GUID
*guid
, UINT32 attrs
, UINTN size
,
46 EFI_STATUS status
= EFI_SUCCESS
;
47 if (mock_set_variable_pre_hook
)
48 status
= mock_set_variable_pre_hook(name
, guid
,
54 mock_sv_post_hook_(CHAR16
*name
, EFI_GUID
*guid
, UINT32 attrs
, UINTN size
,
55 VOID
*data
, EFI_STATUS
*status
, mock_variable_op_t op
,
56 const char * const file
, const int line
,
57 const char * const func
)
59 if (mock_set_variable_post_hook
)
60 mock_set_variable_post_hook(name
, guid
, attrs
, size
,
61 data
, status
, op
, file
, line
, func
);
63 #define mock_sv_post_hook(name, guid, attrs, size, data, status, op) \
64 mock_sv_post_hook_(name, guid, attrs, size, data, status, op,\
65 __FILE__, __LINE__, __func__)
68 mock_gv_pre_hook(CHAR16
*name
, EFI_GUID
*guid
, UINT32
*attrs
, UINTN
*size
,
71 EFI_STATUS status
= EFI_SUCCESS
;
72 if (mock_get_variable_pre_hook
)
73 status
= mock_get_variable_pre_hook(name
, guid
,
79 mock_gv_post_hook_(CHAR16
*name
, EFI_GUID
*guid
, UINT32
*attrs
, UINTN
*size
,
80 VOID
*data
, EFI_STATUS
*status
, const char * const file
,
81 const int line
, const char * const func
)
83 if (mock_get_variable_post_hook
)
84 mock_get_variable_post_hook(name
, guid
, attrs
, size
, data
,
85 status
, file
, line
, func
);
87 #define mock_gv_post_hook(name, guid, attrs, size, data, status) \
88 mock_gv_post_hook_(name, guid, attrs, size, data, status,\
89 __FILE__, __LINE__, __func__)
92 mock_gnvn_pre_hook(UINTN
*size
, CHAR16
*name
, EFI_GUID
*guid
)
94 EFI_STATUS status
= EFI_SUCCESS
;
95 if (mock_get_next_variable_name_pre_hook
)
96 status
= mock_get_next_variable_name_pre_hook(size
, name
, guid
);
101 mock_gnvn_post_hook_(UINTN
*size
, CHAR16
*name
, EFI_GUID
*guid
,
102 EFI_STATUS
*status
, const char * const file
,
103 const int line
, const char * const func
)
105 if (mock_get_next_variable_name_post_hook
)
106 mock_get_next_variable_name_post_hook(size
, name
, guid
, status
,
109 #define mock_gnvn_post_hook(size, name, guid, status) \
110 mock_gnvn_post_hook_(size, name, guid, status,\
111 __FILE__, __LINE__, __func__)
114 mock_qvi_pre_hook(UINT32 attrs
, UINT64
*max_var_storage
,
115 UINT64
*remaining_var_storage
, UINT64
*max_var_size
)
117 EFI_STATUS status
= EFI_SUCCESS
;
118 if (mock_query_variable_info_pre_hook
)
119 status
= mock_query_variable_info_pre_hook(
120 attrs
, max_var_storage
,
121 remaining_var_storage
, max_var_size
);
126 mock_qvi_post_hook_(UINT32 attrs
, UINT64
*max_var_storage
,
127 UINT64
*remaining_var_storage
, UINT64
*max_var_size
,
128 EFI_STATUS
*status
, const char * const file
,
129 const int line
, const char * const func
)
131 if (mock_query_variable_info_post_hook
)
132 mock_query_variable_info_post_hook(attrs
, max_var_storage
,
133 remaining_var_storage
,
134 max_var_size
, status
,
137 #define mock_qvi_post_hook(attrs, max_var_storage, remaining_var_storage,\
138 max_var_size, status) \
139 mock_qvi_post_hook_(attrs, max_var_storage, remaining_var_storage,\
140 max_var_size, status, \
141 __FILE__, __LINE__, __func__)
143 static const size_t guidstr_size
= sizeof("8be4df61-93ca-11d2-aa0d-00e098032b8c");
146 variable_limits_cmp(const struct mock_variable_limits
* const v0
,
147 const struct mock_variable_limits
* const v1
)
149 UINT32 mask
= EFI_VARIABLE_NON_VOLATILE
|
150 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
151 EFI_VARIABLE_RUNTIME_ACCESS
;
153 return (v0
->attrs
& mask
) - (v1
->attrs
& mask
);
157 variable_cmp(const struct mock_variable
* const v0
,
158 const struct mock_variable
* const v1
)
161 if (v0
== NULL
|| v1
== NULL
)
162 return (uintptr_t)v0
- (uintptr_t)v1
;
164 ret
= CompareGuid(&v0
->guid
, &v1
->guid
);
166 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
167 printf("%s:%d:%s(): "GUID_FMT
" %s "GUID_FMT
" (0x%011"PRIx64
" %"PRId64
")\n",
168 __FILE__
, __LINE__
-1, __func__
,
170 ret
< 0 ? "<" : (ret
> 0 ? ">" : "="),
172 (UINT64
)ret
& 0x1fffffffffful
,
179 ret
= StrCmp(v0
->name
, v1
->name
);
180 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
181 printf("%s:%d:%s(): \"%s\" %s \"%s\" (0x%02hhx (%d)\n",
182 __FILE__
, __LINE__
-1, __func__
,
184 ret
< 0 ? "<" : (ret
> 0 ? ">" : "=="),
192 list2var(list_t
*pos
)
194 static char buf0
[1024];
195 static char buf1
[1024];
198 struct mock_variable
*var
;
200 out
= n
++ % 2 ? buf0
: buf1
;
203 SetMem(out
, 1024, 0);
204 if (pos
== &mock_variables
) {
205 strcpy(out
, "list tail");
208 var
= list_entry(pos
, struct mock_variable
, list
);
209 snprintf(out
, 1023, GUID_FMT
"-%s",
210 GUID_ARGS(var
->guid
),
216 mock_get_variable(CHAR16
*name
, EFI_GUID
*guid
, UINT32
*attrs
, UINTN
*size
,
220 struct mock_variable goal
= {
224 struct mock_variable
*result
= NULL
;
227 status
= mock_gv_pre_hook(name
, guid
, attrs
, size
, data
);
228 if (EFI_ERROR(status
))
231 if (name
== NULL
|| guid
== NULL
|| size
== NULL
) {
232 status
= EFI_INVALID_PARAMETER
;
233 mock_gv_post_hook(name
, guid
, attrs
, size
, data
, &status
);
237 list_for_each(pos
, &mock_variables
) {
238 struct mock_variable
*var
;
240 var
= list_entry(pos
, struct mock_variable
, list
);
241 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
242 printf("%s:%d:%s(): varcmp("GUID_FMT
"-%s, "GUID_FMT
"-%s)\n",
243 __FILE__
, __LINE__
-1, __func__
,
244 GUID_ARGS(goal
.guid
), Str2str(goal
.name
),
245 GUID_ARGS(var
->guid
), Str2str(var
->name
));
247 if (variable_cmp(&goal
, var
) == 0) {
250 if (var
->size
> *size
) {
252 status
= EFI_BUFFER_TOO_SMALL
;
253 mock_gv_post_hook(name
, guid
, attrs
, size
, data
,
258 status
= EFI_INVALID_PARAMETER
;
259 mock_gv_post_hook(name
, guid
, attrs
, size
, data
,
264 memcpy(data
, var
->data
, var
->size
);
265 status
= EFI_SUCCESS
;
266 mock_gv_post_hook(name
, guid
, attrs
, size
, data
,
272 status
= EFI_NOT_FOUND
;
273 mock_gv_post_hook(name
, guid
, attrs
, size
, data
, &status
);
278 mock_gnvn_set_result(UINTN
*size
, CHAR16
*name
, EFI_GUID
*guid
,
279 struct mock_variable
*result
)
283 if (*size
< StrSize(result
->name
)) {
284 *size
= StrSize(result
->name
);
285 status
= EFI_BUFFER_TOO_SMALL
;
286 mock_gnvn_post_hook(size
, name
, guid
, &status
);
287 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
288 printf("%s:%d:%s(): returning %lx\n",
289 __FILE__
, __LINE__
-1, __func__
, status
);
294 *size
= StrLen(result
->name
) + 1;
295 StrCpy(name
, result
->name
);
296 memcpy(guid
, &result
->guid
, sizeof(EFI_GUID
));
298 status
= EFI_SUCCESS
;
299 mock_gnvn_post_hook(size
, name
, guid
, &status
);
300 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
301 printf("%s:%d:%s(): returning %lx\n",
302 __FILE__
, __LINE__
-1, __func__
, status
);
308 mock_get_next_variable_name(UINTN
*size
, CHAR16
*name
, EFI_GUID
*guid
)
311 struct mock_variable goal
= {
315 struct mock_variable
*result
= NULL
;
319 status
= mock_gnvn_pre_hook(size
, name
, guid
);
320 if (EFI_ERROR(status
))
323 if (size
== NULL
|| name
== NULL
|| guid
== NULL
) {
324 status
= EFI_INVALID_PARAMETER
;
325 mock_gnvn_post_hook(size
, name
, guid
, &status
);
329 for (size_t i
= 0; i
< *size
; i
++) {
336 if (found
== false) {
337 status
= EFI_INVALID_PARAMETER
;
338 mock_gnvn_post_hook(size
, name
, guid
, &status
);
343 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
344 printf("%s:%d:%s():searching for "GUID_FMT
"%s%s\n",
345 __FILE__
, __LINE__
-1, __func__
,
347 name
[0] == 0 ? "" : "-",
348 name
[0] == 0 ? "" : Str2str(name
));
350 list_for_each(pos
, &mock_variables
) {
351 struct mock_variable
*var
;
353 var
= list_entry(pos
, struct mock_variable
, list
);
354 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
355 printf("%s:%d:%s(): candidate var:%p &var->guid:%p &var->list:%p\n",
356 __FILE__
, __LINE__
-1, __func__
, var
, &var
->guid
, &var
->list
);
359 if (CompareGuid(&var
->guid
, guid
) == 0) {
360 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
361 printf("%s:%d:%s(): found\n",
362 __FILE__
, __LINE__
-1, __func__
);
370 if (CompareGuid(&var
->guid
, guid
) == 0) {
377 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
378 printf("%s:%d:%s(): varcmp("GUID_FMT
"-%s, "GUID_FMT
"-%s)\n",
379 __FILE__
, __LINE__
-1, __func__
,
380 GUID_ARGS(goal
.guid
), Str2str(goal
.name
),
381 GUID_ARGS(var
->guid
), Str2str(var
->name
));
383 if (variable_cmp(&goal
, var
) == 0) {
384 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
385 printf("%s:%d:%s(): found\n",
386 __FILE__
, __LINE__
-1, __func__
);
392 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
394 printf("%s:%d:%s(): found:%d result:%p &result->guid:%p &result->list:%p\n"
395 __FILE__
, __LINE__
-1, __func__
, found
, result
,
396 &result
->guid
, &result
->list
);
397 printf("%s:%d:%s(): "GUID_FMT
"-%s\n",
398 __FILE__
, __LINE__
-1, __func__
, GUID_ARGS(result
->guid
),
399 Str2str(result
->name
));
401 printf("%s:%d:%s(): not found\n",
402 __FILE__
, __LINE__
-1, __func__
);
408 status
= EFI_NOT_FOUND
;
410 status
= EFI_INVALID_PARAMETER
;
411 mock_gnvn_post_hook(size
, name
, guid
, &status
);
416 status
= EFI_NOT_FOUND
;
417 mock_gnvn_post_hook(size
, name
, guid
, &status
);
421 return mock_gnvn_set_result(size
, name
, guid
, result
);
425 free_var(struct mock_variable
*var
)
429 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
430 printf("%s:%d:%s(): var:%p &var->guid:%p ",
431 __FILE__
, __LINE__
-1, __func__
,
432 var
, var
? &var
->guid
: NULL
);
434 printf(GUID_FMT
"-%s", GUID_ARGS(var
->guid
),
435 var
->name
? Str2str(var
->name
) : "");
438 list_del(&var
->list
);
439 if (var
->size
&& var
->data
)
441 SetMem(var
, sizeof(*var
), 0);
446 mock_sv_attrs_match(UINT32 old
, UINT32
new)
448 UINT32 mask
= ~((UINT32
)EFI_VARIABLE_APPEND_WRITE
);
450 return (old
& mask
) == (new & mask
);
454 mock_sv_adjust_usage_data(UINT32 attrs
, size_t size
, ssize_t change
)
456 const UINT32 bs
= EFI_VARIABLE_BOOTSERVICE_ACCESS
;
457 const UINT32 bs_nv
= bs
| EFI_VARIABLE_NON_VOLATILE
;
458 const UINT32 bs_rt
= bs
| EFI_VARIABLE_RUNTIME_ACCESS
;
459 const UINT32 bs_rt_nv
= bs_nv
| bs_rt
;
460 struct mock_variable_limits goal
= {
461 .attrs
= attrs
& bs_rt_nv
,
463 struct mock_variable_limits
*qvi_limits
= NULL
;
464 struct mock_variable_limits
*sv_limits
= NULL
;
465 list_t var
, *pos
= NULL
;
468 list_for_each(pos
, mock_qvi_limits
) {
469 struct mock_variable_limits
*candidate
;
471 candidate
= list_entry(pos
, struct mock_variable_limits
, list
);
472 if (variable_limits_cmp(&goal
, candidate
) == 0) {
473 qvi_limits
= candidate
;
478 list_for_each(pos
, mock_sv_limits
) {
479 struct mock_variable_limits
*candidate
;
481 candidate
= list_entry(pos
, struct mock_variable_limits
, list
);
482 if (variable_limits_cmp(&goal
, candidate
) == 0) {
483 sv_limits
= candidate
;
488 return EFI_UNSUPPORTED
;
491 if (sv_limits
->status
!= EFI_SUCCESS
)
492 return sv_limits
->status
;
494 if (*sv_limits
->max_var_size
< size
) {
495 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
496 printf("%s:%d:%s():*sv_limits->max_var_size:%zu size:%zu\n",
497 __FILE__
, __LINE__
, __func__
,
498 *sv_limits
->max_var_size
, size
);
500 return EFI_OUT_OF_RESOURCES
;
503 if (change
> 0 && (UINT64
)change
> *sv_limits
->remaining_var_storage
) {
504 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
505 printf("%s:%d:%s():*sv_limits->remaining_var_storage:%zu change:%zd\n",
506 __FILE__
, __LINE__
, __func__
,
507 *sv_limits
->remaining_var_storage
, change
);
509 return EFI_OUT_OF_RESOURCES
;
512 *sv_limits
->remaining_var_storage
+= change
;
516 * If the adjustment here is wrong, we don't want to not do
517 * the set variable, we also don't want to not account
518 * for it, and of course we can't have any integer UB. So
519 * just limit it safely and move on, even though that may
520 * result in wrong checks against QueryVariableInfo() later.
522 * As if there are correct checks against QueryVariableInfo()...
524 if (qvi_limits
->remaining_var_storage
== sv_limits
->remaining_var_storage
)
526 else if (change
< 0 && (UINT64
)-change
> *qvi_limits
->remaining_var_storage
)
527 *qvi_limits
->remaining_var_storage
= 0;
528 else if (change
> 0 && UINT64_MAX
- *qvi_limits
->remaining_var_storage
< (UINT64
)change
)
529 *qvi_limits
->remaining_var_storage
= UINT64_MAX
;
531 *qvi_limits
->remaining_var_storage
+= change
;
537 mock_delete_variable(struct mock_variable
*var
)
541 status
= mock_sv_adjust_usage_data(var
->attrs
, 0, - var
->size
);
542 if (EFI_ERROR(status
)) {
543 printf("%s:%d:%s(): status:0x%lx\n",
544 __FILE__
, __LINE__
- 1, __func__
, status
);
545 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, var
->size
,
546 var
->data
, &status
, DELETE
);
550 status
= EFI_SUCCESS
;
551 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, 0, 0, &status
,
558 mock_replace_variable(struct mock_variable
*var
, VOID
*data
, UINTN size
)
563 status
= mock_sv_adjust_usage_data(var
->attrs
, size
,
565 if (EFI_ERROR(status
)) {
566 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, var
->size
,
567 var
->data
, &status
, REPLACE
);
571 new = calloc(1, size
);
573 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
574 printf("%s:%d:%s():calloc(1, %zu) failed\n",
575 __FILE__
, __LINE__
, __func__
,
578 status
= EFI_OUT_OF_RESOURCES
;
579 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, var
->size
,
580 var
->data
, &status
, REPLACE
);
583 memcpy(new, data
, size
);
588 status
= EFI_SUCCESS
;
589 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, var
->size
,
590 var
->data
, &status
, REPLACE
);
595 mock_sv_extend(struct mock_variable
*var
, VOID
*data
, UINTN size
)
601 status
= EFI_SUCCESS
;
602 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, var
->size
,
603 var
->data
, &status
, APPEND
);
607 status
= mock_sv_adjust_usage_data(var
->attrs
, var
->size
+ size
, size
);
608 if (EFI_ERROR(status
)) {
609 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, var
->size
,
610 var
->data
, &status
, APPEND
);
614 new = realloc(var
->data
, var
->size
+ size
);
616 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
617 printf("%s:%d:%s():realloc(%zu) failed\n",
618 __FILE__
, __LINE__
, __func__
,
621 status
= EFI_OUT_OF_RESOURCES
;
622 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, var
->size
,
623 var
->data
, &status
, APPEND
);
627 memcpy(&new[var
->size
], data
, size
);
628 var
->data
= (void *)new;
631 status
= EFI_SUCCESS
;
632 mock_sv_post_hook(var
->name
, &var
->guid
, var
->attrs
, var
->size
,
633 var
->data
, &status
, APPEND
);
638 mock_print_var_list(list_t
*head
)
641 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
642 printf("%s:%d:%s():variables so far:\n", __FILE__
, __LINE__
, __func__
);
644 list_for_each(pos
, head
) {
645 struct mock_variable
*var
= NULL
;
647 var
= list_entry(pos
, struct mock_variable
, list
);
648 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
649 printf("%s:%d:%s(): "GUID_FMT
"-%s (%lu bytes)\n",
650 __FILE__
, __LINE__
- 1, __func__
,
651 GUID_ARGS(var
->guid
), Str2str(var
->name
), var
->size
);
657 mock_new_variable(CHAR16
*name
, EFI_GUID
*guid
, UINT32 attrs
, UINTN size
,
658 VOID
*data
, struct mock_variable
**out
)
661 struct mock_variable
*var
;
665 status
= EFI_INVALID_PARAMETER
;
669 status
= EFI_OUT_OF_RESOURCES
;
670 buf
= calloc(1, sizeof(struct mock_variable
) + StrSize(name
));
672 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
673 printf("%s:%d:%s(): calloc(1, %zu) failed\n",
674 __FILE__
, __LINE__
, __func__
,
675 sizeof(struct mock_variable
) + StrSize(name
));
679 var
= (struct mock_variable
*)buf
;
681 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
682 printf("%s:%d:%s(): var:%p &var->guid:%p &var->list:%p\n",
683 __FILE__
, __LINE__
-1, __func__
, var
, &var
->guid
, &var
->list
);
686 var
->data
= malloc(size
);
690 var
->name
= (CHAR16
*)&buf
[sizeof(*var
)];
691 StrCpy(var
->name
, name
);
692 memcpy(&var
->guid
, guid
, sizeof(EFI_GUID
));
693 memcpy(var
->data
, data
, size
);
696 INIT_LIST_HEAD(&var
->list
);
698 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
699 printf("%s:%d:%s(): var: "GUID_FMT
"-%s\n",
700 __FILE__
, __LINE__
-1, __func__
,
701 GUID_ARGS(var
->guid
), Str2str(var
->name
));
705 status
= EFI_SUCCESS
;
707 if (EFI_ERROR(status
))
714 mock_set_variable(CHAR16
*name
, EFI_GUID
*guid
, UINT32 attrs
, UINTN size
,
717 list_t
*pos
= NULL
, *tmp
= NULL
, *var_list
= NULL
;
718 struct mock_variable goal
= {
722 struct mock_variable
*var
= NULL
;
724 bool add_tail
= true;
728 status
= mock_sv_pre_hook(name
, guid
, attrs
, size
, data
);
729 if (EFI_ERROR(status
))
732 if (!name
|| name
[0] == 0 || !guid
) {
733 status
= EFI_INVALID_PARAMETER
;
734 mock_sv_post_hook(name
, guid
, attrs
, size
, data
, &status
,
739 if ((attrs
& EFI_VARIABLE_RUNTIME_ACCESS
) &&
740 !(attrs
& EFI_VARIABLE_BOOTSERVICE_ACCESS
)) {
741 status
= EFI_INVALID_PARAMETER
;
742 mock_sv_post_hook(name
, guid
, attrs
, size
, data
, &status
,
749 * We don't ever operate after ExitBootServices(), so I'm not
750 * checking for the missing EFI_VARIABLE_RUNTIME_ACCESS case
752 if (has_exited_boot_services() && !(attrs
& EFI_VARIABLE_RUNTIME_ACCESS
)) {
753 status
= EFI_INVALID_PARAMETER
;
754 mock_sv_post_hook(name
, guid
, attrs
, size
, data
, &status
,
762 * For now, we're ignoring that we don't support these.
764 if (attrs
& (EFI_VARIABLE_HARDWARE_ERROR_RECORD
|
765 EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
|
766 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
|
767 EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS
)) {
768 status
= EFI_UNSUPPORTED
;
769 mock_sv_post_hook(name
, guid
, attrs
, size
, data
, &status
,
775 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
776 printf("%s:%d:%s():Setting "GUID_FMT
"-%s\n",
777 __FILE__
, __LINE__
- 1, __func__
,
778 GUID_ARGS(*guid
), Str2str(name
));
780 switch (mock_variable_sort_policy
) {
781 case MOCK_SORT_PREPEND
:
782 var_list
= &mock_variables
;
785 case MOCK_SORT_APPEND
:
786 var_list
= &mock_variables
;
789 case MOCK_SORT_DESCENDING
:
792 case MOCK_SORT_ASCENDING
:
799 pos
= &mock_variables
;
800 list_for_each_safe(pos
, tmp
, &mock_variables
) {
802 var
= list_entry(pos
, struct mock_variable
, list
);
803 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
804 printf("%s:%d:%s(): varcmp("GUID_FMT
"-%s, "GUID_FMT
"-%s)\n",
805 __FILE__
, __LINE__
-1, __func__
,
806 GUID_ARGS(goal
.guid
), Str2str(goal
.name
),
807 GUID_ARGS(var
->guid
), Str2str(var
->name
));
809 cmp
= variable_cmp(&goal
, var
);
810 cmp
= cmp
< 0 ? -1 : (cmp
> 0 ? 1 : 0);
812 switch (mock_variable_sort_policy
) {
813 case MOCK_SORT_DESCENDING
:
819 case MOCK_SORT_ASCENDING
:
835 #if defined(SHIM_DEBUG) && SHIM_DEBUG != 0
836 printf("%s:%d:%s():var_list:%p &mock_variables:%p cmp:%ld\n",
837 __FILE__
, __LINE__
- 1, __func__
,
838 var_list
, &mock_variables
, cmp
);
840 if (cmp
!= 0 || (cmp
== 0 && var_list
== &mock_variables
)) {
841 size_t totalsz
= size
+ StrSize(name
);
842 #if defined(SHIM_DEBUG) && SHIM_DEBUG != 0
843 printf("%s:%d:%s():var:%p attrs:0x%lx\n",
844 __FILE__
, __LINE__
- 1, __func__
, var
, attrs
);
846 status
= mock_new_variable(name
, guid
, attrs
, size
, data
, &var
);
847 if (EFI_ERROR(status
)) {
848 mock_sv_post_hook(name
, guid
, attrs
, size
, data
,
852 mock_sv_adjust_usage_data(attrs
, size
, totalsz
);
853 mock_sv_post_hook(name
, guid
, attrs
, size
, data
,
855 if (EFI_ERROR(status
)) {
856 mock_sv_adjust_usage_data(attrs
, 0, -totalsz
);
860 #if defined(SHIM_DEBUG) && SHIM_DEBUG != 0
861 printf("%s:%d:%s(): Adding "GUID_FMT
"-%s %s %s\n",
862 __FILE__
, __LINE__
- 1, __func__
,
863 GUID_ARGS(var
->guid
), Str2str(var
->name
),
864 add_tail
? "after" : "before",
868 list_add_tail(&var
->list
, pos
);
870 list_add(&var
->list
, pos
);
874 var
= list_entry(var_list
, struct mock_variable
, list
);
875 #if defined(SHIM_DEBUG) && SHIM_DEBUG != 0
876 printf("%s:%d:%s():var:%p attrs:%s cmp:%ld size:%ld\n",
877 __FILE__
, __LINE__
- 1, __func__
,
878 var
, format_var_attrs(var
->attrs
), cmp
, size
);
880 if (!mock_sv_attrs_match(var
->attrs
, attrs
)) {
881 status
= EFI_INVALID_PARAMETER
;
882 if (size
== 0 && !(attrs
& EFI_VARIABLE_APPEND_WRITE
)) {
883 if ((mock_variable_delete_attr_policy
& MOCK_VAR_DELETE_ATTR_ALLOW_ZERO
)
885 status
= EFI_SUCCESS
;
886 } else if (mock_variable_delete_attr_policy
& MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH
) {
887 status
= EFI_SUCCESS
;
890 if (EFI_ERROR(status
)) {
891 printf("%s:%d:%s(): var->attrs:%s attrs:%s\n",
892 __FILE__
, __LINE__
- 1, __func__
,
893 format_var_attrs(var
->attrs
),
894 format_var_attrs(attrs
));
895 mock_sv_post_hook(name
, guid
, attrs
, size
, data
,
901 if (attrs
& EFI_VARIABLE_APPEND_WRITE
)
902 return mock_sv_extend(var
, data
, size
);
905 UINT32 mask
= EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
906 | EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS
;
908 * We can't process deletes on these correctly unless we
912 return EFI_INVALID_PARAMETER
;
915 return mock_delete_variable(var
);
918 return mock_replace_variable(var
, data
, size
);
922 mock_query_variable_info(UINT32 attrs
, UINT64
*max_var_storage
,
923 UINT64
*remaining_var_storage
, UINT64
*max_var_size
)
925 list_t mvl
, *pos
= NULL
;
926 struct mock_variable_limits goal
= {
929 struct mock_variable_limits
*limits
= NULL
;
932 status
= mock_qvi_pre_hook(attrs
, max_var_storage
,
933 remaining_var_storage
, max_var_size
);
934 if (EFI_ERROR(status
))
937 if (max_var_storage
== NULL
||
938 remaining_var_storage
== NULL
||
939 max_var_size
== NULL
) {
940 status
= EFI_INVALID_PARAMETER
;
941 mock_qvi_post_hook(attrs
, max_var_storage
,
942 remaining_var_storage
, max_var_size
,
947 list_for_each(pos
, mock_qvi_limits
) {
948 limits
= list_entry(pos
, struct mock_variable_limits
, list
);
949 if (variable_limits_cmp(&goal
, limits
) == 0) {
950 *max_var_storage
= *limits
->max_var_storage
;
951 *remaining_var_storage
= *limits
->remaining_var_storage
;
952 *max_var_size
= *limits
->max_var_size
;
954 status
= EFI_SUCCESS
;
955 mock_qvi_post_hook(attrs
, max_var_storage
,
956 remaining_var_storage
, max_var_size
,
962 status
= EFI_UNSUPPORTED
;
963 mock_qvi_post_hook(attrs
, max_var_storage
, remaining_var_storage
,
964 max_var_size
, &status
);
968 static UINT64 default_max_var_storage
;
969 static UINT64 default_remaining_var_storage
;
970 static UINT64 default_max_var_size
;
972 static struct mock_variable_limits default_limits
[] = {
973 {.attrs
= EFI_VARIABLE_BOOTSERVICE_ACCESS
,
974 .max_var_storage
= &default_max_var_storage
,
975 .remaining_var_storage
= &default_remaining_var_storage
,
976 .max_var_size
= &default_max_var_size
,
977 .status
= EFI_SUCCESS
,
979 {.attrs
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|
980 EFI_VARIABLE_RUNTIME_ACCESS
,
981 .max_var_storage
= &default_max_var_storage
,
982 .remaining_var_storage
= &default_remaining_var_storage
,
983 .max_var_size
= &default_max_var_size
,
984 .status
= EFI_SUCCESS
,
986 {.attrs
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|
987 EFI_VARIABLE_NON_VOLATILE
,
988 .max_var_storage
= &default_max_var_storage
,
989 .remaining_var_storage
= &default_remaining_var_storage
,
990 .max_var_size
= &default_max_var_size
,
991 .status
= EFI_SUCCESS
,
993 {.attrs
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|
994 EFI_VARIABLE_RUNTIME_ACCESS
|
995 EFI_VARIABLE_NON_VOLATILE
,
996 .max_var_storage
= &default_max_var_storage
,
997 .remaining_var_storage
= &default_remaining_var_storage
,
998 .max_var_size
= &default_max_var_size
,
999 .status
= EFI_SUCCESS
,
1005 mock_set_default_usage_limits(void)
1007 default_max_var_storage
= 65536;
1008 default_remaining_var_storage
= 65536;
1009 default_max_var_size
= 32768;
1011 INIT_LIST_HEAD(&mock_default_variable_limits
);
1012 for (size_t i
= 0; default_limits
[i
].attrs
!= 0; i
++) {
1013 INIT_LIST_HEAD(&default_limits
[i
].list
);
1014 list_add_tail(&default_limits
[i
].list
,
1015 &mock_default_variable_limits
);
1020 mock_load_one_variable(int dfd
, const char * const dirname
, char * const name
)
1025 struct stat statbuf
;
1026 size_t guidlen
, namelen
;
1033 rc
= fstatat(dfd
, name
, &statbuf
, 0);
1035 err(2, "Could not stat \"%s/%s\"", dirname
, name
);
1037 if (!(S_ISREG(statbuf
.st_mode
)))
1040 if (statbuf
.st_size
< 5)
1041 errx(2, "Test data variable \"%s/%s\" is too small (%ld bytes)",
1042 dirname
, name
, statbuf
.st_size
);
1045 mock_print_var_list(&mock_variables
);
1048 uint8_t buf
[statbuf
.st_size
];
1050 fd
= openat(dfd
, name
, O_RDONLY
);
1052 err(2, "Could not open \"%s/%s\"", dirname
, name
);
1054 f
= fdopen(fd
, "r");
1056 err(2, "Could not open \"%s/%s\"", dirname
, name
);
1058 while (offset
!= statbuf
.st_size
) {
1059 sz
= fread(buf
+ offset
, 1, statbuf
.st_size
- offset
, f
);
1062 err(2, "Could not read from \"%s/%s\"",
1065 errx(2, "Unexpected end of file reading \"%s/%s\"",
1072 guidlen
= strlen("8be4df61-93ca-11d2-aa0d-00e098032b8c");
1073 namelen
= strlen(name
) - guidlen
;
1076 errx(2, "namelen for \"%s\" is %zu!?!", name
, namelen
);
1078 CHAR16 namebuf
[namelen
];
1080 name
[namelen
-1] = 0;
1081 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
1082 printf("loading %s-%s\n", &name
[namelen
], name
);
1084 for (size_t i
= 0; i
< namelen
; i
++)
1085 namebuf
[i
] = name
[i
];
1087 rc
= efi_str_to_guid(&name
[namelen
], &guid
);
1089 err(2, "Could not parse \"%s\" as EFI GUID", &name
[namelen
]);
1091 memcpy(&attrs
, (UINT32
*)buf
, sizeof(UINT32
));
1093 status
= RT
->SetVariable(namebuf
, (EFI_GUID
*)&guid
, attrs
,
1094 statbuf
.st_size
- sizeof(attrs
),
1095 &buf
[sizeof(attrs
)]);
1096 if (EFI_ERROR(status
))
1097 errx(2, "%s:%d:%s(): Could not set variable: 0x%llx",
1098 __FILE__
, __LINE__
- 1, __func__
,
1099 (unsigned long long)status
);
1105 mock_load_variables(const char *const dirname
, const char *filters
[],
1110 struct dirent
*entry
;
1112 d
= opendir(dirname
);
1114 err(1, "Could not open directory \"%s\"", dirname
);
1118 err(1, "Could not get directory file descriptor for \"%s\"",
1121 while ((entry
= readdir(d
)) != NULL
) {
1122 size_t len
= strlen(entry
->d_name
);
1124 if (filters
&& len
> guidstr_size
+ 1) {
1127 len
-= guidstr_size
;
1128 SetMem(spacebuf
, sizeof(spacebuf
)-1, ' ');
1129 spacebuf
[len
] = '\0';
1130 for (size_t i
= 0; filters
[i
]; i
++) {
1131 if (strlen(filters
[i
]) > len
)
1133 if (!strncmp(entry
->d_name
, filters
[i
], len
)) {
1139 if ((found
== false && filter_out
== true) ||
1140 (found
== true && filter_out
== false)) {
1141 mock_load_one_variable(dfd
, dirname
, entry
->d_name
);
1147 mock_print_var_list(&mock_variables
);
1151 static bool qvi_installed
= false;
1154 mock_install_query_variable_info(void)
1156 qvi_installed
= true;
1157 RT
->Hdr
.Revision
= 2ul << 16ul;
1158 RT
->QueryVariableInfo
= mock_query_variable_info
;
1162 mock_uninstall_query_variable_info(void)
1164 qvi_installed
= false;
1165 RT
->Hdr
.Revision
= EFI_1_10_SYSTEM_TABLE_REVISION
;
1166 RT
->QueryVariableInfo
= mock_efi_unsupported
;
1169 EFI_CONFIGURATION_TABLE mock_config_table
[MOCK_CONFIG_TABLE_ENTRIES
] = {
1171 .VendorGuid
= { 0, },
1177 mock_config_table_cmp(const void *p0
, const void *p1
)
1179 EFI_CONFIGURATION_TABLE
*entry0
, *entry1
;
1183 cmp
= (int)((intptr_t)p0
- (intptr_t)p1
);
1185 entry0
= (EFI_CONFIGURATION_TABLE
*)p0
;
1186 entry1
= (EFI_CONFIGURATION_TABLE
*)p1
;
1187 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
1188 printf("comparing %p to %p\n", p0
, p1
);
1190 cmp
= CompareGuid(&entry0
->VendorGuid
, &entry1
->VendorGuid
);
1193 if (mock_config_table_sort_policy
== MOCK_SORT_DESCENDING
) {
1201 mock_install_configuration_table(EFI_GUID
*guid
, VOID
*table
)
1204 EFI_CONFIGURATION_TABLE
*entry
;
1209 return EFI_INVALID_PARAMETER
;
1211 for (UINTN i
= 0; i
< ST
->NumberOfTableEntries
; i
++) {
1212 EFI_CONFIGURATION_TABLE
*entry
= &ST
->ConfigurationTable
[i
];
1214 if (CompareGuid(guid
, &entry
->VendorGuid
) == 0) {
1218 entry
->VendorTable
= table
;
1221 ST
->NumberOfTableEntries
-= 1;
1222 sz
= ST
->NumberOfTableEntries
- i
;
1223 sz
*= sizeof(*entry
);
1224 memmove(&entry
[0], &entry
[1], sz
);
1229 if (!found
&& table
== NULL
)
1230 return EFI_NOT_FOUND
;
1231 if (ST
->NumberOfTableEntries
== MOCK_CONFIG_TABLE_ENTRIES
- 1) {
1233 * If necessary, we could allocate another table and copy
1234 * the data, but I'm lazy and we probably don't need to.
1236 return EFI_OUT_OF_RESOURCES
;
1239 switch (mock_config_table_sort_policy
) {
1240 case MOCK_SORT_DESCENDING
:
1241 case MOCK_SORT_ASCENDING
:
1242 case MOCK_SORT_APPEND
:
1243 idx
= ST
->NumberOfTableEntries
;
1245 case MOCK_SORT_PREPEND
:
1246 sz
= ST
->NumberOfTableEntries
? ST
->NumberOfTableEntries
: 0;
1247 sz
*= sizeof(ST
->ConfigurationTable
[0]);
1248 memmove(&ST
->ConfigurationTable
[1], &ST
->ConfigurationTable
[0], sz
);
1255 entry
= &ST
->ConfigurationTable
[idx
];
1256 memcpy(&entry
->VendorGuid
, guid
, sizeof(EFI_GUID
));
1257 entry
->VendorTable
= table
;
1258 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
1259 printf("%s:%d:%s(): installing entry %p={%p,%p} as entry %d\n",
1260 __FILE__
, __LINE__
, __func__
,
1261 entry
, &entry
->VendorGuid
, entry
->VendorTable
, idx
);
1263 ST
->NumberOfTableEntries
+= 1;
1265 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
1266 printf("%s:%d:%s():ST->ConfigurationTable:%p\n"
1269 __FILE__
, __LINE__
, __func__
, ST
->ConfigurationTable
,
1270 0, &ST
->ConfigurationTable
[0],
1271 1, &ST
->ConfigurationTable
[1]);
1273 switch (mock_config_table_sort_policy
) {
1274 case MOCK_SORT_DESCENDING
:
1275 case MOCK_SORT_ASCENDING
:
1276 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
1277 printf("%s:%d:%s(): entries before sorting:\n", __FILE__
, __LINE__
, __func__
);
1278 for (UINTN i
= 0; i
< ST
->NumberOfTableEntries
; i
++) {
1279 printf("\t[%d] = %p = {", i
, &ST
->ConfigurationTable
[i
]);
1280 printf(".VendorGuid=" GUID_FMT
, GUID_ARGS(ST
->ConfigurationTable
[i
].VendorGuid
));
1281 printf(".VendorTable=%p}\n", ST
->ConfigurationTable
[i
].VendorTable
);
1284 qsort(&ST
->ConfigurationTable
[0], ST
->NumberOfTableEntries
,
1285 sizeof(ST
->ConfigurationTable
[0]),
1286 mock_config_table_cmp
);
1291 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
1292 printf("%s:%d:%s(): entries:\n", __FILE__
, __LINE__
, __func__
);
1293 for (UINTN i
= 0; i
< ST
->NumberOfTableEntries
; i
++) {
1294 printf("\t[%d] = %p = {", i
, &ST
->ConfigurationTable
[i
]);
1295 printf(".VendorGuid=" GUID_FMT
, GUID_ARGS(ST
->ConfigurationTable
[i
].VendorGuid
));
1296 printf(".VendorTable=%p}\n", ST
->ConfigurationTable
[i
].VendorTable
);
1304 mock_reset_variables(void)
1306 list_t
*pos
= NULL
, *tmp
= NULL
;
1307 static bool once
= true;
1309 init_efi_system_table();
1311 mock_set_variable_pre_hook
= NULL
;
1312 mock_set_variable_post_hook
= NULL
;
1313 mock_get_variable_pre_hook
= NULL
;
1314 mock_get_variable_post_hook
= NULL
;
1315 mock_get_next_variable_name_pre_hook
= NULL
;
1316 mock_get_next_variable_name_post_hook
= NULL
;
1317 mock_query_variable_info_pre_hook
= NULL
;
1318 mock_query_variable_info_post_hook
= NULL
;
1321 INIT_LIST_HEAD(&mock_variables
);
1323 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
1324 printf("%s:%d:%s():mock_variables = {%p,%p};\n",
1325 __FILE__
, __LINE__
-1, __func__
,
1326 mock_variables
.next
,
1327 mock_variables
.prev
);
1328 printf("%s:%d:%s():list_empty(&mock_variables):%d\n",
1329 __FILE__
, __LINE__
-1, __func__
, list_empty(&mock_variables
));
1330 printf("%s:%d:%s():list_size(&mock_variables):%d\n",
1331 __FILE__
, __LINE__
-1, __func__
, list_size(&mock_variables
));
1335 list_for_each_safe(pos
, tmp
, &mock_variables
) {
1336 struct mock_variable
*var
= NULL
;
1337 var
= list_entry(pos
, struct mock_variable
, list
);
1339 #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
1340 printf("%s:%d:%s():var:"GUID_FMT
"-%s\n",
1341 __FILE__
, __LINE__
-1, __func__
,
1342 GUID_ARGS(var
->guid
), Str2str(var
->name
));
1344 mock_delete_variable(var
);
1346 INIT_LIST_HEAD(&mock_variables
);
1347 mock_set_default_usage_limits();
1349 mock_variable_delete_attr_policy
= MOCK_VAR_DELETE_ATTR_ALLOW_ZERO
;
1351 RT
->GetVariable
= mock_get_variable
;
1352 RT
->GetNextVariableName
= mock_get_next_variable_name
;
1353 RT
->SetVariable
= mock_set_variable
;
1355 mock_install_query_variable_info();
1357 mock_uninstall_query_variable_info();
1361 mock_reset_config_table(void)
1363 init_efi_system_table();
1366 * Note that BS->InstallConfigurationTable() is *not* defined as
1367 * freeing these. If a test case installs non-malloc()ed tables,
1368 * it needs to call BS->InstallConfigurationTable(guid, NULL) to
1371 for (UINTN i
= 0; i
< ST
->NumberOfTableEntries
; i
++) {
1372 EFI_CONFIGURATION_TABLE
*entry
= &ST
->ConfigurationTable
[i
];
1374 if (entry
->VendorTable
)
1375 free(entry
->VendorTable
);
1378 SetMem(ST
->ConfigurationTable
,
1379 ST
->NumberOfTableEntries
* sizeof(EFI_CONFIGURATION_TABLE
),
1382 ST
->NumberOfTableEntries
= 0;
1384 if (ST
->ConfigurationTable
!= mock_config_table
) {
1385 free(ST
->ConfigurationTable
);
1386 ST
->ConfigurationTable
= mock_config_table
;
1387 SetMem(mock_config_table
, sizeof(mock_config_table
), 0);
1390 BS
->InstallConfigurationTable
= mock_install_configuration_table
;
1394 mock_finalize_vars_and_configs(void)
1396 mock_reset_variables();
1397 mock_reset_config_table();
1400 // vim:fenc=utf-8:tw=75:noet