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  * Tests to check that FWU metadata is never left in an invalid state
15  * during the update process when unexpected power failures occur. Power
16  * failures are simulated by shutting down the DUT after each write to
17  * metadata and re-boot to check all conditions for a successful boot
18  * are met.
19  */
TEST_GROUP(FwuPowerFailureTests)20 TEST_GROUP(FwuPowerFailureTests)
21 {
22 	void setup()
23 	{
24 		m_dut = NULL;
25 		m_fwu_client = NULL;
26 		m_metadata_checker = NULL;
27 	}
28 
29 	void teardown()
30 	{
31 		delete m_metadata_checker;
32 		m_metadata_checker = NULL;
33 
34 		delete m_fwu_client;
35 		m_fwu_client = NULL;
36 
37 		delete m_dut;
38 		m_dut = NULL;
39 	}
40 
41 	fwu_dut *m_dut;
42 	metadata_checker *m_metadata_checker;
43 	fwu_client *m_fwu_client;
44 };
45 
TEST(FwuPowerFailureTests,powerFailureDuringStaging)46 TEST(FwuPowerFailureTests, powerFailureDuringStaging)
47 {
48 	int status = 0;
49 	struct uuid_octets uuid;
50 	uint32_t stream_handle = 0;
51 	std::vector<uint8_t> image_data;
52 
53 	m_dut = fwu_dut_factory::create(3, true);
54 	m_fwu_client = m_dut->create_fwu_client();
55 	m_metadata_checker = m_dut->create_metadata_checker();
56 
57 	m_dut->boot();
58 
59 	/* Check assumptions about the first boot. */
60 	struct boot_info boot_info = m_dut->get_boot_info();
61 	UNSIGNED_LONGS_EQUAL(boot_info.boot_index, boot_info.active_index);
62 	m_metadata_checker->check_regular(boot_info.boot_index);
63 
64 	/* Begin staging */
65 	status = m_fwu_client->begin_staging(0, 0, NULL);
66 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
67 	m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
68 
69 	/* Power cycle */
70 	m_dut->shutdown();
71 	m_dut->boot();
72 
73 	/* Expect to reboot into the regular state without errors */
74 	boot_info = m_dut->get_boot_info();
75 	m_metadata_checker->check_regular(boot_info.boot_index);
76 
77 	/* Begin staging again */
78 	status = m_fwu_client->begin_staging(0, 0, NULL);
79 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
80 	m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
81 
82 	/* Start installing an image but don't commit it */
83 	m_dut->whole_volume_image_type_uuid(0, &uuid);
84 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
85 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
86 
87 	m_dut->generate_image_data(&image_data);
88 
89 	status = m_fwu_client->write_stream(stream_handle,
90 					    reinterpret_cast<const uint8_t *>(image_data.data()),
91 					    image_data.size());
92 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
93 
94 	/* Power cycle */
95 	m_dut->shutdown();
96 	m_dut->boot();
97 
98 	/* Expect to reboot into the regular state without errors */
99 	boot_info = m_dut->get_boot_info();
100 	m_metadata_checker->check_regular(boot_info.boot_index);
101 
102 	/* Begin staging again */
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 	/* Start installing an image but this time commit it without ending staging */
108 	m_dut->whole_volume_image_type_uuid(1, &uuid);
109 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
110 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
111 
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 	/* Power cycle */
123 	m_dut->shutdown();
124 	m_dut->boot();
125 
126 	/* Expect to reboot into the regular state without errors */
127 	boot_info = m_dut->get_boot_info();
128 	m_metadata_checker->check_regular(boot_info.boot_index);
129 }
130 
TEST(FwuPowerFailureTests,powerFailureDuringTrial)131 TEST(FwuPowerFailureTests, powerFailureDuringTrial)
132 {
133 	int status = 0;
134 	struct uuid_octets uuid;
135 	uint32_t stream_handle = 0;
136 	std::vector<uint8_t> image_data;
137 
138 	m_dut = fwu_dut_factory::create(3, true);
139 	m_fwu_client = m_dut->create_fwu_client();
140 	m_metadata_checker = m_dut->create_metadata_checker();
141 
142 	m_dut->boot();
143 
144 	/* Check assumptions about the first boot. */
145 	struct boot_info boot_info = m_dut->get_boot_info();
146 	UNSIGNED_LONGS_EQUAL(boot_info.boot_index, boot_info.active_index);
147 	m_metadata_checker->check_regular(boot_info.boot_index);
148 
149 	/* Begin staging */
150 	status = m_fwu_client->begin_staging(0, 0, NULL);
151 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
152 	m_metadata_checker->check_ready_for_staging(boot_info.boot_index);
153 
154 	/* Install a partial update */
155 	m_dut->whole_volume_image_type_uuid(2, &uuid);
156 	status = m_fwu_client->open(&uuid, fwu_client::op_type::WRITE, &stream_handle);
157 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
158 
159 	m_dut->generate_image_data(&image_data);
160 
161 	status = m_fwu_client->write_stream(stream_handle,
162 					    reinterpret_cast<const uint8_t *>(image_data.data()),
163 					    image_data.size());
164 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
165 
166 	status = m_fwu_client->commit(stream_handle, false);
167 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
168 
169 	/* Transition to the trial state */
170 	status = m_fwu_client->end_staging();
171 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
172 	m_metadata_checker->check_ready_to_activate(boot_info.boot_index);
173 
174 	/* Power cycle */
175 	m_dut->shutdown();
176 	m_dut->boot();
177 
178 	/* Check the metadata after the reboot to activate the update */
179 	boot_info = m_dut->get_boot_info();
180 	m_metadata_checker->check_trial(boot_info.boot_index);
181 
182 	/* Power cycle again */
183 	m_dut->shutdown();
184 	m_dut->boot();
185 
186 	/* Check trial is still active */
187 	boot_info = m_dut->get_boot_info();
188 	m_metadata_checker->check_trial(boot_info.boot_index);
189 
190 	/* Power cycle again */
191 	m_dut->shutdown();
192 	m_dut->boot();
193 
194 	/* Check trial is still active */
195 	boot_info = m_dut->get_boot_info();
196 	m_metadata_checker->check_trial(boot_info.boot_index);
197 
198 	/* Only image 2 should need accepting as it was the only image updated */
199 	m_dut->whole_volume_image_type_uuid(2, &uuid);
200 	status = m_fwu_client->accept(&uuid);
201 	LONGS_EQUAL(FWU_STATUS_SUCCESS, status);
202 
203 	/* Power cycle again */
204 	m_dut->shutdown();
205 	m_dut->boot();
206 
207 	/* Check transition to regular */
208 	boot_info = m_dut->get_boot_info();
209 	m_metadata_checker->check_regular(boot_info.boot_index);
210 }
211