common/i2c: Add I2C passthru_protect command

This allows the AP to protect a I2C passthru bus. A board-specific
function then defines what I2C commands are allowed, so that we
can white/black list some addresses (e.g. I2C address allowing
PD chip FW updating).

BRANCH=none
BUG=chrome-os-partner:52431
TEST=Book elm-rev1

Change-Id: Ib106924418b16388ea8ea53c7b6bda6ef92e1d09
Signed-off-by: Nicolas Boichat <drinkcat@google.com>
Reviewed-on: https://chromium-review.googlesource.com/345761
Commit-Ready: Nicolas Boichat <drinkcat@chromium.org>
Tested-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-by: Randall Spangler <rspangler@google.com>
This commit is contained in:
Nicolas Boichat
2016-05-19 08:58:47 +08:00
committed by chrome-bot
parent d2bbc229f3
commit f5bba241fd
5 changed files with 177 additions and 4 deletions

View File

@@ -34,6 +34,7 @@
static struct mutex port_mutex[I2C_CONTROLLER_COUNT];
static uint32_t i2c_port_active_count;
static uint8_t port_protected[I2C_CONTROLLER_COUNT];
const struct i2c_port_t *get_i2c_port(int port)
{
@@ -469,6 +470,7 @@ static int i2c_command_read(struct host_cmd_handler_args *args)
{
const struct ec_params_i2c_read *p = args->params;
struct ec_response_i2c_read *r = args->response;
const struct i2c_port_t *i2c_port;
int data, rv = -1;
#ifdef CONFIG_I2C_PASSTHRU_RESTRICTED
@@ -476,9 +478,15 @@ static int i2c_command_read(struct host_cmd_handler_args *args)
return EC_RES_ACCESS_DENIED;
#endif
if (!get_i2c_port(p->port))
i2c_port = get_i2c_port(p->port);
if (!i2c_port)
return EC_RES_INVALID_PARAM;
if (port_protected[p->port] && i2c_port->passthru_allowed) {
if (!i2c_port->passthru_allowed(i2c_port, p->addr))
return EC_RES_ACCESS_DENIED;
}
if (p->read_size == 16)
rv = i2c_read16(p->port, p->addr, p->offset, &data);
else if (p->read_size == 8)
@@ -496,6 +504,7 @@ DECLARE_HOST_COMMAND(EC_CMD_I2C_READ, i2c_command_read, EC_VER_MASK(0));
static int i2c_command_write(struct host_cmd_handler_args *args)
{
const struct ec_params_i2c_write *p = args->params;
const struct i2c_port_t *i2c_port;
int rv = -1;
#ifdef CONFIG_I2C_PASSTHRU_RESTRICTED
@@ -503,9 +512,15 @@ static int i2c_command_write(struct host_cmd_handler_args *args)
return EC_RES_ACCESS_DENIED;
#endif
if (!get_i2c_port(p->port))
i2c_port = get_i2c_port(p->port);
if (!i2c_port)
return EC_RES_INVALID_PARAM;
if (port_protected[p->port] && i2c_port->passthru_allowed) {
if (!i2c_port->passthru_allowed(i2c_port, p->addr))
return EC_RES_ACCESS_DENIED;
}
if (p->write_size == 16)
rv = i2c_write16(p->port, p->addr, p->offset, p->data);
else if (p->write_size == 8)
@@ -591,9 +606,10 @@ static int i2c_command_passthru(struct host_cmd_handler_args *args)
const struct ec_params_i2c_passthru *params = args->params;
const struct ec_params_i2c_passthru_msg *msg;
struct ec_response_i2c_passthru *resp = args->response;
const struct i2c_port_t *i2c_port;
const uint8_t *out;
int in_len;
int ret;
int ret, i;
#if defined(VIRTUAL_BATTERY_ADDR) && defined(I2C_PORT_VIRTUAL_BATTERY)
uint8_t batt_param = 0;
#endif
@@ -611,13 +627,22 @@ static int i2c_command_passthru(struct host_cmd_handler_args *args)
return EC_RES_ACCESS_DENIED;
#endif
if (!get_i2c_port(params->port))
i2c_port = get_i2c_port(params->port);
if (!i2c_port)
return EC_RES_INVALID_PARAM;
ret = check_i2c_params(args);
if (ret)
return ret;
if (port_protected[params->port] && i2c_port->passthru_allowed) {
for (i = 0; i < params->num_msgs; i++) {
if (!i2c_port->passthru_allowed(i2c_port,
params->msg[i].addr_flags & EC_I2C_ADDR_MASK))
return EC_RES_ACCESS_DENIED;
}
}
/* Loop and process messages */
resp->i2c_status = 0;
out = args->params + sizeof(*params) + params->num_msgs * sizeof(*msg);
@@ -688,9 +713,86 @@ static int i2c_command_passthru(struct host_cmd_handler_args *args)
}
DECLARE_HOST_COMMAND(EC_CMD_I2C_PASSTHRU, i2c_command_passthru, EC_VER_MASK(0));
static int i2c_command_passthru_protect(struct host_cmd_handler_args *args)
{
const struct ec_params_i2c_passthru_protect *params = args->params;
struct ec_response_i2c_passthru_protect *resp = args->response;
if (args->params_size < sizeof(*params)) {
PTHRUPRINTF("i2c passthru protect no params, params_size=%d, "
"need at least %d",
args->params_size, sizeof(*params));
return EC_RES_INVALID_PARAM;
}
if (!get_i2c_port(params->port)) {
PTHRUPRINTF("i2c passthru protect invalid port %d",
params->port);
return EC_RES_INVALID_PARAM;
}
if (params->subcmd == EC_CMD_I2C_PASSTHRU_PROTECT_STATUS) {
if (args->response_max < sizeof(*resp)) {
PTHRUPRINTF("i2c passthru protect no response, "
"response_max=%d, need at least %d",
args->response_max, sizeof(*resp));
return EC_RES_INVALID_PARAM;
}
resp->status = port_protected[params->port];
args->response_size = sizeof(*resp);
} else if (params->subcmd == EC_CMD_I2C_PASSTHRU_PROTECT_ENABLE) {
port_protected[params->port] = 1;
} else {
return EC_RES_INVALID_COMMAND;
}
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_I2C_PASSTHRU_PROTECT, i2c_command_passthru_protect,
EC_VER_MASK(0));
/*****************************************************************************/
/* Console commands */
#ifdef CONFIG_CMD_I2C_PROTECT
static int command_i2cprotect(int argc, char **argv)
{
if (argc == 1) {
int i, port;
for (i = 0; i < i2c_ports_used; i++) {
port = i2c_ports[i].port;
ccprintf("Port %d: %s\n", port,
port_protected[port] ? "Protected" : "Unprotected");
}
} else if (argc == 2) {
int port;
char *e;
port = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
if (!get_i2c_port(port)) {
ccprintf("i2c passthru protect invalid port %d\n",
port);
return EC_RES_INVALID_PARAM;
}
port_protected[port] = 1;
} else {
return EC_ERROR_PARAM_COUNT;
}
return EC_RES_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(i2cprotect, command_i2cprotect,
"[port]",
"Protect I2C bus",
NULL);
#endif
#ifdef CONFIG_CMD_I2C_SCAN
static void scan_bus(int port, const char *desc)
{

View File

@@ -538,6 +538,7 @@
#define CONFIG_CMD_HASH
#define CONFIG_CMD_HCDEBUG
#undef CONFIG_CMD_HOSTCMD
#undef CONFIG_CMD_I2C_PROTECT
#define CONFIG_CMD_I2C_SCAN
#define CONFIG_CMD_I2C_XFER
#undef CONFIG_CMD_I2CWEDGE

View File

@@ -3194,6 +3194,25 @@ struct ec_params_entering_mode {
#define VBOOT_MODE_DEVELOPER 1
#define VBOOT_MODE_RECOVERY 2
/*****************************************************************************/
/* I2C passthru protection command: Protects I2C tunnels against access on
* certain addresses (board-specific). */
#define EC_CMD_I2C_PASSTHRU_PROTECT 0xb7
enum ec_i2c_passthru_protect_subcmd {
EC_CMD_I2C_PASSTHRU_PROTECT_STATUS = 0x0,
EC_CMD_I2C_PASSTHRU_PROTECT_ENABLE = 0x1,
};
struct ec_params_i2c_passthru_protect {
uint8_t subcmd;
uint8_t port; /* I2C port number */
} __packed;
struct ec_response_i2c_passthru_protect {
uint8_t status; /* Status flags (0: unlocked, 1: locked) */
} __packed;
/*****************************************************************************/
/* System commands */

View File

@@ -31,6 +31,10 @@ struct i2c_port_t {
int kbps; /* Speed in kbps */
enum gpio_signal scl; /* Port SCL GPIO line */
enum gpio_signal sda; /* Port SDA GPIO line */
/* When bus is protected, returns true if passthru allowed for address.
* If the function is not defined, the default value is true. */
int (*passthru_allowed)(const struct i2c_port_t *port,
uint16_t address);
};
extern const struct i2c_port_t i2c_ports[];

View File

@@ -123,6 +123,8 @@ const char help_str[] =
" Simulate key press\n"
" kbfactorytest\n"
" Scan out keyboard if any pins are shorted\n"
" i2cprotect <port> [status]\n"
" Protect EC's I2C bus\n"
" i2cread\n"
" Read I2C bus\n"
" i2cwrite\n"
@@ -4880,6 +4882,50 @@ int cmd_wireless(int argc, char *argv[])
}
int cmd_i2c_protect(int argc, char *argv[])
{
struct ec_params_i2c_passthru_protect p;
char *e;
int rv;
if (argc != 2 && (argc != 3 || strcmp(argv[2], "status"))) {
fprintf(stderr, "Usage: %s <port> [status]\n",
argv[0]);
return -1;
}
p.port = strtol(argv[1], &e, 0);
if (e && *e) {
fprintf(stderr, "Bad port.\n");
return -1;
}
if (argc == 3) {
struct ec_response_i2c_passthru_protect r;
p.subcmd = EC_CMD_I2C_PASSTHRU_PROTECT_STATUS;
rv = ec_command(EC_CMD_I2C_PASSTHRU_PROTECT, 0, &p, sizeof(p),
&r, sizeof(r));
if (rv < 0)
return rv;
printf("I2C port %d: %s (%d)\n", p.port,
r.status ? "Protected" : "Unprotected", r.status);
} else {
p.subcmd = EC_CMD_I2C_PASSTHRU_PROTECT_ENABLE;
rv = ec_command(EC_CMD_I2C_PASSTHRU_PROTECT, 0, &p, sizeof(p),
NULL, 0);
if (rv < 0)
return rv;
}
return 0;
}
int cmd_i2c_read(int argc, char *argv[])
{
struct ec_params_i2c_read p;
@@ -6656,6 +6702,7 @@ const struct command commands[] = {
{"hello", cmd_hello},
{"hibdelay", cmd_hibdelay},
{"kbpress", cmd_kbpress},
{"i2cprotect", cmd_i2c_protect},
{"i2cread", cmd_i2c_read},
{"i2cwrite", cmd_i2c_write},
{"i2cxfer", cmd_i2c_xfer},