| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| /* |
| * Firmware code update for FSP systems |
| * |
| * Copyright 2013-2018 IBM Corp. |
| */ |
| |
| #include <skiboot.h> |
| #include <fsp.h> |
| #include <fsp-sysparam.h> |
| #include <lock.h> |
| #include <device.h> |
| #include <ccan/endian/endian.h> |
| #include <errorlog.h> |
| #include <opal-api.h> |
| #include <timebase.h> |
| |
| #include "fsp-codeupdate.h" |
| |
| enum flash_state { |
| FLASH_STATE_ABSENT, |
| FLASH_STATE_INVALID, /* IPL side marker lid is invalid */ |
| FLASH_STATE_READING, |
| FLASH_STATE_READ, |
| FLASH_STATE_ABORT, |
| }; |
| |
| enum lid_fetch_side { |
| FETCH_T_SIDE_ONLY, |
| FETCH_P_SIDE_ONLY, |
| FETCH_BOTH_SIDE, |
| }; |
| |
| static enum flash_state flash_state = FLASH_STATE_INVALID; |
| static enum lid_fetch_side lid_fetch_side = FETCH_BOTH_SIDE; |
| |
| /* Image buffers */ |
| static struct opal_sg_list *image_data; |
| static uint32_t tce_start; |
| static void *lid_data; |
| static char validate_buf[VALIDATE_BUF_SIZE]; |
| |
| /* TCE buffer lock */ |
| static struct lock flash_lock = LOCK_UNLOCKED; |
| |
| /* FW VPD data */ |
| static struct fw_image_vpd fw_vpd[2]; |
| |
| /* Code update related sys parameters */ |
| static uint32_t ipl_side; |
| static uint32_t hmc_managed; |
| static uint32_t update_policy; |
| static uint32_t in_flight_params; |
| |
| /* If non-NULL, this gets called just before rebooting */ |
| int (*fsp_flash_term_hook)(void); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_CU_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, |
| OPAL_PLATFORM_FIRMWARE, |
| OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_CU_FLASH, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, |
| OPAL_PLATFORM_FIRMWARE, |
| OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_CU_SG_LIST, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, |
| OPAL_PLATFORM_FIRMWARE, |
| OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_CU_COMMIT, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, |
| OPAL_PLATFORM_FIRMWARE, |
| OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_CU_MSG, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, |
| OPAL_PLATFORM_FIRMWARE, |
| OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_CU_NOTIFY, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, |
| OPAL_PLATFORM_FIRMWARE, |
| OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_CU_MARKER_LID, OPAL_PLATFORM_ERR_EVT, OPAL_CODEUPDATE, |
| OPAL_PLATFORM_FIRMWARE, |
| OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, OPAL_NA); |
| |
| static inline void code_update_tce_map(uint32_t tce_offset, |
| void *buffer, uint32_t size) |
| { |
| uint32_t tlen = ALIGN_UP(size, TCE_PSIZE); |
| |
| fsp_tce_map(PSI_DMA_CODE_UPD + tce_offset, buffer, tlen); |
| } |
| |
| static inline void code_update_tce_unmap(uint32_t size) |
| { |
| fsp_tce_unmap(PSI_DMA_CODE_UPD, size); |
| } |
| |
| static inline void set_def_fw_version(uint32_t side) |
| { |
| strncpy(fw_vpd[side].mi_keyword, FW_VERSION_UNKNOWN, MI_KEYWORD_SIZE); |
| strncpy(fw_vpd[side].ext_fw_id, FW_VERSION_UNKNOWN, ML_KEYWORD_SIZE); |
| } |
| |
| /* |
| * Get IPL side |
| */ |
| static void get_ipl_side(void) |
| { |
| struct dt_node *iplp; |
| const char *side = NULL; |
| |
| iplp = dt_find_by_path(dt_root, "ipl-params/ipl-params"); |
| if (iplp) |
| side = dt_prop_get_def(iplp, "cec-ipl-side", NULL); |
| prlog(PR_NOTICE, "CUPD: IPL SIDE = %s\n", side); |
| |
| if (!side || !strcmp(side, "temp")) |
| ipl_side = FW_IPL_SIDE_TEMP; |
| else |
| ipl_side = FW_IPL_SIDE_PERM; |
| } |
| |
| |
| /* |
| * Helper routines to retrieve code update related |
| * system parameters from FSP. |
| */ |
| |
| static void inc_in_flight_param(void) |
| { |
| lock(&flash_lock); |
| in_flight_params++; |
| unlock(&flash_lock); |
| } |
| |
| static void dec_in_flight_param(void) |
| { |
| lock(&flash_lock); |
| assert(in_flight_params > 0); |
| in_flight_params--; |
| unlock(&flash_lock); |
| } |
| |
| static void got_code_update_policy(uint32_t param_id __unused, int err_len, |
| void *data __unused) |
| { |
| if (err_len != 4) { |
| log_simple_error(&e_info(OPAL_RC_CU_INIT), "CUPD: Error " |
| "retrieving code update policy: %d\n", err_len); |
| } else { |
| update_policy = be32_to_cpu((__be32)update_policy); |
| prlog(PR_NOTICE, "CUPD: Code update policy from FSP: %d\n", |
| update_policy); |
| } |
| |
| dec_in_flight_param(); |
| } |
| |
| static void get_code_update_policy(void) |
| { |
| int rc; |
| |
| inc_in_flight_param(); |
| rc = fsp_get_sys_param(SYS_PARAM_FLASH_POLICY, &update_policy, 4, |
| got_code_update_policy, NULL); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_CU_INIT), |
| "CUPD: Error %d queueing param request\n", rc); |
| dec_in_flight_param(); |
| } |
| } |
| |
| static void got_platform_hmc_managed(uint32_t param_id __unused, int err_len, |
| void *data __unused) |
| { |
| if (err_len != 4) { |
| log_simple_error(&e_info(OPAL_RC_CU_INIT), "CUPD: Error " |
| "retrieving hmc managed status: %d\n", err_len); |
| } else { |
| hmc_managed = be32_to_cpu((__be32)hmc_managed); |
| prlog(PR_NOTICE, "CUPD: HMC managed status from FSP: %d\n", |
| hmc_managed); |
| } |
| |
| dec_in_flight_param(); |
| } |
| |
| static void get_platform_hmc_managed(void) |
| { |
| int rc; |
| |
| inc_in_flight_param(); |
| rc = fsp_get_sys_param(SYS_PARAM_HMC_MANAGED, &hmc_managed, 4, |
| got_platform_hmc_managed, NULL); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_CU_INIT), |
| "CUPD: Error %d queueing param request\n", rc); |
| dec_in_flight_param(); |
| } |
| } |
| |
| static bool fw_ipl_side_update_notify(struct fsp_msg *msg) |
| { |
| u32 param_id = fsp_msg_get_data_word(msg, 0); |
| int dlen = fsp_msg_get_data_word(msg, 1) & 0xffff; |
| uint32_t state = fsp_msg_get_data_word(msg, 2); |
| |
| if (param_id != SYS_PARAM_FW_IPL_SIDE) |
| return false; |
| |
| if (dlen != 4) { |
| prlog(PR_DEBUG, |
| "CUPD: Invalid sysparams notify len : 0x%x\n", dlen); |
| return false; |
| } |
| |
| prlog(PR_NOTICE, "CUPD: FW IPL side changed. Disable fast reboot\n"); |
| prlog(PR_NOTICE, "CUPD: Next IPL side : %s\n", |
| state == FW_IPL_SIDE_TEMP ? "temp" : "perm"); |
| |
| disable_fast_reboot("FSP IPL Side Change"); |
| return true; |
| } |
| |
| static int64_t code_update_check_state(void) |
| { |
| switch(flash_state) { |
| case FLASH_STATE_ABSENT: |
| return OPAL_HARDWARE; |
| case FLASH_STATE_INVALID: |
| case FLASH_STATE_ABORT: |
| return OPAL_INTERNAL_ERROR; |
| case FLASH_STATE_READING: |
| return OPAL_BUSY; |
| default: |
| break; |
| } |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * Get common marker LID additional data section |
| */ |
| static void *get_adf_sec_data(struct com_marker_adf_sec *adf_sec, |
| uint32_t name) |
| { |
| struct com_marker_adf_header *adf_header; |
| int i; |
| |
| adf_header = (void *)adf_sec->adf_data; |
| for (i = 0; i < be32_to_cpu(adf_sec->adf_cnt); i++) { |
| if (be32_to_cpu(adf_header->name) == name) |
| return adf_header; |
| |
| adf_header = (void *)adf_header + be32_to_cpu(adf_header->size); |
| } |
| return NULL; |
| } |
| |
| /* |
| * Parse common marker LID to get FW version details |
| * |
| * Note: |
| * At present, we are parsing "Service Pack Nomenclature ADF" |
| * section only. If we are adding FW IP support, then we have |
| * to parse "Firmware IP Protection ADF" as well. |
| */ |
| static void parse_marker_lid(uint32_t side) |
| { |
| struct com_marker_header *header; |
| struct com_marker_mi_section *mi_sec; |
| struct com_marker_adf_sec *adf_sec; |
| struct com_marker_adf_sp *adf_sp; |
| |
| header = (void *)lid_data; |
| |
| /* Get MI details */ |
| mi_sec = (void *)header + be32_to_cpu(header->MI_offset); |
| /* |
| * If Marker LID is invalid, then FSP will return a Marker |
| * LID with ASCII zeros for the entire MI keyword. |
| */ |
| if (mi_sec->mi_keyword[0] == '0') |
| return; |
| |
| strncpy(fw_vpd[side].mi_keyword, mi_sec->mi_keyword, MI_KEYWORD_SIZE); |
| fw_vpd[side].mi_keyword[MI_KEYWORD_SIZE - 1] = '\0'; |
| prlog(PR_NOTICE, "CUPD: %s side MI Keyword = %s\n", |
| side == 0x00 ? "P" : "T", fw_vpd[side].mi_keyword); |
| |
| /* Get ML details */ |
| adf_sec = (void *)header + be32_to_cpu(mi_sec->adf_offset); |
| adf_sp = get_adf_sec_data(adf_sec, ADF_NAME_SP); |
| if (!adf_sp) |
| return; |
| |
| strncpy(fw_vpd[side].ext_fw_id, |
| (void *)adf_sp + be32_to_cpu(adf_sp->sp_name_offset), |
| ML_KEYWORD_SIZE); |
| fw_vpd[side].ext_fw_id[ML_KEYWORD_SIZE - 1] = '\0'; |
| prlog(PR_NOTICE, "CUPD: %s side ML Keyword = %s\n", |
| side == 0x00 ? "P" : "T", fw_vpd[side].ext_fw_id); |
| } |
| |
| static void validate_com_marker_lid(void) |
| { |
| if (!strncmp(fw_vpd[ipl_side].mi_keyword, FW_VERSION_UNKNOWN, |
| sizeof(FW_VERSION_UNKNOWN))) { |
| log_simple_error(&e_info(OPAL_RC_CU_MARKER_LID), |
| "CUPD: IPL side Marker LID is not valid\n"); |
| flash_state = FLASH_STATE_INVALID; |
| return; |
| } |
| |
| flash_state = FLASH_STATE_READ; |
| } |
| |
| static void fetch_lid_data_complete(struct fsp_msg *msg) |
| { |
| void *buffer; |
| size_t length, chunk; |
| uint32_t lid_id, offset; |
| uint16_t id; |
| uint8_t flags, status; |
| int rc; |
| |
| status = (msg->resp->word1 >> 8) & 0xff; |
| flags = (fsp_msg_get_data_word(msg, 0) >> 16) & 0xff; |
| id = fsp_msg_get_data_word(msg, 0) & 0xffff; |
| lid_id = fsp_msg_get_data_word(msg, 1); |
| offset = fsp_msg_get_data_word(msg->resp, 1); |
| length = fsp_msg_get_data_word(msg->resp, 2); |
| |
| prlog(PR_NOTICE, "CUPD: Marker LID id : size : status = " |
| "0x%x : 0x%x : 0x%x\n", |
| fsp_msg_get_data_word(msg, 1), fsp_msg_get_data_word(msg->resp, 2), status); |
| |
| fsp_freemsg(msg); |
| |
| switch (status) { |
| case FSP_STATUS_SUCCESS: /* Read complete, parse VPD */ |
| parse_marker_lid(lid_id == P_COM_MARKER_LID_ID ? 0 : 1); |
| break; |
| case FSP_STATUS_MORE_DATA: /* More data left */ |
| offset += length; |
| chunk = MARKER_LID_SIZE - offset; |
| if (chunk > 0) { |
| buffer = (void *)PSI_DMA_CODE_UPD + offset; |
| rc = fsp_fetch_data_queue(flags, id, lid_id, |
| offset, buffer, &chunk, |
| fetch_lid_data_complete); |
| |
| /* If queue msg fails, then continue with marker LID |
| * validation hoping that we have at least boot side |
| * information. |
| */ |
| if (rc == OPAL_SUCCESS) |
| return; |
| } |
| break; |
| default: /* Fetch LID call failed */ |
| break; |
| } |
| |
| /* If required, fetch T side marker LID */ |
| if (lid_id == P_COM_MARKER_LID_ID && |
| lid_fetch_side == FETCH_BOTH_SIDE) { |
| length = MARKER_LID_SIZE; |
| rc = fsp_fetch_data_queue(flags, id, T_COM_MARKER_LID_ID, |
| 0, (void *)PSI_DMA_CODE_UPD, |
| &length, fetch_lid_data_complete); |
| |
| /* If queue msg fails, then continue with marker LID |
| * validation hoping that we have at least boot side |
| * information. |
| */ |
| if (rc == OPAL_SUCCESS) |
| return; |
| } |
| |
| lock(&flash_lock); |
| |
| /* Validate marker LID data */ |
| validate_com_marker_lid(); |
| /* TCE unmap */ |
| code_update_tce_unmap(MARKER_LID_SIZE); |
| |
| unlock(&flash_lock); |
| } |
| |
| static void fetch_com_marker_lid(void) |
| { |
| size_t length = MARKER_LID_SIZE; |
| uint32_t lid_id; |
| int rc; |
| |
| /* Read in progress? */ |
| rc = code_update_check_state(); |
| if (rc == OPAL_HARDWARE || rc == OPAL_BUSY) |
| return; |
| |
| if (lid_fetch_side == FETCH_T_SIDE_ONLY) { |
| lid_id = T_COM_MARKER_LID_ID; |
| set_def_fw_version(FW_IPL_SIDE_TEMP); |
| } else if (lid_fetch_side == FETCH_P_SIDE_ONLY) { |
| lid_id = P_COM_MARKER_LID_ID; |
| set_def_fw_version(FW_IPL_SIDE_PERM); |
| } else { |
| lid_id = P_COM_MARKER_LID_ID; |
| set_def_fw_version(FW_IPL_SIDE_PERM); |
| set_def_fw_version(FW_IPL_SIDE_TEMP); |
| } |
| |
| code_update_tce_map(0, lid_data, length); |
| rc = fsp_fetch_data_queue(0x00, 0x05, lid_id, 0, |
| (void *)PSI_DMA_CODE_UPD, &length, |
| fetch_lid_data_complete); |
| if (!rc) |
| flash_state = FLASH_STATE_READING; |
| else |
| flash_state = FLASH_STATE_INVALID; |
| } |
| |
| /* |
| * Add MI and ML keyword details into DT |
| */ |
| #define FW_VER_SIZE 64 |
| static void add_opal_firmware_version(void) |
| { |
| struct dt_node *dt_fw; |
| char buffer[FW_VER_SIZE]; |
| int offset; |
| |
| dt_fw = dt_find_by_path(dt_root, "ibm,opal/firmware"); |
| if (!dt_fw) |
| return; |
| |
| /* MI version */ |
| offset = snprintf(buffer, FW_VER_SIZE, "MI %s %s", |
| fw_vpd[FW_IPL_SIDE_TEMP].mi_keyword, |
| fw_vpd[FW_IPL_SIDE_PERM].mi_keyword); |
| if (ipl_side == FW_IPL_SIDE_TEMP) |
| snprintf(buffer + offset, FW_VER_SIZE - offset, |
| " %s", fw_vpd[FW_IPL_SIDE_TEMP].mi_keyword); |
| else |
| snprintf(buffer + offset, FW_VER_SIZE - offset, |
| " %s", fw_vpd[FW_IPL_SIDE_PERM].mi_keyword); |
| |
| dt_add_property(dt_fw, "mi-version", buffer, strlen(buffer)); |
| |
| /* ML version */ |
| offset = snprintf(buffer, FW_VER_SIZE, "ML %s %s", |
| fw_vpd[FW_IPL_SIDE_TEMP].ext_fw_id, |
| fw_vpd[FW_IPL_SIDE_PERM].ext_fw_id); |
| if (ipl_side == FW_IPL_SIDE_TEMP) |
| snprintf(buffer + offset, FW_VER_SIZE - offset, |
| " %s", fw_vpd[FW_IPL_SIDE_TEMP].ext_fw_id); |
| else |
| snprintf(buffer + offset, FW_VER_SIZE - offset, |
| " %s", fw_vpd[FW_IPL_SIDE_PERM].ext_fw_id); |
| |
| dt_add_property(dt_fw, "ml-version", buffer, strlen(buffer)); |
| } |
| |
| /* |
| * This is called right before starting the payload (Linux) to |
| * ensure the common marker LID read and parsing has happened |
| * before we transfer control. |
| */ |
| void fsp_code_update_wait_vpd(bool is_boot) |
| { |
| int waited = 0; |
| |
| if (!fsp_present()) |
| return; |
| |
| prlog(PR_NOTICE, "CUPD: Waiting read marker LID" |
| " and in flight parsm completion...\n"); |
| |
| lock(&flash_lock); |
| while(true) { |
| if (!(flash_state == FLASH_STATE_READING || in_flight_params)) |
| break; |
| unlock(&flash_lock); |
| time_wait_ms(5); |
| waited+=5; |
| lock(&flash_lock); |
| } |
| unlock(&flash_lock); |
| |
| if (waited) |
| prlog(PR_DEBUG, "CUPD: fsp_code_update_wait_vpd %d\n", waited); |
| |
| if (is_boot) |
| add_opal_firmware_version(); |
| } |
| |
| static int code_update_start(void) |
| { |
| struct fsp_msg *msg; |
| int rc; |
| uint16_t comp = 0x00; /* All components */ |
| uint8_t side = OPAL_COMMIT_TMP_SIDE; /* Temporary side */ |
| |
| msg = fsp_mkmsg(FSP_CMD_FLASH_START, 1, side << 16 | comp); |
| if (!msg) { |
| log_simple_error(&e_info(OPAL_RC_CU_MSG), |
| "CUPD: CMD_FLASH_START message allocation failed !\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| if (fsp_sync_msg(msg, false)) { |
| fsp_freemsg(msg); |
| return OPAL_INTERNAL_ERROR; |
| } |
| rc = (msg->resp->word1 >> 8) & 0xff; |
| fsp_freemsg(msg); |
| return rc; |
| } |
| |
| static int code_update_write_lid(uint32_t lid_id, uint32_t size) |
| { |
| struct fsp_msg *msg; |
| int rc, n_pairs = 1; |
| |
| msg = fsp_mkmsg(FSP_CMD_FLASH_WRITE, 5, lid_id, |
| n_pairs, 0, tce_start, size); |
| if (!msg) { |
| log_simple_error(&e_info(OPAL_RC_CU_MSG), |
| "CUPD: CMD_FLASH_WRITE message allocation failed !\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| if (fsp_sync_msg(msg, false)) { |
| fsp_freemsg(msg); |
| return OPAL_INTERNAL_ERROR; |
| } |
| rc = (msg->resp->word1 >> 8) & 0xff; |
| fsp_freemsg(msg); |
| return rc; |
| } |
| |
| static int code_update_del_lid(uint32_t lid_id) |
| { |
| struct fsp_msg *msg; |
| int rc; |
| |
| msg = fsp_mkmsg(FSP_CMD_FLASH_DEL, 1, lid_id); |
| if (!msg) { |
| log_simple_error(&e_info(OPAL_RC_CU_MSG), |
| "CUPD: CMD_FLASH_DEL message allocation failed !\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| if (fsp_sync_msg(msg, false)) { |
| fsp_freemsg(msg); |
| return OPAL_INTERNAL_ERROR; |
| } |
| rc = (msg->resp->word1 >> 8) & 0xff; |
| fsp_freemsg(msg); |
| return rc; |
| } |
| |
| static int code_update_complete(uint32_t cmd) |
| { |
| struct fsp_msg *msg; |
| int rc; |
| |
| msg = fsp_mkmsg(cmd, 0); |
| if (!msg) { |
| log_simple_error(&e_info(OPAL_RC_CU_MSG), |
| "CUPD: CUPD COMPLETE message allocation failed !\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| if (fsp_sync_msg(msg, false)) { |
| fsp_freemsg(msg); |
| return OPAL_INTERNAL_ERROR; |
| } |
| rc = (msg->resp->word1 >> 8) & 0xff; |
| fsp_freemsg(msg); |
| return rc; |
| } |
| |
| static int code_update_swap_side(void) |
| { |
| struct fsp_msg *msg; |
| int rc; |
| |
| msg = fsp_mkmsg(FSP_CMD_FLASH_SWAP, 0); |
| if (!msg) { |
| log_simple_error(&e_info(OPAL_RC_CU_MSG), |
| "CUPD: CMD_FLASH_SWAP message allocation failed !\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| |
| if (fsp_sync_msg(msg, false)) { |
| fsp_freemsg(msg); |
| return OPAL_INTERNAL_ERROR; |
| } |
| rc = (msg->resp->word1 >> 8) & 0xff; |
| fsp_freemsg(msg); |
| return rc; |
| } |
| |
| static int code_update_set_ipl_side(void) |
| { |
| struct fsp_msg *msg; |
| uint8_t side = FW_IPL_SIDE_TEMP; /* Next IPL side */ |
| int rc; |
| |
| msg = fsp_mkmsg(FSP_CMD_SET_IPL_SIDE, 1, side << 16); |
| if (!msg) { |
| log_simple_error(&e_info(OPAL_RC_CU_MSG), |
| "CUPD: CMD_SET_IPL_SIDE message allocation failed!\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| if (fsp_sync_msg(msg, false)) { |
| fsp_freemsg(msg); |
| log_simple_error(&e_info(OPAL_RC_CU_MSG), |
| "CUPD: Setting next IPL side failed!\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| rc = (msg->resp->word1 >> 8) & 0xff; |
| fsp_freemsg(msg); |
| return rc; |
| } |
| |
| static void code_update_commit_complete(struct fsp_msg *msg) |
| { |
| int rc; |
| uint8_t type; |
| |
| rc = (msg->resp->word1 >> 8) & 0xff; |
| type = (msg->word1 >> 8) & 0xff; |
| fsp_freemsg(msg); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_CU_COMMIT), |
| "CUPD: Code update commit failed, err 0x%x\n", rc); |
| return; |
| } |
| |
| /* Reset cached VPD data */ |
| lock(&flash_lock); |
| |
| /* Find commit type */ |
| if (type == 0x01) { |
| lid_fetch_side = FETCH_P_SIDE_ONLY; |
| } else if (type == 0x02) |
| lid_fetch_side = FETCH_T_SIDE_ONLY; |
| else |
| lid_fetch_side = FETCH_BOTH_SIDE; |
| |
| fetch_com_marker_lid(); |
| |
| unlock(&flash_lock); |
| } |
| |
| static int code_update_commit(uint32_t cmd) |
| { |
| struct fsp_msg *msg; |
| |
| msg = fsp_mkmsg(cmd, 0); |
| if (!msg) { |
| log_simple_error(&e_info(OPAL_RC_CU_MSG), |
| "CUPD: COMMIT message allocation failed !\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| if (fsp_queue_msg(msg, code_update_commit_complete)) { |
| log_simple_error(&e_info(OPAL_RC_CU_COMMIT), |
| "CUPD: Failed to queue code update commit message\n"); |
| fsp_freemsg(msg); |
| return OPAL_INTERNAL_ERROR; |
| } |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * Inband code update is allowed? |
| */ |
| static int64_t validate_inband_policy(void) |
| { |
| /* Quirk: |
| * If the code update policy is out-of-band, but the system |
| * is not HMC-managed, then inband update is allowed. |
| */ |
| if (hmc_managed != PLATFORM_HMC_MANAGED) |
| return 0; |
| if (update_policy == INBAND_UPDATE_ALLOWED) |
| return 0; |
| |
| return -1; |
| } |
| |
| /* |
| * Validate magic Number |
| */ |
| static int64_t validate_magic_num(uint16_t magic) |
| { |
| if (magic != IMAGE_MAGIC_NUMBER) |
| return -1; |
| return 0; |
| } |
| |
| /* |
| * Compare MI keyword to make sure candidate image |
| * is valid for this platform. |
| */ |
| static int64_t validate_image_version(struct update_image_header *header, |
| uint32_t *result) |
| { |
| struct fw_image_vpd vpd; |
| int t_valid = 0, p_valid = 0, cton_ver = -1, ptot_ver = -1; |
| |
| /* Valid flash image level? */ |
| if (strncmp(fw_vpd[0].mi_keyword, FW_VERSION_UNKNOWN, |
| sizeof(FW_VERSION_UNKNOWN)) != 0) |
| p_valid = 1; |
| |
| if (strncmp(fw_vpd[1].mi_keyword, FW_VERSION_UNKNOWN, |
| sizeof(FW_VERSION_UNKNOWN)) != 0) |
| t_valid = 1; |
| |
| /* Validate with IPL side image */ |
| vpd = fw_vpd[ipl_side]; |
| |
| /* Validate platform identifier (first two char of MI keyword) */ |
| if (strncmp(vpd.mi_keyword, header->mi_keyword_data, 2) != 0) { |
| *result = VALIDATE_INVALID_IMG; |
| return OPAL_SUCCESS; |
| } |
| |
| /* Don't flash different FW series (like P7 image on P8) */ |
| if (vpd.mi_keyword[2] != header->mi_keyword_data[2]) { |
| *result = VALIDATE_INVALID_IMG; |
| return OPAL_SUCCESS; |
| } |
| |
| /* Get current to new version difference */ |
| cton_ver = strncmp(vpd.mi_keyword + 3, header->mi_keyword_data + 3, 6); |
| |
| /* Get P to T version difference */ |
| if (t_valid && p_valid) |
| ptot_ver = strncmp(fw_vpd[0].mi_keyword + 3, |
| fw_vpd[1].mi_keyword + 3, 6); |
| |
| /* Update validation result */ |
| if (ipl_side == FW_IPL_SIDE_TEMP) { |
| if (!ptot_ver && cton_ver > 0) /* downgrade T side */ |
| *result = VALIDATE_TMP_UPDATE_DL; |
| else if (!ptot_ver && cton_ver <= 0) /* upgrade T side */ |
| *result = VALIDATE_TMP_UPDATE; |
| else if (cton_ver > 0) /* Implied commit & downgrade T side */ |
| *result = VALIDATE_TMP_COMMIT_DL; |
| else /* Implied commit & upgrade T side */ |
| *result = VALIDATE_TMP_COMMIT; |
| } else { |
| if (!t_valid) /* Current unknown */ |
| *result = VALIDATE_CUR_UNKNOWN; |
| else if (cton_ver > 0) /* downgrade FW version */ |
| *result = VALIDATE_TMP_UPDATE_DL; |
| else /* upgrade FW version */ |
| *result = VALIDATE_TMP_UPDATE; |
| } |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * Validate candidate image |
| */ |
| static int validate_candidate_image(uint64_t buffer, |
| uint32_t size, uint32_t *result) |
| { |
| struct update_image_header *header; |
| int rc = OPAL_PARAMETER; |
| |
| if (size < VALIDATE_BUF_SIZE) |
| goto out; |
| |
| rc = code_update_check_state(); |
| if (rc != OPAL_SUCCESS) |
| goto out; |
| |
| if (validate_inband_policy() != 0) { |
| *result = VALIDATE_FLASH_AUTH; |
| rc = OPAL_SUCCESS; |
| goto out; |
| } |
| |
| memcpy(validate_buf, (void *)buffer, VALIDATE_BUF_SIZE); |
| header = (struct update_image_header *)validate_buf; |
| |
| if (validate_magic_num(be16_to_cpu(header->magic)) != 0) { |
| *result = VALIDATE_INVALID_IMG; |
| rc = OPAL_SUCCESS; |
| goto out; |
| } |
| rc = validate_image_version(header, result); |
| out: |
| return rc; |
| } |
| |
| static int validate_out_buf_mi_data(void *buffer, int offset, uint32_t result) |
| { |
| struct update_image_header *header = (void *)validate_buf; |
| |
| /* Current T & P side MI data */ |
| offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, |
| "MI %s %s\n", |
| fw_vpd[1].mi_keyword, fw_vpd[0].mi_keyword); |
| |
| /* New T & P side MI data */ |
| offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, |
| "MI %s", header->mi_keyword_data); |
| if (result == VALIDATE_TMP_COMMIT_DL || |
| result == VALIDATE_TMP_COMMIT) |
| offset += snprintf(buffer + offset, |
| VALIDATE_BUF_SIZE - offset, |
| " %s\n", fw_vpd[1].mi_keyword); |
| else |
| offset += snprintf(buffer + offset, |
| VALIDATE_BUF_SIZE - offset, |
| " %s\n", fw_vpd[0].mi_keyword); |
| return offset; |
| } |
| |
| static int validate_out_buf_ml_data(void *buffer, int offset, uint32_t result) |
| { |
| struct update_image_header *header = (void *)validate_buf; |
| /* Candidate image ML data */ |
| char *ext_fw_id = (void *)header->data; |
| |
| /* Current T & P side ML data */ |
| offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, |
| "ML %s %s\n", |
| fw_vpd[1].ext_fw_id, fw_vpd[0].ext_fw_id); |
| |
| /* New T & P side ML data */ |
| offset += snprintf(buffer + offset, VALIDATE_BUF_SIZE - offset, |
| "ML %s", ext_fw_id); |
| if (result == VALIDATE_TMP_COMMIT_DL || |
| result == VALIDATE_TMP_COMMIT) |
| offset += snprintf(buffer + offset, |
| VALIDATE_BUF_SIZE - offset, |
| " %s\n", fw_vpd[1].ext_fw_id); |
| else |
| offset += snprintf(buffer + offset, |
| VALIDATE_BUF_SIZE - offset, |
| " %s\n", fw_vpd[0].ext_fw_id); |
| |
| return offset; |
| } |
| |
| /* |
| * Copy LID data to TCE buffer |
| */ |
| static int get_lid_data(struct opal_sg_list *list, |
| int lid_size, int lid_offset) |
| { |
| struct opal_sg_list *sg; |
| struct opal_sg_entry *entry; |
| int length, num_entries, i, buf_pos = 0; |
| int map_act, map_size; |
| bool last = false; |
| |
| /* Reset TCE start address */ |
| tce_start = 0; |
| |
| for (sg = list; sg; sg = (struct opal_sg_list*)be64_to_cpu(sg->next)) { |
| length = (be64_to_cpu(sg->length) & ~(SG_LIST_VERSION << 56)) - 16; |
| num_entries = length / sizeof(struct opal_sg_entry); |
| if (num_entries <= 0) |
| return -1; |
| |
| for (i = 0; i < num_entries; i++) { |
| entry = &sg->entry[i]; |
| |
| /* |
| * Continue until we get data block which |
| * contains LID data |
| */ |
| if (lid_offset > be64_to_cpu(entry->length)) { |
| lid_offset -= be64_to_cpu(entry->length); |
| continue; |
| } |
| |
| /* |
| * SG list entry size can be more than 4k. |
| * Map only required pages, instead of |
| * mapping entire entry. |
| */ |
| map_act = be64_to_cpu(entry->length); |
| map_size = be64_to_cpu(entry->length); |
| |
| /* First TCE mapping */ |
| if (!tce_start) { |
| tce_start = PSI_DMA_CODE_UPD + |
| (lid_offset & 0xfff); |
| map_act = be64_to_cpu(entry->length) - lid_offset; |
| lid_offset &= ~0xfff; |
| map_size = be64_to_cpu(entry->length) - lid_offset; |
| } |
| |
| /* Check pending LID size to map */ |
| if (lid_size <= map_act) { |
| /* (map_size - map_act) gives page |
| * start to tce offset difference. |
| * This is required when LID size |
| * is <= 4k. |
| */ |
| map_size = (map_size - map_act) + lid_size; |
| last = true; |
| } |
| |
| /* Ajust remaining size to map */ |
| lid_size -= map_act; |
| |
| /* TCE mapping */ |
| code_update_tce_map(buf_pos, |
| (void*)(be64_to_cpu(entry->data) |
| + lid_offset), |
| map_size); |
| buf_pos += map_size; |
| /* Reset LID offset count */ |
| lid_offset = 0; |
| |
| if (last) |
| return OPAL_SUCCESS; |
| } |
| } /* outer loop */ |
| return -1; |
| } |
| |
| /* |
| * If IPL side is T, then swap P & T sides to add |
| * new fix to T side. |
| */ |
| static int validate_ipl_side(void) |
| { |
| if (ipl_side == FW_IPL_SIDE_PERM) |
| return 0; |
| return code_update_swap_side(); |
| } |
| |
| static int64_t fsp_opal_validate_flash(uint64_t buffer, |
| __be32 *size, __be32 *result) |
| { |
| int64_t rc = 0; |
| int offset; |
| uint32_t r; |
| |
| lock(&flash_lock); |
| |
| rc = validate_candidate_image(buffer, be32_to_cpu(*size), &r); |
| /* Fill output buffer |
| * |
| * Format: |
| * MI<sp>current-T-image<sp>current-P-image<0x0A> |
| * MI<sp>new-T-image<sp>new-P-image<0x0A> |
| * ML<sp>current-T-image<sp>current-P-image<0x0A> |
| * ML<sp>new-T-image<sp>new-P-image<0x0A> |
| */ |
| if (!rc && (r != VALIDATE_FLASH_AUTH && r != VALIDATE_INVALID_IMG)) { |
| /* Clear output buffer */ |
| memset((void *)buffer, 0, VALIDATE_BUF_SIZE); |
| |
| offset = validate_out_buf_mi_data((void *)buffer, 0, r); |
| offset += validate_out_buf_ml_data((void *)buffer, offset, r); |
| *size = cpu_to_be32(offset); |
| } |
| *result = cpu_to_be32(r); |
| |
| unlock(&flash_lock); |
| return rc; |
| } |
| |
| /* Commit/Reject T side image */ |
| static int64_t fsp_opal_manage_flash(uint8_t op) |
| { |
| uint32_t cmd; |
| int rc; |
| |
| lock(&flash_lock); |
| rc = code_update_check_state(); |
| unlock(&flash_lock); |
| |
| if (rc != OPAL_SUCCESS) |
| return rc; |
| |
| if (op != OPAL_REJECT_TMP_SIDE && op != OPAL_COMMIT_TMP_SIDE) |
| return OPAL_PARAMETER; |
| |
| if ((op == OPAL_COMMIT_TMP_SIDE && ipl_side == FW_IPL_SIDE_PERM) || |
| (op == OPAL_REJECT_TMP_SIDE && ipl_side == FW_IPL_SIDE_TEMP)) |
| return OPAL_ACTIVE_SIDE_ERR; |
| |
| if (op == OPAL_COMMIT_TMP_SIDE) |
| cmd = FSP_CMD_FLASH_NORMAL; |
| else |
| cmd = FSP_CMD_FLASH_REMOVE; |
| |
| return code_update_commit(cmd); |
| } |
| |
| static int fsp_flash_firmware(void) |
| { |
| struct update_image_header *header; |
| struct lid_index_entry *idx_entry; |
| struct opal_sg_list *list; |
| struct opal_sg_entry *entry; |
| int rc, i; |
| |
| /* Make sure no outstanding LID read is in progress */ |
| rc = code_update_check_state(); |
| if (rc == OPAL_BUSY) |
| fsp_code_update_wait_vpd(false); |
| |
| /* Get LID Index */ |
| list = image_data; |
| if (!list) |
| goto out; |
| entry = &list->entry[0]; |
| header = (struct update_image_header *)be64_to_cpu(entry->data); |
| idx_entry = (void *)header + be16_to_cpu(header->lid_index_offset); |
| |
| /* FIXME: |
| * At present we depend on FSP to validate CRC for |
| * individual LIDs. Calculate and validate individual |
| * LID CRC here. |
| */ |
| |
| if (validate_ipl_side() != 0) { |
| log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " |
| "Rename (Swap T and P) failed!\n"); |
| goto out; |
| } |
| |
| /* Set next IPL side */ |
| if (code_update_set_ipl_side() != 0) { |
| log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " |
| "Setting next IPL side failed!\n"); |
| goto out; |
| } |
| |
| /* Start code update process */ |
| if (code_update_start() != 0) { |
| log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " |
| "Code update start failed!\n"); |
| goto out; |
| } |
| |
| /* |
| * Delete T side LIDs before writing. |
| * |
| * Note: |
| * - Applicable for FWv >= 760. |
| * - Current Code Update design is to ignore |
| * any delete lid failure, and continue with |
| * the update. |
| */ |
| rc = code_update_del_lid(DEL_UPD_SIDE_LIDS); |
| |
| if (rc) |
| prlog(PR_TRACE, "CUPD: Failed to delete LIDs (%d). This is okay, continuing..", rc); |
| |
| for (i = 0; i < be16_to_cpu(header->number_lids); i++) { |
| if (be32_to_cpu(idx_entry->size) > LID_MAX_SIZE) { |
| log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: LID" |
| " (0x%x) size 0x%x is > max LID size (0x%x).\n", |
| be32_to_cpu(idx_entry->id), |
| be32_to_cpu(idx_entry->size), LID_MAX_SIZE); |
| goto abort_update; |
| } |
| |
| rc = get_lid_data(list, be32_to_cpu(idx_entry->size), |
| be32_to_cpu(idx_entry->offset)); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " |
| "Failed to parse LID from firmware image." |
| " (rc : %d).\n", rc); |
| goto abort_update; |
| } |
| |
| rc = code_update_write_lid(be32_to_cpu(idx_entry->id), |
| be32_to_cpu(idx_entry->size)); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " |
| "Failed to write LID to FSP. (rc : %d).\n", rc); |
| goto abort_update; |
| } |
| |
| /* Unmap TCE */ |
| code_update_tce_unmap(PSI_DMA_CODE_UPD_SIZE); |
| |
| /* Next LID index */ |
| idx_entry = (void *)idx_entry + sizeof(struct lid_index_entry); |
| } |
| |
| /* Code update completed */ |
| rc = code_update_complete(FSP_CMD_FLASH_COMPLETE); |
| |
| return rc; |
| |
| abort_update: |
| rc = code_update_complete(FSP_CMD_FLASH_ABORT); |
| if (rc) |
| log_simple_error(&e_info(OPAL_RC_CU_FLASH), "CUPD: " |
| "Code update abort command failed. (rc : %d).", rc); |
| |
| out: |
| return -1; |
| } |
| |
| static int64_t validate_sglist(struct opal_sg_list *list) |
| { |
| struct opal_sg_list *sg; |
| struct opal_sg_entry *prev_entry, *entry; |
| int length, num_entries, i; |
| |
| prev_entry = NULL; |
| for (sg = list; sg; sg = (struct opal_sg_list*)be64_to_cpu(sg->next)) { |
| length = (be64_to_cpu(sg->length) & ~(SG_LIST_VERSION << 56)) - 16; |
| num_entries = length / sizeof(struct opal_sg_entry); |
| if (num_entries <= 0) |
| return -1; |
| |
| for (i = 0; i < num_entries; i++) { |
| entry = &sg->entry[i]; |
| |
| /* All entries must be aligned */ |
| if (((uint64_t)be64_to_cpu(entry->data)) & 0xfff) |
| return OPAL_PARAMETER; |
| |
| /* All non-terminal entries size must be aligned */ |
| if (prev_entry && (be64_to_cpu(prev_entry->length) & 0xfff)) |
| return OPAL_PARAMETER; |
| |
| prev_entry = entry; |
| } |
| } |
| return OPAL_SUCCESS; |
| } |
| |
| static int64_t fsp_opal_update_flash(struct opal_sg_list *list) |
| { |
| struct opal_sg_entry *entry; |
| int length, num_entries, result = 0, rc = OPAL_PARAMETER; |
| |
| /* Ensure that the sg list honors our alignment requirements */ |
| rc = validate_sglist(list); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_CU_SG_LIST), |
| "CUPD: sglist fails alignment requirements\n"); |
| return rc; |
| } |
| |
| lock(&flash_lock); |
| if (!list) { /* Cancel update request */ |
| fsp_flash_term_hook = NULL; |
| image_data = NULL; |
| rc = OPAL_SUCCESS; |
| goto out; |
| } |
| |
| disable_fast_reboot("FSP Code Update"); |
| |
| length = (be64_to_cpu(list->length) & ~(SG_LIST_VERSION << 56)) - 16; |
| num_entries = length / sizeof(struct opal_sg_entry); |
| if (num_entries <= 0) |
| goto out; |
| |
| /* Validate image header */ |
| entry = &list->entry[0]; |
| rc = validate_candidate_image((uint64_t)be64_to_cpu(entry->data), |
| VALIDATE_BUF_SIZE, &result); |
| if (!rc && (result != VALIDATE_FLASH_AUTH && |
| result != VALIDATE_INVALID_IMG)) { |
| image_data = list; |
| fsp_flash_term_hook = fsp_flash_firmware; |
| goto out; |
| } |
| |
| /* Adjust return code */ |
| if (result == VALIDATE_FLASH_AUTH) |
| rc = OPAL_FLASH_NO_AUTH; |
| else if (result == VALIDATE_INVALID_IMG) |
| rc = OPAL_INVALID_IMAGE; |
| |
| out: |
| unlock(&flash_lock); |
| return rc; |
| } |
| |
| /* |
| * Code Update notifications |
| * |
| * Note: At present we just ACK these notifications. |
| * Reset cached VPD data if we are going to support |
| * concurrent image maint in future. |
| */ |
| static bool code_update_notify(uint32_t cmd_sub_mod, struct fsp_msg *msg) |
| { |
| int rc; |
| uint32_t cmd; |
| |
| switch(cmd_sub_mod) { |
| case FSP_CMD_FLASH_CACHE: |
| cmd = FSP_CMD_FLASH_CACHE_RSP; |
| prlog(PR_NOTICE, "CUPD: Update LID cache event [data = 0x%x]\n", |
| fsp_msg_get_data_word(msg, 0)); |
| break; |
| case FSP_CMD_FLASH_OUTC: |
| case FSP_CMD_FLASH_OUTR: |
| case FSP_CMD_FLASH_OUTS: |
| cmd = FSP_CMD_FLASH_OUT_RSP; |
| prlog(PR_NOTICE, "CUPD: Out of band commit notify " |
| "[Type = 0x%x]\n", (msg->word1 >> 8) & 0xff); |
| break; |
| default: |
| log_simple_error(&e_info(OPAL_RC_CU_NOTIFY), "CUPD: Unknown " |
| "notification [cmd = 0x%x]\n", cmd_sub_mod); |
| return false; |
| } |
| |
| rc = fsp_queue_msg(fsp_mkmsg(cmd, 0), fsp_freemsg); |
| if (rc) |
| log_simple_error(&e_info(OPAL_RC_CU_NOTIFY), "CUPD: Failed to " |
| "queue code update notification response :%d\n", rc); |
| |
| return true; |
| } |
| |
| /* |
| * Handle FSP R/R event. |
| * |
| * Note: |
| * If FSP R/R happens during code update, then entire system reboots |
| * and comes up with P side image (and T side image will be invalid). |
| * Hence we don't need to handle R/R during code update. |
| * |
| * Also if FSP R/R happens in init path (while retrieving in_flight_params) |
| * then system fails to continue booting (because we have not yet loaded |
| * all required data/LID from FSP). Hence we don't need to handle R/R |
| * for system params. |
| */ |
| static bool fsp_code_update_rr(uint32_t cmd_sub_mod, |
| struct fsp_msg *msg __unused) |
| { |
| switch (cmd_sub_mod) { |
| case FSP_RESET_START: |
| lock(&flash_lock); |
| |
| if (code_update_check_state() == OPAL_BUSY) |
| flash_state = FLASH_STATE_ABORT; |
| |
| unlock(&flash_lock); |
| return true; |
| case FSP_RELOAD_COMPLETE: |
| lock(&flash_lock); |
| |
| /* Lets try to parse marker LID again, if we failed |
| * to parse marker LID last time. |
| */ |
| if (code_update_check_state() == OPAL_INTERNAL_ERROR) |
| fetch_com_marker_lid(); |
| |
| unlock(&flash_lock); |
| return true; |
| } |
| return false; |
| } |
| |
| static struct fsp_client fsp_cupd_client_rr = { |
| .message = fsp_code_update_rr, |
| }; |
| |
| static struct fsp_client fsp_get_notify = { |
| .message = code_update_notify, |
| }; |
| |
| void fsp_code_update_init(void) |
| { |
| if (!fsp_present()) { |
| flash_state = FLASH_STATE_ABSENT; |
| return; |
| } |
| |
| /* OPAL interface */ |
| opal_register(OPAL_FLASH_VALIDATE, fsp_opal_validate_flash, 3); |
| opal_register(OPAL_FLASH_MANAGE, fsp_opal_manage_flash, 1); |
| opal_register(OPAL_FLASH_UPDATE, fsp_opal_update_flash, 1); |
| |
| /* register Code Update Class D3 */ |
| fsp_register_client(&fsp_get_notify, FSP_MCLASS_CODE_UPDATE); |
| /* Register for Class AA (FSP R/R) */ |
| fsp_register_client(&fsp_cupd_client_rr, FSP_MCLASS_RR_EVENT); |
| |
| /* Register for firmware IPL side update notification */ |
| sysparam_add_update_notifier(fw_ipl_side_update_notify); |
| |
| /* Flash hook */ |
| fsp_flash_term_hook = NULL; |
| |
| /* Fetch various code update related sys parameters */ |
| get_ipl_side(); |
| get_code_update_policy(); |
| get_platform_hmc_managed(); |
| |
| /* Fetch common marker LID */ |
| lid_data = memalign(TCE_PSIZE, MARKER_LID_SIZE); |
| if (!lid_data) { |
| log_simple_error(&e_info(OPAL_RC_CU_INIT), |
| "CUPD: Failed to allocate memory for marker LID\n"); |
| flash_state = FLASH_STATE_ABSENT; |
| return; |
| } |
| fetch_com_marker_lid(); |
| } |