1 /* SPDX-License-Identifier: GPL-2.0-only */
5 #include <console/console.h>
7 #include <vendorcode/intel/edk2/UDK2017/MdePkg/Include/Uefi/UefiBaseType.h>
8 #include <vendorcode/intel/edk2/UDK2017/MdePkg/Include/Uefi/UefiMultiPhase.h>
9 #include <vendorcode/intel/edk2/UDK2017/MdePkg/Include/Pi/PiFirmwareVolume.h>
10 #include <vendorcode/intel/edk2/UDK2017/MdeModulePkg/Include/Guid/VariableFormat.h>
14 #define PREFIX "EFIVARS: "
16 static const EFI_GUID EfiVariableGuid
= {
17 0xddcf3616, 0x3275, 0x4164, { 0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d } };
18 static const EFI_GUID EfiAuthenticatedVariableGuid
= {
19 0xaaf32c78, 0x947b, 0x439a, { 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92 } };
20 static const EFI_GUID EfiSystemNvDataFvGuid
= {
21 0xfff12b8d, 0x7696, 0x4c8b, { 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50 } };
23 static void print_guid(int log_level
, const EFI_GUID
*g
)
25 printk(log_level
, "GUID: %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
26 g
->Data1
, g
->Data2
, g
->Data3
, g
->Data4
[0], g
->Data4
[1], g
->Data4
[2],
27 g
->Data4
[3], g
->Data4
[4], g
->Data4
[5], g
->Data4
[6], g
->Data4
[7]);
30 static bool compare_guid(const EFI_GUID
*a
, const EFI_GUID
*b
)
32 return memcmp(a
, b
, sizeof(*a
)) == 0;
35 /* Reads the CHAR16 string from rdev at offset and prints it */
36 static enum cb_err
rdev_print_wchar(int log_level
, struct region_device
*rdev
, size_t offset
)
41 /* Convert ASCII to UTF-16 */
43 if (rdev_readat(rdev
, &c
, offset
+ i
* sizeof(c
), sizeof(c
)) != sizeof(c
))
44 return CB_EFI_ACCESS_ERROR
;
46 printk(log_level
, "%c", (char)c
);
48 printk(log_level
, "\\u%04x", c
);
55 /* Convert an ASCII string to UTF-16 and write it to the rdev starting at offset. */
56 static enum cb_err
rdev_write_wchar(struct region_device
*rdev
, size_t offset
, const char *msg
)
61 /* Convert ASCII to UTF-16 */
62 for (i
= 0; i
< strlen(msg
) + 1; i
++) {
65 if (rdev_writeat(rdev
, &c
, offset
+ i
* sizeof(c
), sizeof(c
)) != sizeof(c
))
66 return CB_EFI_ACCESS_ERROR
;
71 /* Read an UTF-16 string from rdev at offset and compare it to ASCII string */
72 static int rdev_strcmp_wchar_ascii(struct region_device
*rdev
, size_t offset
, const char *msg
)
79 /* Compare UTF-16 and ASCII */
81 if (rdev_readat(rdev
, &c
, offset
+ i
* sizeof(c
), sizeof(c
)) != sizeof(c
))
82 return CB_EFI_ACCESS_ERROR
;
83 if ((r
= (c
- msg
[i
])) != 0 || !c
)
91 /* Compare an rdev region and a data buffer */
92 static int rdev_memcmp(struct region_device
*rdev
, size_t offset
, uint8_t *data
, size_t size
)
99 while (size
>= sizeof(buf
)) {
100 if (rdev_readat(rdev
, buf
, offset
+ i
, sizeof(buf
)) != sizeof(buf
))
101 return CB_EFI_ACCESS_ERROR
;
102 r
= memcmp(buf
, data
+ i
, sizeof(buf
));
109 if (rdev_readat(rdev
, buf
, offset
+ i
, 1) != 1)
110 return CB_EFI_ACCESS_ERROR
;
111 r
= buf
[0] - data
[i
];
121 static enum cb_err
validate_fv_header(const struct region_device
*rdev
,
122 EFI_FIRMWARE_VOLUME_HEADER
*fw_vol_hdr
)
124 uint16_t checksum
, data
;
127 if (rdev_readat(rdev
, fw_vol_hdr
, 0, sizeof(*fw_vol_hdr
)) != sizeof(*fw_vol_hdr
))
128 return CB_EFI_ACCESS_ERROR
;
131 * Verify the header revision, header signature, length
132 * Length of FvBlock cannot be 2**64-1
133 * HeaderLength cannot be an odd number
135 if ((fw_vol_hdr
->Revision
!= EFI_FVH_REVISION
)
136 || (fw_vol_hdr
->Signature
!= EFI_FVH_SIGNATURE
)
137 || (fw_vol_hdr
->FvLength
> region_device_sz(rdev
))
138 || (fw_vol_hdr
->HeaderLength
> region_device_sz(rdev
))
139 || (fw_vol_hdr
->HeaderLength
& 1)) {
140 printk(BIOS_WARNING
, PREFIX
"No Firmware Volume header present\n");
141 return CB_EFI_FVH_INVALID
;
144 /* Check the Firmware Volume Guid */
145 if (!compare_guid(&fw_vol_hdr
->FileSystemGuid
, &EfiSystemNvDataFvGuid
)) {
146 printk(BIOS_WARNING
, PREFIX
"Firmware Volume Guid non-compatible\n");
147 return CB_EFI_FVH_INVALID
;
150 /* Verify the header checksum */
152 for (i
= 0; i
< fw_vol_hdr
->HeaderLength
; i
+= 2) {
153 if (rdev_readat(rdev
, &data
, i
, sizeof(data
)) != sizeof(data
))
154 return CB_EFI_ACCESS_ERROR
;
155 checksum
= (uint16_t)(checksum
+ data
); /* intentionally overflows */
158 printk(BIOS_WARNING
, PREFIX
"FV checksum is invalid: 0x%X\n", checksum
);
159 return CB_EFI_CHECKSUM_INVALID
;
162 printk(BIOS_SPEW
, PREFIX
"UEFI FV with size %lld found\n", fw_vol_hdr
->FvLength
);
168 validate_variable_store_header(const EFI_FIRMWARE_VOLUME_HEADER
*fv_hdr
,
169 struct region_device
*rdev
,
172 VARIABLE_STORE_HEADER hdr
;
175 if (rdev_readat(rdev
, &hdr
, fv_hdr
->HeaderLength
, sizeof(hdr
)) != sizeof(hdr
))
176 return CB_EFI_ACCESS_ERROR
;
178 /* Check the Variable Store Guid */
179 if (!compare_guid(&hdr
.Signature
, &EfiVariableGuid
) &&
180 !compare_guid(&hdr
.Signature
, &EfiAuthenticatedVariableGuid
)) {
181 printk(BIOS_WARNING
, PREFIX
"Variable Store Guid non-compatible\n");
182 return CB_EFI_VS_CORRUPTED_INVALID
;
185 *auth_format
= compare_guid(&hdr
.Signature
, &EfiAuthenticatedVariableGuid
);
187 length
= region_device_sz(rdev
) - fv_hdr
->HeaderLength
;
188 if (hdr
.Size
> length
) {
189 printk(BIOS_WARNING
, PREFIX
"Variable Store Length does not match\n");
190 return CB_EFI_VS_CORRUPTED_INVALID
;
193 if (hdr
.Format
!= VARIABLE_STORE_FORMATTED
)
194 return CB_EFI_VS_NOT_FORMATTED_INVALID
;
196 if (hdr
.State
!= VARIABLE_STORE_HEALTHY
)
197 return CB_EFI_VS_CORRUPTED_INVALID
;
199 if (rdev_chain(rdev
, rdev
, fv_hdr
->HeaderLength
+ sizeof(hdr
), hdr
.Size
)) {
200 printk(BIOS_WARNING
, PREFIX
"rdev_chain failed\n");
201 return CB_EFI_ACCESS_ERROR
;
204 printk(BIOS_SPEW
, PREFIX
"UEFI variable store with size %zu found\n",
205 region_device_sz(rdev
));
210 struct efi_find_args
{
211 const EFI_GUID
*guid
;
217 static bool match(struct region_device
*rdev
, VARIABLE_HEADER
*hdr
, size_t hdr_size
,
218 const char *name
, const EFI_GUID
*guid
)
220 /* Only search for valid or in transition to be deleted variables */
221 if ((hdr
->State
!= VAR_ADDED
) &&
222 (hdr
->State
!= (VAR_IN_DELETED_TRANSITION
& VAR_ADDED
)))
225 if ((!compare_guid(&hdr
->VendorGuid
, guid
)) ||
230 if (rdev_strcmp_wchar_ascii(rdev
, hdr_size
, name
) != 0)
237 enum cb_err
find_and_copy(struct region_device
*rdev
, VARIABLE_HEADER
*hdr
, size_t hdr_size
,
238 void *arg
, bool *stop
)
240 struct efi_find_args
*fa
= (struct efi_find_args
*)arg
;
242 if (!match(rdev
, hdr
, hdr_size
, fa
->name
, fa
->guid
))
246 if (*(fa
->size
) < hdr
->DataSize
)
247 return CB_EFI_BUFFER_TOO_SMALL
;
249 if (rdev_readat(rdev
, fa
->data
, hdr_size
+ hdr
->NameSize
, hdr
->DataSize
) !=
251 return CB_EFI_ACCESS_ERROR
;
253 *(fa
->size
) = hdr
->DataSize
;
257 struct efi_find_compare_args
{
258 const EFI_GUID
*guid
;
266 enum cb_err
find_and_compare(struct region_device
*rdev
, VARIABLE_HEADER
*hdr
, size_t hdr_size
,
267 void *arg
, bool *stop
)
269 struct efi_find_compare_args
*fa
= (struct efi_find_compare_args
*)arg
;
271 if (!match(rdev
, hdr
, hdr_size
, fa
->name
, fa
->guid
))
275 if (fa
->size
!= hdr
->DataSize
) {
280 fa
->match
= rdev_memcmp(rdev
, hdr_size
+ hdr
->NameSize
, fa
->data
, hdr
->DataSize
) == 0;
285 static enum cb_err
noop(struct region_device
*rdev
, VARIABLE_HEADER
*hdr
, size_t hdr_size
,
286 void *arg
, bool *stop
)
292 static enum cb_err
print_var(struct region_device
*rdev
, VARIABLE_HEADER
*hdr
, size_t hdr_size
,
293 void *arg
, bool *stop
)
298 printk(BIOS_DEBUG
, "%08zx: Var ", region_device_offset(rdev
));
299 print_guid(BIOS_DEBUG
, &hdr
->VendorGuid
);
301 printk(BIOS_DEBUG
, "-");
303 rdev_print_wchar(BIOS_DEBUG
, rdev
, hdr_size
);
305 printk(BIOS_DEBUG
, ", State %02x, Size %02x\n", hdr
->State
, hdr
->DataSize
);
307 if (hdr
->DataSize
&& hdr
->NameSize
) {
308 len
= sizeof(buf
) < hdr
->DataSize
? sizeof(buf
) : hdr
->DataSize
;
309 if (rdev_readat(rdev
, buf
, hdr_size
+ hdr
->NameSize
, len
) != len
)
310 return CB_EFI_ACCESS_ERROR
;
311 printk(BIOS_DEBUG
, " Data: ");
313 for (i
= 0; i
< len
; i
++)
314 printk(BIOS_DEBUG
, "0x%02x ", buf
[i
]);
316 if (hdr
->DataSize
> len
)
317 printk(BIOS_DEBUG
, "...");
319 printk(BIOS_DEBUG
, "\n");
325 static enum cb_err
walk_variables(struct region_device
*rdev
,
327 enum cb_err (*walker
)(struct region_device
*rdev
,
328 VARIABLE_HEADER
*hdr
,
334 AUTHENTICATED_VARIABLE_HEADER auth_hdr
;
335 size_t header_size
, var_size
;
341 header_size
= sizeof(AUTHENTICATED_VARIABLE_HEADER
);
343 header_size
= sizeof(VARIABLE_HEADER
);
347 if (rdev_readat(rdev
, &auth_hdr
, 0, sizeof(auth_hdr
))
349 return CB_EFI_ACCESS_ERROR
;
350 hdr
.Reserved
= auth_hdr
.Reserved
;
351 hdr
.StartId
= auth_hdr
.StartId
;
352 hdr
.State
= auth_hdr
.State
;
353 hdr
.Attributes
= auth_hdr
.Attributes
;
354 hdr
.NameSize
= auth_hdr
.NameSize
;
355 hdr
.DataSize
= auth_hdr
.DataSize
;
356 memcpy(&hdr
.VendorGuid
, &auth_hdr
.VendorGuid
, sizeof(hdr
.VendorGuid
));
357 } else if (rdev_readat(rdev
, &hdr
, 0, sizeof(hdr
)) != sizeof(hdr
)) {
358 return CB_EFI_ACCESS_ERROR
;
360 if (hdr
.StartId
!= VARIABLE_DATA
)
363 if (hdr
.State
== UINT8_MAX
||
364 hdr
.DataSize
== UINT32_MAX
||
365 hdr
.NameSize
== UINT32_MAX
||
366 hdr
.Attributes
== UINT32_MAX
) {
371 printk(BIOS_SPEW
, "Found variable with state %02x and ", hdr
.State
);
372 print_guid(BIOS_SPEW
, &hdr
.VendorGuid
);
373 printk(BIOS_SPEW
, "\n");
377 ret
= walker(rdev
, &hdr
, header_size
, walker_arg
, &stop
);
379 if (ret
!= CB_SUCCESS
|| stop
)
382 var_size
= ALIGN_UP(header_size
+ hdr
.NameSize
+ hdr
.DataSize
,
384 } while (!rdev_chain(rdev
, rdev
, var_size
, region_device_sz(rdev
) - var_size
));
386 return CB_EFI_OPTION_NOT_FOUND
;
389 static enum cb_err
efi_fv_init(struct region_device
*rdev
, bool *auth_format
)
391 EFI_FIRMWARE_VOLUME_HEADER fv_hdr
;
394 ret
= validate_fv_header(rdev
, &fv_hdr
);
395 if (ret
!= CB_SUCCESS
) {
396 printk(BIOS_WARNING
, PREFIX
"Failed to validate firmware header\n");
400 ret
= validate_variable_store_header(&fv_hdr
, rdev
, auth_format
);
401 if (ret
!= CB_SUCCESS
)
402 printk(BIOS_WARNING
, PREFIX
"Failed to validate variable store header\n");
407 enum cb_err
efi_fv_print_options(struct region_device
*rdev
)
412 ret
= efi_fv_init(rdev
, &auth_format
);
413 if (ret
!= CB_SUCCESS
)
416 return walk_variables(rdev
, auth_format
, print_var
, NULL
);
421 * - writes up to *size bytes into a buffer pointed to by *dest
422 * - rdev is the spi flash region to operate on
423 * - the FVH and variable store header must have been initialized by a third party
425 enum cb_err
efi_fv_get_option(struct region_device
*rdev
,
426 const EFI_GUID
*guid
,
431 struct efi_find_args args
;
435 ret
= efi_fv_init(rdev
, &auth_format
);
436 if (ret
!= CB_SUCCESS
)
444 return walk_variables(rdev
, auth_format
, find_and_copy
, &args
);
447 static enum cb_err
write_auth_hdr(struct region_device
*rdev
, const EFI_GUID
*guid
,
448 const char *name
, void *data
, size_t size
)
450 AUTHENTICATED_VARIABLE_HEADER auth_hdr
;
451 size_t name_size
, var_size
;
454 name_size
= (strlen(name
) + 1) * sizeof(CHAR16
);
455 var_size
= name_size
+ size
+ sizeof(auth_hdr
);
457 if (var_size
> region_device_sz(rdev
))
458 return CB_EFI_STORE_FULL
;
460 /* Sanity check. flash must be blank */
461 if (rdev_readat(rdev
, &auth_hdr
, 0, sizeof(auth_hdr
)) != sizeof(auth_hdr
))
462 return CB_EFI_ACCESS_ERROR
;
464 if (auth_hdr
.StartId
!= UINT16_MAX
||
465 auth_hdr
.State
!= UINT8_MAX
||
466 auth_hdr
.DataSize
!= UINT32_MAX
||
467 auth_hdr
.NameSize
!= UINT32_MAX
||
468 auth_hdr
.Attributes
!= UINT32_MAX
) {
469 return CB_EFI_ACCESS_ERROR
;
472 memset(&auth_hdr
, 0xff, sizeof(auth_hdr
));
474 auth_hdr
.StartId
= VARIABLE_DATA
;
475 auth_hdr
.Attributes
= EFI_VARIABLE_NON_VOLATILE
|
476 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
477 EFI_VARIABLE_RUNTIME_ACCESS
;
478 auth_hdr
.NameSize
= name_size
;
479 auth_hdr
.DataSize
= size
;
480 memcpy(&auth_hdr
.VendorGuid
, guid
, sizeof(EFI_GUID
));
482 /* Write header with no State */
483 if (rdev_writeat(rdev
, &auth_hdr
, 0, sizeof(auth_hdr
)) != sizeof(auth_hdr
))
484 return CB_EFI_ACCESS_ERROR
;
486 /* Set header State to valid header */
487 auth_hdr
.State
= VAR_HEADER_VALID_ONLY
;
488 if (rdev_writeat(rdev
, &auth_hdr
.State
, offsetof(AUTHENTICATED_VARIABLE_HEADER
, State
),
489 sizeof(auth_hdr
.State
)) != sizeof(auth_hdr
.State
))
490 return CB_EFI_ACCESS_ERROR
;
493 ret
= rdev_write_wchar(rdev
, sizeof(auth_hdr
), name
);
494 if (ret
!= CB_SUCCESS
)
498 if (rdev_writeat(rdev
, data
, sizeof(auth_hdr
) + name_size
, size
) != size
)
499 return CB_EFI_ACCESS_ERROR
;
501 /* Set header State to valid data */
502 auth_hdr
.State
= VAR_ADDED
;
503 if (rdev_writeat(rdev
, &auth_hdr
.State
, offsetof(AUTHENTICATED_VARIABLE_HEADER
, State
),
504 sizeof(auth_hdr
.State
)) != sizeof(auth_hdr
.State
))
505 return CB_EFI_ACCESS_ERROR
;
510 static enum cb_err
write_hdr(struct region_device
*rdev
, const EFI_GUID
*guid
,
516 size_t name_size
, var_size
;
519 name_size
= (strlen(name
) + 1) * sizeof(CHAR16
);
520 var_size
= name_size
+ size
+ sizeof(hdr
);
521 if (var_size
> region_device_sz(rdev
))
522 return CB_EFI_STORE_FULL
;
524 /* Sanity check. flash must be blank */
525 if (rdev_readat(rdev
, &hdr
, 0, sizeof(hdr
)) != sizeof(hdr
))
526 return CB_EFI_ACCESS_ERROR
;
528 if (hdr
.StartId
!= UINT16_MAX
||
529 hdr
.State
!= UINT8_MAX
||
530 hdr
.DataSize
!= UINT32_MAX
||
531 hdr
.NameSize
!= UINT32_MAX
||
532 hdr
.Attributes
!= UINT32_MAX
) {
533 return CB_EFI_ACCESS_ERROR
;
536 memset(&hdr
, 0xff, sizeof(hdr
));
538 hdr
.StartId
= VARIABLE_DATA
;
539 hdr
.Attributes
= EFI_VARIABLE_NON_VOLATILE
|
540 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
541 EFI_VARIABLE_RUNTIME_ACCESS
;
542 hdr
.NameSize
= name_size
;
544 memcpy(&hdr
.VendorGuid
, guid
, sizeof(EFI_GUID
));
546 /* Write header with no State */
547 if (rdev_writeat(rdev
, &hdr
, 0, sizeof(hdr
)) != sizeof(hdr
))
548 return CB_EFI_ACCESS_ERROR
;
550 /* Set header State to valid header */
551 hdr
.State
= VAR_HEADER_VALID_ONLY
;
552 if (rdev_writeat(rdev
, &hdr
.State
, offsetof(VARIABLE_HEADER
, State
),
553 sizeof(hdr
.State
)) != sizeof(hdr
.State
))
554 return CB_EFI_ACCESS_ERROR
;
557 ret
= rdev_write_wchar(rdev
, sizeof(hdr
), name
);
558 if (ret
!= CB_SUCCESS
)
562 if (rdev_writeat(rdev
, data
, sizeof(hdr
) + name_size
, size
) != size
)
563 return CB_EFI_ACCESS_ERROR
;
565 /* Set header State to valid data */
566 hdr
.State
= VAR_ADDED
;
567 if (rdev_writeat(rdev
, &hdr
.State
, offsetof(VARIABLE_HEADER
, State
),
568 sizeof(hdr
.State
)) != sizeof(hdr
.State
))
569 return CB_EFI_ACCESS_ERROR
;
576 * - writes size bytes read from the buffer pointed to by *data
577 * - rdev is the spi flash region to operate on
578 * - the FVH and variable store header must have been initialized by a third party
580 enum cb_err
efi_fv_set_option(struct region_device
*rdev
,
581 const EFI_GUID
*guid
,
586 struct region_device rdev_old
;
587 struct efi_find_compare_args args
;
593 ret
= efi_fv_init(rdev
, &auth_format
);
594 if (ret
!= CB_SUCCESS
)
597 /* Find existing variable */
604 ret
= walk_variables(rdev
, auth_format
, find_and_compare
, &args
);
605 found_existing
= ret
== CB_SUCCESS
;
607 if (found_existing
) {
608 printk(BIOS_ERR
, "found existing variable %s, match =%d\n", name
, args
.match
);
615 /* Mark as to be deleted */
616 hdr
.State
= VAR_IN_DELETED_TRANSITION
;
617 if (rdev_writeat(rdev
, &hdr
.State
, offsetof(VARIABLE_HEADER
, State
),
618 sizeof(hdr
.State
)) != sizeof(hdr
.State
))
619 return CB_EFI_ACCESS_ERROR
;
622 /* Walk to end of variable store */
623 ret
= walk_variables(rdev
, auth_format
, noop
, NULL
);
624 if (ret
!= CB_EFI_OPTION_NOT_FOUND
)
627 /* Now append new variable:
628 * 1. Write the header without State field.
629 * 2. Write the State field and set it to HEADER_VALID.
631 * 4. Write the State field and set it to VAR_ADDED
635 ret
= write_auth_hdr(rdev
, guid
, name
, data
, size
);
637 ret
= write_hdr(rdev
, guid
, name
, data
, size
);
638 if (ret
!= CB_SUCCESS
)
641 if (found_existing
) {
642 /* Mark old variable as deleted */
643 hdr
.State
= VAR_DELETED
;
644 if (rdev_writeat(&rdev_old
, &hdr
.State
, offsetof(VARIABLE_HEADER
, State
),
645 sizeof(hdr
.State
)) != sizeof(hdr
.State
))
646 return CB_EFI_ACCESS_ERROR
;