blob: 55cdcff36e28c35189dcc11a3d7ec81c7f359dbb [file] [log] [blame]
Julian Hall51a3fb32022-11-07 16:25:56 +00001/*
2 * Copyright (c) 2022, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8#include <assert.h>
9#include <stddef.h>
10#include <string.h>
11#include <media/volume/index/volume_index.h>
12#include <service/fwu/agent/fw_directory.h>
13#include <protocols/service/fwu/packed-c/status.h>
14#include "raw_installer.h"
15
16
17static int raw_installer_begin(void *context,
18 unsigned int current_volume_id,
19 unsigned int update_volume_id)
20{
21 struct raw_installer *subject = (struct raw_installer *)context;
22
23 (void)current_volume_id;
24
25 int status = volume_index_find(
26 update_volume_id,
27 &subject->target_volume);
28
29 if (status == 0) {
30
31 assert(subject->target_volume);
32
33 subject->commit_count = 0;
34 subject->is_open = false;
35 }
36
37 return status;
38}
39
40static int raw_installer_finalize(void *context)
41{
42 struct raw_installer *subject = (struct raw_installer *)context;
43
44 /* Close volume if left open */
45 if (subject->is_open) {
46
47 assert(subject->target_volume);
48
49 volume_close(subject->target_volume);
50 subject->is_open = false;
51 }
52
53 return FWU_STATUS_SUCCESS;
54}
55
56static void raw_installer_abort(void *context)
57{
58 raw_installer_finalize(context);
59}
60
61static int raw_installer_open(void *context,
62 const struct image_info *image_info)
63{
64 struct raw_installer *subject = (struct raw_installer *)context;
65 int status = FWU_STATUS_DENIED;
66
67 /* Because the raw_installer uses a single image to update the
68 * target volume, it only makes sense to commit a single image
69 * during an update transaction. Defend against the case where
70 * an input update package contains more than one raw image to
71 * install into a particular location.
72 */
73 if (!subject->is_open && subject->commit_count < 1) {
74
75 assert(subject->target_volume);
76
77 status = volume_open(subject->target_volume);
78
79 if (!status) {
80
81 /* Prior to writing to the volume to install the image, ensure
82 * that the volume is erased.
83 */
84 status = volume_erase(subject->target_volume);
85
86 if (!status) {
87
88 subject->is_open = true;
89 subject->bytes_written = 0;
90 } else {
91 /* Failed to erase */
92 volume_close(subject->target_volume);
93 }
94 }
95 }
96
97 return status;
98}
99
100static int raw_installer_commit(void *context)
101{
102 struct raw_installer *subject = (struct raw_installer *)context;
103 int status = FWU_STATUS_DENIED;
104
105 if (subject->is_open) {
106
107 assert(subject->target_volume);
108
109 status = volume_close(subject->target_volume);
110
111 ++subject->commit_count;
112 subject->is_open = false;
113
114 if (!status && !subject->bytes_written) {
115
116 /* Installing a zero length image can imply an image delete
117 * operation. For certain types of installer, this is a legitimate
118 * operation. For a raw_installer, there really is no way to
119 * delete an image so return an error if an attempt was made.
120 */
121 status = FWU_STATUS_NOT_AVAILABLE;
122 }
123 }
124
125 return status;
126}
127
128static int raw_installer_write(void *context,
129 const uint8_t *data,
130 size_t data_len)
131{
132 struct raw_installer *subject = (struct raw_installer *)context;
133 int status = FWU_STATUS_DENIED;
134
135 if (subject->is_open) {
136
137 assert(subject->target_volume);
138
139 size_t len_written = 0;
140
141 status = volume_write(subject->target_volume,
142 (const uintptr_t)data, data_len,
143 &len_written);
144
145 subject->bytes_written += len_written;
146
147 /* Check for the volume full condition where not all the requested
148 * data was written.
149 */
150 if (!status && (len_written != data_len))
151 status = FWU_STATUS_OUT_OF_BOUNDS;
152 }
153
154 return status;
155}
156
157static int raw_installer_enumerate(void *context,
158 uint32_t volume_id,
159 struct fw_directory *fw_directory)
160{
161 struct raw_installer *subject = (struct raw_installer *)context;
162 struct volume *volume = NULL;
163
164 int status = volume_index_find(volume_id, &volume);
165
166 if (status != 0)
167 return status;
168
169 assert(volume);
170
171 /* Found the active volume so query it for information in order to
172 * prepare an entry in the fw_directory to represent the whole volume
173 * as an advertised updatable image.
174 */
175 struct image_info image_info = {0};
176
177 /* Limit the advertised max size to the volume size. The volume needs
178 * to be open to query its size.
179 */
180 if (!subject->is_open) {
181 /* Open if necessary */
182 status = volume_open(volume);
183 if (status != 0)
184 return status;
185 }
186
187 status = volume_size(volume, &image_info.max_size);
188 if (status != 0)
189 return status;
190
191 if (!subject->is_open) {
192 /* Leave volume in the same open state */
193 status = volume_close(volume);
194 if (status)
195 return status;
196 }
197
198 /* These attributes will have been assigned during platform configuration */
199 image_info.img_type_uuid = subject->base_installer.location_uuid;
200 image_info.location_id = subject->base_installer.location_id;
201 image_info.install_type = subject->base_installer.install_type;
202
203 status = fw_directory_add_image_info(fw_directory, &image_info);
204
205 return status;
206}
207
208void raw_installer_init(struct raw_installer *subject,
209 const struct uuid_octets *location_uuid,
210 uint32_t location_id)
211{
212 /* Define concrete installer interface */
213 static const struct installer_interface interface = {
214 raw_installer_begin,
215 raw_installer_finalize,
216 raw_installer_abort,
217 raw_installer_open,
218 raw_installer_commit,
219 raw_installer_write,
220 raw_installer_enumerate
221 };
222
223 /* Initialize base installer - a raw_installer is a type of
224 * installer that always updates a whole volume.
225 */
226 installer_init(&subject->base_installer,
227 INSTALL_TYPE_WHOLE_VOLUME,
228 location_id,
229 location_uuid,
230 subject, &interface);
231
232 /* Initialize raw_installer specifics */
233 subject->target_volume = NULL;
234 subject->commit_count = 0;
235 subject->bytes_written = 0;
236 subject->is_open = false;
237}
238
239void raw_installer_deinit(struct raw_installer *subject)
240{
241 (void)subject;
242}