diff options
-rw-r--r-- | common/i2c.c | 110 | ||||
-rw-r--r-- | include/config.h | 1 | ||||
-rw-r--r-- | include/ec_commands.h | 19 | ||||
-rw-r--r-- | include/i2c.h | 4 | ||||
-rw-r--r-- | util/ectool.c | 47 |
5 files changed, 177 insertions, 4 deletions
diff --git a/common/i2c.c b/common/i2c.c index f22c86540e..03d7e2b9c6 100644 --- a/common/i2c.c +++ b/common/i2c.c @@ -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) { diff --git a/include/config.h b/include/config.h index effc6d9cc7..591af79c5f 100644 --- a/include/config.h +++ b/include/config.h @@ -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 diff --git a/include/ec_commands.h b/include/ec_commands.h index bc5b342817..27049eac5a 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -3195,6 +3195,25 @@ struct ec_params_entering_mode { #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 */ /* diff --git a/include/i2c.h b/include/i2c.h index fdda5a0187..d3329df64a 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -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[]; diff --git a/util/ectool.c b/util/ectool.c index 0e3c1ee596..f5787efcf2 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -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}, |