diff --git a/examples/BUILD b/examples/BUILD index c176fac..e39cdcf 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -235,6 +235,7 @@ cc_binary( "//protocol:chipinfo", "//protocol:controlled_storage", "//protocol:dfu_hostcmd", + "//protocol:dfu_check", "//protocol:hello", "//protocol:host_cmd", "//protocol:i2c", diff --git a/examples/htool.c b/examples/htool.c index 213a81a..6d2ba45 100644 --- a/examples/htool.c +++ b/examples/htool.c @@ -1005,6 +1005,16 @@ static const struct htool_cmd CMDS[] = { {}}, .func = htool_dfu_update, }, + { + .verbs = (const char*[]){"dfu", "check", NULL}, + .desc = "Check that the device is running firmware matching a fwupdate bundle.", + .params = + (const struct htool_param[]){ + {HTOOL_POSITIONAL, .name = "fwupdate-file", + .desc = "A .fwupdate file compatible with this device."}, + {}}, + .func = htool_dfu_check, + }, {.verbs = (const char*[]){"flash_spi_info", NULL}, .desc = "Get SPI NOR flash info.", .params = (const struct htool_param[]){{}}, diff --git a/examples/htool_dfu.c b/examples/htool_dfu.c index caf0fd2..262d472 100644 --- a/examples/htool_dfu.c +++ b/examples/htool_dfu.c @@ -15,6 +15,7 @@ #include "htool_cmd.h" #include "protocol/dfu_hostcmd.h" #include "protocol/opentitan_version.h" +#include "protocol/dfu_check.h" int htool_dfu_update(const struct htool_invocation* inv) { struct libhoth_device* dev = htool_libhoth_device(); @@ -149,4 +150,69 @@ int htool_dfu_update(const struct htool_invocation* inv) { } return retval; +} + +int htool_dfu_check(const struct htool_invocation* inv) { + struct libhoth_device* dev = htool_libhoth_device(); + if (!dev) { + return -1; + } + + struct opentitan_get_version_resp resp = {0}; + + const char* fwupdate_file; + if (htool_get_param_string(inv, "fwupdate-file", &fwupdate_file)) { + return -1; + } + + int fd = open(fwupdate_file, O_RDONLY, 0); + if (fd == -1) { + fprintf(stderr, "Error opening file %s: %s\n", fwupdate_file, + strerror(errno)); + return -1; + } + + int retval = -1; + + struct stat statbuf; + if (fstat(fd, &statbuf)) { + fprintf(stderr, "fstat error: %s\n", strerror(errno)); + goto cleanup; + } + if (statbuf.st_size > SIZE_MAX) { + fprintf(stderr, "file too large\n"); + goto cleanup; + } + + uint8_t* image = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (image == MAP_FAILED) { + fprintf(stderr, "mmap error: %s\n", strerror(errno)); + goto cleanup; + } + + if (libhoth_opentitan_version(dev, &resp) != 0) { + fprintf(stderr, "Failed to get current version\n"); + goto cleanup2; + } + + if (libhoth_dfu_check(image, statbuf.st_size, &resp) != 0) { + fprintf(stderr, "DFU check failed.\n"); + goto cleanup2; + } + + retval = 0; + + int ret; +cleanup2: + ret = munmap(image, statbuf.st_size); + if (ret != 0) { + fprintf(stderr, "munmap error: %d\n", ret); + } + +cleanup: + ret = close(fd); + if (ret != 0) { + fprintf(stderr, "close error: %d\n", ret); + } + return retval; } \ No newline at end of file diff --git a/examples/htool_dfu.h b/examples/htool_dfu.h index 39cc0d0..940d169 100644 --- a/examples/htool_dfu.h +++ b/examples/htool_dfu.h @@ -24,6 +24,7 @@ extern "C" { struct htool_invocation; int htool_dfu_update(const struct htool_invocation* inv); +int htool_dfu_check(const struct htool_invocation* inv); #ifdef __cplusplus } diff --git a/protocol/BUILD b/protocol/BUILD index 14a2b38..d54bf21 100644 --- a/protocol/BUILD +++ b/protocol/BUILD @@ -433,4 +433,15 @@ cc_library( ":host_cmd", "//transports:libhoth_device", ], +) + +cc_library( + name = "dfu_check", + srcs = ["dfu_check.c"], + hdrs = ["dfu_check.h", + "opentitan_version.h"], + deps = [ + ":host_cmd", + "//transports:libhoth_device", + ], ) \ No newline at end of file diff --git a/protocol/dfu_check.c b/protocol/dfu_check.c new file mode 100644 index 0000000..7870070 --- /dev/null +++ b/protocol/dfu_check.c @@ -0,0 +1,55 @@ + +#include "dfu_check.h" + +#include +#include +#include +#include +#include +#include +#include +// for MIN() +#include +#include +#include +#include + +#include "protocol/host_cmd.h" +#include "protocol/opentitan_version.h" + +int libhoth_dfu_check(const uint8_t* image, size_t image_size, struct opentitan_get_version_resp * resp) { + + int retval = 0; + struct opentitan_image_version desired_rom_ext = {0}; + struct opentitan_image_version desired_app = {0}; + + // Populate rom_ext and app with the desired extracted versions from the image + retval = libhoth_extract_ot_bundle(image, &desired_rom_ext, &desired_app); + + if(retval != 0) { + fprintf(stderr, "Failed to extract bundle\n"); + } + + // Determine the stage slot for each ot get version to compare + uint32_t rom_ext_boot_slot = bootslot_int(resp->rom_ext.booted_slot); + uint32_t rom_ext_stage_slot = rom_ext_boot_slot == 0 ? 1 : 0; + uint32_t app_boot_slot = bootslot_int(resp->app.booted_slot); + uint32_t app_stage_slot = app_boot_slot == 0 ? 1 : 0; + + bool booted_slot_eq = libhoth_ot_version_eq(&resp->rom_ext.slots[rom_ext_boot_slot], &desired_rom_ext) && libhoth_ot_version_eq(&resp->app.slots[app_boot_slot], &desired_app); + if(!booted_slot_eq) { + // TODO print failure message + printf("Booted slot does not match desired version.\n"); + return -1; + } + + bool staging_slot_eq = libhoth_ot_version_eq(&resp->rom_ext.slots[rom_ext_stage_slot], &desired_rom_ext) && libhoth_ot_version_eq(&resp->app.slots[app_stage_slot], &desired_app); + if(!staging_slot_eq) { + // TODO print failure message + printf("Staging slot does not match desired version.\n"); + return -1; + } + + return 0; + +} diff --git a/protocol/dfu_check.h b/protocol/dfu_check.h new file mode 100644 index 0000000..79cc39d --- /dev/null +++ b/protocol/dfu_check.h @@ -0,0 +1,31 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBHOTH_EXAMPLES_HTOOL_DFU_CHECK_H_ +#define LIBHOTH_EXAMPLES_HTOOL_DFU_CHECK_H_ + +#include +#include +#include "protocol/opentitan_version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int libhoth_dfu_check(const uint8_t* image, size_t image_size, struct opentitan_get_version_resp * resp); +#ifdef __cplusplus +} +#endif + +#endif // LIBHOTH_EXAMPLES_HTOOL_DFU_CHECKH_ diff --git a/protocol/meson.build b/protocol/meson.build index e8a2e50..24c6c38 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -20,6 +20,7 @@ protocol_srcs = [ 'secure_boot.c', 'command_version.c', 'dfu_hostcmd.c', + 'dfu_check.c', ] incdir = include_directories('..')