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