From 1966b5c800319dff48995e19c6f6624c23295602 Mon Sep 17 00:00:00 2001 From: Tim Wawrzynczak Date: Mon, 21 Oct 2019 13:09:09 -0600 Subject: 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 Reviewed-on: https://review.coreboot.org/c/coreboot/+/36207 Tested-by: build bot (Jenkins) Reviewed-by: Julius Werner --- src/ec/google/chromeec/ec.c | 331 +++++++++++++++++++++++++++++++++++++++++--- src/ec/google/chromeec/ec.h | 121 ++++++++++++++++ 2 files changed, 432 insertions(+), 20 deletions(-) (limited to 'src/ec') diff --git a/src/ec/google/chromeec/ec.c b/src/ec/google/chromeec/ec.c index 32f06bbd56..2715e0b7f6 100644 --- a/src/ec/google/chromeec/ec.c +++ b/src/ec/google/chromeec/ec.c @@ -483,7 +483,6 @@ void google_chromeec_events_init(const struct google_chromeec_event_info *info, /* Clear wake event mask. */ google_chromeec_set_wake_mask(0); - } 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); } +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 = ¶ms, + .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 = ¶ms, + .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 = ¶ms, + .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 = ¶ms, + .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 = ¶ms, + .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 = ¶ms, + .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 = ¶ms, + .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 = ¶ms, + .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 = ¶ms, + .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) { - struct ec_sku_id_info set_skuid = { + struct ec_sku_id_info params = { .sku_id = skuid }; struct chromeec_command cmd = { .cmd_code = EC_CMD_SET_SKU_ID, .cmd_version = 0, - .cmd_size_in = sizeof(set_skuid), - .cmd_data_in = &set_skuid, + .cmd_size_in = sizeof(params), + .cmd_data_in = ¶ms, .cmd_data_out = NULL, .cmd_size_out = 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); } -static int google_chromeec_hello(void) +int google_chromeec_hello(void) { struct ec_params_hello params = { .in_data = 0x10203040, @@ -963,6 +1241,10 @@ static int google_chromeec_hello(void) int rv = google_chromeec_command(&cmd); if (rv) return -1; + + if (resp.out_data != (params.in_data + 0x01020304)) + return -1; + return 0; } @@ -1087,10 +1369,14 @@ static void google_chromeec_log_uptimeinfo(void) printk(BIOS_DEBUG, "\n"); } -static int ec_image_type; /* Cached EC image type (ro or rw). */ - -void google_chromeec_init(void) +/* Cache and retrieve the EC image type (ro or rw) */ +enum ec_current_image google_chromeec_get_current_image(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 chromeec_command cmd = { .cmd_code = EC_CMD_GET_VERSION, @@ -1101,7 +1387,6 @@ void google_chromeec_init(void) .cmd_dev_index = 0, }; - google_chromeec_hello(); google_chromeec_command(&cmd); if (cmd.cmd_code) { @@ -1116,12 +1401,18 @@ void google_chromeec_init(void) 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(); } 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; for (i = 0; i < resp.num_ports; i++) { - struct ec_params_usb_pd_get_mode_request p; - struct ec_params_usb_pd_get_mode_response res; + struct ec_params_usb_pd_get_mode_request params; + struct ec_params_usb_pd_get_mode_response resp2; int svid_idx = 0; do { /* Reset cmd in each iteration in case google_chromeec_command changes it. */ - p.port = i; - p.svid_idx = svid_idx; + params.port = i; + params.svid_idx = svid_idx; cmd.cmd_code = EC_CMD_USB_PD_GET_AMODE; cmd.cmd_version = 0; - cmd.cmd_data_in = &p; - cmd.cmd_size_in = sizeof(p); - cmd.cmd_data_out = &res; - cmd.cmd_size_out = sizeof(res); + cmd.cmd_data_in = ¶ms; + cmd.cmd_size_in = sizeof(params); + cmd.cmd_data_out = &resp2; + cmd.cmd_size_out = sizeof(resp2); cmd.cmd_dev_index = 0; if (google_chromeec_command(&cmd) < 0) return -1; - if (res.svid == svid) + if (resp2.svid == svid) return 1; svid_idx++; - } while (res.svid); + } while (resp2.svid); } return 0; diff --git a/src/ec/google/chromeec/ec.h b/src/ec/google/chromeec/ec.h index 25c77751cc..9fb9c391cc 100644 --- a/src/ec/google/chromeec/ec.h +++ b/src/ec/google/chromeec/ec.h @@ -35,6 +35,7 @@ uint8_t google_chromeec_get_event(void); /* Check if EC supports feature EC_FEATURE_UNIFIED_WAKE_MASKS */ bool google_chromeec_is_uhepi_supported(void); int google_ec_running_ro(void); +enum ec_current_image google_chromeec_get_current_image(void); void google_chromeec_init(void); int google_chromeec_pd_get_amode(uint16_t svid); 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, 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 * @@ -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. */ 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 */ -- cgit v1.2.3