1 /*
2  * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <CppUTest/TestHarness.h>
8 #include <cassert>
9 #include <cstring>
10 #include <vector>
11 
12 #include "common/uuid/uuid.h"
13 #include "protocols/service/fwu/fwu_proto.h"
14 #include "protocols/service/fwu/status.h"
15 #include "service/fwu/test/fwu_dut/fwu_dut.h"
16 #include "service/fwu/test/fwu_dut_factory/fwu_dut_factory.h"
17 
18 #define EFI_BUFFER_TOO_SMALL ((int32_t)5)
19 #define EFI_SUCCESS	     ((int32_t)0)
20 
21 //*************************************************************
22 // EFI_FIRMWARE_IMAGE_DESCRIPTOR
23 //*************************************************************
24 typedef struct {
25 	uint8_t ImageIndex;
26 	struct uuid_octets ImageTypeId;
27 	uint64_t ImageId;
28 	char16_t *ImageIdName;
29 	uint32_t Version;
30 	char16_t *VersionName;
31 	uint64_t Size;
32 	uint64_t AttributesSupported;
33 	uint64_t AttributesSetting;
34 	uint64_t Compatibilities;
35 	//Introduced with DescriptorVersion 2+
36 	uint32_t LowestSupportedImageVersion;
37 	//Introduced with DescriptorVersion 3+
38 	uint32_t LastAttemptVersion;
39 	uint32_t LastAttemptStatus;
40 	uint64_t HardwareInstance;
41 } EFI_FIRMWARE_IMAGE_DESCRIPTOR;
42 
43 struct fmp {
fmpfmp44 	explicit fmp(fwu_client *m_fwu_client)
45 		: client(m_fwu_client)
46 		, is_staging(false)
47 		, payload_max_size(0)
48 		, img_info(NULL)
49 		, num_images(0)
50 
51 	{
52 		parse_img_directory();
53 	}
54 
~fmpfmp55 	~fmp()
56 	{
57 		if (img_info)
58 			delete[] img_info;
59 	}
60 
get_image_infofmp61 	int get_image_info(uint64_t *ImageInfoSize, EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo,
62 			   uint32_t *DescriptorVersion, uint8_t *DescriptorCount,
63 			   uint64_t *DescriptorSize, uint32_t *PackageVersion,
64 			   char16_t **PackageVersionName)
65 	{
66 		const uint64_t img_info_size = num_images * sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR);
67 		if (img_info_size > *ImageInfoSize) {
68 			*ImageInfoSize = img_info_size;
69 
70 			return -EFI_BUFFER_TOO_SMALL;
71 		}
72 
73 		memcpy(ImageInfo, img_info, *ImageInfoSize);
74 
75 		*DescriptorVersion = 3;
76 		*DescriptorCount = num_images;
77 		*DescriptorSize = sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR);
78 
79 		// PackageVersion 0xFFFFFFFF means that package version
80 		// is not supported. See UEFI specification.
81 		*PackageVersion = 0xFFFFFFFF;
82 
83 		return EFI_SUCCESS;
84 	};
85 
set_imagefmp86 	int set_image(uint8_t ImageIndex, const void *Image, uint64_t ImageSize,
87 		      const void *VendorCode, void *Progress, char16_t **AbortReason)
88 	{
89 		int status = 0;
90 		uint32_t stream_handle = 0;
91 
92 		struct uuid_octets *uuid = &(img_info[ImageIndex].ImageTypeId);
93 
94 		if (!is_staging) {
95 			status = client->begin_staging(0, 0, NULL);
96 			LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
97 			is_staging = true;
98 		}
99 
100 		status = client->open(uuid, fwu_client::op_type::WRITE, &stream_handle);
101 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
102 
103 		status = client->write_stream(stream_handle, static_cast<const uint8_t *>(Image),
104 					      ImageSize);
105 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
106 
107 		status = client->commit(stream_handle, false);
108 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
109 
110 		return 0;
111 	};
112 
113 private:
parse_img_directoryfmp114 	int parse_img_directory()
115 	{
116 		int status = 0;
117 		uint32_t stream_handle = 0;
118 		size_t reported_total_len = 0;
119 		struct uuid_octets uuid;
120 
121 		struct fwu_image_directory *img_dir = NULL;
122 
123 		size_t data_len_read = 0;
124 		int num_img = 0;
125 
126 		assert(img_info == NULL);
127 
128 		uuid_guid_octets_from_canonical(&uuid, FWU_DIRECTORY_CANONICAL_UUID);
129 
130 		status = client->open(&uuid, fwu_client::op_type::READ, &stream_handle);
131 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
132 
133 		// Determine the size of the FW directory without reading any info.
134 		status = client->read_stream(stream_handle, NULL, 0, &data_len_read,
135 					     &reported_total_len);
136 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
137 
138 		// Close and reopen the firmware directory stream
139 		// to reset the read seek.
140 		status = client->commit(stream_handle, false);
141 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
142 		status = client->open(&uuid, fwu_client::op_type::READ, &stream_handle);
143 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
144 
145 		img_dir = (fwu_image_directory *)new uint8_t[reported_total_len];
146 
147 		// Read the firmware directory info into img_dir.
148 		status = client->read_stream(stream_handle, reinterpret_cast<uint8_t *>(img_dir),
149 					     reported_total_len, &data_len_read,
150 					     &reported_total_len);
151 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
152 		LONGS_EQUAL(data_len_read, reported_total_len);
153 
154 		status = client->commit(stream_handle, false);
155 		LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
156 
157 		num_img = img_dir->num_images;
158 
159 		// Translate the data from each entry in the img directory into the img
160 		// info array.
161 		img_info = (EFI_FIRMWARE_IMAGE_DESCRIPTOR
162 				    *)new uint8_t[num_img * sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR)];
163 
164 		for (int idx = 0; idx < num_img; idx++) {
165 			img_info[idx].ImageIndex = idx;
166 			memcpy(img_info[idx].ImageTypeId.octets,
167 			       img_dir->img_info_entry[idx].img_type_uuid, UUID_OCTETS_LEN);
168 			img_info[idx].ImageId = idx;
169 			img_info[idx].ImageIdName = NULL;
170 			img_info[idx].Version = img_dir->img_info_entry[idx].img_version;
171 			img_info[idx].VersionName = NULL;
172 			img_info[idx].Size = img_dir->img_info_entry[idx].img_max_size;
173 			img_info[idx].AttributesSupported = 0;
174 			img_info[idx].AttributesSetting = 0;
175 			img_info[idx].Compatibilities = 0;
176 			img_info[idx].LowestSupportedImageVersion = 0;
177 			img_info[idx].LastAttemptVersion = 0;
178 			img_info[idx].LastAttemptStatus = 0;
179 			img_info[idx].HardwareInstance = 0;
180 		}
181 
182 		num_images = num_img;
183 
184 		delete[] img_dir;
185 		return status;
186 	}
187 
188 	fwu_client *client;
189 	bool is_staging;
190 
191 	int payload_max_size;
192 	EFI_FIRMWARE_IMAGE_DESCRIPTOR *img_info;
193 	uint32_t num_images;
194 };
195 
196 /*
197  * Tests that perform a range of normal update scenarios
198  */
TEST_GROUP(FwuUpdateFmpTests)199 TEST_GROUP(FwuUpdateFmpTests)
200 {
201 	void setup()
202 	{
203 		m_dut = NULL;
204 		m_fwu_client = NULL;
205 		m_fmp_protocol = NULL;
206 	}
207 
208 	void teardown()
209 	{
210 		delete m_fwu_client;
211 		m_fwu_client = NULL;
212 
213 		delete m_dut;
214 		m_dut = NULL;
215 
216 		delete m_fmp_protocol;
217 		m_fmp_protocol = NULL;
218 	}
219 
220 	fwu_dut *m_dut;
221 	fwu_client *m_fwu_client;
222 	fmp *m_fmp_protocol;
223 };
224 
TEST(FwuUpdateFmpTests,wholeFmpFlow)225 TEST(FwuUpdateFmpTests, wholeFmpFlow)
226 {
227 	struct uuid_octets uuid;
228 
229 	uint64_t ImageInfoSize;
230 	uint32_t DescriptorVersion;
231 	uint8_t DescriptorCount;
232 	uint64_t DescriptorSize;
233 	uint32_t PackageVersion;
234 	int status;
235 
236 	EFI_FIRMWARE_IMAGE_DESCRIPTOR *img_info = NULL;
237 
238 	// Create a FW Store with two firmware images.
239 	m_dut = fwu_dut_factory::create(1, false);
240 	m_fwu_client = m_dut->create_fwu_client();
241 
242 	assert(m_fwu_client != NULL);
243 
244 	// Generate the images to be installed.
245 	std::vector<uint8_t> image_data;
246 	m_dut->generate_image_data(&image_data);
247 
248 	// System start
249 	m_dut->boot();
250 
251 	// Obtain the image_type_guid -- emulate the guid from the payload in an FMP capsule.
252 	m_dut->whole_volume_image_type_uuid(0, &uuid);
253 
254 	m_fmp_protocol = new fmp(m_fwu_client);
255 
256 	ImageInfoSize = 0;
257 
258 	// First call to fmp.get_image_info obtains the required size.
259 	m_fmp_protocol->get_image_info(&ImageInfoSize, img_info, &DescriptorVersion,
260 				       &DescriptorCount, &DescriptorSize, &PackageVersion, NULL);
261 
262 	img_info = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)new uint8_t[ImageInfoSize];
263 
264 	m_fmp_protocol->get_image_info(&ImageInfoSize, img_info, &DescriptorVersion,
265 				       &DescriptorCount, &DescriptorSize, &PackageVersion, NULL);
266 
267 	// Iterate over all the image descriptors returned by get_img_info
268 	// to obtain the ImageId.
269 	for (int idx = 0; idx < DescriptorCount; idx++) {
270 		if (uuid_is_equal(img_info[idx].ImageTypeId.octets, uuid.octets)) {
271 			uint8_t img_idx = img_info[idx].ImageIndex;
272 
273 			m_fmp_protocol->set_image(img_idx, image_data.data(), image_data.size(),
274 						  NULL, NULL, NULL);
275 			break;
276 		}
277 	}
278 
279 	status = m_fwu_client->end_staging();
280 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
281 
282 	delete[] img_info;
283 }
284