| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <drivers/efi/efivars.h> |
| #include <limits.h> |
| #include <vendorcode/intel/edk2/UDK2017/MdePkg/Include/Pi/PiFirmwareVolume.h> |
| #include <vendorcode/intel/edk2/UDK2017/MdeModulePkg/Include/Guid/VariableFormat.h> |
| #include <string.h> |
| #include <tests/test.h> |
| #include <types.h> |
| |
| /* Dummy firmware volume header for a 0x30000 byte partition with a single entry |
| * in a formatted variable store. |
| */ |
| static const uint8_t FVH[] = { |
| /* EFI_FIRMWARE_VOLUME_HEADER */ |
| /* UINT8 ZeroVector[16] */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, |
| /* EFI_GUID FileSystemGuid */ |
| 0x8d, 0x2b, 0xf1, 0xff, 0x96, 0x76, 0x8b, 0x4c, 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, |
| 0x4f, 0x50, |
| /* UINT64 FvLength */ |
| 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /* UINT32 Signature */ |
| 0x5f, 0x46, 0x56, 0x48, |
| /* EFI_FVB_ATTRIBUTES_2 Attributes */ |
| 0x36, 0x0e, 0x00, 0x00, |
| /* UINT16 HeaderLength */ |
| 0x48, 0x00, |
| /* UINT16 Checksum */ |
| 0x00, 0xfa, |
| /* UINT16 ExtHeaderOffset */ |
| 0x00, 0x00, |
| /* UINT8 Reserved[1] */ |
| 0x00, |
| /* UINT8 Revision */ |
| 0x02, |
| /* EFI_FV_BLOCK_MAP_ENTRY BlockMap[2] */ |
| 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| /* Variable Info Header */ |
| /* EFI_GUID Signature */ |
| 0x78, 0x2c, 0xf3, 0xaa, 0x7b, 0x94, 0x9a, 0x43, 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, |
| 0x77, 0x92, |
| /* UINT32 Size */ |
| 0xb8, 0xff, 0x00, 0x00, |
| /* UINT8 Format */ |
| 0x5a, |
| /* UINT8 State */ |
| 0xfe, |
| /* UINT16 Reserved */ |
| 0x00, 0x00, |
| /* UINT32 Reserved1 */ |
| 0x00, 0x00, 0x00, 0x00, |
| /* AUTHENTICATED_VARIABLE_HEADER */ |
| /* UINT16 StartId */ |
| 0xaa, 0x55, |
| /* UINT8 State */ |
| 0x3f, |
| /* UINT8 Reserved */ |
| 0xff, |
| /* UINT32 Attributes */ |
| 0x07, 0x00, 0x00, 0x00, |
| /* UINT64 MonotonicCount */ |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| /* EFI_TIME TimeStamp */ |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, |
| /* UINT32 PubKeyIndex */ |
| 0xff, 0xff, 0xff, 0xff, |
| /* UINT32 NameSize */ |
| 0x12, 0x00, 0x00, 0x00, |
| /* UINT32 DataSize */ |
| 0x09, 0x00, 0x00, 0x00, |
| /* EFI_GUID VendorGuid */ |
| 0x1d, 0x4c, 0xae, 0xce, 0x5b, 0x33, 0x85, 0x46, 0xa4, 0xa0, 0xfc, 0x4a, |
| 0x94, 0xee, 0xa0, 0x85, |
| /* L"coreboot" */ |
| 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x62, 0x00, |
| 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x00, 0x00, |
| /* "is great" */ |
| 0x69, 0x73, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x00, |
| }; |
| |
| #define FVH_CHECKSUMMED_SIZE (sizeof(EFI_FIRMWARE_VOLUME_HEADER) + 8 + sizeof(EFI_GUID)) |
| |
| static struct region_device flash_rdev_rw; |
| static uint8_t flash_buffer[0x30000]; |
| |
| static const char *name = "coreboot"; |
| |
| static void mock_rdev(bool init) |
| { |
| if (init) { |
| /* Emulate NOR flash by setting all bits to 1 */ |
| memset(flash_buffer, 0xff, sizeof(flash_buffer)); |
| /* Place _FVH and VIH headers, as well as test data */ |
| memcpy(flash_buffer, FVH, sizeof(FVH)); |
| } |
| |
| rdev_chain_mem_rw(&flash_rdev_rw, flash_buffer, sizeof(flash_buffer)); |
| } |
| |
| static const EFI_GUID EficorebootNvDataGuid = { |
| 0xceae4c1d, 0x335b, 0x4685, { 0xa4, 0xa0, 0xfc, 0x4a, 0x94, 0xee, 0xa0, 0x85 } }; |
| |
| /* Test valid and corrupted FVH header */ |
| static void efi_test_header(void **state) |
| { |
| enum cb_err ret; |
| uint8_t buf[16]; |
| uint32_t size; |
| int i; |
| |
| mock_rdev(true); |
| |
| /* Test variable lookup with intact header */ |
| size = sizeof(buf); |
| ret = efi_fv_get_option(&flash_rdev_rw, &EficorebootNvDataGuid, name, buf, &size); |
| assert_int_equal(ret, CB_SUCCESS); |
| assert_int_equal(size, strlen("is great")+1); |
| assert_string_equal((const char *)buf, "is great"); |
| |
| for (i = 0; i < FVH_CHECKSUMMED_SIZE; i++) { |
| mock_rdev(true); |
| |
| /* Flip some bits */ |
| flash_buffer[i] ^= 0xff; |
| |
| size = sizeof(buf); |
| ret = efi_fv_get_option(&flash_rdev_rw, &EficorebootNvDataGuid, name, buf, |
| &size); |
| assert_int_not_equal(ret, CB_SUCCESS); |
| } |
| } |
| |
| /* Write with the same key and value should not modify the store */ |
| static void efi_test_noop_existing_write(void **state) |
| { |
| enum cb_err ret; |
| int i; |
| |
| mock_rdev(true); |
| |
| ret = efi_fv_set_option(&flash_rdev_rw, |
| &EficorebootNvDataGuid, |
| name, |
| "is great", |
| strlen("is great") + 1); |
| |
| assert_int_equal(ret, CB_SUCCESS); |
| |
| for (i = sizeof(FVH); i < sizeof(flash_buffer); i++) |
| assert_int_equal(flash_buffer[i], 0xff); |
| } |
| |
| static void efi_test_new_write(void **state) |
| { |
| enum cb_err ret; |
| uint8_t buf[16]; |
| uint32_t size; |
| int i; |
| |
| mock_rdev(true); |
| |
| ret = efi_fv_set_option(&flash_rdev_rw, &EficorebootNvDataGuid, |
| name, "is awesome", strlen("is awesome") + 1); |
| assert_int_equal(ret, CB_SUCCESS); |
| |
| /* New variable has been written */ |
| assert_int_equal(flash_buffer[ALIGN_UP(sizeof(FVH), 4)], 0xaa); |
| assert_int_equal(flash_buffer[ALIGN_UP(sizeof(FVH), 4) + 1], 0x55); |
| |
| /* Remaining space is blank */ |
| for (i = ALIGN_UP(sizeof(FVH), 4) + 89; i < sizeof(flash_buffer); i++) |
| assert_int_equal(flash_buffer[i], 0xff); |
| |
| mock_rdev(false); |
| |
| memset(buf, 0, sizeof(buf)); |
| size = sizeof(buf); |
| ret = efi_fv_get_option(&flash_rdev_rw, &EficorebootNvDataGuid, name, buf, |
| &size); |
| assert_int_equal(ret, CB_SUCCESS); |
| assert_int_equal(size, strlen("is awesome")+1); |
| |
| assert_int_equal(flash_buffer[ALIGN_UP(sizeof(FVH), 4) + 1], 0x55); |
| assert_string_equal((const char *)buf, "is awesome"); |
| } |
| |
| int main(void) |
| { |
| const struct CMUnitTest tests[] = { |
| cmocka_unit_test(efi_test_header), |
| cmocka_unit_test(efi_test_noop_existing_write), |
| cmocka_unit_test(efi_test_new_write) |
| }; |
| |
| return cb_run_group_tests(tests, NULL, NULL); |
| } |