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 "sim_fwu_dut.h"
8
9 #include <CppUTest/TestHarness.h>
10 #include <cassert>
11 #include <cstring>
12 #include <sstream>
13
14 #include "common/endian/le.h"
15 #include "media/disk/guid.h"
16 #include "media/volume/index/volume_index.h"
17 #include "service/fwu/agent/update_agent.h"
18 #include "service/fwu/common/update_agent_interface.h"
19 #include "service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.h"
20 #include "service/fwu/fw_store/banked/metadata_serializer/v2/metadata_serializer_v2.h"
21 #include "service/fwu/fw_store/banked/volume_id.h"
22 #include "service/fwu/inspector/direct/direct_fw_inspector.h"
23 #include "service/fwu/installer/installer_index.h"
24 #include "service/fwu/test/fwu_client/direct/direct_fwu_client.h"
25 #include "service/fwu/test/metadata_fetcher/volume/volume_metadata_fetcher.h"
26
sim_fwu_dut(unsigned int num_locations,unsigned int metadata_version,bool allow_partial_updates)27 sim_fwu_dut::sim_fwu_dut(unsigned int num_locations, unsigned int metadata_version,
28 bool allow_partial_updates)
29 : fwu_dut(metadata_version)
30 , m_is_booted(false)
31 , m_is_first_boot(true)
32 , m_boot_info()
33 , m_metadata_checker(NULL)
34 , m_num_locations(num_locations)
35 , m_service_iface(NULL)
36 , m_fw_flash()
37 , m_partitioned_block_store()
38 , m_block_store(NULL)
39 , m_fw_volume_used_count(0)
40 , m_fw_volume_pool()
41 , m_raw_installer_used_count(0)
42 , m_raw_installer_pool()
43 , m_copy_installer_used_count(0)
44 , m_copy_installer_pool()
45 , m_update_agent()
46 , m_fw_store()
47 , m_fwu_provider()
48 {
49 volume_index_init();
50 installer_index_init();
51
52 construct_storage(num_locations);
53 construct_fw_volumes(num_locations);
54 construct_installers(num_locations, allow_partial_updates);
55
56 install_factory_images(num_locations);
57
58 m_service_iface = fwu_provider_init(&m_fwu_provider, NULL);
59
60 m_metadata_checker = create_metadata_checker();
61 }
62
~sim_fwu_dut()63 sim_fwu_dut::~sim_fwu_dut()
64 {
65 shutdown();
66
67 delete m_metadata_checker;
68 m_metadata_checker = NULL;
69
70 fwu_provider_deinit(&m_fwu_provider);
71
72 destroy_installers();
73 destroy_fw_volumes();
74 destroy_storage();
75
76 installer_index_clear();
77 volume_index_clear();
78 }
79
boot(bool from_active_bank)80 void sim_fwu_dut::boot(bool from_active_bank)
81 {
82 if (m_is_booted)
83 return;
84
85 if (m_is_first_boot) {
86 /* First boot where valid FWU metadata does not yet exist. */
87 m_boot_info.boot_index = m_boot_info.active_index =
88 m_boot_info.previous_active_index = FIRST_BOOT_BANK_INDEX;
89 m_is_first_boot = false;
90
91 } else {
92 /* On subsequent boots, mimic the boot loader and derive boot
93 * info from the FWU metadata.
94 */
95 m_metadata_checker->get_active_indices(&m_boot_info.active_index,
96 &m_boot_info.previous_active_index);
97
98 m_boot_info.boot_index = (from_active_bank) ? m_boot_info.active_index :
99 m_boot_info.previous_active_index;
100 }
101
102 /* Now mimic boot loader image verification */
103 verify_boot_images(m_boot_info.boot_index);
104
105 /* Performs the generic update agent initialization that occurs on
106 * each system boot.
107 */
108 int status = banked_fw_store_init(&m_fw_store, select_metadata_serializer());
109 LONGS_EQUAL(0, status);
110
111 m_update_agent = update_agent_init(m_boot_info.boot_index, direct_fw_inspector_inspect,
112 &m_fw_store);
113 CHECK(m_update_agent != NULL);
114
115 m_fwu_provider.update_agent = m_update_agent;
116
117 m_is_booted = true;
118 }
119
shutdown(void)120 void sim_fwu_dut::shutdown(void)
121 {
122 if (!m_is_booted)
123 return;
124
125 /* Ensure all install streams are closed */
126 update_agent_cancel_staging(m_update_agent);
127
128 m_fwu_provider.update_agent = NULL;
129 update_agent_deinit(m_update_agent);
130 banked_fw_store_deinit(&m_fw_store);
131
132 m_is_booted = false;
133 }
134
get_service_interface(void)135 struct rpc_service_interface *sim_fwu_dut::get_service_interface(void)
136 {
137 return m_service_iface;
138 }
139
get_boot_info(void) const140 struct boot_info sim_fwu_dut::get_boot_info(void) const
141 {
142 return m_boot_info;
143 }
144
create_metadata_checker(bool is_primary) const145 metadata_checker *sim_fwu_dut::create_metadata_checker(bool is_primary) const
146 {
147 struct uuid_octets partition_guid;
148
149 fwu_metadata_partition_guid(is_primary, &partition_guid);
150
151 metadata_fetcher *metadata_fetcher =
152 new volume_metadata_fetcher(&partition_guid, m_block_store);
153
154 return fwu_dut::create_metadata_checker(metadata_fetcher, m_num_locations);
155 }
156
create_fwu_client(void)157 fwu_client *sim_fwu_dut::create_fwu_client(void)
158 {
159 return new direct_fwu_client(&m_update_agent);
160 }
161
fw_partition_guid(unsigned int location_index,unsigned int bank_index,struct uuid_octets * uuid) const162 void sim_fwu_dut::fw_partition_guid(unsigned int location_index, unsigned int bank_index,
163 struct uuid_octets *uuid) const
164 {
165 static const char *partition_guid[MAX_LOCATIONS][BANK_SCHEME_NUM_BANKS] = {
166 { "318757ec-82a0-48ce-a0d9-dfdeee6847a9", "3455a361-4074-42a3-ac0f-0e217c58494a" },
167 { "5feb0dff-3d4b-42ab-9635-c112cf641f2b", "d75c1efc-6c8c-458a-aa72-41810d1d8a99" },
168 { "0558ce63-db89-40ad-8039-1fafeb057fc8", "f07f1be5-077d-487f-9bd9-1ae33ed580e9" },
169 { "3b00979c-e776-4e79-b675-f78681b4cce3", "3de167ca-5e8c-4f05-9f87-e0c6a57538a5" }
170 };
171
172 CHECK_TRUE(location_index < MAX_LOCATIONS);
173 CHECK_TRUE(bank_index < BANK_SCHEME_NUM_BANKS);
174
175 uuid_guid_octets_from_canonical(uuid, partition_guid[location_index][bank_index]);
176 }
177
fwu_metadata_partition_guid(bool is_primary,struct uuid_octets * uuid) const178 void sim_fwu_dut::fwu_metadata_partition_guid(bool is_primary, struct uuid_octets *uuid) const
179 {
180 if (is_primary)
181 uuid_guid_octets_from_canonical(uuid,
182 DISK_GUID_UNIQUE_PARTITION_PRIMARY_FWU_METADATA);
183 else
184 uuid_guid_octets_from_canonical(uuid,
185 DISK_GUID_UNIQUE_PARTITION_BACKUP_FWU_METADATA);
186 }
187
disk_guid(struct uuid_octets * uuid) const188 void sim_fwu_dut::disk_guid(struct uuid_octets *uuid) const
189 {
190 uuid_guid_octets_from_canonical(uuid, "da92a93d-91d3-4b74-9102-7b45c21fe7db");
191 }
192
construct_storage(unsigned int num_locations)193 void sim_fwu_dut::construct_storage(unsigned int num_locations)
194 {
195 size_t required_storage_blocks =
196 METADATA_VOLUME_NUM_BLOCKS * 2 +
197 FW_VOLUME_NUM_BLOCKS * BANK_SCHEME_NUM_BANKS * num_locations;
198
199 struct uuid_octets flash_store_guid;
200
201 disk_guid(&flash_store_guid);
202
203 /* Construct the 'flash' */
204 struct block_store *flash_store = ram_block_store_init(
205 &m_fw_flash, &flash_store_guid, required_storage_blocks, FLASH_BLOCK_SIZE);
206
207 /* Stack a partitioned_block_store over the flash */
208 m_block_store = partitioned_block_store_init(&m_partitioned_block_store, 0,
209 &flash_store_guid, flash_store, NULL);
210
211 /* Add all disk partitions */
212 unsigned int lba = 0;
213 struct uuid_octets partition_guid;
214
215 /* First the primary fwu metadata partition */
216 fwu_metadata_partition_guid(true, &partition_guid);
217 bool is_added = partitioned_block_store_add_partition(&m_partitioned_block_store,
218 &partition_guid, lba,
219 lba + METADATA_VOLUME_NUM_BLOCKS - 1,
220 0, NULL);
221
222 CHECK_TRUE(is_added);
223 lba += METADATA_VOLUME_NUM_BLOCKS;
224
225 /* Add partitions for each fw location */
226 for (unsigned int location = 0; location < num_locations; location++) {
227 for (unsigned int bank = 0; bank < BANK_SCHEME_NUM_BANKS; bank++) {
228 fw_partition_guid(location, bank, &partition_guid);
229 is_added = partitioned_block_store_add_partition(
230 &m_partitioned_block_store, &partition_guid, lba,
231 lba + FW_VOLUME_NUM_BLOCKS - 1, 0, NULL);
232
233 CHECK_TRUE(is_added);
234 lba += FW_VOLUME_NUM_BLOCKS;
235 }
236 }
237
238 /* Finally, add the backup fwu metadata partition */
239 fwu_metadata_partition_guid(false, &partition_guid);
240 is_added = partitioned_block_store_add_partition(&m_partitioned_block_store,
241 &partition_guid, lba,
242 lba + METADATA_VOLUME_NUM_BLOCKS - 1, 0,
243 NULL);
244
245 CHECK_TRUE(is_added);
246 }
247
destroy_storage(void)248 void sim_fwu_dut::destroy_storage(void)
249 {
250 partitioned_block_store_deinit(&m_partitioned_block_store);
251 ram_block_store_deinit(&m_fw_flash);
252 }
253
construct_fw_volumes(unsigned int num_locations)254 void sim_fwu_dut::construct_fw_volumes(unsigned int num_locations)
255 {
256 int status = 0;
257 struct volume *volume = NULL;
258 struct uuid_octets partition_guid;
259
260 /* Construct volume for primary fwu metadata access */
261 fwu_metadata_partition_guid(true, &partition_guid);
262
263 status = block_volume_init(&m_fw_volume_pool[m_fw_volume_used_count], m_block_store,
264 &partition_guid, &volume);
265 LONGS_EQUAL(0, status);
266 CHECK_TRUE(volume);
267
268 status = volume_index_add(BANKED_VOLUME_ID_PRIMARY_METADATA, volume);
269 LONGS_EQUAL(0, status);
270 ++m_fw_volume_used_count;
271
272 /* Construct volume for backup fwu metadata access */
273 fwu_metadata_partition_guid(false, &partition_guid);
274
275 status = block_volume_init(&m_fw_volume_pool[m_fw_volume_used_count], m_block_store,
276 &partition_guid, &volume);
277 LONGS_EQUAL(0, status);
278 CHECK_TRUE(volume);
279
280 status = volume_index_add(BANKED_VOLUME_ID_BACKUP_METADATA, volume);
281 LONGS_EQUAL(0, status);
282 ++m_fw_volume_used_count;
283
284 /* Construct volumes for each fw storage partition */
285 for (unsigned int location = 0; location < num_locations; location++) {
286 for (unsigned int bank = 0; bank < BANK_SCHEME_NUM_BANKS; bank++) {
287 fw_partition_guid(location, bank, &partition_guid);
288
289 status = block_volume_init(&m_fw_volume_pool[m_fw_volume_used_count],
290 m_block_store, &partition_guid, &volume);
291 LONGS_EQUAL(0, status);
292 CHECK_TRUE(volume);
293
294 status = volume_index_add(banked_volume_id(location, banked_usage_id(bank)),
295 volume);
296 LONGS_EQUAL(0, status);
297 ++m_fw_volume_used_count;
298 }
299 }
300 }
301
destroy_fw_volumes(void)302 void sim_fwu_dut::destroy_fw_volumes(void)
303 {
304 for (unsigned int i = 0; i < m_fw_volume_used_count; i++)
305 block_volume_deinit(&m_fw_volume_pool[i]);
306
307 m_fw_volume_used_count = 0;
308 }
309
construct_installers(unsigned int num_locations,bool allow_partial_updates)310 void sim_fwu_dut::construct_installers(unsigned int num_locations, bool allow_partial_updates)
311 {
312 for (unsigned int location = 0; location < num_locations; location++) {
313 /* Provides a raw and optional copy installer per location. The raw_installer
314 * is used for installing whole volume images using an externally streamed
315 * image while the copy installer is used to copy the previously good whole
316 * volume image to the update bank for cases where an incoming update
317 * did not include images for all locations. Use of a copy_installer to
318 * support this case is optional. By not registering a copy_installer for
319 * a location, an update attempt will fail if and image for the location
320 * was not included in an incoming update package.
321 */
322 struct uuid_octets img_type_uuid;
323
324 whole_volume_image_type_uuid(location, &img_type_uuid);
325
326 struct raw_installer *raw_installer =
327 &m_raw_installer_pool[m_raw_installer_used_count];
328
329 raw_installer_init(raw_installer, &img_type_uuid, location);
330 installer_index_register(&raw_installer->base_installer);
331 ++m_raw_installer_used_count;
332
333 if (allow_partial_updates) {
334 struct copy_installer *copy_installer =
335 &m_copy_installer_pool[m_copy_installer_used_count];
336
337 copy_installer_init(copy_installer, &img_type_uuid, location);
338 installer_index_register(©_installer->base_installer);
339 ++m_copy_installer_used_count;
340 }
341 }
342 }
343
destroy_installers(void)344 void sim_fwu_dut::destroy_installers(void)
345 {
346 for (unsigned int i = 0; i < m_raw_installer_used_count; i++)
347 raw_installer_deinit(&m_raw_installer_pool[i]);
348
349 m_raw_installer_used_count = 0;
350
351 for (unsigned int i = 0; i < m_copy_installer_used_count; i++)
352 copy_installer_deinit(&m_copy_installer_pool[i]);
353
354 m_copy_installer_used_count = 0;
355 }
356
install_factory_images(unsigned int num_locations)357 void sim_fwu_dut::install_factory_images(unsigned int num_locations)
358 {
359 /* Install valid images into bank 0 to mimic the state of
360 * a device with factory programmed flash.
361 */
362 for (unsigned int location = 0; location < num_locations; location++) {
363 struct volume *volume = NULL;
364 size_t len_written = 0;
365
366 int status = volume_index_find(
367 banked_volume_id(location, banked_usage_id(FIRST_BOOT_BANK_INDEX)),
368 &volume);
369 LONGS_EQUAL(0, status);
370 CHECK_TRUE(volume);
371
372 std::vector<uint8_t> image_data;
373
374 generate_image_data(&image_data);
375
376 status = volume_open(volume);
377 LONGS_EQUAL(0, status);
378
379 status = volume_write(volume, (uintptr_t)image_data.data(), image_data.size(),
380 &len_written);
381 LONGS_EQUAL(0, status);
382 UNSIGNED_LONGS_EQUAL(image_data.size(), len_written);
383
384 status = volume_close(volume);
385 LONGS_EQUAL(0, status);
386 }
387 }
388
verify_boot_images(unsigned int boot_index)389 void sim_fwu_dut::verify_boot_images(unsigned int boot_index)
390 {
391 for (unsigned int location = 0; location < m_num_locations; location++) {
392 struct volume *volume = NULL;
393
394 int status = volume_index_find(
395 banked_volume_id(location, banked_usage_id(boot_index)), &volume);
396 LONGS_EQUAL(0, status);
397 CHECK_TRUE(volume);
398
399 status = volume_open(volume);
400 LONGS_EQUAL(0, status);
401
402 sim_fwu_dut::verify_image(volume);
403
404 status = volume_close(volume);
405 LONGS_EQUAL(0, status);
406 }
407 }
408
select_metadata_serializer(void) const409 const struct metadata_serializer *sim_fwu_dut::select_metadata_serializer(void) const
410 {
411 unsigned int version = metadata_version();
412
413 if (version == 1)
414 return metadata_serializer_v1();
415
416 if (version == 2)
417 return metadata_serializer_v2();
418
419 /* Metadata version not supported */
420 assert(false);
421
422 return NULL;
423 }
424
verify_image(struct volume * volume)425 void sim_fwu_dut::verify_image(struct volume *volume)
426 {
427 std::string fixed_header(VALID_IMAGE_HEADER);
428 size_t header_len = fixed_header.size() + sizeof(uint32_t) + sizeof(uint32_t);
429
430 /* Read image header */
431 uint8_t header_buf[header_len];
432 size_t total_bytes_read = 0;
433
434 int status = volume_read(volume, (uintptr_t)header_buf, header_len, &total_bytes_read);
435 LONGS_EQUAL(0, status);
436 CHECK_TRUE(total_bytes_read == header_len);
437
438 /* Verify header and extract values */
439 MEMCMP_EQUAL(fixed_header.data(), header_buf, fixed_header.size());
440
441 size_t image_size = load_u32_le(header_buf, fixed_header.size());
442 uint32_t seq_num = load_u32_le(header_buf, fixed_header.size() + sizeof(uint32_t));
443
444 CHECK_TRUE(image_size >= header_len);
445
446 /* Read the remainder of the image and check data is as expected */
447 uint8_t expected_fill_val = static_cast<uint8_t>(seq_num);
448
449 while (total_bytes_read < image_size) {
450 uint8_t read_buf[1024];
451 size_t bytes_read = 0;
452 size_t bytes_remaining = image_size - total_bytes_read;
453 size_t bytes_to_read = (bytes_remaining > sizeof(read_buf)) ? sizeof(read_buf) :
454 bytes_remaining;
455
456 status = volume_read(volume, (uintptr_t)read_buf, bytes_to_read, &bytes_read);
457 LONGS_EQUAL(0, status);
458 UNSIGNED_LONGS_EQUAL(bytes_to_read, bytes_read);
459
460 for (size_t i = 0; i < bytes_read; i++)
461 BYTES_EQUAL(expected_fill_val, read_buf[i]);
462
463 total_bytes_read += bytes_read;
464 }
465
466 UNSIGNED_LONGS_EQUAL(image_size, total_bytes_read);
467 }
468