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(&copy_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