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