1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Test ESRT tables support
4 *
5 * Copyright (C) 2021 Arm Ltd.
6 */
7 #include <efi_loader.h>
8 #include <efi_selftest.h>
9
10 // This value must not exceed 255.
11 // An FMP cannot contain more than 255 FW images.
12 #define TEST_ESRT_NUM_ENTRIES 255
13
14 static
15 struct efi_firmware_image_descriptor static_img_info[TEST_ESRT_NUM_ENTRIES];
16
17 static const struct efi_system_table *local_systable;
18
19 static efi_handle_t fmp_handle;
20
21 static const efi_guid_t efi_fmp_guid =
22 EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
23
efi_test_esrt_init_info(void)24 static void efi_test_esrt_init_info(void)
25 {
26 for (int idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++) {
27 static_img_info[idx].image_index = idx;
28
29 // Note: the 16 byte value present in
30 // static_img_info[idx].image_type_id is not strictly a GUID.
31 // The value is used for the sake of code testing.
32 static_img_info[idx].image_type_id.b[0] = idx;
33
34 static_img_info[idx].image_id = 0;
35 static_img_info[idx].image_id_name = NULL;
36 static_img_info[idx].version = 0;
37 static_img_info[idx].version_name = NULL;
38 static_img_info[idx].size = 0;
39 static_img_info[idx].lowest_supported_image_version = 1;
40 static_img_info[idx].last_attempt_version = 2;
41 static_img_info[idx].last_attempt_status = 3;
42 static_img_info[idx].hardware_instance = 1;
43 }
44 }
45
46 static efi_status_t
efi_test_fmp_get_image_info(struct efi_firmware_management_protocol * this,efi_uintn_t * image_info_size,struct efi_firmware_image_descriptor * image_info,u32 * descriptor_version,u8 * descriptor_count,efi_uintn_t * descriptor_size,u32 * package_version,u16 ** package_version_name)47 EFIAPI efi_test_fmp_get_image_info(struct efi_firmware_management_protocol *this,
48 efi_uintn_t *image_info_size,
49 struct efi_firmware_image_descriptor *image_info,
50 u32 *descriptor_version,
51 u8 *descriptor_count,
52 efi_uintn_t *descriptor_size,
53 u32 *package_version,
54 u16 **package_version_name)
55 {
56 efi_status_t ret = EFI_SUCCESS;
57
58 if (!image_info_size)
59 return EFI_INVALID_PARAMETER;
60
61 if (descriptor_version)
62 *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
63 if (descriptor_count)
64 *descriptor_count = TEST_ESRT_NUM_ENTRIES;
65 if (descriptor_size)
66 *descriptor_size = sizeof(*image_info);
67 if (package_version)
68 *package_version = 0xffffffff;
69 if (package_version_name)
70 *package_version_name = NULL;
71
72 if (*image_info_size < sizeof(*image_info) * TEST_ESRT_NUM_ENTRIES) {
73 *image_info_size = sizeof(*image_info) * TEST_ESRT_NUM_ENTRIES;
74 return EFI_BUFFER_TOO_SMALL;
75 }
76 if (!image_info)
77 return EFI_INVALID_PARAMETER;
78
79 for (int idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++)
80 image_info[idx] = static_img_info[idx];
81
82 return ret;
83 }
84
85 static struct efi_firmware_management_protocol efi_test_fmp = {
86 .get_image_info = efi_test_fmp_get_image_info,
87 .get_image = NULL,
88 .set_image = NULL,
89 .check_image = NULL,
90 .get_package_info = NULL,
91 .set_package_info = NULL,
92 };
93
lib_test_get_esrt(void)94 static void *lib_test_get_esrt(void)
95 {
96 for (int idx = 0; idx < local_systable->nr_tables; idx++)
97 if (!guidcmp(&efi_esrt_guid, &local_systable->tables[idx].guid))
98 return local_systable->tables[idx].table;
99
100 return NULL;
101 }
102
103 /**
104 * lib_test_check_uuid_entry: Find an ESRT entry for which the fw_calss field matches
105 * the image_type_id in the @img_info.
106 * Ensure that all of the field in the ESRT entry have the same value as the corresponding
107 * fields in the @img_info.
108 *
109 * @esrt: pointer to the ESRT
110 * @img_info: an image_info_descriptor output by the FMP get_image_info
111 *
112 * Return: true if matching ESRT entry is found and if all the ESRT entry fields match the
113 * corresponding @img_info fields.
114 */
lib_test_check_uuid_entry(struct efi_system_resource_table * esrt,struct efi_firmware_image_descriptor * img_info)115 static bool lib_test_check_uuid_entry(struct efi_system_resource_table *esrt,
116 struct efi_firmware_image_descriptor
117 *img_info)
118 {
119 const u32 filled_entries = esrt->fw_resource_count;
120 struct efi_system_resource_entry *entry = esrt->entries;
121
122 for (u32 idx = 0; idx < filled_entries; idx++) {
123 if (!guidcmp(&entry[idx].fw_class, &img_info->image_type_id)) {
124 if (entry[idx].fw_version != img_info->version) {
125 efi_st_error("ESRT field mismatch for entry with fw_class=%pU\n",
126 &img_info->image_type_id);
127 return false;
128 }
129
130 if (entry[idx].lowest_supported_fw_version !=
131 img_info->lowest_supported_image_version) {
132 efi_st_error("ESRT field mismatch for entry with fw_class=%pU\n",
133 &img_info->image_type_id);
134 return false;
135 }
136
137 if (entry[idx].last_attempt_version !=
138 img_info->last_attempt_version) {
139 efi_st_error("ESRT field mismatch for entry with fw_class=%pU\n",
140 &img_info->image_type_id);
141 return false;
142 }
143
144 if (entry[idx].last_attempt_status !=
145 img_info->last_attempt_status) {
146 efi_st_error("ESRT field mismatch for entry with fw_class=%pU\n",
147 &img_info->image_type_id);
148 return false;
149 }
150
151 /*
152 * The entry with fw_class = img_uuid matches with the
153 * remainder fmp input.
154 */
155 return true;
156 }
157 }
158
159 /* There exists no entry with fw_class equal to img_uuid in the ESRT. */
160 efi_st_error("ESRT no entry with fw_class= %pUl\n", &img_info->image_type_id);
161
162 return false;
163 }
164
165 /*
166 * Setup unit test.
167 *
168 * Initialize the test FMP datastructure.
169 *
170 * @handle: handle of the loaded image
171 * @systable: system table
172 * Return: EFI_ST_SUCCESS for success
173 */
setup(const efi_handle_t handle,const struct efi_system_table * systable)174 static int setup(const efi_handle_t handle,
175 const struct efi_system_table *systable)
176 {
177 local_systable = systable;
178
179 efi_test_esrt_init_info();
180
181 return EFI_ST_SUCCESS;
182 }
183
184 /*
185 * Tear down unit test.
186 *
187 * Uninstall the test FMP.
188 *
189 * Return: EFI_ST_SUCCESS for success
190 */
teardown(void)191 static int teardown(void)
192 {
193 efi_status_t ret = EFI_SUCCESS;
194 struct efi_boot_services *bt;
195
196 bt = local_systable->boottime;
197
198 if (!bt) {
199 efi_st_error("Cannot find boottime services structure\n");
200 return EFI_ST_FAILURE;
201 }
202
203 ret = bt->uninstall_multiple_protocol_interfaces
204 (fmp_handle, &efi_fmp_guid,
205 &efi_test_fmp, NULL);
206
207 if (ret != EFI_SUCCESS) {
208 efi_st_error("Failed to uninstall FMP\n");
209 return EFI_ST_FAILURE;
210 }
211
212 return EFI_ST_SUCCESS;
213 }
214
215 /*
216 * Perform the test
217 *
218 * The test consists of the following steps:
219 *
220 * 1) Obtain the ESRT
221 * 2) Record the number of ESRT entries prior to test start
222 * 3) Install the test FMP
223 * 4) Re-obtain the ESRT (the ESRT pointer may have changed with the FMP install)
224 * 5) verify that the ESRT entries have increased by the number of entries in the
225 * test FMP.
226 * 6) Traverse all the elements used as the test FMP input and verify that each
227 * has a corresponding ESRT entry and that the fields are correctly set.
228 *
229 * The failure of any of the above steps results in a test failure.
230 *
231 */
execute(void)232 static int execute(void)
233 {
234 struct efi_system_resource_table *esrt;
235 efi_status_t ret = EFI_SUCCESS;
236 u32 base_entry_count;
237 u32 entry_delta;
238 struct efi_boot_services *bt;
239
240 bt = local_systable->boottime;
241
242 if (!bt) {
243 efi_st_error("Cannot find boottime services structure\n");
244 return EFI_ST_FAILURE;
245 }
246
247 esrt = lib_test_get_esrt();
248 if (!esrt) {
249 efi_st_error("ESRT table not present\n");
250 return EFI_ST_FAILURE;
251 }
252 base_entry_count = esrt->fw_resource_count;
253
254 ret = bt->install_multiple_protocol_interfaces(&fmp_handle,
255 &efi_fmp_guid,
256 &efi_test_fmp,
257 NULL);
258
259 if (ret != EFI_SUCCESS) {
260 efi_st_error("Failed to install FMP\n");
261 return EFI_ST_FAILURE;
262 }
263
264 esrt = lib_test_get_esrt();
265 if (!esrt) {
266 efi_st_error("ESRT table not present\n");
267 return EFI_ST_FAILURE;
268 }
269
270 entry_delta = esrt->fw_resource_count - base_entry_count;
271 if (entry_delta != TEST_ESRT_NUM_ENTRIES) {
272 efi_st_error("ESRT mismatch in new entry count (%d), expected (%d).\n",
273 entry_delta, TEST_ESRT_NUM_ENTRIES);
274 return EFI_ST_FAILURE;
275 }
276
277 for (u32 idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++)
278 if (!lib_test_check_uuid_entry(esrt, &static_img_info[idx])) {
279 efi_st_error("ESRT entry mismatch\n");
280 return EFI_ST_FAILURE;
281 }
282
283 return EFI_ST_SUCCESS;
284 }
285
286 EFI_UNIT_TEST(esrt) = {
287 .name = "esrt",
288 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
289 .setup = setup,
290 .execute = execute,
291 .teardown = teardown,
292 };
293