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