1 /*
2  * Copyright (c) 2022-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 
14 /*
15  * Tests that perform a range of normal update scenarios
16  */
TEST_GROUP(FwuUpdateScenarioTests)17 TEST_GROUP(FwuUpdateScenarioTests)
18 {
19 	void setup()
20 	{
21 		m_dut = NULL;
22 		m_fwu_client = NULL;
23 		m_metadata_checker = NULL;
24 	}
25 
26 	void teardown()
27 	{
28 		delete m_metadata_checker;
29 		m_metadata_checker = NULL;
30 
31 		delete m_fwu_client;
32 		m_fwu_client = NULL;
33 
34 		delete m_dut;
35 		m_dut = NULL;
36 	}
37 
38 	fwu_dut *m_dut;
39 	metadata_checker *m_metadata_checker;
40 	fwu_client *m_fwu_client;
41 };
42 
TEST(FwuUpdateScenarioTests,discover)43 TEST(FwuUpdateScenarioTests, discover)
44 {
45 	int16_t service_status = 0;
46 	uint8_t version_major = 0;
47 	uint8_t version_minor = 0;
48 	uint16_t num_func = 0;
49 	uint64_t max_payload_size = 0;
50 	uint32_t flags = 0;
51 	uint32_t vendor_specific_flags = 0;
52 	uint8_t function_presence[10] = { 0 };
53 	uint8_t expected_functions[10] = { 0, 16, 17, 18, 19, 20, 21, 22, 23, 24 };
54 
55 	m_dut = fwu_dut_factory::create(1, false);
56 	m_fwu_client = m_dut->create_fwu_client();
57 	m_metadata_checker = m_dut->create_metadata_checker();
58 
59 	m_dut->boot();
60 
61 	int res = m_fwu_client->discover(&service_status, &version_major, &version_minor, &num_func,
62 					 &max_payload_size, &flags, &vendor_specific_flags,
63 					 function_presence);
64 	LONGS_EQUAL(0, res);
65 
66 	UNSIGNED_LONGS_EQUAL(0, service_status);
67 	UNSIGNED_LONGS_EQUAL(1, version_major);
68 	UNSIGNED_LONGS_EQUAL(0, version_minor);
69 	UNSIGNED_LONGS_EQUAL(10, sizeof(expected_functions));
70 	UNSIGNED_LONGS_EQUAL(0, max_payload_size);
71 	UNSIGNED_LONGS_EQUAL(0, flags);
72 	UNSIGNED_LONGS_EQUAL(0, vendor_specific_flags);
73 	MEMCMP_EQUAL(expected_functions, function_presence, sizeof(expected_functions));
74 }
75 
TEST(FwuUpdateScenarioTests,wholeFirmwareUpdateFlow)76 TEST(FwuUpdateScenarioTests, wholeFirmwareUpdateFlow)
77 {
78 	int status = 0;
79 	struct uuid_octets uuid;
80 	uint32_t stream_handle = 0;
81 
82 	/* Performs an update flow where firmware for a device with a
83 	 * single location is updated using a whole firmware image.
84 	 */
85 	m_dut = fwu_dut_factory::create(1, false);
86 	m_fwu_client = m_dut->create_fwu_client();
87 	m_metadata_checker = m_dut->create_metadata_checker();
88 
89 	m_dut->boot();
90 
91 	/* Check assumptions about the first boot. This represents the fresh out
92 	 * of the factory state of the device where no updates have ever been
93 	 * performed,
94 	 */
95 	struct boot_info boot_info = m_dut->get_boot_info();
96 	UNSIGNED_LONGS_EQUAL(boot_info.boot_index, boot_info.active_index);
97 	m_metadata_checker->check_regular(boot_info.boot_index);
98 
99 	/* Note the pre-update bank index */
100 	unsigned int pre_update_bank_index = boot_info.boot_index;
101 
102 	/* Perform staging steps where a single image is installed */
103 	status = m_fwu_client->begin_staging(0, 0, NULL);
104 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
105 	m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
106 
107 	m_dut->whole_volume_image_type_uuid(0, &uuid);
108 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
109 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
110 
111 	std::vector<uint8_t> image_data;
112 	m_dut->generate_image_data(&image_data);
113 
114 	status = m_fwu_client->write_stream(stream_handle,
115 					    reinterpret_cast<const uint8_t *>(image_data.data()),
116 					    image_data.size());
117 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
118 
119 	status = m_fwu_client->commit(stream_handle, false);
120 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
121 
122 	status = m_fwu_client->end_staging();
123 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
124 	m_metadata_checker->check_ready_to_activate(boot_info.boot_index);
125 
126 	/* Reboot to activate the update */
127 	m_dut->shutdown();
128 	m_dut->boot();
129 
130 	/* Check the metadata after the reboot to activate the update */
131 	boot_info = m_dut->get_boot_info();
132 	m_metadata_checker->check_trial(boot_info.boot_index);
133 
134 	/* Accept the update. Because the DUT was configured with a single updatable
135 	 * image, only a single image needs to be accepted to complete the update
136 	 * transaction.
137 	 */
138 	m_dut->whole_volume_image_type_uuid(0, &uuid);
139 	status = m_fwu_client->accept(&uuid);
140 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
141 
142 	/* The FWU metadata should now reflect the post update state */
143 	m_metadata_checker->check_regular(boot_info.boot_index);
144 
145 	/* The update agent should also have transitioned from the trial state.
146 	 * Confirm this trying an operation that's only permitted in the trial state.
147 	 */
148 	status = m_fwu_client->accept(&uuid);
149 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
150 
151 	/* Reboot and expect the update to be active */
152 	m_dut->shutdown();
153 	m_dut->boot();
154 
155 	boot_info = m_dut->get_boot_info();
156 	m_metadata_checker->check_regular(boot_info.boot_index);
157 	CHECK_TRUE(boot_info.boot_index != pre_update_bank_index);
158 }
159 
TEST(FwuUpdateScenarioTests,partialFirmwareUpdateFlow)160 TEST(FwuUpdateScenarioTests, partialFirmwareUpdateFlow)
161 {
162 	int status = 0;
163 	struct uuid_octets uuid;
164 	uint32_t stream_handle = 0;
165 
166 	/* Performs an update flow where firmware for a device with
167 	 * multiple firmware locations is updated with a partial update
168 	 * that only updates a subset of locations. The expectation is
169 	 * that locations that have not been updated will use the current
170 	 * version of firmware for the location.
171 	 */
172 	m_dut = fwu_dut_factory::create(3, true);
173 	m_fwu_client = m_dut->create_fwu_client();
174 	m_metadata_checker = m_dut->create_metadata_checker();
175 
176 	m_dut->boot();
177 
178 	/* Check state on first boot */
179 	struct boot_info boot_info = m_dut->get_boot_info();
180 	UNSIGNED_LONGS_EQUAL(boot_info.boot_index, boot_info.active_index);
181 	m_metadata_checker->check_regular(boot_info.boot_index);
182 
183 	/* Note the pre-update bank index */
184 	unsigned int pre_update_bank_index = boot_info.boot_index;
185 
186 	/* Perform staging steps where multiple images are installed */
187 	std::vector<uint8_t> image_data;
188 	status = m_fwu_client->begin_staging(0, 0, NULL);
189 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
190 	m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
191 
192 	/* Install whole image for location 0 */
193 	m_dut->whole_volume_image_type_uuid(0, &uuid);
194 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
195 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
196 
197 	m_dut->generate_image_data(&image_data);
198 
199 	status = m_fwu_client->write_stream(stream_handle,
200 					    reinterpret_cast<const uint8_t *>(image_data.data()),
201 					    image_data.size());
202 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
203 
204 	/* Accept on commit. No need to accept during trial */
205 	status = m_fwu_client->commit(stream_handle, true);
206 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
207 
208 	/* Install whole image for location 1 */
209 	m_dut->whole_volume_image_type_uuid(1, &uuid);
210 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
211 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
212 
213 	m_dut->generate_image_data(&image_data);
214 
215 	status = m_fwu_client->write_stream(stream_handle,
216 					    reinterpret_cast<const uint8_t *>(image_data.data()),
217 					    image_data.size());
218 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
219 
220 	status = m_fwu_client->commit(stream_handle, false);
221 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
222 
223 	/* Don't update location 2 */
224 
225 	/* End staging */
226 	status = m_fwu_client->end_staging();
227 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
228 	m_metadata_checker->check_ready_to_activate(boot_info.boot_index);
229 
230 	/* Reboot to activate the update */
231 	m_dut->shutdown();
232 	m_dut->boot();
233 
234 	/* Check the metadata after the reboot to activate the update */
235 	boot_info = m_dut->get_boot_info();
236 	m_metadata_checker->check_trial(boot_info.boot_index);
237 
238 	/* Accept the update. Image for location 0 was accepted on commit.
239 	 * Image 2 wasn't updated so it should have been copied as accepted
240 	 * so only image 1 needs to be accepted.
241 	 */
242 	m_dut->whole_volume_image_type_uuid(1, &uuid);
243 	status = m_fwu_client->accept(&uuid);
244 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
245 
246 	/* The FWU metadata should now reflect the post update state */
247 	m_metadata_checker->check_regular(boot_info.boot_index);
248 
249 	/* The update agent should also have transitioned from the trial state. */
250 	status = m_fwu_client->accept(&uuid);
251 	LONGS_EQUAL(FWU_STATUS_DENIED, status);
252 
253 	/* Reboot and expect the update to be active */
254 	m_dut->shutdown();
255 	m_dut->boot();
256 
257 	boot_info = m_dut->get_boot_info();
258 	m_metadata_checker->check_regular(boot_info.boot_index);
259 	CHECK_TRUE(boot_info.boot_index != pre_update_bank_index);
260 }
261