summaryrefslogtreecommitdiff
path: root/tests/drivers/efivars.c
blob: 326e1fb7d51c326c0619b96fc81cb645830f9916 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/* SPDX-License-Identifier: GPL-2.0-only */

#include <drivers/efi/efivars.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);
}