fwupd/plugins/nordic-hid/fu-nordic-hid-archive.c
Denis Pynkin dabc9657bc plugins/nordic-hid: support images for MCUBoot
- added images for MCUBoot bootloader
2021-12-30 10:24:22 +00:00

185 lines
5.3 KiB
C

/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2021 Denis Pynkin <denis.pynkin@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fu-nordic-hid-archive.h"
#include "fu-nordic-hid-firmware-b0.h"
#include "fu-nordic-hid-firmware-mcuboot.h"
/* current version format is 0 */
#define MAX_VERSION_FORMAT 0
struct _FuNordicHidArchive {
FuFirmwareClass parent_instance;
};
G_DEFINE_TYPE(FuNordicHidArchive, fu_nordic_hid_archive, FU_TYPE_FIRMWARE)
static gboolean
fu_nordic_hid_archive_parse(FuFirmware *firmware,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{
JsonNode *json_root_node;
JsonObject *json_obj;
JsonArray *json_files;
guint manifest_ver;
guint files_cnt = 0;
GBytes *manifest = NULL;
g_autoptr(FuArchive) archive = NULL;
g_autoptr(JsonParser) parser = json_parser_new();
/* load archive */
archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error);
if (archive == NULL)
return FALSE;
manifest = fu_archive_lookup_by_fn(archive, "manifest.json", error);
if (manifest == NULL)
return FALSE;
/* parse JSON */
if (!json_parser_load_from_data(parser,
(const gchar *)g_bytes_get_data(manifest, NULL),
(gssize)g_bytes_get_size(manifest),
error)) {
g_prefix_error(error, "manifest not in JSON format: ");
return FALSE;
}
json_root_node = json_parser_get_root(parser);
if (json_root_node == NULL || !JSON_NODE_HOLDS_OBJECT(json_root_node)) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"manifest invalid as has no root");
return FALSE;
}
json_obj = json_node_get_object(json_root_node);
if (!json_object_has_member(json_obj, "format-version")) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"manifest has invalid format");
return FALSE;
}
manifest_ver = json_object_get_int_member(json_obj, "format-version");
if (manifest_ver > MAX_VERSION_FORMAT) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"unsupported manifest version");
return FALSE;
}
json_files = json_object_get_array_member(json_obj, "files");
if (json_files == NULL) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"manifest invalid as has no 'files' array");
return FALSE;
}
files_cnt = json_array_get_length(json_files);
if (files_cnt == 0) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"manifest invalid as contains no update images");
return FALSE;
}
for (guint i = 0; i < files_cnt; i++) {
const gchar *filename = NULL;
const gchar *bootloader_name = NULL;
guint image_addr = 0;
JsonObject *obj = json_array_get_object_element(json_files, i);
GBytes *blob = NULL;
g_autoptr(FuFirmware) image = NULL;
g_autofree gchar *image_id = NULL;
g_auto(GStrv) board_split = NULL;
if (!json_object_has_member(obj, "file")) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"manifest invalid as has no file name for the image");
return FALSE;
}
filename = json_object_get_string_member(obj, "file");
blob = fu_archive_lookup_by_fn(archive, filename, error);
if (blob == NULL)
return FALSE;
if (json_object_has_member(obj, "version_B0")) {
bootloader_name = "B0";
image = g_object_new(FU_TYPE_NORDIC_HID_FIRMWARE_B0, NULL);
} else if (json_object_has_member(obj, "version_MCUBOOT")) {
bootloader_name = "MCUBOOT";
image = g_object_new(FU_TYPE_NORDIC_HID_FIRMWARE_MCUBOOT, NULL);
} else {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"only B0 and MCUboot bootloaders are supported");
return FALSE;
}
/* the "board" field contains board name before "_" symbol */
if (!json_object_has_member(obj, "board")) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"manifest invalid as has no target board information");
return FALSE;
}
board_split = g_strsplit(json_object_get_string_member(obj, "board"), "_", -1);
if (board_split[0] == NULL) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"manifest invalid as has no target board information");
return FALSE;
}
/* images for B0 bootloader are listed in strict order:
* this is guaranteed by producer set the id format as
* <board>_<bl>_<bank>N, i.e "nrf52840dk_B0_bank0".
* For MCUBoot bootloader only the single image is available */
image_id = g_strdup_printf("%s_%s_bank%01u", board_split[0], bootloader_name, i);
if (!fu_firmware_parse(image, blob, flags, error))
return FALSE;
fu_firmware_set_id(image, image_id);
fu_firmware_set_idx(image, i);
if (json_object_has_member(obj, "load_address")) {
image_addr = json_object_get_int_member(obj, "load_address");
fu_firmware_set_addr(image, image_addr);
}
fu_firmware_add_image(firmware, image);
}
/* success */
return TRUE;
}
static void
fu_nordic_hid_archive_init(FuNordicHidArchive *self)
{
}
static void
fu_nordic_hid_archive_class_init(FuNordicHidArchiveClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
klass_firmware->parse = fu_nordic_hid_archive_parse;
}