From f052817aca8e1f570ade0654e77d81842d187e59 Mon Sep 17 00:00:00 2001 From: Ellis Sarza-Nguyen Date: Mon, 16 Feb 2026 23:00:52 -0800 Subject: [PATCH] [protocol] Add payload update confirm command With the need to have payload update confirm to interact for hoth device, we need this feature for testing. This will allow the use payload update confirm verification. Signed-off-by: Ellis Sarza-Nguyen --- examples/htool.c | 19 +++++- examples/htool_payload_update.c | 56 ++++++++++++++++ examples/htool_payload_update.h | 4 +- protocol/payload_update.c | 113 ++++++++++++++++++++++++++++++++ protocol/payload_update.h | 82 +++++++++++++++++++++++ 5 files changed, 272 insertions(+), 2 deletions(-) diff --git a/examples/htool.c b/examples/htool.c index e2dc011..8466fa2 100644 --- a/examples/htool.c +++ b/examples/htool.c @@ -973,7 +973,8 @@ static const struct htool_cmd CMDS[] = { }, { .verbs = (const char*[]){"payload", "update", NULL}, - .desc = "Perform payload update protocol for Titan images.", + .desc = "Perform payload update protocol for Titan images. Optional " + "allows for payload confirmation.", .params = (const struct htool_param[]){ {HTOOL_POSITIONAL, .name = "source-file"}, @@ -981,9 +982,25 @@ static const struct htool_cmd CMDS[] = { .desc = "Skip erasing the staging side."}, {HTOOL_FLAG_BOOL, 'b', "binary", "false", .desc = "Update with generic binary file"}, + {HTOOL_FLAG_BOOL, 'e', "enable", "false", + .desc = "Enable confirm mode"}, + {HTOOL_FLAG_VALUE, 't', "timeout", "0", + .desc = "Timeout in seconds"}, {}}, .func = htool_payload_update, }, + { + .verbs = (const char*[]){"payload", "confirm", NULL}, + .desc = "Finish a payload update confirmation.", + .params = (const struct htool_param[]){{}}, + .func = htool_payload_update_confirm, + }, + { + .verbs = (const char*[]){"payload", "get_timeout", NULL}, + .desc = "Get the current payload update confirmation timeout.", + .params = (const struct htool_param[]){{}}, + .func = htool_payload_update_confirm_get_timeout, + }, { .verbs = (const char*[]){"payload", "read", NULL}, .desc = "Read content of staging flash for Titan images.", diff --git a/examples/htool_payload_update.c b/examples/htool_payload_update.c index 92849d9..80fd78f 100644 --- a/examples/htool_payload_update.c +++ b/examples/htool_payload_update.c @@ -49,6 +49,16 @@ int htool_payload_update(const struct htool_invocation* inv) { return -1; } + uint32_t timeout = 0; + if (htool_get_param_u32(inv, "timeout", &timeout)) { + return -1; + } + + bool enable_confirm = false; + if (htool_get_param_bool(inv, "enable", &enable_confirm)) { + return -1; + } + int fd = open(image_file, O_RDONLY, 0); if (fd == -1) { fprintf(stderr, "Error opening file %s: %s\n", image_file, strerror(errno)); @@ -99,6 +109,15 @@ int htool_payload_update(const struct htool_invocation* inv) { break; } + if (enable_confirm) { + int ret = + libhoth_payload_update_confirm_enable(dev, enable_confirm, timeout); + if (ret != 0) { + fprintf(stderr, "Failed to confirm payload update\n"); + goto cleanup; + } + } + int ret = munmap(image, statbuf.st_size); if (ret != 0) { fprintf(stderr, "munmap error: %d\n", ret); @@ -207,3 +226,40 @@ int htool_payload_update_getstatus(const struct htool_invocation* inv) { return 0; } + +int htool_payload_update_confirm(const struct htool_invocation* inv) { + struct libhoth_device* dev = htool_libhoth_device(); + if (!dev) { + return -1; + } + + int ret = 0; + + ret = libhoth_payload_update_confirm(dev); + if (ret != 0) { + fprintf(stderr, "Failed to confirm payload update\n"); + return -1; + } + + return ret; +} + +int htool_payload_update_confirm_get_timeout( + const struct htool_invocation* inv) { + struct libhoth_device* dev = htool_libhoth_device(); + if (!dev) { + return -1; + } + + payload_update_confirm_response_t response = {0}; + + int ret = libhoth_payload_update_confirm_get_timeout(dev, &response); + if (ret != 0) { + fprintf(stderr, "Failed to get payload update timeout\n"); + return -1; + } + + printf("Current timeout: %u seconds\n", response.timeouts.current); + + return 0; +} \ No newline at end of file diff --git a/examples/htool_payload_update.h b/examples/htool_payload_update.h index f9c6a16..87f5edd 100644 --- a/examples/htool_payload_update.h +++ b/examples/htool_payload_update.h @@ -26,7 +26,9 @@ struct htool_invocation; int htool_payload_update(const struct htool_invocation* inv); int htool_payload_read(const struct htool_invocation* inv); int htool_payload_update_getstatus(const struct htool_invocation* inv); - +int htool_payload_update_confirm(const struct htool_invocation* inv); +int htool_payload_update_confirm_get_timeout( + const struct htool_invocation* inv); #ifdef __cplusplus } #endif diff --git a/protocol/payload_update.c b/protocol/payload_update.c index 9f17721..0d248a4 100644 --- a/protocol/payload_update.c +++ b/protocol/payload_update.c @@ -26,6 +26,12 @@ #include "transports/libhoth_device.h" #include "util.h" +#define PAYLOAD_UPDATE_CONFIRM_OP_ENABLE 0 +#define PAYLOAD_UPDATE_CONFIRM_OP_ENABLE_WITH_TIMEOUT 1 +#define PAYLOAD_UPDATE_CONFIRM_OP_DISABLE 2 +#define PAYLOAD_UPDATE_CONFIRM_OP_CONFIRM 3 +#define PAYLOAD_UPDATE_CONFIRM_OP_GET_TIMEOUT_VALUES 4 + static int send_payload_update_request_with_command(struct libhoth_device* dev, uint8_t command) { struct payload_update_packet request; @@ -269,3 +275,110 @@ enum payload_update_err libhoth_payload_update_read_chunk( return PAYLOAD_UPDATE_OK; } + +int libhoth_payload_update_confirm(struct libhoth_device* dev) { + payload_update_confirm_response_t confirm_response = {0}; + + // 1. Create the structures + payload_update_confirm_request_t confirm_request = {0}; + confirm_request.op = PAYLOAD_UPDATE_CONFIRM_OP_CONFIRM; + + struct payload_update_packet pkt_header = { + .type = PAYLOAD_UPDATE_CONFIRM, + .offset = 0, + .len = sizeof(confirm_request), + }; + + uint8_t send_buf[sizeof(pkt_header) + sizeof(confirm_request)] = {0}; + memcpy(&send_buf[0], &pkt_header, sizeof(pkt_header)); + memcpy(&send_buf[sizeof(pkt_header)], &confirm_request, + sizeof(confirm_request)); + + int ret = libhoth_hostcmd_exec( + dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_PAYLOAD_UPDATE, 0, + &send_buf, sizeof(send_buf), &confirm_response, sizeof(confirm_response), + NULL); + if (ret != 0) { + fprintf(stderr, "Payload update confirm failed, err code: %d\n", ret); + return -1; + } + + return 0; +} + +int libhoth_payload_update_confirm_enable(struct libhoth_device* dev, + bool enable, + uint32_t timeout_seconds) { + payload_update_confirm_response_t confirm_response = {0}; + + payload_update_confirm_request_t confirm_request = {0}; + confirm_request.op = enable ? PAYLOAD_UPDATE_CONFIRM_OP_ENABLE_WITH_TIMEOUT + : PAYLOAD_UPDATE_CONFIRM_OP_DISABLE; + confirm_request.timeout = timeout_seconds; + + struct payload_update_packet pkt_header = { + .type = PAYLOAD_UPDATE_CONFIRM, + .offset = 0, + .len = sizeof(confirm_request), + }; + + // timout_seconds of 0 is treated as a special value to use the default + // timeout value defined in the firmware. + if (timeout_seconds == 0) { + confirm_request.op = PAYLOAD_UPDATE_CONFIRM_OP_ENABLE; + confirm_request.timeout = PAYLOAD_UPDATE_CONFIRM_SECONDS_DEFAULT; + } + + if (confirm_request.timeout < PAYLOAD_UPDATE_CONFIRM_SECONDS_MIN || + confirm_request.timeout > PAYLOAD_UPDATE_CONFIRM_SECONDS_MAX) { + fprintf(stderr, + "Invalid timeout value: %u. Must be between %u and %u seconds.\n", + confirm_request.timeout, PAYLOAD_UPDATE_CONFIRM_SECONDS_MIN, + PAYLOAD_UPDATE_CONFIRM_SECONDS_MAX); + return -1; + } + + uint8_t send_buf[sizeof(pkt_header) + sizeof(confirm_request)] = {0}; + memcpy(&send_buf[0], &pkt_header, sizeof(pkt_header)); + memcpy(&send_buf[sizeof(pkt_header)], &confirm_request, + sizeof(confirm_request)); + + int ret = libhoth_hostcmd_exec( + dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_PAYLOAD_UPDATE, 0, + &send_buf, sizeof(send_buf), &confirm_response, sizeof(confirm_response), + NULL); + if (ret != 0) { + fprintf(stderr, "Payload update confirm enable failed, err code: %d\n", + ret); + return -1; + } + + return 0; +} + +int libhoth_payload_update_confirm_get_timeout( + struct libhoth_device* dev, payload_update_confirm_response_t* response) { + payload_update_confirm_request_t confirm_request = {0}; + confirm_request.op = PAYLOAD_UPDATE_CONFIRM_OP_GET_TIMEOUT_VALUES; + + struct payload_update_packet pkt_header = { + .type = PAYLOAD_UPDATE_CONFIRM, + .offset = 0, + .len = sizeof(confirm_request), + }; + + uint8_t send_buf[sizeof(pkt_header) + sizeof(confirm_request)] = {0}; + memcpy(&send_buf[0], &pkt_header, sizeof(pkt_header)); + memcpy(&send_buf[sizeof(pkt_header)], &confirm_request, + sizeof(confirm_request)); + + int ret = libhoth_hostcmd_exec( + dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_PAYLOAD_UPDATE, 0, + &send_buf, sizeof(send_buf), response, sizeof(*response), NULL); + if (ret != 0) { + fprintf(stderr, "Payload update get timeout failed, err code: %d\n", ret); + return -1; + } + + return 0; +} diff --git a/protocol/payload_update.h b/protocol/payload_update.h index 32f2baa..9f96b29 100644 --- a/protocol/payload_update.h +++ b/protocol/payload_update.h @@ -19,6 +19,7 @@ extern "C" { #endif +#include #include #include @@ -39,6 +40,60 @@ extern "C" { #define PAYLOAD_UPDATE_CONFIRM 10 #define PAYLOAD_UPDATE_VERIFY_DESCRIPTOR 11 +typedef uint32_t payload_update_confirm_seconds; +typedef uint8_t payload_update_confirm_op; + +static const payload_update_confirm_seconds + PAYLOAD_UPDATE_CONFIRM_SECONDS_ZERO = 0; +static const payload_update_confirm_seconds PAYLOAD_UPDATE_CONFIRM_SECONDS_MIN = + 30; +static const payload_update_confirm_seconds PAYLOAD_UPDATE_CONFIRM_SECONDS_MAX = + 60 * 60; +static const payload_update_confirm_seconds + PAYLOAD_UPDATE_CONFIRM_SECONDS_DEFAULT = 15 * 60; + +typedef struct { + payload_update_confirm_seconds min; + payload_update_confirm_seconds max; + payload_update_confirm_seconds default_val; + payload_update_confirm_seconds current; +} payload_update_confirm_timeouts_t; +static_assert(sizeof(payload_update_confirm_timeouts_t) == 16, + "Unexpected struct size for payload_update_confirm_timeouts_t"); +static_assert(offsetof(payload_update_confirm_timeouts_t, min) == 0, + "Unexpected offset for min"); +static_assert(offsetof(payload_update_confirm_timeouts_t, max) == 4, + "Unexpected offset for max"); +static_assert(offsetof(payload_update_confirm_timeouts_t, default_val) == 8, + "Unexpected offset for default_val"); +static_assert(offsetof(payload_update_confirm_timeouts_t, current) == 12, + "Unexpected offset for current"); + +typedef struct { + payload_update_confirm_op op; + uint8_t padding[3]; + payload_update_confirm_seconds timeout; + uint64_t cookie; +} payload_update_confirm_request_t; +static_assert(offsetof(payload_update_confirm_request_t, op) == 0, + "Unexpected offset for op"); +static_assert(offsetof(payload_update_confirm_request_t, padding) == 1, + "Unexpected offset for padding"); +static_assert(offsetof(payload_update_confirm_request_t, timeout) == 4, + "Unexpected offset for timeout"); +static_assert(offsetof(payload_update_confirm_request_t, cookie) == 8, + "Unexpected offset for cookie"); +static_assert(sizeof(payload_update_confirm_request_t) == 16, + "Unexpected struct size for payload_update_confirm_request_t"); + +typedef struct { + payload_update_confirm_timeouts_t timeouts; +} payload_update_confirm_response_t; +static_assert(offsetof(payload_update_confirm_response_t, timeouts) == 0, ""); +static_assert(sizeof(payload_update_confirm_response_t) == + sizeof(payload_update_confirm_timeouts_t), + ""); + struct payload_update_status { uint8_t a_valid; /* 0 = invalid, 1 = unverified, 2 = valid, */ /* 3 = descriptor valid */ @@ -48,6 +103,18 @@ struct payload_update_status { uint8_t next_half; /* 0, 1 */ uint8_t persistent_half; /* 0, 1 */ } __attribute__((packed)); +static_assert(sizeof(struct payload_update_status) == 5, + "Unexpected struct size"); +static_assert(offsetof(struct payload_update_status, a_valid) == 0, + "Unexpected offset for a_valid"); +static_assert(offsetof(struct payload_update_status, b_valid) == 1, + "Unexpected offset for b_valid"); +static_assert(offsetof(struct payload_update_status, active_half) == 2, + "Unexpected offset for active_half"); +static_assert(offsetof(struct payload_update_status, next_half) == 3, + "Unexpected offset for next_half"); +static_assert(offsetof(struct payload_update_status, persistent_half) == 4, + "Unexpected offset for persistent_half"); enum payload_update_err { PAYLOAD_UPDATE_OK = 0, @@ -66,6 +133,14 @@ struct payload_update_packet { uint8_t type; /* One of PAYLOAD_UPDATE_* */ /* payload data immediately follows */ } __attribute__((packed)); +static_assert(sizeof(struct payload_update_packet) == 9, + "Unexpected struct size"); +static_assert(offsetof(struct payload_update_packet, offset) == 0, + "Unexpected offset for offset"); +static_assert(offsetof(struct payload_update_packet, len) == 4, + "Unexpected offset for len"); +static_assert(offsetof(struct payload_update_packet, type) == 8, + "Unexpected offset for type"); struct payload_update_finalize_response_v1 { // Non-zero if configuration currently running on PLD needs to be @@ -82,6 +157,13 @@ int libhoth_payload_update_getstatus( struct libhoth_device* dev, struct payload_update_status* update_status); enum payload_update_err libhoth_payload_update_read_chunk( struct libhoth_device* dev, int fd, size_t len, size_t offset); +int libhoth_payload_update_confirm(struct libhoth_device* dev); +int libhoth_payload_update_confirm_enable(struct libhoth_device* dev, + bool enable, + uint32_t timeout_seconds); +int libhoth_payload_update_confirm_get_timeout( + struct libhoth_device* dev, + payload_update_confirm_response_t* timeout_seconds); #ifdef __cplusplus }