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