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 }