1 /*
2  * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <CppUTest/TestHarness.h>
8 #include <cstdlib>
9 #include <cstring>
10 
11 #include "common/uuid/uuid.h"
12 #include "media/disk/guid.h"
13 #include "media/volume/block_volume/block_volume.h"
14 #include "media/volume/index/volume_index.h"
15 #include "media/volume/volume.h"
16 #include "service/block_storage/config/ref/ref_partition_configurator.h"
17 #include "service/block_storage/factory/ref_ram_gpt/block_store_factory.h"
18 #include "service/fwu/agent/fw_directory.h"
19 #include "service/fwu/fw_store/banked/volume_id.h"
20 #include "service/fwu/installer/installer_index.h"
21 #include "service/fwu/installer/raw/raw_installer.h"
22 
TEST_GROUP(FwuRawInstallerTests)23 TEST_GROUP(FwuRawInstallerTests)
24 {
25 	void setup()
26 	{
27 		int result;
28 		struct uuid_octets partition_guid;
29 
30 		m_image = NULL;
31 
32 		installer_index_init();
33 		volume_index_init();
34 
35 		/* Use the reference disk configuration and use partition 1 & 2 as
36 		 * storage for A and B firmware banks.
37 		 */
38 		m_block_store = ref_ram_gpt_block_store_factory_create();
39 
40 		/* Construct fw volume A */
41 		uuid_guid_octets_from_canonical(&partition_guid, REF_PARTITION_1_GUID);
42 
43 		result = block_volume_init(&m_block_volume_a, m_block_store, &partition_guid,
44 					   &m_fw_volume_a);
45 
46 		LONGS_EQUAL(0, result);
47 		CHECK_TRUE(m_fw_volume_a);
48 
49 		/* Construct fw volume B */
50 		uuid_guid_octets_from_canonical(&partition_guid, REF_PARTITION_2_GUID);
51 
52 		result = block_volume_init(&m_block_volume_b, m_block_store, &partition_guid,
53 					   &m_fw_volume_b);
54 
55 		LONGS_EQUAL(0, result);
56 		CHECK_TRUE(m_fw_volume_b);
57 
58 		/* Prepare an image_info structure to describe the image to
59 		 * install. In a complete integration, this will come from the
60 		 * fw_directory.
61 		 */
62 		uuid_guid_octets_from_canonical(&m_image_info.img_type_uuid,
63 						"1c22ca2c-9732-49e6-ba3b-eed40e27fda3");
64 
65 		m_image_info.max_size =
66 			(REF_PARTITION_1_ENDING_LBA - REF_PARTITION_1_STARTING_LBA + 1) *
67 			REF_PARTITION_BLOCK_SIZE;
68 		m_image_info.lowest_accepted_version = 1;
69 		m_image_info.active_version = 1;
70 		m_image_info.permissions = 0;
71 		m_image_info.image_index = 0;
72 		m_image_info.location_id = FW_STORE_LOCATION_ID;
73 		m_image_info.install_type = INSTALL_TYPE_WHOLE_VOLUME;
74 
75 		/* Mimic a platform configuration where storage volumes are assigned
76 		 * location and usage IDs. These usage IDs correspond to an A/B banked
77 		 * firmware store. Multiple locations could be configured here
78 		 * but for these tests, there is only one. This enables the generic
79 		 * banked_fw_store to be completely decoupled from the details
80 		 * of which fw volumes need updating on a platform.
81 		 */
82 		volume_index_add(banked_volume_id(FW_STORE_LOCATION_ID, BANKED_USAGE_ID_FW_BANK_A),
83 				 m_fw_volume_a);
84 		volume_index_add(banked_volume_id(FW_STORE_LOCATION_ID, BANKED_USAGE_ID_FW_BANK_B),
85 				 m_fw_volume_b);
86 
87 		/* A platform configuration will also determine which installers are
88 		 * assigned to which locations. This provides flexibility to configure
89 		 * appropriate installers to handle alternative fw packages and installation
90 		 * strategies.
91 		 */
92 		raw_installer_init(&m_installer, &m_image_info.img_type_uuid, FW_STORE_LOCATION_ID);
93 
94 		installer_index_register(&m_installer.base_installer);
95 	}
96 
97 	void teardown()
98 	{
99 		delete[] m_image;
100 
101 		raw_installer_deinit(&m_installer);
102 
103 		installer_index_clear();
104 		volume_index_clear();
105 
106 		block_volume_deinit(&m_block_volume_a);
107 		block_volume_deinit(&m_block_volume_b);
108 		ref_ram_gpt_block_store_factory_destroy(m_block_store);
109 	}
110 
111 	void create_image(size_t len)
112 	{
113 		m_image = new uint8_t[len];
114 		m_image_len = len;
115 
116 		for (size_t i = 0; i < len; i++)
117 			m_image[i] = (uint8_t)rand();
118 	}
119 
120 	int write_image_in_random_len_chunks(struct installer * installer)
121 	{
122 		int status = 0;
123 		size_t bytes_written = 0;
124 
125 		while ((bytes_written < m_image_len) && !status) {
126 			size_t write_len = rand() % 100 + 1;
127 
128 			if ((bytes_written + write_len) > m_image_len)
129 				write_len = m_image_len - bytes_written;
130 
131 			status = installer_write(installer, &m_image[bytes_written], write_len);
132 			bytes_written += write_len;
133 		}
134 
135 		return status;
136 	}
137 
138 	void check_update_installed(struct volume * volume)
139 	{
140 		int status = 0;
141 		size_t total_read = 0;
142 		uintptr_t file_handle = 0;
143 
144 		status = io_open(volume->dev_handle, volume->io_spec, &file_handle);
145 		LONGS_EQUAL(0, status);
146 
147 		while (total_read < m_image_len) {
148 			uint8_t read_buf[1000];
149 			size_t len_read = 0;
150 			size_t bytes_remaining = m_image_len - total_read;
151 			size_t req_len = (bytes_remaining > sizeof(read_buf)) ? sizeof(read_buf) :
152 										bytes_remaining;
153 
154 			memset(read_buf, 0, sizeof(read_buf));
155 
156 			status = io_read(file_handle, (uintptr_t)read_buf, req_len, &len_read);
157 			LONGS_EQUAL(0, status);
158 			UNSIGNED_LONGS_EQUAL(req_len, len_read);
159 
160 			MEMCMP_EQUAL(&m_image[total_read], read_buf, len_read);
161 
162 			total_read += len_read;
163 		}
164 
165 		io_close(file_handle);
166 	}
167 
168 	static const unsigned int FW_STORE_LOCATION_ID = 0x100;
169 
170 	struct block_store *m_block_store;
171 	struct block_volume m_block_volume_a;
172 	struct block_volume m_block_volume_b;
173 	struct volume *m_fw_volume_a;
174 	struct volume *m_fw_volume_b;
175 	struct raw_installer m_installer;
176 	struct image_info m_image_info;
177 	uint8_t *m_image;
178 	size_t m_image_len;
179 };
180 
TEST(FwuRawInstallerTests,normalInstallFlow)181 TEST(FwuRawInstallerTests, normalInstallFlow)
182 {
183 	int status = 0;
184 
185 	/* Create arbitrary length image that should easily fit into the destination partition */
186 	create_image(REF_PARTITION_BLOCK_SIZE * 2 + 111);
187 
188 	/* Expect to find a suitable installer for the given image_info */
189 	struct installer *installer =
190 		installer_index_find(m_image_info.install_type, m_image_info.location_id);
191 	CHECK_TRUE(installer);
192 	UNSIGNED_LONGS_EQUAL(FW_STORE_LOCATION_ID, installer->location_id);
193 
194 	/* Begin installation transaction - installing into volume A */
195 	status = installer_begin(installer,
196 				 banked_volume_id(FW_STORE_LOCATION_ID,
197 						  BANKED_USAGE_ID_FW_BANK_B), /* Current volume */
198 				 banked_volume_id(FW_STORE_LOCATION_ID,
199 						  BANKED_USAGE_ID_FW_BANK_A)); /* Update volume */
200 	LONGS_EQUAL(0, status);
201 
202 	/* Open install stream */
203 	status = installer_open(installer, &m_image_info);
204 	LONGS_EQUAL(0, status);
205 
206 	/* Stream the update image into the installer */
207 	status = write_image_in_random_len_chunks(installer);
208 	LONGS_EQUAL(0, status);
209 
210 	/* Commit after writing all image data */
211 	status = installer_commit(installer);
212 	LONGS_EQUAL(0, status);
213 
214 	/* Finalize installation transaction */
215 	status = installer_finalize(installer);
216 	LONGS_EQUAL(0, status);
217 
218 	/* Expect the update volume to contain the update */
219 	check_update_installed(m_fw_volume_a);
220 }
221