Add fwu raw_installer
The raw_installer can be used to install a raw image directly into
a storage volume. The raw_installer can be used for say installing
a whole firmware update contained within a single image into target
storage such as a flash partition.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I78de31ffdd8185adaf91c98865f457776cada2a5
diff --git a/components/service/fwu/installer/raw/raw_installer.c b/components/service/fwu/installer/raw/raw_installer.c
new file mode 100644
index 0000000..55cdcff
--- /dev/null
+++ b/components/service/fwu/installer/raw/raw_installer.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include <media/volume/index/volume_index.h>
+#include <service/fwu/agent/fw_directory.h>
+#include <protocols/service/fwu/packed-c/status.h>
+#include "raw_installer.h"
+
+
+static int raw_installer_begin(void *context,
+ unsigned int current_volume_id,
+ unsigned int update_volume_id)
+{
+ struct raw_installer *subject = (struct raw_installer *)context;
+
+ (void)current_volume_id;
+
+ int status = volume_index_find(
+ update_volume_id,
+ &subject->target_volume);
+
+ if (status == 0) {
+
+ assert(subject->target_volume);
+
+ subject->commit_count = 0;
+ subject->is_open = false;
+ }
+
+ return status;
+}
+
+static int raw_installer_finalize(void *context)
+{
+ struct raw_installer *subject = (struct raw_installer *)context;
+
+ /* Close volume if left open */
+ if (subject->is_open) {
+
+ assert(subject->target_volume);
+
+ volume_close(subject->target_volume);
+ subject->is_open = false;
+ }
+
+ return FWU_STATUS_SUCCESS;
+}
+
+static void raw_installer_abort(void *context)
+{
+ raw_installer_finalize(context);
+}
+
+static int raw_installer_open(void *context,
+ const struct image_info *image_info)
+{
+ struct raw_installer *subject = (struct raw_installer *)context;
+ int status = FWU_STATUS_DENIED;
+
+ /* Because the raw_installer uses a single image to update the
+ * target volume, it only makes sense to commit a single image
+ * during an update transaction. Defend against the case where
+ * an input update package contains more than one raw image to
+ * install into a particular location.
+ */
+ if (!subject->is_open && subject->commit_count < 1) {
+
+ assert(subject->target_volume);
+
+ status = volume_open(subject->target_volume);
+
+ if (!status) {
+
+ /* Prior to writing to the volume to install the image, ensure
+ * that the volume is erased.
+ */
+ status = volume_erase(subject->target_volume);
+
+ if (!status) {
+
+ subject->is_open = true;
+ subject->bytes_written = 0;
+ } else {
+ /* Failed to erase */
+ volume_close(subject->target_volume);
+ }
+ }
+ }
+
+ return status;
+}
+
+static int raw_installer_commit(void *context)
+{
+ struct raw_installer *subject = (struct raw_installer *)context;
+ int status = FWU_STATUS_DENIED;
+
+ if (subject->is_open) {
+
+ assert(subject->target_volume);
+
+ status = volume_close(subject->target_volume);
+
+ ++subject->commit_count;
+ subject->is_open = false;
+
+ if (!status && !subject->bytes_written) {
+
+ /* Installing a zero length image can imply an image delete
+ * operation. For certain types of installer, this is a legitimate
+ * operation. For a raw_installer, there really is no way to
+ * delete an image so return an error if an attempt was made.
+ */
+ status = FWU_STATUS_NOT_AVAILABLE;
+ }
+ }
+
+ return status;
+}
+
+static int raw_installer_write(void *context,
+ const uint8_t *data,
+ size_t data_len)
+{
+ struct raw_installer *subject = (struct raw_installer *)context;
+ int status = FWU_STATUS_DENIED;
+
+ if (subject->is_open) {
+
+ assert(subject->target_volume);
+
+ size_t len_written = 0;
+
+ status = volume_write(subject->target_volume,
+ (const uintptr_t)data, data_len,
+ &len_written);
+
+ subject->bytes_written += len_written;
+
+ /* Check for the volume full condition where not all the requested
+ * data was written.
+ */
+ if (!status && (len_written != data_len))
+ status = FWU_STATUS_OUT_OF_BOUNDS;
+ }
+
+ return status;
+}
+
+static int raw_installer_enumerate(void *context,
+ uint32_t volume_id,
+ struct fw_directory *fw_directory)
+{
+ struct raw_installer *subject = (struct raw_installer *)context;
+ struct volume *volume = NULL;
+
+ int status = volume_index_find(volume_id, &volume);
+
+ if (status != 0)
+ return status;
+
+ assert(volume);
+
+ /* Found the active volume so query it for information in order to
+ * prepare an entry in the fw_directory to represent the whole volume
+ * as an advertised updatable image.
+ */
+ struct image_info image_info = {0};
+
+ /* Limit the advertised max size to the volume size. The volume needs
+ * to be open to query its size.
+ */
+ if (!subject->is_open) {
+ /* Open if necessary */
+ status = volume_open(volume);
+ if (status != 0)
+ return status;
+ }
+
+ status = volume_size(volume, &image_info.max_size);
+ if (status != 0)
+ return status;
+
+ if (!subject->is_open) {
+ /* Leave volume in the same open state */
+ status = volume_close(volume);
+ if (status)
+ return status;
+ }
+
+ /* These attributes will have been assigned during platform configuration */
+ image_info.img_type_uuid = subject->base_installer.location_uuid;
+ image_info.location_id = subject->base_installer.location_id;
+ image_info.install_type = subject->base_installer.install_type;
+
+ status = fw_directory_add_image_info(fw_directory, &image_info);
+
+ return status;
+}
+
+void raw_installer_init(struct raw_installer *subject,
+ const struct uuid_octets *location_uuid,
+ uint32_t location_id)
+{
+ /* Define concrete installer interface */
+ static const struct installer_interface interface = {
+ raw_installer_begin,
+ raw_installer_finalize,
+ raw_installer_abort,
+ raw_installer_open,
+ raw_installer_commit,
+ raw_installer_write,
+ raw_installer_enumerate
+ };
+
+ /* Initialize base installer - a raw_installer is a type of
+ * installer that always updates a whole volume.
+ */
+ installer_init(&subject->base_installer,
+ INSTALL_TYPE_WHOLE_VOLUME,
+ location_id,
+ location_uuid,
+ subject, &interface);
+
+ /* Initialize raw_installer specifics */
+ subject->target_volume = NULL;
+ subject->commit_count = 0;
+ subject->bytes_written = 0;
+ subject->is_open = false;
+}
+
+void raw_installer_deinit(struct raw_installer *subject)
+{
+ (void)subject;
+}