ec/google/chromeec: Add EC driver support for software sync

Quite a few new functions added here in order to support the use-case
of performing EC software sync within coreboot.

Most of these functions are related to retrieving the EC's hash, and
writing a new image into the EC's flash.

BUG=b:112198832
BRANCH=none
TEST=With whole patch series, successfully performed EC software sync

Change-Id: I0d3c5184dbe96f04b92878f2c19c7875503a910a
Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/36207
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
This commit is contained in:
Tim Wawrzynczak 2019-10-21 13:09:09 -06:00 committed by Patrick Georgi
parent 5ce66da1b5
commit 1966b5c800
2 changed files with 432 additions and 20 deletions

View File

@ -483,7 +483,6 @@ void google_chromeec_events_init(const struct google_chromeec_event_info *info,
/* Clear wake event mask. */ /* Clear wake event mask. */
google_chromeec_set_wake_mask(0); google_chromeec_set_wake_mask(0);
} }
int google_chromeec_check_feature(int feature) int google_chromeec_check_feature(int feature)
@ -507,16 +506,295 @@ int google_chromeec_check_feature(int feature)
return resp.flags[feature / 32] & EC_FEATURE_MASK_0(feature); return resp.flags[feature / 32] & EC_FEATURE_MASK_0(feature);
} }
int google_chromeec_get_cmd_versions(int command, uint32_t *pmask)
{
struct ec_params_get_cmd_versions_v1 params = {
.cmd = command,
};
struct ec_response_get_cmd_versions resp = {};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_GET_CMD_VERSIONS,
.cmd_version = 1,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_size_out = sizeof(resp),
.cmd_data_out = &resp,
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd) != 0)
return -1;
*pmask = resp.version_mask;
return 0;
}
int google_chromeec_get_vboot_hash(uint32_t offset,
struct ec_response_vboot_hash *resp)
{
struct ec_params_vboot_hash params = {
.cmd = EC_VBOOT_HASH_GET,
.offset = offset,
};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_VBOOT_HASH,
.cmd_version = 0,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_size_out = sizeof(*resp),
.cmd_data_out = resp,
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd) != 0)
return -1;
return 0;
}
int google_chromeec_start_vboot_hash(enum ec_vboot_hash_type hash_type,
uint32_t hash_offset,
struct ec_response_vboot_hash *resp)
{
struct ec_params_vboot_hash params = {
.cmd = EC_VBOOT_HASH_START,
.hash_type = hash_type,
.nonce_size = 0,
.offset = hash_offset,
};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_VBOOT_HASH,
.cmd_version = 0,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_size_out = sizeof(*resp),
.cmd_data_out = resp,
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd) != 0)
return -1;
return 0;
}
int google_chromeec_flash_protect(uint32_t mask, uint32_t flags,
struct ec_response_flash_protect *resp)
{
struct ec_params_flash_protect params = {
.mask = mask,
.flags = flags,
};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_FLASH_PROTECT,
.cmd_version = EC_VER_FLASH_PROTECT,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_size_out = sizeof(*resp),
.cmd_data_out = resp,
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd) != 0)
return -1;
return 0;
}
int google_chromeec_flash_region_info(enum ec_flash_region region,
uint32_t *offset, uint32_t *size)
{
struct ec_params_flash_region_info params = {
.region = region,
};
struct ec_response_flash_region_info resp = {};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_FLASH_REGION_INFO,
.cmd_version = EC_VER_FLASH_REGION_INFO,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_size_out = sizeof(resp),
.cmd_data_out = &resp,
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd) != 0)
return -1;
if (offset)
*offset = resp.offset;
if (size)
*size = resp.size;
return 0;
}
int google_chromeec_flash_erase(uint32_t offset, uint32_t size)
{
struct ec_params_flash_erase params = {
.offset = offset,
.size = size,
};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_FLASH_ERASE,
.cmd_version = 0,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_size_out = 0,
.cmd_data_out = NULL,
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd) != 0)
return -1;
return 0;
}
int google_chromeec_flash_info(struct ec_response_flash_info *info)
{
struct chromeec_command cmd;
cmd.cmd_code = EC_CMD_FLASH_INFO;
cmd.cmd_version = 0;
cmd.cmd_size_in = 0;
cmd.cmd_data_in = NULL;
cmd.cmd_size_out = sizeof(*info);
cmd.cmd_data_out = info;
cmd.cmd_dev_index = 0;
if (google_chromeec_command(&cmd) != 0)
return -1;
return 0;
}
/*
* Write a block into EC flash. Expects params_data to be a buffer where
* the first N bytes are a struct ec_params_flash_write, and the rest of it
* is the data to write to flash.
*/
int google_chromeec_flash_write_block(const uint8_t *params_data,
uint32_t bufsize)
{
struct chromeec_command cmd = {
.cmd_code = EC_CMD_FLASH_WRITE,
.cmd_version = EC_VER_FLASH_WRITE,
.cmd_size_out = 0,
.cmd_data_out = NULL,
.cmd_size_in = bufsize,
.cmd_data_in = params_data,
.cmd_dev_index = 0,
};
assert(params_data);
return google_chromeec_command(&cmd);
}
/*
* EFS verification of flash.
*/
int google_chromeec_efs_verify(enum ec_flash_region region)
{
struct ec_params_efs_verify params = {
.region = region,
};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_EFS_VERIFY,
.cmd_version = 0,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_size_out = 0,
.cmd_data_out = NULL,
.cmd_dev_index = 0,
};
int rv;
/* It's okay if the EC doesn't support EFS */
rv = google_chromeec_command(&cmd);
if (rv != 0 && (cmd.cmd_code == EC_RES_INVALID_COMMAND))
return 0;
else if (rv != 0)
return -1;
return 0;
}
int google_chromeec_battery_cutoff(uint8_t flags)
{
struct ec_params_battery_cutoff params = {
.flags = flags,
};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_BATTERY_CUT_OFF,
.cmd_version = 1,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_data_out = NULL,
.cmd_size_out = 0,
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd) != 0)
return -1;
return 0;
}
int google_chromeec_read_limit_power_request(int *limit_power)
{
struct ec_params_charge_state params = {
.cmd = CHARGE_STATE_CMD_GET_PARAM,
.get_param.param = CS_PARAM_LIMIT_POWER,
};
struct ec_response_charge_state resp = {};
struct chromeec_command cmd = {
.cmd_code = EC_CMD_CHARGE_STATE,
.cmd_version = 0,
.cmd_size_in = sizeof(params),
.cmd_data_in = &params,
.cmd_size_out = sizeof(resp),
.cmd_data_out = &resp,
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd))
return -1;
*limit_power = resp.get_param.value;
return 0;
}
int google_chromeec_get_protocol_info(
struct ec_response_get_protocol_info *resp)
{
struct chromeec_command cmd = {
.cmd_code = EC_CMD_GET_PROTOCOL_INFO,
.cmd_version = 0,
.cmd_size_in = 0,
.cmd_data_in = NULL,
.cmd_data_out = resp,
.cmd_size_out = sizeof(*resp),
.cmd_dev_index = 0,
};
if (google_chromeec_command(&cmd))
return -1;
return 0;
}
int google_chromeec_set_sku_id(uint32_t skuid) int google_chromeec_set_sku_id(uint32_t skuid)
{ {
struct ec_sku_id_info set_skuid = { struct ec_sku_id_info params = {
.sku_id = skuid .sku_id = skuid
}; };
struct chromeec_command cmd = { struct chromeec_command cmd = {
.cmd_code = EC_CMD_SET_SKU_ID, .cmd_code = EC_CMD_SET_SKU_ID,
.cmd_version = 0, .cmd_version = 0,
.cmd_size_in = sizeof(set_skuid), .cmd_size_in = sizeof(params),
.cmd_data_in = &set_skuid, .cmd_data_in = &params,
.cmd_data_out = NULL, .cmd_data_out = NULL,
.cmd_size_out = 0, .cmd_size_out = 0,
.cmd_dev_index = 0, .cmd_dev_index = 0,
@ -944,7 +1222,7 @@ int google_chromeec_set_usb_pd_role(uint8_t port, enum usb_pd_control_role role)
return google_chromeec_command(&cmd); return google_chromeec_command(&cmd);
} }
static int google_chromeec_hello(void) int google_chromeec_hello(void)
{ {
struct ec_params_hello params = { struct ec_params_hello params = {
.in_data = 0x10203040, .in_data = 0x10203040,
@ -963,6 +1241,10 @@ static int google_chromeec_hello(void)
int rv = google_chromeec_command(&cmd); int rv = google_chromeec_command(&cmd);
if (rv) if (rv)
return -1; return -1;
if (resp.out_data != (params.in_data + 0x01020304))
return -1;
return 0; return 0;
} }
@ -1087,10 +1369,14 @@ static void google_chromeec_log_uptimeinfo(void)
printk(BIOS_DEBUG, "\n"); printk(BIOS_DEBUG, "\n");
} }
static int ec_image_type; /* Cached EC image type (ro or rw). */ /* Cache and retrieve the EC image type (ro or rw) */
enum ec_current_image google_chromeec_get_current_image(void)
void google_chromeec_init(void)
{ {
MAYBE_STATIC_BSS enum ec_current_image ec_image_type = EC_IMAGE_UNKNOWN;
if (ec_image_type != EC_IMAGE_UNKNOWN)
return ec_image_type;
struct ec_response_get_version resp = {}; struct ec_response_get_version resp = {};
struct chromeec_command cmd = { struct chromeec_command cmd = {
.cmd_code = EC_CMD_GET_VERSION, .cmd_code = EC_CMD_GET_VERSION,
@ -1101,7 +1387,6 @@ void google_chromeec_init(void)
.cmd_dev_index = 0, .cmd_dev_index = 0,
}; };
google_chromeec_hello();
google_chromeec_command(&cmd); google_chromeec_command(&cmd);
if (cmd.cmd_code) { if (cmd.cmd_code) {
@ -1116,12 +1401,18 @@ void google_chromeec_init(void)
ec_image_type = resp.current_image; ec_image_type = resp.current_image;
} }
/* Will still be UNKNOWN if command failed */
return ec_image_type;
}
void google_chromeec_init(void)
{
google_chromeec_log_uptimeinfo(); google_chromeec_log_uptimeinfo();
} }
int google_ec_running_ro(void) int google_ec_running_ro(void)
{ {
return (ec_image_type == EC_IMAGE_RO); return (google_chromeec_get_current_image() == EC_IMAGE_RO);
} }
/** /**
@ -1148,29 +1439,29 @@ int google_chromeec_pd_get_amode(uint16_t svid)
return -1; return -1;
for (i = 0; i < resp.num_ports; i++) { for (i = 0; i < resp.num_ports; i++) {
struct ec_params_usb_pd_get_mode_request p; struct ec_params_usb_pd_get_mode_request params;
struct ec_params_usb_pd_get_mode_response res; struct ec_params_usb_pd_get_mode_response resp2;
int svid_idx = 0; int svid_idx = 0;
do { do {
/* Reset cmd in each iteration in case /* Reset cmd in each iteration in case
google_chromeec_command changes it. */ google_chromeec_command changes it. */
p.port = i; params.port = i;
p.svid_idx = svid_idx; params.svid_idx = svid_idx;
cmd.cmd_code = EC_CMD_USB_PD_GET_AMODE; cmd.cmd_code = EC_CMD_USB_PD_GET_AMODE;
cmd.cmd_version = 0; cmd.cmd_version = 0;
cmd.cmd_data_in = &p; cmd.cmd_data_in = &params;
cmd.cmd_size_in = sizeof(p); cmd.cmd_size_in = sizeof(params);
cmd.cmd_data_out = &res; cmd.cmd_data_out = &resp2;
cmd.cmd_size_out = sizeof(res); cmd.cmd_size_out = sizeof(resp2);
cmd.cmd_dev_index = 0; cmd.cmd_dev_index = 0;
if (google_chromeec_command(&cmd) < 0) if (google_chromeec_command(&cmd) < 0)
return -1; return -1;
if (res.svid == svid) if (resp2.svid == svid)
return 1; return 1;
svid_idx++; svid_idx++;
} while (res.svid); } while (resp2.svid);
} }
return 0; return 0;

View File

@ -35,6 +35,7 @@ uint8_t google_chromeec_get_event(void);
/* Check if EC supports feature EC_FEATURE_UNIFIED_WAKE_MASKS */ /* Check if EC supports feature EC_FEATURE_UNIFIED_WAKE_MASKS */
bool google_chromeec_is_uhepi_supported(void); bool google_chromeec_is_uhepi_supported(void);
int google_ec_running_ro(void); int google_ec_running_ro(void);
enum ec_current_image google_chromeec_get_current_image(void);
void google_chromeec_init(void); void google_chromeec_init(void);
int google_chromeec_pd_get_amode(uint16_t svid); int google_chromeec_pd_get_amode(uint16_t svid);
int google_chromeec_wait_for_displayport(long timeout); int google_chromeec_wait_for_displayport(long timeout);
@ -149,6 +150,13 @@ typedef int (*crosec_io_t)(size_t req_size, size_t resp_size, void *context);
int crosec_command_proto(struct chromeec_command *cec_command, int crosec_command_proto(struct chromeec_command *cec_command,
crosec_io_t crosec_io, void *context); crosec_io_t crosec_io, void *context);
/**
* Performs light verification of the EC<->AP communcation channel.
*
* @return 0 on success, -1 on error
*/
int google_chromeec_hello(void);
/** /**
* Send a command to a CrOS EC * Send a command to a CrOS EC
* *
@ -178,4 +186,117 @@ int google_chromeec_get_mkbp_event(struct ec_response_get_next_event *event);
/* Log host events to eventlog based on the mask provided. */ /* Log host events to eventlog based on the mask provided. */
void google_chromeec_log_events(uint64_t mask); void google_chromeec_log_events(uint64_t mask);
/**
* Protect/un-protect EC flash regions.
*
* @param mask Set/clear the requested bits in 'flags'
* @param flags Flash protection flags
* @param resp Pointer to response structure
* @return 0 on success, -1 on error
*/
int google_chromeec_flash_protect(uint32_t mask, uint32_t flags,
struct ec_response_flash_protect *resp);
/**
* Calculate image hash for vboot.
*
* @param hash_type The hash types supported by the EC for vboot
* @param offset The offset to start hashing in flash
* @param resp Pointer to response structure
* @return 0 on success, -1 on error
*/
int google_chromeec_start_vboot_hash(enum ec_vboot_hash_type hash_type,
uint32_t offset,
struct ec_response_vboot_hash *resp);
/**
* Return the EC's vboot image hash.
*
* @param offset Get hash for flash region beginning here
* @param resp Pointer to response structure
* @return 0 on success, -1 on error
*
*/
int google_chromeec_get_vboot_hash(uint32_t offset,
struct ec_response_vboot_hash *resp);
/**
* Get offset and size of the specified EC flash region.
*
* @param region Which region of EC flash
* @param offset Gets filled with region's offset
* @param size Gets filled with region's size
* @return 0 on success, -1 on error
*/
int google_chromeec_flash_region_info(enum ec_flash_region region,
uint32_t *offset, uint32_t *size);
/**
* Erase a region of EC flash.
*
* @param offset Where to begin erasing
* @param size Size of area to erase
* @return 0 on success, -1 on error
*/
int google_chromeec_flash_erase(uint32_t region_offset, uint32_t region_size);
/**
* Return information about the entire flash.
*
* @param info Pointer to response structure
* @return 0 on success, -1 on error
*/
int google_chromeec_flash_info(struct ec_response_flash_info *info);
/**
* Write a block into EC flash.
*
* @param data Pointer to data to write to flash, prefixed by a
* struct ec_params_flash_write
* @param offset Offset to begin writing data
* @param size Number of bytes to be written to flash from data
* @return 0 on success, -1 on error
*/
int google_chromeec_flash_write_block(const uint8_t *data, uint32_t size);
/**
* Verify flash using EFS if available.
*
* @param region Which flash region to verify
* @return 0 on success, -1 on error
*/
int google_chromeec_efs_verify(enum ec_flash_region region);
/**
* Command EC to perform battery cutoff.
*
* @param flags Flags to pass to the EC
* @return 0 on success, -1 on error
*/
int google_chromeec_battery_cutoff(uint8_t flags);
/**
* Check if the EC is requesting the system to limit input power.
*
* @param limit_power If successful, limit_power is 1 if EC is requesting
* input power limits, otherwise 0.
* @return 0 on success, -1 on error
*/
int google_chromeec_read_limit_power_request(int *limit_power);
/**
* Get information about the protocol that the EC speaks.
*
* @param resp Filled with host command protocol information.
* @return 0 on success, -1 on error
*/
int google_chromeec_get_protocol_info(
struct ec_response_get_protocol_info *resp);
/**
* Get available versions of the specified command.
*
* @param command Command ID
* @param pmask Pointer to version mask
* @return 0 on success, -1 on error
*/
int google_chromeec_get_cmd_versions(int command, uint32_t *pmask);
#endif /* _EC_GOOGLE_CHROMEEC_EC_H */ #endif /* _EC_GOOGLE_CHROMEEC_EC_H */