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 <vector>
9 
10 #include "protocols/service/fwu/status.h"
11 #include "service/fwu/test/fwu_dut/fwu_dut.h"
12 #include "service/fwu/test/fwu_dut_factory/fwu_dut_factory.h"
13 #include "service/fwu/test/image_directory_checker/image_directory_checker.h"
14 
15 /*
16  * Tests that check behaviour when oversize images are installed.
17  */
TEST_GROUP(FwuOversizeImageTests)18 TEST_GROUP(FwuOversizeImageTests)
19 {
20 	void setup()
21 	{
22 		m_dut = NULL;
23 		m_fwu_client = NULL;
24 	}
25 
26 	void teardown()
27 	{
28 		delete m_fwu_client;
29 		m_fwu_client = NULL;
30 
31 		delete m_dut;
32 		m_dut = NULL;
33 	}
34 
35 	size_t max_image_size(const struct uuid_octets *uuid)
36 	{
37 		image_directory_checker dir_checker;
38 
39 		int status = dir_checker.fetch_image_directory(m_fwu_client);
40 		LONGS_EQUAL(0, status);
41 
42 		const struct fwu_image_info_entry *img_entry = dir_checker.find_entry(uuid);
43 		CHECK_TRUE(img_entry);
44 
45 		return static_cast<size_t>(img_entry->img_max_size);
46 	}
47 
48 	fwu_dut *m_dut;
49 	fwu_client *m_fwu_client;
50 };
51 
TEST(FwuOversizeImageTests,maxSizeInstall)52 TEST(FwuOversizeImageTests, maxSizeInstall)
53 {
54 	int status = 0;
55 	struct uuid_octets uuid;
56 	uint32_t stream_handle = 0;
57 
58 	/* Performs an update with an image of the maximum size advertised by
59 	 * the image directory.
60 	 */
61 	m_dut = fwu_dut_factory::create(1, false);
62 	m_fwu_client = m_dut->create_fwu_client();
63 
64 	m_dut->boot();
65 
66 	/* Generate image that should just fit */
67 	m_dut->whole_volume_image_type_uuid(0, &uuid);
68 	size_t img_size = max_image_size(&uuid);
69 	std::vector<uint8_t> image_data;
70 	m_dut->generate_image_data(&image_data, img_size);
71 
72 	/* Install the image */
73 	status = m_fwu_client->begin_staging(0, 0, NULL);
74 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
75 
76 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
77 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
78 
79 	status = m_fwu_client->write_stream(stream_handle,
80 					    reinterpret_cast<const uint8_t *>(image_data.data()),
81 					    image_data.size());
82 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
83 
84 	status = m_fwu_client->commit(stream_handle, false);
85 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
86 
87 	status = m_fwu_client->end_staging();
88 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
89 
90 	/* Check for reboot with no errors */
91 	m_dut->shutdown();
92 	m_dut->boot();
93 }
94 
TEST(FwuOversizeImageTests,oversizeInstallCancelStaging)95 TEST(FwuOversizeImageTests, oversizeInstallCancelStaging)
96 {
97 	int status = 0;
98 	struct uuid_octets uuid;
99 	uint32_t stream_handle = 0;
100 
101 	/* Performs an update with an oversized image where the client
102 	 * cancels staging on seeing the error. This is the expected
103 	 * client behavior when an error occurs during installation.
104 	 */
105 	m_dut = fwu_dut_factory::create(1, false);
106 	m_fwu_client = m_dut->create_fwu_client();
107 
108 	m_dut->boot();
109 
110 	/* Generate image that's too big */
111 	m_dut->whole_volume_image_type_uuid(0, &uuid);
112 	size_t img_size = max_image_size(&uuid) + 1;
113 	std::vector<uint8_t> image_data;
114 	m_dut->generate_image_data(&image_data, img_size);
115 
116 	/* Install the image */
117 	status = m_fwu_client->begin_staging(0, 0, NULL);
118 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
119 
120 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
121 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
122 
123 	status = m_fwu_client->write_stream(stream_handle,
124 					    reinterpret_cast<const uint8_t *>(image_data.data()),
125 					    image_data.size());
126 	LONGS_EQUAL(FWU_STATUS_OUT_OF_BOUNDS, status);
127 
128 	/* Client response to the error by cancelling staging */
129 	status = m_fwu_client->cancel_staging();
130 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
131 
132 	/* Check for reboot with no errors */
133 	m_dut->shutdown();
134 	m_dut->boot();
135 }
136 
TEST(FwuOversizeImageTests,oversizeInstallEndStaging)137 TEST(FwuOversizeImageTests, oversizeInstallEndStaging)
138 {
139 	int status = 0;
140 	struct uuid_octets uuid;
141 	uint32_t stream_handle = 0;
142 
143 	/* Performs an update with an oversized image where the client
144 	 * attempts to proceed with the update beyond the point where an error
145 	 * was reported due to an attempt to install an oversized image.
146 	 */
147 	m_dut = fwu_dut_factory::create(1, false);
148 	m_fwu_client = m_dut->create_fwu_client();
149 
150 	m_dut->boot();
151 
152 	/* Generate image that's too big */
153 	m_dut->whole_volume_image_type_uuid(0, &uuid);
154 	size_t img_size = max_image_size(&uuid) + 1;
155 	std::vector<uint8_t> image_data;
156 	m_dut->generate_image_data(&image_data, img_size);
157 
158 	/* Install the image */
159 	status = m_fwu_client->begin_staging(0, 0, NULL);
160 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
161 
162 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
163 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
164 
165 	status = m_fwu_client->write_stream(stream_handle,
166 					    reinterpret_cast<const uint8_t *>(image_data.data()),
167 					    image_data.size());
168 	LONGS_EQUAL(FWU_STATUS_OUT_OF_BOUNDS, status);
169 
170 	status = m_fwu_client->commit(stream_handle, false);
171 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
172 
173 	/* Client has ignored the error and proceeded to end staging. Because
174 	 * the DUT was configured to not support partial updates, expect end_staging
175 	 * to fail.
176 	 */
177 	status = m_fwu_client->end_staging();
178 	LONGS_EQUAL(FWU_STATUS_NOT_AVAILABLE, status);
179 
180 	/* Check for reboot with no errors */
181 	m_dut->shutdown();
182 	m_dut->boot();
183 }
184 
TEST(FwuOversizeImageTests,oversizeInstallMultiLocationEndStaging)185 TEST(FwuOversizeImageTests, oversizeInstallMultiLocationEndStaging)
186 {
187 	int status = 0;
188 	struct uuid_octets uuid;
189 	uint32_t stream_handle = 0;
190 
191 	/* Performs an update with one oversized image when the DUT has
192 	 * multiple locations. This checks handling when some installs are
193 	 * successful but one fails because the image is too big. Because
194 	 * the DUT allows partial updates, if the client proceeds to
195 	 * finalize the update, any images handled by the errored installer
196 	 * will be ignored and treated as if they weren't updated.
197 	 */
198 	m_dut = fwu_dut_factory::create(3, true);
199 	m_fwu_client = m_dut->create_fwu_client();
200 
201 	m_dut->boot();
202 
203 	/* Perform multi-image update transaction */
204 	status = m_fwu_client->begin_staging(0, 0, NULL);
205 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
206 
207 	/* Install good image for location 0 */
208 	std::vector<uint8_t> image_data;
209 	m_dut->whole_volume_image_type_uuid(0, &uuid);
210 	size_t img_size = max_image_size(&uuid);
211 	m_dut->generate_image_data(&image_data, img_size);
212 
213 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
214 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
215 
216 	status = m_fwu_client->write_stream(stream_handle,
217 					    reinterpret_cast<const uint8_t *>(image_data.data()),
218 					    image_data.size());
219 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
220 
221 	status = m_fwu_client->commit(stream_handle, false);
222 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
223 
224 	/* Install oversized image for location 1 */
225 	m_dut->whole_volume_image_type_uuid(1, &uuid);
226 	img_size = max_image_size(&uuid) + 1;
227 	m_dut->generate_image_data(&image_data, img_size);
228 
229 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
230 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
231 
232 	status = m_fwu_client->write_stream(stream_handle,
233 					    reinterpret_cast<const uint8_t *>(image_data.data()),
234 					    image_data.size());
235 	LONGS_EQUAL(FWU_STATUS_OUT_OF_BOUNDS, status);
236 
237 	status = m_fwu_client->commit(stream_handle, false);
238 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
239 
240 	/* Install good image for location 2 */
241 	m_dut->whole_volume_image_type_uuid(2, &uuid);
242 	img_size = max_image_size(&uuid);
243 	m_dut->generate_image_data(&image_data, img_size);
244 
245 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
246 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
247 
248 	status = m_fwu_client->write_stream(stream_handle,
249 					    reinterpret_cast<const uint8_t *>(image_data.data()),
250 					    image_data.size());
251 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
252 
253 	status = m_fwu_client->commit(stream_handle, false);
254 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
255 
256 	/* Expect end staging to be successful and that the errored image is
257 	 * excluded from the update. This should be confirmed by the following
258 	 * boot being error-free.
259 	 */
260 	status = m_fwu_client->end_staging();
261 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
262 
263 	/* Check for reboot with no errors */
264 	m_dut->shutdown();
265 	m_dut->boot();
266 }
267