diff --git a/common/service/pldm/pldm_firmware_update.c b/common/service/pldm/pldm_firmware_update.c index 29946f1a99..fa85a42680 100644 --- a/common/service/pldm/pldm_firmware_update.c +++ b/common/service/pldm/pldm_firmware_update.c @@ -86,6 +86,13 @@ __weak uint16_t plat_find_update_info_work(uint16_t comp_id) return comp_id; } +__weak uint8_t plat_pldm_pass_component_table_check(uint16_t num_of_comp, + const uint8_t *comp_image_version_str, + uint8_t comp_image_version_str_len) +{ + return PLDM_SUCCESS; +} + int get_descriptor_type_length(uint16_t type) { switch (type) { @@ -645,7 +652,7 @@ static void state_update(uint8_t state) } } -static void pldm_status_reset() +void pldm_status_reset() { state_update(STATE_IDLE); cur_aux_state = STATE_AUX_NOT_IN_UPDATE; @@ -658,7 +665,7 @@ static void pldm_status_reset() static void exit_update_mode() { - printk("PLDM update mode timeout, exiting update mode...\n"); + LOG_WRN("PLDM update mode timeout, exiting update mode..."); pldm_status_reset(); } @@ -1069,6 +1076,14 @@ static uint8_t pass_component_table(void *mctp_inst, uint8_t *buf, uint16_t len, LOG_HEXDUMP_INF(buf + sizeof(struct pldm_pass_component_table_req), req_p->comp_ver_str_len, ""); + uint8_t check_result = plat_pldm_pass_component_table_check( + req_p->comp_identifier, buf + sizeof(struct pldm_pass_component_table_req), + req_p->comp_ver_str_len); + if (check_result != PLDM_SUCCESS) { + resp_p->completion_code = check_result; + goto exit; + } + if (current_state != STATE_LEARN_COMP) { LOG_ERR("Firmware update failed because current state %d is not %d", current_state, STATE_LEARN_COMP); diff --git a/common/service/pldm/pldm_firmware_update.h b/common/service/pldm/pldm_firmware_update.h index 75c9025fb5..88c27a457a 100644 --- a/common/service/pldm/pldm_firmware_update.h +++ b/common/service/pldm/pldm_firmware_update.h @@ -726,6 +726,10 @@ uint8_t fill_descriptor_into_buf(struct pldm_descriptor_string *descriptor, uint uint8_t *fill_length, uint16_t current_length); bool is_update_state_download_phase(); bool is_update_state_idle(); +void pldm_status_reset(); +uint8_t plat_pldm_pass_component_table_check(uint16_t num_of_comp, + const uint8_t *comp_image_version_str, + uint8_t comp_image_version_str_len); #ifdef __cplusplus } diff --git a/common/service/pldm/pldm_oem.c b/common/service/pldm/pldm_oem.c index 5f88594223..fef7fb3899 100644 --- a/common/service/pldm/pldm_oem.c +++ b/common/service/pldm/pldm_oem.c @@ -43,6 +43,42 @@ static uint8_t oem_wf_read_spd_chunk(void *mctp_inst, uint8_t *buf, uint16_t len void *ext_params); #endif +__weak uint8_t force_update_flag_set_cmd(void *mctp_inst, uint8_t *buf, uint16_t len, + uint8_t instance_id, uint8_t *resp, uint16_t *resp_len, + void *ext_params) +{ + CHECK_NULL_ARG_WITH_RETURN(mctp_inst, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(buf, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(resp, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(resp_len, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(ext_params, PLDM_ERROR); + + uint8_t *completion_code_p = resp; + + *completion_code_p = PLDM_ERROR_UNSUPPORTED_PLDM_CMD; + *resp_len = 1; + + return PLDM_ERROR_UNSUPPORTED_PLDM_CMD; +} + +__weak uint8_t force_update_flag_get_cmd(void *mctp_inst, uint8_t *buf, uint16_t len, + uint8_t instance_id, uint8_t *resp, uint16_t *resp_len, + void *ext_params) +{ + CHECK_NULL_ARG_WITH_RETURN(mctp_inst, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(buf, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(resp, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(resp_len, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(ext_params, PLDM_ERROR); + + uint8_t *completion_code_p = resp; + + *completion_code_p = PLDM_ERROR_UNSUPPORTED_PLDM_CMD; + *resp_len = 1; + + return PLDM_ERROR_UNSUPPORTED_PLDM_CMD; +} + __weak uint8_t sensor_polling_cmd(void *mctp_inst, uint8_t *buf, uint16_t len, uint8_t instance_id, uint8_t *resp, uint16_t *resp_len, void *ext_params) { @@ -251,6 +287,8 @@ static pldm_cmd_handler pldm_oem_cmd_tbl[] = { #ifdef ENABLE_VISTARA { PLDM_OEM_WF_READ_SPD_CHUNK, oem_wf_read_spd_chunk }, #endif + { PLDM_OEM_FORCE_UPDATE_SETTING_CMD, force_update_flag_set_cmd }, + { PLDM_OEM_FORCE_UPDATE_GETTING_CMD, force_update_flag_get_cmd }, }; uint8_t pldm_oem_handler_query(uint8_t code, void **ret_fn) diff --git a/common/service/pldm/pldm_oem.h b/common/service/pldm/pldm_oem.h index 20c5767c09..30edef865c 100644 --- a/common/service/pldm/pldm_oem.h +++ b/common/service/pldm/pldm_oem.h @@ -37,6 +37,8 @@ extern "C" { #define PLDM_OEM_READ_FILE_IO 0x03 #define PLDM_OEM_SENSOR_POLLING_CMD 0x04 #define PLDM_OEM_WF_READ_SPD_CHUNK 0x05 +#define PLDM_OEM_FORCE_UPDATE_SETTING_CMD 0x06 +#define PLDM_OEM_FORCE_UPDATE_GETTING_CMD 0x07 #define POWER_CONTROL_LEN 0x01 @@ -286,6 +288,27 @@ struct _sensor_polling_cmd_resp { uint8_t set_value; } __attribute__((packed)); +struct _force_update_flag_set_cmd_req { + uint8_t iana[IANA_LEN]; + uint8_t set_value; +} __attribute__((packed)); + +struct _force_update_flag_set_cmd_resp { + uint8_t completion_code; + uint8_t iana[IANA_LEN]; + uint8_t set_value; +} __attribute__((packed)); + +struct _force_update_flag_get_cmd_req { + uint8_t iana[IANA_LEN]; +} __attribute__((packed)); + +struct _force_update_flag_get_cmd_resp { + uint8_t completion_code; + uint8_t iana[IANA_LEN]; + uint8_t get_value; +} __attribute__((packed)); + uint8_t check_iana(const uint8_t *iana); uint8_t set_iana(uint8_t *buf, uint8_t buf_len); uint8_t send_event_log_to_bmc(struct pldm_addsel_data msg); diff --git a/meta-facebook/minerva-ag/boards/npcm400f_evb.conf b/meta-facebook/minerva-ag/boards/npcm400f_evb.conf index c16fa2dd95..e7a7d239ba 100644 --- a/meta-facebook/minerva-ag/boards/npcm400f_evb.conf +++ b/meta-facebook/minerva-ag/boards/npcm400f_evb.conf @@ -15,4 +15,5 @@ CONFIG_I2C_NPCM4XX=y CONFIG_I3C_NPCM4XX=y CONFIG_ADC_NPCM4XX=y CONFIG_USB_DC_NPCM4XX=y -CONFIG_JTAG_NPCM4XX=y \ No newline at end of file +CONFIG_JTAG_NPCM4XX=y +CONFIG_LOG_STRDUP_BUF_COUNT=16 \ No newline at end of file diff --git a/meta-facebook/minerva-ag/src/platform/plat_pldm_fw_update.c b/meta-facebook/minerva-ag/src/platform/plat_pldm_fw_update.c index c0ff7edcf0..e7aaed6ca2 100644 --- a/meta-facebook/minerva-ag/src/platform/plat_pldm_fw_update.c +++ b/meta-facebook/minerva-ag/src/platform/plat_pldm_fw_update.c @@ -39,9 +39,12 @@ #include "plat_hook.h" #include "plat_event.h" #include "drivers/i2c_npcm4xx.h" +#include LOG_MODULE_REGISTER(plat_fwupdate); +static bool plat_force_update_flag = false; + static uint8_t pldm_pre_vr_update(void *fw_update_param); static uint8_t pldm_post_vr_update(void *fw_update_param); static uint8_t pldm_pre_bic_update(void *fw_update_param); @@ -698,3 +701,244 @@ void pal_warm_reset_prepare() LOG_INF("cmd platform warm reset prepare"); plat_reset_prepare(); } + +uint8_t force_update_flag_set_cmd(void *mctp_inst, uint8_t *buf, uint16_t len, uint8_t instance_id, + uint8_t *resp, uint16_t *resp_len, void *ext_params) +{ + CHECK_NULL_ARG_WITH_RETURN(mctp_inst, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(buf, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(resp, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(resp_len, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(ext_params, PLDM_ERROR); + + struct _force_update_flag_set_cmd_req *req_p = (struct _force_update_flag_set_cmd_req *)buf; + struct _force_update_flag_set_cmd_resp *resp_p = + (struct _force_update_flag_set_cmd_resp *)resp; + + if (len < (sizeof(*req_p) - 1)) { + LOG_WRN("request len %d is invalid", len); + resp_p->completion_code = PLDM_ERROR_INVALID_LENGTH; + set_iana(resp_p->iana, sizeof(resp_p->iana)); + goto exit; + } + + if (check_iana(req_p->iana) == PLDM_ERROR) { + resp_p->completion_code = PLDM_ERROR_INVALID_DATA; + set_iana(resp_p->iana, sizeof(resp_p->iana)); + goto exit; + } + + if (!(req_p->set_value == 1 || req_p->set_value == 0)) { + LOG_ERR("set force_update_flag:%x is out of range", req_p->set_value); + resp_p->completion_code = PLDM_ERROR_INVALID_DATA; + set_iana(resp_p->iana, sizeof(resp_p->iana)); + goto exit; + } + + plat_force_update_flag = req_p->set_value; + + LOG_INF("set force_update_flag:%x success", req_p->set_value); + resp_p->completion_code = PLDM_SUCCESS; + set_iana(resp_p->iana, sizeof(resp_p->iana)); + resp_p->set_value = req_p->set_value; + +exit: + *resp_len = sizeof(struct _force_update_flag_set_cmd_resp); + return PLDM_SUCCESS; +} + +uint8_t force_update_flag_get_cmd(void *mctp_inst, uint8_t *buf, uint16_t len, uint8_t instance_id, + uint8_t *resp, uint16_t *resp_len, void *ext_params) +{ + CHECK_NULL_ARG_WITH_RETURN(mctp_inst, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(buf, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(resp, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(resp_len, PLDM_ERROR); + CHECK_NULL_ARG_WITH_RETURN(ext_params, PLDM_ERROR); + + struct _force_update_flag_get_cmd_req *req_p = (struct _force_update_flag_get_cmd_req *)buf; + struct _force_update_flag_get_cmd_resp *resp_p = + (struct _force_update_flag_get_cmd_resp *)resp; + + if (len < (sizeof(*req_p) - 1)) { + LOG_WRN("request len %d is invalid", len); + resp_p->completion_code = PLDM_ERROR_INVALID_LENGTH; + set_iana(resp_p->iana, sizeof(resp_p->iana)); + goto exit; + } + + if (check_iana(req_p->iana) == PLDM_ERROR) { + resp_p->completion_code = PLDM_ERROR_INVALID_DATA; + set_iana(resp_p->iana, sizeof(resp_p->iana)); + goto exit; + } + + resp_p->completion_code = PLDM_SUCCESS; + set_iana(resp_p->iana, sizeof(resp_p->iana)); + resp_p->get_value = plat_force_update_flag; + +exit: + *resp_len = sizeof(struct _sensor_polling_cmd_resp); + return PLDM_SUCCESS; +} + +bool pal_vr_update_check_allowable(uint16_t comp_identifier, const char *new_version_hex) +{ + if (comp_identifier == AG_COMPNT_BIC) + return false; + if (!new_version_hex) { + LOG_ERR("VR update check failed: Invalid new_version_hex"); + return false; + } + + pldm_fw_update_info_t info = { 0 }; + uint8_t buf[64] = { 0 }; + uint8_t len = 0; + info.comp_identifier = comp_identifier; + + if (!get_vr_fw_version(&info, buf, &len)) { + LOG_ERR("VR update check failed: comp_identifier 0x%x not found", comp_identifier); + return false; + } + + char vr_version_info[64] = { 0 }; + memcpy(vr_version_info, buf, len); + + char *p = strchr(vr_version_info, ' '); + if (!p) { + LOG_ERR("VR update check failed: Failed to parse version from VR string"); + return false; + } + p++; + + char current_version[16] = { 0 }; + int i = 0; + while (*p && *p != ',' && i < sizeof(current_version) - 1) { + current_version[i++] = *p++; + } + current_version[i] = '\0'; + + char *remain_p = strstr(vr_version_info, "Remaining Write:"); + if (!remain_p) { + LOG_ERR("VR update check failed: remain not found"); + return false; + } + + remain_p += strlen("Remaining Write:"); + + // Skip any spaces after "Remaining Write:" + while (*remain_p == ' ') { + remain_p++; + } + + char remain_hex[8] = { 0 }; + strncpy(remain_hex, remain_p, 4); + + uint16_t remain_val = (uint16_t)strtol(remain_hex, NULL, 16); + + // Convert both versions to uppercase for case-insensitive comparison + char current_version_upper[16] = { 0 }; + char new_version_upper[16] = { 0 }; + + // Convert current_version to uppercase + for (int j = 0; j < sizeof(current_version_upper) - 1 && current_version[j]; j++) { + current_version_upper[j] = toupper(current_version[j]); + } + + // Convert new_version_hex to uppercase + for (int j = 0; j < sizeof(new_version_upper) - 1 && new_version_hex[j]; j++) { + new_version_upper[j] = toupper(new_version_hex[j]); + } + + if (remain_val != 0xffff) { + if (remain_val < 10) { + LOG_INF("VR update check blocked: Remaining write cycles (%d) < 10", + remain_val); + return false; + } + } + + if (strcmp(current_version_upper, new_version_upper) == 0) { + LOG_INF("VR update check blocked: Version same for comp_identifier=0x%x", + comp_identifier); + LOG_INF(" Current version: %s", log_strdup(current_version)); + LOG_INF(" New version: %s", log_strdup(new_version_hex)); + return false; + } + + LOG_INF("VR update check allowable: comp_identifier=0x%x, version=%s, remain=%d", + comp_identifier, log_strdup(current_version), remain_val); + return true; +} + +uint8_t plat_pldm_pass_component_table_check(uint16_t num_of_comp, + const uint8_t *comp_image_version_str, + uint8_t comp_image_version_str_len) +{ + if (plat_force_update_flag == 1) { + LOG_INF("Force update is set, skip the update check"); + return PLDM_SUCCESS; + } + + if (num_of_comp == AG_COMPNT_BIC) { + LOG_INF("BIC component, no need to check for update"); + return PLDM_SUCCESS; + } + + const char *comp_ver_str = (const char *)comp_image_version_str; + size_t comp_ver_len = comp_image_version_str_len; + + char comp_ver_str_buf[256] = { 0 }; + if (comp_ver_len >= sizeof(comp_ver_str_buf)) + comp_ver_len = sizeof(comp_ver_str_buf) - 1; + + memcpy(comp_ver_str_buf, comp_ver_str, comp_ver_len); + comp_ver_str_buf[comp_ver_len] = '\0'; + + LOG_INF("ComponentVersionString (len=%d):", comp_ver_len); + + const int chunk_size = 30; + for (int offset = 0; offset < comp_ver_len; offset += chunk_size) { + int remaining = comp_ver_len - offset; + int current_chunk = (remaining < chunk_size) ? remaining : chunk_size; + + char chunk_buf[32] = { 0 }; + memcpy(chunk_buf, comp_ver_str_buf + offset, current_chunk); + chunk_buf[current_chunk] = '\0'; + + LOG_INF(" [%02d-%02d]: %s", offset, offset + current_chunk - 1, + log_strdup(chunk_buf)); + } + + char *ver_pos = strstr(comp_ver_str_buf, "ver: "); + if (!ver_pos) { + LOG_ERR("version not found in ComponentVersionString"); + return PLDM_FW_UPDATE_CC_UNABLE_TO_INITIATE_UPDATE; + } + + ver_pos += 5; + + char new_version_hex[16] = { 0 }; + int i = 0; + + while (*ver_pos && !isspace((unsigned char)*ver_pos) && i < sizeof(new_version_hex) - 1) { + new_version_hex[i++] = *ver_pos++; + } + new_version_hex[i] = '\0'; + + LOG_INF("Parsed new version = %s", log_strdup(new_version_hex)); + + bool is_vr_update_check_allowable = + pal_vr_update_check_allowable(num_of_comp, new_version_hex); + if (is_vr_update_check_allowable) { + LOG_INF("VR update check allowable: version and remaining checks passed"); + return PLDM_SUCCESS; + } else { + LOG_INF("VR update check blocked: protection mechanism activated (same version or low remaining writes)"); + + LOG_INF("Reset PLDM FW update state machine due to update not allowable"); + pldm_status_reset(); + + return PLDM_FW_UPDATE_CC_UNABLE_TO_INITIATE_UPDATE; + } +}