diff --git a/host/protocol/BUILD b/host/protocol/BUILD index 73fff29a..42666f68 100644 --- a/host/protocol/BUILD +++ b/host/protocol/BUILD @@ -269,7 +269,9 @@ cc_test( name = "i2c_test", srcs = ["i2c_test.cc"], deps = [ + ":host_cmd", ":i2c", + ":libhoth_status", "//host/protocol/test:libhoth_device_mock", "//host/transports:libhoth_device", "@googletest//:gtest", diff --git a/host/protocol/i2c.c b/host/protocol/i2c.c index 8946c500..87f3fe56 100644 --- a/host/protocol/i2c.c +++ b/host/protocol/i2c.c @@ -85,3 +85,61 @@ libhoth_error libhoth_i2c_transfer(struct libhoth_device* dev, return HOTH_SUCCESS; } + +libhoth_error libhoth_i2c_filtering_enable_endpoints( + struct libhoth_device* dev, + const struct hoth_request_enable_i2c_endpoints_for_filtering* req, + size_t req_len, + struct hoth_response_i2c_endpoints_enabled_for_filtering* resp, + size_t max_resp_len, size_t* resp_len) { + if (dev == NULL || req == NULL || resp == NULL || req_len < sizeof(*req) || + max_resp_len < sizeof(*resp)) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } + size_t rLen = 0; + libhoth_error err = libhoth_hostcmd_exec( + dev, + HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_ENABLE_I2C_ENDPOINTS_FOR_FILTERING, + /*version=*/0, req, req_len, resp, max_resp_len, &rLen); + if (err != HOTH_SUCCESS) { + return err; + } + if (rLen < sizeof(*resp) || rLen > max_resp_len) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_FAIL); + } + if (resp_len != NULL) { + *resp_len = rLen; + } + return HOTH_SUCCESS; +} + +libhoth_error libhoth_i2c_filtering_get_enabled_endpoints( + struct libhoth_device* dev, + struct hoth_response_i2c_endpoints_enabled_for_filtering* resp, + size_t max_resp_len, size_t* resp_len) { + if (dev == NULL || resp == NULL || max_resp_len < sizeof(*resp)) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } + size_t rLen = 0; + libhoth_error err = libhoth_hostcmd_exec( + dev, + HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_GET_ENABLED_I2C_ENDPOINTS_FOR_FILTERING, + /*version=*/0, /*req_payload=*/NULL, /*req_payload_size=*/0, resp, + max_resp_len, &rLen); + if (err != HOTH_SUCCESS) { + return err; + } + if (rLen < sizeof(*resp) || rLen > max_resp_len) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_FAIL); + } + if (resp_len != NULL) { + *resp_len = rLen; + } + return HOTH_SUCCESS; +} diff --git a/host/protocol/i2c.h b/host/protocol/i2c.h index 369837ea..12bbf9c7 100644 --- a/host/protocol/i2c.h +++ b/host/protocol/i2c.h @@ -19,6 +19,7 @@ extern "C" { #endif +#include #include #include "protocol/status.h" @@ -111,6 +112,54 @@ libhoth_error libhoth_i2c_transfer(struct libhoth_device* dev, struct hoth_request_i2c_transfer* req, struct hoth_response_i2c_transfer* resp); +// Select I2C endpoints in the virtual I2C hierarchy that will be enabled when +// RoT firmware activates filtering rules for the protected I2C endpoints. +// For all remaining unselected endpoints, all write and write-then-read +// operations will be dropped but read operations will be allowed. + +// Takes `struct hoth_request_enable_i2c_endpoints_for_filtering` as input and +// returns `struct hoth_response_i2c_endpoints_enabled_for_filtering` as output. +#define HOTH_PRV_CMD_ENABLE_I2C_ENDPOINTS_FOR_FILTERING 0x0061 + +// No input. Returns `struct hoth_response_i2c_endpoints_enabled_for_filtering` +// as output. +#define HOTH_PRV_CMD_GET_ENABLED_I2C_ENDPOINTS_FOR_FILTERING 0x0062 + +struct hoth_request_enable_i2c_endpoints_for_filtering { + uint8_t + ids_count; // Number of endpoint identifiers in this request. + // `ids_count=0` is allowed and will disable all I2C endpoints + uint8_t reserved[3]; // Padding for alignment for rest of the data; must be 0 + // Variable length sequence: + // - Repeat `ids_count` times (i.e. for each identifier): + // - First byte represents I2C port (Port0 is encoded as 0) + // - The next byte (N) represents number of I2C switches in the path to the + // endpoint + // - The next `2*N` bytes represent the path along I2C switches + // - Each byte pair is (I2C switch address, Channel number) + // - Next 1 byte represents I2C endpoint address + // - Next byte (I) represents 1-byte identifier for the endpoint +} __attribute__((packed, aligned(4))); + +struct hoth_response_i2c_endpoints_enabled_for_filtering { + uint8_t ids_count; // Number of endpoints that have been enabled + uint8_t reserved[3]; // Padding for alignment for rest of the data; must be 0 + // Variable length sequence similar to request representing enabled I2C + // endpoints +} __attribute__((packed, aligned(4))); + +libhoth_error libhoth_i2c_filtering_enable_endpoints( + struct libhoth_device* dev, + const struct hoth_request_enable_i2c_endpoints_for_filtering* req, + size_t req_len, + struct hoth_response_i2c_endpoints_enabled_for_filtering* resp, + size_t max_resp_len, size_t* resp_len); + +libhoth_error libhoth_i2c_filtering_get_enabled_endpoints( + struct libhoth_device* dev, + struct hoth_response_i2c_endpoints_enabled_for_filtering* resp, + size_t max_resp_len, size_t* resp_len); + #ifdef __cplusplus } #endif diff --git a/host/protocol/i2c_test.cc b/host/protocol/i2c_test.cc index 0ef146e8..b2ba96b8 100644 --- a/host/protocol/i2c_test.cc +++ b/host/protocol/i2c_test.cc @@ -17,9 +17,12 @@ #include #include +#include #include -#include "test/libhoth_device_mock.h" +#include "protocol/host_cmd.h" +#include "protocol/status.h" +#include "protocol/test/libhoth_device_mock.h" using ::testing::_; using ::testing::DoAll; @@ -114,3 +117,162 @@ TEST_F(LibHothTest, i2c_transfer_null_param_test) { EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); } + +TEST_F(LibHothTest, i2c_filtering_enable_endpoints_test) { + alignas(4) uint8_t req_buf[8] = { + 1, 0, 0, 0, // ids_count = 1, reserved = {0, 0, 0} + 0x00, 0x00, 0x34, 0x01, + }; + alignas(4) uint8_t ex_resp_buf[8] = { + 1, 0, 0, 0, // ids_count = 1, reserved = {0, 0, 0} + 0x00, 0x00, 0x34, 0x01, + }; + + EXPECT_CALL(mock_, + send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_ENABLE_I2C_ENDPOINTS_FOR_FILTERING), + _)) + .WillOnce(Return(LIBHOTH_OK)); + + EXPECT_CALL(mock_, receive) + .WillOnce(DoAll(CopyResp(ex_resp_buf, sizeof(ex_resp_buf)), + Return(LIBHOTH_OK))); + + alignas(4) uint8_t resp_buf[16] = {0}; + size_t resp_len = 0; + EXPECT_EQ( + libhoth_i2c_filtering_enable_endpoints( + &hoth_dev_, + reinterpret_cast< + const struct hoth_request_enable_i2c_endpoints_for_filtering*>( + req_buf), + sizeof(req_buf), + reinterpret_cast< + struct hoth_response_i2c_endpoints_enabled_for_filtering*>( + resp_buf), + sizeof(resp_buf), &resp_len), + HOTH_SUCCESS); + EXPECT_EQ(resp_len, sizeof(ex_resp_buf)); + EXPECT_EQ(resp_buf[0], 1); + EXPECT_EQ(resp_buf[6], 0x34); + EXPECT_EQ(resp_buf[7], 0x01); +} + +TEST_F(LibHothTest, i2c_filtering_get_enabled_endpoints_test) { + alignas(4) uint8_t ex_resp_buf[8] = { + 1, 0, 0, 0, // ids_count = 1, reserved = {0, 0, 0} + 0x00, 0x00, 0x34, 0x01, + }; + + EXPECT_CALL( + mock_, + send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_GET_ENABLED_I2C_ENDPOINTS_FOR_FILTERING), + _)) + .WillOnce(Return(LIBHOTH_OK)); + + EXPECT_CALL(mock_, receive) + .WillOnce(DoAll(CopyResp(ex_resp_buf, sizeof(ex_resp_buf)), + Return(LIBHOTH_OK))); + + alignas(4) uint8_t resp_buf[16] = {0}; + size_t resp_len = 0; + EXPECT_EQ(libhoth_i2c_filtering_get_enabled_endpoints( + &hoth_dev_, + reinterpret_cast< + struct hoth_response_i2c_endpoints_enabled_for_filtering*>( + resp_buf), + sizeof(resp_buf), &resp_len), + HOTH_SUCCESS); + EXPECT_EQ(resp_len, sizeof(ex_resp_buf)); + EXPECT_EQ(resp_buf[0], 1); + EXPECT_EQ(resp_buf[6], 0x34); + EXPECT_EQ(resp_buf[7], 0x01); +} + +TEST_F(LibHothTest, i2c_filtering_enable_endpoints_invalid_param_test) { + struct hoth_request_enable_i2c_endpoints_for_filtering req = {}; + struct hoth_response_i2c_endpoints_enabled_for_filtering resp = {}; + size_t resp_len = 0; + + libhoth_error err = libhoth_i2c_filtering_enable_endpoints( + nullptr, &req, sizeof(req), &resp, sizeof(resp), &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_i2c_filtering_enable_endpoints(&hoth_dev_, nullptr, sizeof(req), + &resp, sizeof(resp), &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_i2c_filtering_enable_endpoints( + &hoth_dev_, &req, sizeof(req), nullptr, sizeof(resp), &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_i2c_filtering_enable_endpoints( + &hoth_dev_, &req, sizeof(req) - 1, &resp, sizeof(resp), &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_i2c_filtering_enable_endpoints( + &hoth_dev_, &req, sizeof(req), &resp, sizeof(resp) - 1, &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); +} + +TEST_F(LibHothTest, i2c_filtering_enable_endpoints_truncated_resp_test) { + struct hoth_request_enable_i2c_endpoints_for_filtering req = {}; + uint8_t short_resp[2] = {0, 0}; + + EXPECT_CALL(mock_, + send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_ENABLE_I2C_ENDPOINTS_FOR_FILTERING), + _)) + .WillOnce(Return(LIBHOTH_OK)); + EXPECT_CALL(mock_, receive) + .WillOnce( + DoAll(CopyResp(short_resp, sizeof(short_resp)), Return(LIBHOTH_OK))); + + struct hoth_response_i2c_endpoints_enabled_for_filtering resp = {}; + size_t resp_len = 0; + libhoth_error err = libhoth_i2c_filtering_enable_endpoints( + &hoth_dev_, &req, sizeof(req), &resp, sizeof(resp), &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_FAIL); +} + +TEST_F(LibHothTest, i2c_filtering_get_enabled_endpoints_invalid_param_test) { + struct hoth_response_i2c_endpoints_enabled_for_filtering resp = {}; + size_t resp_len = 0; + + libhoth_error err = libhoth_i2c_filtering_get_enabled_endpoints( + nullptr, &resp, sizeof(resp), &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_i2c_filtering_get_enabled_endpoints(&hoth_dev_, nullptr, + sizeof(resp), &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_i2c_filtering_get_enabled_endpoints( + &hoth_dev_, &resp, sizeof(resp) - 1, &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); +} + +TEST_F(LibHothTest, i2c_filtering_get_enabled_endpoints_truncated_resp_test) { + uint8_t short_resp[2] = {0, 0}; + + EXPECT_CALL( + mock_, + send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_GET_ENABLED_I2C_ENDPOINTS_FOR_FILTERING), + _)) + .WillOnce(Return(LIBHOTH_OK)); + EXPECT_CALL(mock_, receive) + .WillOnce( + DoAll(CopyResp(short_resp, sizeof(short_resp)), Return(LIBHOTH_OK))); + + struct hoth_response_i2c_endpoints_enabled_for_filtering resp = {}; + size_t resp_len = 0; + libhoth_error err = libhoth_i2c_filtering_get_enabled_endpoints( + &hoth_dev_, &resp, sizeof(resp), &resp_len); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_FAIL); +} diff --git a/tools/htool/htool.c b/tools/htool/htool.c index 25da4e7c..fc06f33a 100644 --- a/tools/htool/htool.c +++ b/tools/htool/htool.c @@ -1635,6 +1635,30 @@ static const struct htool_cmd CMDS[] = { .params = (const struct htool_param[]){{}}, .func = htool_i2c_muxctrl_select_host, }, + { + .verbs = (const char*[]){"i2c", "filter", "enable_endpoints", NULL}, + .desc = "Select I2C endpoints to enable when filtering is active. " + "Write and write-then-read operations to unselected I2C " + "endpoints will be blocked", + .params = + (const struct htool_param[]){ + // TODO: Find better interface for sending data + {HTOOL_FLAG_VALUE, 'c', "ids_count", "0", + .desc = "Count of IDs in `byte_stream`"}, + {HTOOL_POSITIONAL, .name = "byte_stream", .default_value = "", + .desc = "Byte stream containing select data"}, + {}}, + .func = htool_i2c_filtering_enable_endpoints, + }, + { + .verbs = + (const char*[]){"i2c", "filter", "get_enabled_endpoints", NULL}, + .desc = "Get I2C endpoints which are enabled when I2C filtering is " + "active. Write and write-then-read operations to unselected " + "I2C endpoints will be blocked", + .params = (const struct htool_param[]){{}}, + .func = htool_i2c_filtering_get_enabled_endpoints, + }, { .verbs = (const char*[]){"target_usb", ROT_USB_MUXCTRL_CMD_STR, ROT_USB_MUXCTRL_GET_SUBCMD_STR, NULL}, diff --git a/tools/htool/htool_i2c.c b/tools/htool/htool_i2c.c index 26a4cfab..feb61bbb 100644 --- a/tools/htool/htool_i2c.c +++ b/tools/htool/htool_i2c.c @@ -23,8 +23,11 @@ #include "host_commands.h" #include "htool.h" #include "htool_target_control.h" +#include "protocol/host_cmd.h" #include "protocol/i2c.h" +#include "protocol/status.h" #include "tools/common/htool_cmd.h" +#include "transports/libhoth_device.h" static int i2c_detect(struct libhoth_device* dev, const struct htool_invocation* inv) { @@ -276,3 +279,121 @@ int htool_i2c_muxctrl_select_rot(const struct htool_invocation* inv) { int htool_i2c_muxctrl_select_host(const struct htool_invocation* inv) { return i2c_mux_control_change_select(I2C_MUXCTRL_ACTION_SELECT_HOST); } + +static void print_enabled_i2c_endpoints( + const struct hoth_response_i2c_endpoints_enabled_for_filtering* resp, + size_t resp_len) { + printf("Count of endpoints enabled: %u\n", resp->ids_count); + printf("Enabled endpoints data:"); + const uint8_t* data = (const uint8_t*)resp + sizeof(*resp); + const size_t data_len = resp_len - sizeof(*resp); + for (size_t i = 0; i < data_len; ++i) { + printf(" 0x%02X", data[i]); + } + printf("\n"); +} + +int htool_i2c_filtering_enable_endpoints(const struct htool_invocation* inv) { + struct libhoth_device* dev = htool_libhoth_device(); + if (!dev) { + return -1; + } + + uint32_t ids_count = 0; + char* byte_stream = NULL; + if (htool_get_param_u32(inv, "ids_count", &ids_count) || + htool_get_param_string(inv, "byte_stream", (const char**)&byte_stream)) { + return -1; + } + + if (ids_count > UINT8_MAX) { + fprintf(stderr, "Invalid ids_count: %u (max %u)\n", ids_count, UINT8_MAX); + return -1; + } + + // `ids_count` = 0 is a supported value to disable all endpoints when + // filtering is active. + if (byte_stream == NULL) { + return -1; + } + + uint8_t __attribute__((aligned(4))) + request_payload[LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_request)]; + + struct hoth_request_enable_i2c_endpoints_for_filtering* req = + (struct hoth_request_enable_i2c_endpoints_for_filtering*)request_payload; + req->ids_count = (uint8_t)ids_count; + memset(req->reserved, 0, sizeof(req->reserved)); + size_t idx = sizeof(*req); + + char* tk = strtok(byte_stream, " "); + while (tk && (idx < sizeof(request_payload))) { + char* endptr; + unsigned long int parsed = strtoul(tk, &endptr, 0); + if (tk == endptr || parsed > UINT8_MAX || *endptr != '\0') { + fprintf(stderr, "Invalid input data.\n"); + return -1; + } + request_payload[idx++] = (uint8_t)parsed; + tk = strtok(NULL, " "); + } + if (tk != NULL) { + fprintf(stderr, "Request overflow!\n"); + return -1; + } + if ((ids_count == 0) != (idx == sizeof(*req))) { + fprintf(stderr, "Invalid input data.\n"); + return -1; + } + + uint8_t __attribute__((aligned(4))) + response_payload[LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_response)]; + size_t resp_len = 0; + + libhoth_error err = libhoth_i2c_filtering_enable_endpoints( + dev, + (const struct hoth_request_enable_i2c_endpoints_for_filtering*) + request_payload, + idx, + (struct hoth_response_i2c_endpoints_enabled_for_filtering*) + response_payload, + sizeof(response_payload), &resp_len); + if (err != HOTH_SUCCESS) { + htool_report_error("i2c_filtering", err); + return -1; + } + + print_enabled_i2c_endpoints( + (const struct hoth_response_i2c_endpoints_enabled_for_filtering*) + response_payload, + resp_len); + return 0; +} + +int htool_i2c_filtering_get_enabled_endpoints( + const struct htool_invocation* inv) { + struct libhoth_device* dev = htool_libhoth_device(); + if (!dev) { + return -1; + } + + uint8_t __attribute__((aligned(4))) + response_payload[LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_response)]; + size_t resp_len = 0; + + libhoth_error err = libhoth_i2c_filtering_get_enabled_endpoints( + dev, + (struct hoth_response_i2c_endpoints_enabled_for_filtering*) + response_payload, + sizeof(response_payload), &resp_len); + if (err != HOTH_SUCCESS) { + htool_report_error("i2c_filtering", err); + return -1; + } + + print_enabled_i2c_endpoints( + (const struct hoth_response_i2c_endpoints_enabled_for_filtering*) + response_payload, + resp_len); + return 0; +} diff --git a/tools/htool/htool_i2c.h b/tools/htool/htool_i2c.h index e9200082..06c470c0 100644 --- a/tools/htool/htool_i2c.h +++ b/tools/htool/htool_i2c.h @@ -39,6 +39,11 @@ int htool_i2c_muxctrl_select_rot(const struct htool_invocation* inv); int htool_i2c_muxctrl_select_host(const struct htool_invocation* inv); +int htool_i2c_filtering_enable_endpoints(const struct htool_invocation* inv); + +int htool_i2c_filtering_get_enabled_endpoints( + const struct htool_invocation* inv); + #ifdef __cplusplus } #endif