diff options
Diffstat (limited to 'src/soc/intel')
-rw-r--r-- | src/soc/intel/common/block/cse/cse_lite.c | 78 | ||||
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/cse.h | 24 |
2 files changed, 102 insertions, 0 deletions
diff --git a/src/soc/intel/common/block/cse/cse_lite.c b/src/soc/intel/common/block/cse/cse_lite.c index 9ea6c78225..d0b287368b 100644 --- a/src/soc/intel/common/block/cse/cse_lite.c +++ b/src/soc/intel/common/block/cse/cse_lite.c @@ -995,6 +995,10 @@ static enum cb_err cse_prep_for_rw_update(enum cse_update_status status) return CB_ERR; if ((status == CSE_UPDATE_DOWNGRADE) || (status == CSE_UPDATE_CORRUPTED)) { + /* Reset the PSR backup command status in CMOS */ + if (CONFIG(SOC_INTEL_CSE_LITE_PSR)) + update_psr_backup_status(PSR_BACKUP_PENDING); + if (cse_data_clear_request() != CB_SUCCESS) { printk(BIOS_ERR, "cse_lite: CSE data clear failed!\n"); return CB_ERR; @@ -1055,6 +1059,78 @@ error_exit: return rv; } +static bool is_psr_data_backed_up(void) +{ + /* Track PSR backup status in CMOS */ + return (get_psr_backup_status() == PSR_BACKUP_DONE); +} + +/* + * PSR data needs to be backed up prior to downgrade. So switch the CSE boot mode to RW, send + * PSR back-up command to CSE and update the PSR back-up state in CMOS. + */ +static void backup_psr_data(void) +{ + printk(BIOS_DEBUG, "cse_lite: Initiate PSR data backup flow\n"); + /* Switch CSE to RW to send PSR_HECI_FW_DOWNGRADE_BACKUP command */ + if (cse_boot_to_rw() != CB_SUCCESS) + goto update_and_exit; + + /* + * Prerequisites: + * 1) HFSTS1 Current Working State is Normal + * 2) HFSTS1 Current Operation Mode is Normal + */ + if (!cse_is_hfs1_cws_normal() || !cse_is_hfs1_com_normal()) { + printk(BIOS_DEBUG, "cse_lite: PSR_HECI_FW_DOWNGRADE_BACKUP command " + "prerequisites not met!\n"); + goto update_and_exit; + } + + /* Send PSR_HECI_FW_DOWNGRADE_BACKUP command */ + struct psr_heci_fw_downgrade_backup_req { + struct psr_heci_header header; + } __packed; + + struct psr_heci_fw_downgrade_backup_req req = { + .header.command = PSR_HECI_FW_DOWNGRADE_BACKUP, + }; + + struct psr_heci_fw_downgrade_backup_res { + struct psr_heci_header header; + uint32_t status; + } __packed; + + struct psr_heci_fw_downgrade_backup_res backup_psr_resp; + size_t resp_size = sizeof(backup_psr_resp); + + printk(BIOS_DEBUG, "cse_lite: Send PSR_HECI_FW_DOWNGRADE_BACKUP command\n"); + if (heci_send_receive(&req, sizeof(req), + &backup_psr_resp, &resp_size, HECI_PSR_ADDR)) + printk(BIOS_ERR, "cse_lite: could not backup PSR data\n"); + else + if (backup_psr_resp.status != PSR_STATUS_SUCCESS) + printk(BIOS_ERR, "cse_lite: PSR_HECI_FW_DOWNGRADE_BACKUP command " + "returned %u\n", backup_psr_resp.status); + +update_and_exit: + /* + * An attempt to send PSR back-up command has been made. Update this info in CMOS and + * send success once backup_psr_data() has been called. We do not want to put the system + * into recovery for PSR data backup command pre-requisites not being met. + */ + update_psr_backup_status(PSR_BACKUP_DONE); + return; +} + +static void initiate_psr_data_backup(void) +{ + if (is_psr_data_backed_up()) + return; + + backup_psr_data(); +} + static uint8_t cse_fw_update(void) { struct region_device target_rdev; @@ -1070,6 +1146,8 @@ static uint8_t cse_fw_update(void) return CSE_NO_ERROR; if (status == CSE_UPDATE_METADATA_ERROR) return CSE_LITE_SKU_RW_METADATA_NOT_FOUND; + if (CONFIG(SOC_INTEL_CSE_LITE_PSR) && status == CSE_UPDATE_DOWNGRADE) + initiate_psr_data_backup(); printk(BIOS_DEBUG, "cse_lite: CSE RW update is initiated\n"); return cse_trigger_fw_update(status, &target_rdev); diff --git a/src/soc/intel/common/block/include/intelblocks/cse.h b/src/soc/intel/common/block/include/intelblocks/cse.h index ebf20ed857..b177e1364f 100644 --- a/src/soc/intel/common/block/include/intelblocks/cse.h +++ b/src/soc/intel/common/block/include/intelblocks/cse.h @@ -80,6 +80,9 @@ enum me_fw_sku { /* Number of cse boot performance data */ #define NUM_CSE_BOOT_PERF_DATA 64 +/* PSR_HECI_FW_DOWNGRADE_BACKUP Command */ +#define PSR_HECI_FW_DOWNGRADE_BACKUP 0x3 + /* HFSTS register offsets in PCI config space */ enum { PCI_ME_HFSTS1 = 0x40, @@ -105,6 +108,24 @@ struct mkhi_hdr { uint8_t result; } __packed; +/* PSR HECI message status */ +enum psr_status { + PSR_STATUS_SUCCESS, + PSR_STATUS_FEATURE_NOT_SUPPORTED, + PSR_STATUS_UPID_DISABLED, + PSR_STATUS_ACTION_NOT_ALLOWED, + PSR_STATUS_INVALID_INPUT_PARAMETER, + PSR_STATUS_INTERNAL_ERROR, + PSR_STATUS_NOT_ALLOWED_AFTER_EOP, +}; + +/* PSR HECI message header */ +struct psr_heci_header { + uint8_t command; + uint8_t reserved; + uint16_t length; +} __packed; + /* CSE FW Version */ struct fw_version { uint16_t major; @@ -398,6 +419,9 @@ int cse_hmrfpo_get_status(void); /* Fixed Address MEI Header's ME Address field value */ #define HECI_MKHI_ADDR 0x07 +/* Fixed Address MEI Header's ME Address field value for PSR messages */ +#define HECI_PSR_ADDR 0x04 + /* Fixed Address MEI Header's ME Address for MEI bus messages */ #define HECI_MEI_ADDR 0x00 |