From 96b00b6963fe04c58e9891c6d5afd91939e8ba64 Mon Sep 17 00:00:00 2001 From: Ellis Nguyen Date: Wed, 25 Mar 2026 17:49:03 +0000 Subject: [PATCH 1/2] [protocol] Add the centeralized status to libhoth Add a place to hold the centeralized error status and link it to the basic framework without changing the overall error system. Signed-off-by: Ellis Nguyen --- BUILD | 8 ++++ examples/meson.build | 2 +- include/libhoth/status.h | 87 ++++++++++++++++++++++++++++++++++++++++ meson.build | 4 ++ protocol/meson.build | 4 +- transports/meson.build | 6 +-- 6 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 include/libhoth/status.h diff --git a/BUILD b/BUILD index d0048cb..1ce7d8d 100644 --- a/BUILD +++ b/BUILD @@ -7,6 +7,14 @@ cc_library( hdrs = [":gen_version_header"], ) +cc_library( + name = "libhoth_status", + hdrs = ["include/libhoth/status.h"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) + + genrule( name = "gen_version_header", outs = ["git_version.h"], diff --git a/examples/meson.build b/examples/meson.build index 889f715..3766041 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -6,7 +6,7 @@ git_version_h = vcs_tag( replace_string: '@GIT_COMMIT@', ) -incdir = include_directories('..') +incdir = libhoth_include_dirs link_with = [libhoth.get_static_lib()] c_args = [] diff --git a/include/libhoth/status.h b/include/libhoth/status.h new file mode 100644 index 0000000..b17ea09 --- /dev/null +++ b/include/libhoth/status.h @@ -0,0 +1,87 @@ + +// Copyright 2026 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_INCLUDE_LIBHOTH_STATUS_H_ +#define LIBHOTH_INCLUDE_LIBHOTH_STATUS_H_ + +#include + +// Represents success +#define HOTH_SUCCESS 0x0ULL + +// Typedef for the error code +typedef uint64_t libhoth_error; + +#define LIBHOTH_ERR_CONSTRUCT(ctx, space, code) \ + ((((uint64_t)(ctx) & 0xFFFFFFFFULL) << 32) | \ + (((uint64_t)(space) & 0xFFFFULL) << 16) | ((uint64_t)(code) & 0xFFFFULL)) + +// hoth_context_id: High 32 bits of the error code. +// Uniquely identifies the libhoth operation or subsystem. +enum hoth_context_id { + HOTH_CTX_NONE = 0, + + // Initialization / General + HOTH_CTX_INIT = 1, + + // Transport layers + HOTH_CTX_USB = 10, + HOTH_CTX_SPI = 20, + + // Command Execution + HOTH_CTX_CMD_EXEC = 30, +}; + +// hoth_host_space: Top 16 bits of the low 32-bit Base Error Code. +// Indicates the domain of host-side errors. +enum hoth_host_space { + HOTH_HOST_SPACE_FW = 0x0000, // Firmware errors directly + HOTH_HOST_SPACE_POSIX = 0x0001, // errno values + HOTH_HOST_SPACE_LIBUSB = 0x0002, // libusb_error values + HOTH_HOST_SPACE_LIBHOTH = 0x0005, // libhoth internal errors +}; + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +// Hoth error code: Low 16 bits of the 32-bit Base Error Code +// Firmware Error +enum hoth_fw_error_status { + HOTH_RES_SUCCESS = 0, + HOTH_RES_INVALID_COMMAND = 1, + HOTH_RES_ERROR = 2, + HOTH_RES_INVALID_PARAM = 3, + HOTH_RES_ACCESS_DENIED = 4, + HOTH_RES_INVALID_RESPONSE = 5, + HOTH_RES_INVALID_VERSION = 6, + HOTH_RES_INVALID_CHECKSUM = 7, + HOTH_RES_IN_PROGRESS = 8, + HOTH_RES_UNAVAILABLE = 9, + HOTH_RES_TIMEOUT = 10, + HOTH_RES_OVERFLOW = 11, + HOTH_RES_INVALID_HEADER = 12, + HOTH_RES_REQUEST_TRUNCATED = 13, + HOTH_RES_RESPONSE_TOO_BIG = 14, + HOTH_RES_BUS_ERROR = 15, + HOTH_RES_BUSY = 16, + HOTH_RES_INVALID_HEADER_VERSION = 17, + HOTH_RES_INVALID_HEADER_CRC = 18, + HOTH_RES_INVALID_DATA_CRC = 19, + HOTH_RES_DUP_UNAVAILABLE = 20, + HOTH_RES_MAX = UINT16_MAX +} __packed; + +#endif // LIBHOTH_INCLUDE_LIBHOTH_STATUS_H_ diff --git a/meson.build b/meson.build index 0cd1e43..424b7a5 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,9 @@ project('libhoth', 'c', 'cpp', license: 'Apache-2.0', version: '0.0.0') +install_headers('include/libhoth/status.h', subdir: 'libhoth') + +libhoth_include_dirs = include_directories('include', '.') + header_subdirs = ['libhoth'] libhoth_objs = [] libhoth_deps = [] diff --git a/protocol/meson.build b/protocol/meson.build index 8681075..3900512 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -26,12 +26,12 @@ protocol_srcs = [ 'console.c' ] -incdir = include_directories('..') +incdir = libhoth_include_dirs libhoth_protocol = static_library( 'hoth_protocols', protocol_srcs, - include_directories: incdir, + include_directories: libhoth_include_dirs, link_with: [libhoth_transport], ) libhoth_objs += [libhoth_protocol.extract_all_objects(recursive: false)] diff --git a/transports/meson.build b/transports/meson.build index e9f30d4..8bd107c 100644 --- a/transports/meson.build +++ b/transports/meson.build @@ -7,7 +7,7 @@ transport_srcs = [ 'libhoth_usb_mailbox.c', ] -incdir = include_directories('..') +incdir = libhoth_include_dirs libusb = dependency('libusb-1.0') libsystemd = dependency('libsystemd') libcap = dependency('libcap') @@ -16,7 +16,7 @@ if get_option('dbus_backend') libhoth_dbus = static_library( 'hoth_dbus', 'libhoth_dbus.c', - include_directories: incdir, + include_directories: libhoth_include_dirs, dependencies: [libsystemd], ) libhoth_objs += [libhoth_dbus.extract_all_objects(recursive: false)] @@ -27,7 +27,7 @@ endif libhoth_transport = static_library( 'hoth_transports', transport_srcs, - include_directories: incdir, + include_directories: libhoth_include_dirs, dependencies: [libusb], ) From fd3e89cf676c3d11beb4f595ce0e3caf4ce5aaab Mon Sep 17 00:00:00 2001 From: Ellis Sarza-Nguyen Date: Wed, 8 Apr 2026 14:04:13 -0700 Subject: [PATCH 2/2] [protocol] Convert hello world to use new error format Hello world in protocol uses the new error formart. This makes adjustment to how the libhoth error is reported from an error in FW, libhoth, and htool. Signed-off-by: Ellis Sarza-Nguyen --- examples/BUILD | 2 + examples/htool.c | 4 +- examples/htool_provisioning_test.cc | 15 ++++++-- examples/htool_security_v2_test.cc | 24 ++++++++---- examples/htool_target_control.c | 17 ++++++--- examples/htool_target_control.h | 2 + include/libhoth/status.h | 59 ++++++++++++++++++----------- protocol/BUILD | 3 ++ protocol/authz_record.c | 29 +++++++------- protocol/authz_record.h | 20 ++++++---- protocol/authz_record_test.cc | 16 ++++++-- protocol/controlled_storage.c | 15 +++++--- protocol/controlled_storage.h | 11 +++--- protocol/controlled_storage_test.cc | 7 +++- protocol/firmware_update.c | 24 +++++++----- protocol/firmware_update.h | 5 ++- protocol/firmware_update_test.cc | 3 +- protocol/hello.c | 8 ++-- protocol/hello.h | 4 +- protocol/host_cmd.c | 41 ++++++++++++-------- protocol/host_cmd.h | 9 +++-- protocol/host_cmd_test.cc | 8 ++-- 22 files changed, 210 insertions(+), 116 deletions(-) diff --git a/examples/BUILD b/examples/BUILD index be17dd9..49565b8 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -161,6 +161,7 @@ cc_test( ":htool_security_version", "//examples/test:test_util", "//protocol/test:libhoth_device_mock", + "//:libhoth_status", "@googletest//:gtest", "@googletest//:gtest_main", ], @@ -265,6 +266,7 @@ cc_binary( "//transports:libhoth_spi", "//transports:libhoth_usb", "//transports:libhoth_usb_device", + "//:libhoth_status", "@libusb", ] + select({ ":dbus_backend": ["//transports:libhoth_dbus"], diff --git a/examples/htool.c b/examples/htool.c index 3df934c..4a24d0f 100644 --- a/examples/htool.c +++ b/examples/htool.c @@ -769,11 +769,13 @@ static int command_hello(const struct htool_invocation* inv) { } uint32_t output = 0; - const int rv = libhoth_hello(dev, input, &output); + const libhoth_error rv = libhoth_hello(dev, input, &output); if (rv) { return rv; } + printf("Rv: %ld\n", rv); + printf("output: 0x%08x\n", output); return 0; } diff --git a/examples/htool_provisioning_test.cc b/examples/htool_provisioning_test.cc index 5178b46..9c22201 100644 --- a/examples/htool_provisioning_test.cc +++ b/examples/htool_provisioning_test.cc @@ -297,7 +297,10 @@ TEST_F(HtoolProvisioningTest, GetProvisioningLogUnexpectedErrorFromDevice) { .WillOnce(DoAll(CopyResp(&response, sizeof(header) + header.size), Return(LIBHOTH_ERR_INTERFACE_NOT_FOUND))); - ASSERT_EQ(htool_get_provisioning_log(&inv), -1); + ASSERT_EQ( + htool_get_provisioning_log(&inv), + (int)LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_RECEIVE_ERROR)); // Verify output file is empty FILE* fp = fopen(tmp_output_file.c_str(), "rb"); @@ -548,7 +551,10 @@ TEST_F(HtoolProvisioningTest, ValidateAndSignUnexpectedErrorFromDevice) { .WillOnce(DoAll(CopyResp(signed_log_data.data(), signed_log_data.size()), Return(LIBHOTH_ERR_INTERFACE_NOT_FOUND))); - ASSERT_EQ(htool_validate_and_sign(&inv), -1); + ASSERT_EQ( + htool_validate_and_sign(&inv), + (int)LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_RECEIVE_ERROR)); // Verify output file is empty FILE* fp = fopen(tmp_output_file.c_str(), "rb"); @@ -712,7 +718,10 @@ TEST_F(HtoolProvisioningTest, ValidateAndSignTooLargeResponse) { .WillOnce(DoAll(CopyResp(signed_log_data.data(), signed_log_data.size()), Return(LIBHOTH_OK))); - ASSERT_EQ(htool_validate_and_sign(&inv), -1); + ASSERT_EQ( + htool_validate_and_sign(&inv), + (int)LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_RESPONSE_TOO_BIG)); // Verify output file size is 0 FILE* fp = fopen(tmp_output_file.c_str(), "rb"); diff --git a/examples/htool_security_v2_test.cc b/examples/htool_security_v2_test.cc index 798f98e..594a322 100644 --- a/examples/htool_security_v2_test.cc +++ b/examples/htool_security_v2_test.cc @@ -9,6 +9,7 @@ #include "examples/host_commands.h" #include "examples/test/test_util.h" +#include "libhoth/status.h" #include "protocol/test/libhoth_device_mock.h" using ::testing::_; @@ -21,10 +22,11 @@ using ::testing::SetArrayArgument; struct libhoth_device* mock_dev = nullptr; struct libhoth_device* htool_libhoth_device() { return mock_dev; } -int libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command, - uint8_t version, const void* request, - size_t request_size, void* response, - size_t max_response_size, size_t* bytes_read) { +libhoth_error libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command, + uint8_t version, const void* request, + size_t request_size, void* response, + size_t max_response_size, + size_t* bytes_read) { LibHothDeviceMock* mock = (LibHothDeviceMock*)dev->user_ctx; return mock->hostcmd_exec(dev, command, version, request, request_size, response, max_response_size, bytes_read); @@ -92,13 +94,18 @@ TEST_F(HtoolSecurityV2Test, ExecCmdHostCmdFailure) { struct security_v2_buffer response_buffer = { .data = response_storage, .size = sizeof(response_storage)}; - EXPECT_CALL(mock_, hostcmd_exec(_, _, _, _, _, _, _, _)).WillOnce(Return(-1)); + EXPECT_CALL(mock_, hostcmd_exec(_, _, _, _, _, _, _, _)) + .WillOnce(Return(LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, + HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_SEND_ERROR))); int status = htool_exec_security_v2_cmd(mock_dev, 1, 2, 3, &request_buffer, nullptr, 0, &response_buffer, nullptr, 0); - ASSERT_EQ(status, -1); + ASSERT_EQ(status, (int)LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, + HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_SEND_ERROR)); } TEST_F(HtoolSecurityV2Test, SerializedCmdSuccess) { @@ -167,7 +174,10 @@ TEST_F(HtoolSecurityV2Test, SerializedCmdExecFailure) { struct security_v2_buffer response_buffer = { .data = response_storage, .size = sizeof(response_storage)}; - EXPECT_CALL(mock_, hostcmd_exec(_, _, _, _, _, _, _, _)).WillOnce(Return(-1)); + EXPECT_CALL(mock_, hostcmd_exec(_, _, _, _, _, _, _, _)) + .WillOnce(Return(LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, + HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_SEND_ERROR))); const struct security_v2_serialized_param* param_out1 = nullptr; const struct security_v2_serialized_param** response_params[] = {¶m_out1}; diff --git a/examples/htool_target_control.c b/examples/htool_target_control.c index 72e402b..18e7c71 100644 --- a/examples/htool_target_control.c +++ b/examples/htool_target_control.c @@ -39,32 +39,39 @@ int target_control_perform_action( }; size_t response_length = 0; - int ret = libhoth_hostcmd_exec( + libhoth_error ret = libhoth_hostcmd_exec( dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_TARGET_CONTROL, /*version=*/0, &request, sizeof(request), response, sizeof(*response), &response_length); if (ret != 0) { - fprintf(stderr, "HOTH_TARGET_CONTROL error code: %d\n", ret); + fprintf(stderr, "HOTH_TARGET_CONTROL error code: %ld\n", ret); switch (ret) { - case (HTOOL_ERROR_HOST_COMMAND_START + HOTH_RES_INVALID_COMMAND): + case (LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_FW, + HOTH_RES_FW_INVALID_COMMAND)): fprintf(stderr, "Command not supported, or requested action is forbidden on " "function. Please confirm if the RoT FW version supports this " "command, and requested action is allowed on the function\n"); break; - case (HTOOL_ERROR_HOST_COMMAND_START + HOTH_RES_INVALID_PARAM): + case (LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_FW, + HOTH_RES_FW_INVALID_PARAM)): fprintf(stderr, "Invalid function or action. Please confirm if the RoT " "firmware version supports the given function, and action on " "that function is correct\n"); break; - case (HTOOL_ERROR_HOST_COMMAND_START + HOTH_RES_ACCESS_DENIED): + case (LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_FW, + HOTH_RES_FW_ACCESS_DENIED)): fprintf(stderr, "Not authorized to perform requested action on function. " "Please use `authz_host_command` commands to authorize RoT to " "perform requested action on function\n"); break; + default: + fprintf(stderr, + "Unexpected error. Please check the transport layer and " + "RoT firmware are working correctly\n"); } return -1; } diff --git a/examples/htool_target_control.h b/examples/htool_target_control.h index 7a04575..838c3a8 100644 --- a/examples/htool_target_control.h +++ b/examples/htool_target_control.h @@ -17,6 +17,8 @@ #define LIBHOTH_EXAMPLES_HTOOL_TARGET_CONTROL_H_ #include "host_commands.h" +#include "libhoth/status.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/include/libhoth/status.h b/include/libhoth/status.h index b17ea09..ff489fd 100644 --- a/include/libhoth/status.h +++ b/include/libhoth/status.h @@ -50,6 +50,7 @@ enum hoth_host_space { HOTH_HOST_SPACE_FW = 0x0000, // Firmware errors directly HOTH_HOST_SPACE_POSIX = 0x0001, // errno values HOTH_HOST_SPACE_LIBUSB = 0x0002, // libusb_error values + HOTH_HOST_SPACE_HTOOL = 0x0003, // htool error codes HOTH_HOST_SPACE_LIBHOTH = 0x0005, // libhoth internal errors }; @@ -60,28 +61,42 @@ enum hoth_host_space { // Hoth error code: Low 16 bits of the 32-bit Base Error Code // Firmware Error enum hoth_fw_error_status { - HOTH_RES_SUCCESS = 0, - HOTH_RES_INVALID_COMMAND = 1, - HOTH_RES_ERROR = 2, - HOTH_RES_INVALID_PARAM = 3, - HOTH_RES_ACCESS_DENIED = 4, - HOTH_RES_INVALID_RESPONSE = 5, - HOTH_RES_INVALID_VERSION = 6, - HOTH_RES_INVALID_CHECKSUM = 7, - HOTH_RES_IN_PROGRESS = 8, - HOTH_RES_UNAVAILABLE = 9, - HOTH_RES_TIMEOUT = 10, - HOTH_RES_OVERFLOW = 11, - HOTH_RES_INVALID_HEADER = 12, - HOTH_RES_REQUEST_TRUNCATED = 13, - HOTH_RES_RESPONSE_TOO_BIG = 14, - HOTH_RES_BUS_ERROR = 15, - HOTH_RES_BUSY = 16, - HOTH_RES_INVALID_HEADER_VERSION = 17, - HOTH_RES_INVALID_HEADER_CRC = 18, - HOTH_RES_INVALID_DATA_CRC = 19, - HOTH_RES_DUP_UNAVAILABLE = 20, - HOTH_RES_MAX = UINT16_MAX + HOTH_RES_FW_SUCCESS = 0, + HOTH_RES_FW_INVALID_COMMAND = 1, + HOTH_RES_FW_ERROR = 2, + HOTH_RES_FW_INVALID_PARAM = 3, + HOTH_RES_FW_ACCESS_DENIED = 4, + HOTH_RES_FW_INVALID_RESPONSE = 5, + HOTH_RES_FW_INVALID_VERSION = 6, + HOTH_RES_FW_INVALID_CHECKSUM = 7, + HOTH_RES_FW_IN_PROGRESS = 8, + HOTH_RES_FW_UNAVAILABLE = 9, + HOTH_RES_FW_TIMEOUT = 10, + HOTH_RES_FW_OVERFLOW = 11, + HOTH_RES_FW_INVALID_HEADER = 12, + HOTH_RES_FW_REQUEST_TRUNCATED = 13, + HOTH_RES_FW_RESPONSE_TOO_BIG = 14, + HOTH_RES_FW_BUS_ERROR = 15, + HOTH_RES_FW_BUSY = 16, + HOTH_RES_FW_INVALID_HEADER_VERSION = 17, + HOTH_RES_FW_INVALID_HEADER_CRC = 18, + HOTH_RES_FW_INVALID_DATA_CRC = 19, + HOTH_RES_FW_DUP_UNAVAILABLE = 20, + HOTH_RES_FW_MAX = UINT16_MAX +} __packed; + +// Hoth error code: Low 16 bits of the 32-bit Base Error Code +// Libhoth Error +enum hoth_libhoth_error_status { + HOTH_LIBHOTH_SUCCESS = 0, + HOTH_LIBHOTH_ERROR = 1, + HOTH_LIBHOTH_REQUEST_TOO_BIG = 2, + HOTH_LIBHOTH_EC_ERROR = 3, + HOTH_LIBHOTH_SEND_ERROR = 4, + HOTH_LIBHOTH_RECEIVE_ERROR = 5, + HOTH_LIBHOTH_VALIDATE_ERROR = 6, + HOTH_LIBHOTH_RESPONSE_TOO_BIG = 7, + HOTH_LIBHOTH_MAX = UINT16_MAX } __packed; #endif // LIBHOTH_INCLUDE_LIBHOTH_STATUS_H_ diff --git a/protocol/BUILD b/protocol/BUILD index 564598f..f876481 100644 --- a/protocol/BUILD +++ b/protocol/BUILD @@ -8,6 +8,7 @@ cc_library( srcs = ["host_cmd.c"], hdrs = ["host_cmd.h"], deps = [ + "//:libhoth_status", "//transports:libhoth_device", ], ) @@ -17,6 +18,7 @@ cc_test( srcs = ["host_cmd_test.cc"], deps = [ ":host_cmd", + "//:libhoth_status", "//protocol/test:libhoth_device_mock", "//transports:libhoth_device", "@googletest//:gtest", @@ -390,6 +392,7 @@ cc_library( hdrs = ["hello.h"], deps = [ ":host_cmd", + "//:libhoth_status", "//transports:libhoth_device", ], ) diff --git a/protocol/authz_record.c b/protocol/authz_record.c index 78205eb..cca0380 100644 --- a/protocol/authz_record.c +++ b/protocol/authz_record.c @@ -19,7 +19,7 @@ #include "chipinfo.h" -int libhoth_authz_record_erase(struct libhoth_device* dev) { +libhoth_error libhoth_authz_record_erase(struct libhoth_device* dev) { struct hoth_authz_record_set_request request = { .index = 0, .erase = 1, @@ -29,17 +29,17 @@ int libhoth_authz_record_erase(struct libhoth_device* dev) { /*version=*/0, &request, sizeof(request), NULL, 0, NULL); } -int libhoth_authz_record_read(struct libhoth_device* dev, - struct hoth_authz_record_get_response* resp) { +libhoth_error libhoth_authz_record_read( + struct libhoth_device* dev, struct hoth_authz_record_get_response* resp) { struct hoth_authz_record_get_request request = {.index = 0}; return libhoth_hostcmd_exec( dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_GET_AUTHZ_RECORD, /*version=*/0, &request, sizeof(request), resp, sizeof(*resp), NULL); } -int libhoth_authz_record_build(struct libhoth_device* dev, - uint32_t capabilities, - struct authorization_record* record) { +libhoth_error libhoth_authz_record_build(struct libhoth_device* dev, + uint32_t capabilities, + struct authorization_record* record) { memset(record, 0, sizeof(*record)); memcpy(&record->magic, AUTHORIZATION_RECORD_MAGIC, sizeof(record->magic)); record->version = 1; @@ -49,9 +49,9 @@ int libhoth_authz_record_build(struct libhoth_device* dev, *(uint32_t*)record->capabilities = capabilities; struct hoth_response_chip_info chipinfo_resp; - int status = libhoth_chipinfo(dev, &chipinfo_resp); + libhoth_error status = libhoth_chipinfo(dev, &chipinfo_resp); if (status != 0) { - return -1; + return status; } record->dev_id_0 = chipinfo_resp.hardware_identity & 0xfffffffful; record->dev_id_1 = (chipinfo_resp.hardware_identity >> 32); @@ -67,14 +67,16 @@ int libhoth_authz_record_build(struct libhoth_device* dev, if (nonce_resp.ro_supported_key_id == 0) { fprintf(stderr, "ro_supported_key_id = 0. Please reset the chip and retry\n"); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_ERROR); } if (nonce_resp.ro_supported_key_id != nonce_resp.rw_supported_key_id) { fprintf( stderr, "RO and RW supported key_ids do not match: (RO) 0x%x != (RW) 0x%x\n", nonce_resp.ro_supported_key_id, nonce_resp.rw_supported_key_id); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_ERROR); } record->key_id = nonce_resp.ro_supported_key_id; if (sizeof(record->authorization_nonce) != @@ -82,7 +84,8 @@ int libhoth_authz_record_build(struct libhoth_device* dev, fprintf(stderr, "Nonce size does not match. Expecting %ld, got %ld", sizeof(nonce_resp.authorization_nonce), sizeof(record->authorization_nonce)); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_ERROR); } memcpy(record->authorization_nonce, nonce_resp.authorization_nonce, sizeof(record->authorization_nonce)); @@ -90,8 +93,8 @@ int libhoth_authz_record_build(struct libhoth_device* dev, return 0; } -int libhoth_authz_record_set(struct libhoth_device* dev, - const struct authorization_record* record) { +libhoth_error libhoth_authz_record_set( + struct libhoth_device* dev, const struct authorization_record* record) { struct hoth_authz_record_set_request request = { .index = 0, .erase = 0, diff --git a/protocol/authz_record.h b/protocol/authz_record.h index 747e1f7..b9831d9 100644 --- a/protocol/authz_record.h +++ b/protocol/authz_record.h @@ -107,17 +107,21 @@ struct hoth_authz_record_get_nonce_response { uint32_t rw_supported_key_id; } __attribute__((packed)); -int libhoth_authz_record_erase(struct libhoth_device* dev); -int libhoth_authz_record_read(struct libhoth_device* dev, - struct hoth_authz_record_get_response* resp); -int libhoth_authz_record_build(struct libhoth_device* dev, - uint32_t capabilities, - struct authorization_record* record); -int libhoth_authz_record_set(struct libhoth_device* dev, - const struct authorization_record* record); +libhoth_error libhoth_authz_record_erase(struct libhoth_device* dev); + +libhoth_error libhoth_authz_record_read( + struct libhoth_device* dev, struct hoth_authz_record_get_response* resp); + +libhoth_error libhoth_authz_record_build(struct libhoth_device* dev, + uint32_t capabilities, + struct authorization_record* record); + +libhoth_error libhoth_authz_record_set( + struct libhoth_device* dev, const struct authorization_record* record); int libhoth_authorization_record_print_hex_string( const struct authorization_record* record); + int libhoth_authorization_record_from_hex_string( struct authorization_record* record, const char* buf, size_t length); diff --git a/protocol/authz_record_test.cc b/protocol/authz_record_test.cc index 1416e73..6a279b1 100644 --- a/protocol/authz_record_test.cc +++ b/protocol/authz_record_test.cc @@ -47,7 +47,9 @@ TEST_F(LibHothTest, authz_erase_test) { EXPECT_CALL(mock_, receive).WillOnce(DoAll(CopyResp(&dummy, 0), Return(-1))); - EXPECT_EQ(libhoth_authz_record_erase(&hoth_dev_), -1); + EXPECT_EQ(libhoth_authz_record_erase(&hoth_dev_), + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_RECEIVE_ERROR)); } TEST_F(LibHothTest, authz_read_test) { @@ -147,7 +149,9 @@ TEST_F(LibHothTest, authz_build_fail_test) { uint32_t cap = 123; struct authorization_record record = {}; - EXPECT_EQ(libhoth_authz_record_build(&hoth_dev_, cap, &record), -1); + EXPECT_EQ(libhoth_authz_record_build(&hoth_dev_, cap, &record), + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_ERROR)); } TEST_F(LibHothTest, authz_mismatch_key_id_test) { @@ -183,7 +187,9 @@ TEST_F(LibHothTest, authz_mismatch_key_id_test) { uint32_t cap = 123; struct authorization_record record = {}; - EXPECT_EQ(libhoth_authz_record_build(&hoth_dev_, cap, &record), -1); + EXPECT_EQ(libhoth_authz_record_build(&hoth_dev_, cap, &record), + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_ERROR)); } TEST_F(LibHothTest, authz_nonce_fail_test) { @@ -215,7 +221,9 @@ TEST_F(LibHothTest, authz_nonce_fail_test) { uint32_t cap = 123; struct authorization_record record; - EXPECT_EQ(libhoth_authz_record_build(&hoth_dev_, cap, &record), -1); + EXPECT_EQ(libhoth_authz_record_build(&hoth_dev_, cap, &record), + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_ERROR)); } TEST_F(LibHothTest, authz_set_test) { diff --git a/protocol/controlled_storage.c b/protocol/controlled_storage.c index ed6a41e..7ed7952 100644 --- a/protocol/controlled_storage.c +++ b/protocol/controlled_storage.c @@ -18,7 +18,7 @@ #include "controlled_storage.h" -int libhoth_controlled_storage_read( +libhoth_error libhoth_controlled_storage_read( struct libhoth_device* dev, uint32_t slot, struct hoth_payload_controlled_storage* payload, size_t* payload_len) { struct hoth_request_controlled_storage req = {}; @@ -30,11 +30,14 @@ int libhoth_controlled_storage_read( /*version=*/0, &req, sizeof(req), payload, sizeof(*payload), payload_len); } -int libhoth_controlled_storage_write(struct libhoth_device* dev, uint32_t slot, - const uint8_t* data, size_t len) { +libhoth_error libhoth_controlled_storage_write(struct libhoth_device* dev, + uint32_t slot, + const uint8_t* data, + size_t len) { struct hoth_request_controlled_storage req = {}; if (len > sizeof(req.payload.data)) { - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_REQUEST_TOO_BIG); } req.operation = CONTROLLED_STORAGE_WRITE; @@ -47,8 +50,8 @@ int libhoth_controlled_storage_write(struct libhoth_device* dev, uint32_t slot, 0, NULL); } -int libhoth_controlled_storage_delete(struct libhoth_device* dev, - uint32_t slot) { +libhoth_error libhoth_controlled_storage_delete(struct libhoth_device* dev, + uint32_t slot) { struct hoth_request_controlled_storage req = {}; req.operation = CONTROLLED_STORAGE_DELETE; diff --git a/protocol/controlled_storage.h b/protocol/controlled_storage.h index e842cf6..a4b0bda 100644 --- a/protocol/controlled_storage.h +++ b/protocol/controlled_storage.h @@ -48,13 +48,14 @@ struct hoth_request_controlled_storage { struct hoth_payload_controlled_storage payload; } __attribute__((packed)); -int libhoth_controlled_storage_read( +libhoth_error libhoth_controlled_storage_read( struct libhoth_device* dev, uint32_t slot, struct hoth_payload_controlled_storage* payload, size_t* payload_len); -int libhoth_controlled_storage_write(struct libhoth_device* dev, uint32_t slot, - const uint8_t* data, size_t len); -int libhoth_controlled_storage_delete(struct libhoth_device* dev, - uint32_t slot); +libhoth_error libhoth_controlled_storage_write(struct libhoth_device* dev, + uint32_t slot, + const uint8_t* data, size_t len); +libhoth_error libhoth_controlled_storage_delete(struct libhoth_device* dev, + uint32_t slot); #ifdef __cplusplus } diff --git a/protocol/controlled_storage_test.cc b/protocol/controlled_storage_test.cc index 7651584..b574df0 100644 --- a/protocol/controlled_storage_test.cc +++ b/protocol/controlled_storage_test.cc @@ -73,7 +73,8 @@ TEST_F(LibHothTest, controlled_storage_write_test) { LIBHOTH_OK); EXPECT_EQ( libhoth_controlled_storage_write(&hoth_dev_, 0, payload, sizeof(payload)), - -1); + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_SEND_ERROR)); } TEST_F(LibHothTest, controlled_storage_delete_test) { @@ -89,5 +90,7 @@ TEST_F(LibHothTest, controlled_storage_delete_test) { .WillOnce(DoAll(CopyResp(&dummy, 0), Return(LIBHOTH_OK))); EXPECT_EQ(libhoth_controlled_storage_delete(&hoth_dev_, 0), LIBHOTH_OK); - EXPECT_EQ(libhoth_controlled_storage_delete(&hoth_dev_, 0), -1); + EXPECT_EQ(libhoth_controlled_storage_delete(&hoth_dev_, 0), + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_SEND_ERROR)); } diff --git a/protocol/firmware_update.c b/protocol/firmware_update.c index e7f2700..4796317 100644 --- a/protocol/firmware_update.c +++ b/protocol/firmware_update.c @@ -20,14 +20,14 @@ #include "protocol/host_cmd.h" #include "transports/libhoth_device.h" -int libhoth_firmware_update_from_flash_and_reset(struct libhoth_device* dev, - uint32_t offset) { +libhoth_error libhoth_firmware_update_from_flash_and_reset( + struct libhoth_device* dev, uint32_t offset) { const struct hoth_request_firmware_update request = { .operation = HOTH_FIRMWARE_UPDATE_OP_UPDATE_AND_RESET, .flags = 0, .offset = offset, }; - const int rv = + const libhoth_error rv = libhoth_hostcmd_exec(dev, HOTH_CMD_FIRMWARE_UPDATE, /*version=*/0, &request, sizeof(request), NULL, 0, NULL); if (rv == 0) { @@ -37,17 +37,21 @@ int libhoth_firmware_update_from_flash_and_reset(struct libhoth_device* dev, offset); return 0; } - if (rv > HTOOL_ERROR_HOST_COMMAND_START) { + if (rv != 0 && (rv >> 32) == HOTH_CTX_CMD_EXEC && + ((rv >> 16) & 0xFFFF) == HOTH_HOST_SPACE_FW) { fprintf(stderr, "Firmware update from flash offset 0x%x failed with error code: " - "%d. Aborting.\n", + "%ld. Aborting.\n", offset, rv); return rv; } - fprintf(stderr, - "Lost connection after firmware update command (error code %d). " - "This is expected if the device reset. Attempting to reconnect...\n", - rv); - return libhoth_device_reconnect(dev); + if (rv != 0) { + fprintf( + stderr, + "Lost connection after firmware update command (error code %ld). " + "This is expected if the device reset. Attempting to reconnect...\n", + rv); + } + return (libhoth_error)libhoth_device_reconnect(dev); } diff --git a/protocol/firmware_update.h b/protocol/firmware_update.h index 2a404d9..2657b6c 100644 --- a/protocol/firmware_update.h +++ b/protocol/firmware_update.h @@ -17,6 +17,7 @@ #include +#include "libhoth/status.h" #include "protocol/host_cmd.h" #ifdef __cplusplus @@ -37,8 +38,8 @@ struct hoth_request_firmware_update { uint32_t offset; // Bundle offset in the active side of flash. } __attribute__((packed, aligned(4))); -int libhoth_firmware_update_from_flash_and_reset(struct libhoth_device* dev, - uint32_t offset); +libhoth_error libhoth_firmware_update_from_flash_and_reset( + struct libhoth_device* dev, uint32_t offset); #ifdef __cplusplus } diff --git a/protocol/firmware_update_test.cc b/protocol/firmware_update_test.cc index e0c22f8..17cf0ee 100644 --- a/protocol/firmware_update_test.cc +++ b/protocol/firmware_update_test.cc @@ -64,5 +64,6 @@ TEST_F(LibHothTest, firmware_update_test_failed_without_rebooting) { EXPECT_CALL(mock_, reconnect).Times(0); EXPECT_EQ( libhoth_firmware_update_from_flash_and_reset(&hoth_dev_, /*offset=*/0), - HTOOL_ERROR_HOST_COMMAND_START + HOTH_RES_UNAVAILABLE); + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_FW, + HOTH_RES_FW_UNAVAILABLE)); } diff --git a/protocol/hello.c b/protocol/hello.c index 9a05779..9ac2bde 100644 --- a/protocol/hello.c +++ b/protocol/hello.c @@ -16,13 +16,13 @@ #include -int libhoth_hello(struct libhoth_device* const dev, const uint32_t input, - uint32_t* const output) { +libhoth_error libhoth_hello(struct libhoth_device* const dev, + const uint32_t input, uint32_t* const output) { const struct hoth_request_hello request = { .input = input, }; - struct hoth_response_hello response; - const int rv = + struct hoth_response_hello response = {0}; + const libhoth_error rv = libhoth_hostcmd_exec(dev, HOTH_CMD_HELLO, /*version=*/0, &request, sizeof(request), &response, sizeof(response), NULL); *output = response.output; diff --git a/protocol/hello.h b/protocol/hello.h index 88804d2..1a73c0c 100644 --- a/protocol/hello.h +++ b/protocol/hello.h @@ -17,6 +17,7 @@ #include +#include "libhoth/status.h" #include "protocol/host_cmd.h" #ifdef __cplusplus @@ -35,7 +36,8 @@ struct hoth_response_hello { uint32_t output; } __hoth_align4; -int libhoth_hello(struct libhoth_device* dev, uint32_t input, uint32_t* output); +libhoth_error libhoth_hello(struct libhoth_device* dev, uint32_t input, + uint32_t* output); #ifdef __cplusplus } diff --git a/protocol/host_cmd.c b/protocol/host_cmd.c index 95cea32..3dad689 100644 --- a/protocol/host_cmd.c +++ b/protocol/host_cmd.c @@ -162,19 +162,21 @@ static int validate_ec_response_header( return 0; } -int libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command, - uint8_t version, const void* req_payload, - size_t req_payload_size, void* resp_buf, - size_t resp_buf_size, size_t* out_resp_size) { +libhoth_error libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command, + uint8_t version, const void* req_payload, + size_t req_payload_size, void* resp_buf, + size_t resp_buf_size, + size_t* out_resp_size) { struct { struct hoth_host_request hdr; uint8_t payload_buf[LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_request)]; - } req; + } req = {0}; if (req_payload_size > sizeof(req.payload_buf)) { fprintf(stderr, "req_payload_size too large: %d > %d\n", (int)req_payload_size, (int)sizeof(req.payload_buf)); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_REQUEST_TOO_BIG); } if (req_payload) { memcpy(req.payload_buf, req_payload, req_payload_size); @@ -183,40 +185,47 @@ int libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command, req_payload_size, &req.hdr); if (status != 0) { fprintf(stderr, "populate_ec_request_header() failed: %d\n", status); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_EC_ERROR); } status = libhoth_send_request(dev, &req, sizeof(req.hdr) + req_payload_size); if (status != LIBHOTH_OK) { fprintf(stderr, "libhoth_send_request() failed: %d\n", status); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_SEND_ERROR); } struct { struct hoth_host_response hdr; uint8_t payload_buf[LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_response)]; - } resp; + } resp = {0}; size_t resp_size = 0; status = libhoth_receive_response(dev, &resp, sizeof(resp), &resp_size, HOTH_CMD_TIMEOUT_MS_DEFAULT); if (status != LIBHOTH_OK) { fprintf(stderr, "libhoth_receive_response() failed: %d\n", status); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_RECEIVE_ERROR); } status = validate_ec_response_header(&resp.hdr, resp.payload_buf, resp_size); if (status != 0) { fprintf(stderr, "EC response header invalid: %d\n", status); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_VALIDATE_ERROR); } if (resp.hdr.result != HOTH_RES_SUCCESS) { fprintf(stderr, "EC response contained error: %d", resp.hdr.result); if (resp.hdr.data_len >= 4) { - uint32_t error_code; + uint32_t error_code = 0; memcpy(&error_code, resp.payload_buf, sizeof(error_code)); fprintf(stderr, " (extended: 0x%08x)\n", error_code); + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_FW, + error_code); } else { fprintf(stderr, "\n"); + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_FW, + resp.hdr.result); } - return HTOOL_ERROR_HOST_COMMAND_START + resp.hdr.result; } size_t resp_payload_size = resp_size - sizeof(struct hoth_host_response); @@ -226,14 +235,16 @@ int libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command, stderr, "Response payload too large to fit in supplied buffer: %zu > %zu\n", resp_payload_size, resp_buf_size); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_RESPONSE_TOO_BIG); } } else { if (resp_payload_size != resp_buf_size) { fprintf(stderr, "Unexpected response payload size: got %zu expected %zu\n", resp_payload_size, resp_buf_size); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + HOTH_LIBHOTH_RESPONSE_TOO_BIG); } } if (resp_buf) { diff --git a/protocol/host_cmd.h b/protocol/host_cmd.h index 17e8100..e2f7474 100644 --- a/protocol/host_cmd.h +++ b/protocol/host_cmd.h @@ -18,6 +18,7 @@ #include #include +#include "libhoth/status.h" #include "transports/libhoth_device.h" #ifdef __cplusplus @@ -101,10 +102,10 @@ struct hoth_host_response { uint16_t reserved; } __hoth_align4; -int libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command, - uint8_t version, const void* req_payload, - size_t req_payload_size, void* resp_buf, - size_t resp_buf_size, size_t* out_resp_size); +libhoth_error libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command, + uint8_t version, const void* req_payload, + size_t req_payload_size, void* resp_buf, + size_t resp_buf_size, size_t* out_resp_size); uint8_t libhoth_calculate_checksum(const void* header, size_t header_size, const void* data, size_t data_size); diff --git a/protocol/host_cmd_test.cc b/protocol/host_cmd_test.cc index 79ed151..6cbf0c4 100644 --- a/protocol/host_cmd_test.cc +++ b/protocol/host_cmd_test.cc @@ -17,7 +17,7 @@ #include #include -#include "protocol/host_cmd.h" +#include "libhoth/status.h" #include "test/libhoth_device_mock.h" #include "transports/libhoth_device.h" @@ -47,7 +47,7 @@ TEST_F(LibHothTest, response_failure_legacy) { size_t out_resp_size; EXPECT_EQ(libhoth_hostcmd_exec(&hoth_dev_, kCmd, 0, nullptr, 0, resp_buf, sizeof(resp_buf), &out_resp_size), - HTOOL_ERROR_HOST_COMMAND_START + 2); + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_FW, 2)); } TEST_F(LibHothTest, response_failure_extended) { @@ -60,7 +60,9 @@ TEST_F(LibHothTest, response_failure_extended) { uint8_t resp_buf[1024]; size_t out_resp_size; + // Payload is 0x05, 0x00, 0xc7, 0x89 -> 0x89c70005. The code is the low 16 + // bits: 5. EXPECT_EQ(libhoth_hostcmd_exec(&hoth_dev_, kCmd, 0, nullptr, 0, resp_buf, sizeof(resp_buf), &out_resp_size), - HTOOL_ERROR_HOST_COMMAND_START + 2); + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_FW, 5)); }