Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions host/protocol/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
58 changes: 58 additions & 0 deletions host/protocol/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
49 changes: 49 additions & 0 deletions host/protocol/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
extern "C" {
#endif

#include <stddef.h>
#include <stdint.h>

#include "protocol/status.h"
Expand Down Expand Up @@ -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
Expand Down
164 changes: 163 additions & 1 deletion host/protocol/i2c_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <cstddef>
#include <cstdint>

#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;
Expand Down Expand Up @@ -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);
}
24 changes: 24 additions & 0 deletions tools/htool/htool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
Loading
Loading