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