mirror of
https://git.proxmox.com/git/fwupd
synced 2025-06-05 10:40:20 +00:00
185 lines
5.3 KiB
C
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;
|
|
}
|