// Copyright(c) 2019-2020, Intel Corporation // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of Intel Corporation nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #include #include #include #include #include "board_rc.h" // BMC sysfs path #define SYSFS_DEVID_FILE "avmmi-bmc.*.auto/bmc_info/device_id" #define SYSFS_RESET_FILE "avmmi-bmc.*.auto/bmc_info/reset_cause" #define SYSFS_PWRDN_FILE "avmmi-bmc.*.auto/bmc_info/power_down_cause" #define SYSFS_TCM_GLOB "tcm/*" #define SYSFS_TCM_BIP_VER "tcm/bip_version" #define SYSFS_TCM_BMC_CANCEL "tcm/bmc_canceled_csks" #define SYSFS_TCM_BMC_FLASH_COUNT "tcm/bmc_flash_count" #define SYSFS_TCM_BMC_FWVERS "tcm/bmcfw_version" #define SYSFS_TCM_BMC_ROOT "tcm/bmc_root_hash" #define SYSFS_TCM_CRYPTO_VER "tcm/crypto_version" #define SYSFS_TCM_PR_CANCEL "tcm/pr_canceled_csks" #define SYSFS_TCM_PR_ROOT "tcm/pr_root_hash" #define SYSFS_TCM_QSPI_COUNT "tcm/qspi_flash_count" #define SYSFS_TCM_SR_CANCEL "tcm/sr_canceled_csks" #define SYSFS_TCM_SR_ROOT "tcm/sr_root_hash" #define SYSFS_TCM_FW_VER "tcm/tcmfw_version" #define FPGA_STR_SIZE 256 #define SDR_HEADER_LEN 3 #define SDR_MSG_LEN 40 typedef struct _bmc_powerdown_cause { uint8_t _header[SDR_HEADER_LEN]; uint8_t completion_code; uint8_t iana[SDR_HEADER_LEN]; uint8_t count; uint8_t message[SDR_MSG_LEN]; } bmc_powerdown_cause; typedef struct _bmc_reset_cause { uint8_t _header[SDR_HEADER_LEN]; uint8_t completion_code; uint8_t iana[SDR_HEADER_LEN]; uint8_t reset_cause; } bmc_reset_cause; typedef enum { CHIP_RESET_CAUSE_POR = 0x01, CHIP_RESET_CAUSE_EXTRST = 0x02, CHIP_RESET_CAUSE_BOD_IO = 0x04, CHIP_RESET_CAUSE_WDT = 0x08, CHIP_RESET_CAUSE_OCD = 0x10, CHIP_RESET_CAUSE_SOFT = 0x20, CHIP_RESET_CAUSE_SPIKE = 0x40, } bmc_ResetCauses; typedef struct _bmc_device_id { uint8_t _header[SDR_HEADER_LEN]; uint8_t completion_code; uint8_t device_id; union { struct { uint8_t device_revision : 3; uint8_t _unused : 3; uint8_t provides_sdrs : 2; } bits; uint8_t _value; } device_revision; union { struct { uint8_t device_available : 7; uint8_t major_fw_revision : 1; } bits; uint8_t _value; } firmware_revision_1; uint8_t firmware_revision_2; uint8_t ipmi_version; union { struct { uint8_t sensor_device : 1; uint8_t sdr_repository_device : 1; uint8_t sel_device : 1; uint8_t fru_inventory_device : 1; uint8_t ipmb_event_receiver : 1; uint8_t ipmb_event_generator : 1; uint8_t bridge : 1; uint8_t chassis_device : 1; } bits; uint8_t _value; } additional_device_support; uint8_t manufacturer_id_0_7; uint8_t manufacturer_id_8_15; uint8_t manufacturer_id_16_23; uint8_t product_id_0_7; uint8_t product_id_8_15; uint8_t aux_fw_rev_0_7; uint8_t aux_fw_rev_8_15; uint8_t aux_fw_rev_16_23; uint8_t aux_fw_rev_24_31; } bmc_device_id; // Read bmc version fpga_result read_bmc_version(fpga_token token, int *version) { fpga_result res = FPGA_OK; fpga_result resval = FPGA_OK; bmc_device_id bmc_dev; fpga_object bmc_object; if (version == NULL) { OPAE_ERR("Invalid input parameter"); return FPGA_INVALID_PARAM; } res = fpgaTokenGetObject(token, SYSFS_DEVID_FILE, &bmc_object, FPGA_OBJECT_GLOB); if (res != FPGA_OK) { OPAE_ERR("Failed to get token Object"); return res; } memset(&bmc_dev, 0, sizeof(bmc_dev)); res = fpgaObjectRead(bmc_object, (uint8_t *)(&bmc_dev), 0, sizeof(bmc_dev), 0); if (res != FPGA_OK) { OPAE_ERR("Failed to Read object "); resval = res; goto out_destroy; } *version = bmc_dev.aux_fw_rev_0_7 | (bmc_dev.aux_fw_rev_8_15 << 8) | (bmc_dev.aux_fw_rev_16_23 << 16) | (bmc_dev.aux_fw_rev_24_31 << 24); out_destroy: res = fpgaDestroyObject(&bmc_object); if (res != FPGA_OK) { OPAE_ERR("Failed to Destroy Object"); } return resval; } // Read power down cause fpga_result read_bmc_pwr_down_cause(fpga_token token, char *pwr_down_cause) { fpga_result res = FPGA_OK; fpga_result resval = FPGA_OK; fpga_object bmc_object; bmc_powerdown_cause bmc_pd; if (pwr_down_cause == NULL) { OPAE_ERR("Invalid input parameter"); return FPGA_INVALID_PARAM; } res = fpgaTokenGetObject(token, SYSFS_PWRDN_FILE, &bmc_object, FPGA_OBJECT_GLOB); if (res != FPGA_OK) { OPAE_ERR("Failed to get token Object"); return res; } memset(&bmc_pd, 0, sizeof(bmc_pd)); res = fpgaObjectRead(bmc_object, (uint8_t *)(&bmc_pd), 0, sizeof(bmc_pd), 0); if (res != FPGA_OK) { OPAE_ERR("Failed to Read object "); resval = res; goto out_destroy; } if (bmc_pd.completion_code == 0) { strncpy(pwr_down_cause, (char *)bmc_pd.message, bmc_pd.count); } else { OPAE_ERR("unavailable read power down cause: %d ", bmc_pd.completion_code); resval = FPGA_EXCEPTION; } out_destroy: res = fpgaDestroyObject(&bmc_object); if (res != FPGA_OK) { OPAE_ERR("Failed to Destroy Object"); resval = res; } return resval; } // Read reset cause fpga_result read_bmc_reset_cause(fpga_token token, char *reset_cause_str) { fpga_result res = FPGA_OK; fpga_result resval = FPGA_OK; fpga_object bmc_object; bmc_reset_cause bmc_rc; if (reset_cause_str == NULL) { OPAE_ERR("Invalid input parameter"); return FPGA_INVALID_PARAM; } res = fpgaTokenGetObject(token, SYSFS_RESET_FILE, &bmc_object, FPGA_OBJECT_GLOB); if (res != FPGA_OK) { OPAE_ERR("Failed to get token Object"); return res; } memset(&bmc_rc, 0, sizeof(bmc_rc)); res = fpgaObjectRead(bmc_object, (uint8_t *)(&bmc_rc), 0, sizeof(bmc_rc), 0); if (res != FPGA_OK) { OPAE_ERR("Failed to Read Object "); resval = res; goto out_destroy; } if (bmc_rc.completion_code != 0) { OPAE_ERR("Failed to Read Reset cause \n"); resval = FPGA_EXCEPTION; goto out_destroy; } if (0 == bmc_rc.reset_cause) { strncpy(reset_cause_str, "None", 5); goto out_destroy; } if (bmc_rc.reset_cause & CHIP_RESET_CAUSE_EXTRST) { strncpy(reset_cause_str, "External reset", 15); } if (bmc_rc.reset_cause & CHIP_RESET_CAUSE_BOD_IO) { strncpy(reset_cause_str, "Brown-out detected", 19); } if (bmc_rc.reset_cause & CHIP_RESET_CAUSE_OCD) { strncpy(reset_cause_str, "On-chip debug system", 21); } if (bmc_rc.reset_cause & CHIP_RESET_CAUSE_POR) { strncpy(reset_cause_str, "Power-on-reset", 15); } if (bmc_rc.reset_cause & CHIP_RESET_CAUSE_SOFT) { strncpy(reset_cause_str, "Software reset", 15); } if (bmc_rc.reset_cause & CHIP_RESET_CAUSE_SPIKE) { strncpy(reset_cause_str, "Spike detected", 15); } if (bmc_rc.reset_cause & CHIP_RESET_CAUSE_WDT) { strncpy(reset_cause_str, "Watchdog timeout", 17); } out_destroy: res = fpgaDestroyObject(&bmc_object); if (res != FPGA_OK) { OPAE_ERR("Failed to Destroy Object"); } return resval; } // Print BMC version, Power down cause and Reset cause fpga_result print_board_info(fpga_token token) { fpga_result res = FPGA_OK; int version = 0; char pwr_down_cause[FPGA_STR_SIZE] = { 0 }; char reset_cause[FPGA_STR_SIZE] = { 0 }; struct stat st; fpga_object bmc_object; if (!stat("/sys/bus/pci/drivers/dfl-pci", &st)) { res = fpgaTokenGetObject(token, SYSFS_DEVID_FILE, &bmc_object, FPGA_OBJECT_GLOB); if (res != FPGA_OK) { printf("Board Management Controller, microcontroller FW version: %s\n", "Not Supported"); printf("Last Power down cause: %s\n", "Not Supported"); printf("Last Reset cause: %s\n", "Not Supported"); return res; } res = fpgaDestroyObject(&bmc_object); if (res != FPGA_OK) { OPAE_ERR("Failed to Destroy Object"); } } res = read_bmc_version(token, &version); if (res != FPGA_OK) { OPAE_ERR("Failed to read BMC FW version"); } res = read_bmc_pwr_down_cause(token, pwr_down_cause); if (res != FPGA_OK) { OPAE_ERR("Failed to read power down cause"); } res = read_bmc_reset_cause(token, reset_cause); if (res != FPGA_OK) { OPAE_ERR("Failed to read reset cause"); } // Print BMC info printf("Board Management Controller, microcontroller FW version: %d\n", version); printf("Last Power down cause:%s\n", pwr_down_cause); printf("Last Reset cause: %s\n", reset_cause); return res; } fpga_result read_sysfs(fpga_token token, char *sysfs_path, char *sysfs_name) { fpga_result res = FPGA_OK; fpga_result resval = FPGA_OK; uint32_t size = 0; char name[FPGA_STR_SIZE] = { 0, }; fpga_object sec_object; size_t len; if (sysfs_path == NULL || sysfs_name == NULL) { OPAE_ERR("Invalid input parameter"); return FPGA_INVALID_PARAM; } res = fpgaTokenGetObject(token, sysfs_path, &sec_object, FPGA_OBJECT_GLOB); if (res != FPGA_OK) { OPAE_ERR("Failed to get token Object"); return res; } res = fpgaObjectGetSize(sec_object, &size, 0); if (res != FPGA_OK) { OPAE_ERR("Failed to get object size "); resval = res; goto out_destroy; } if (size > FPGA_STR_SIZE) { OPAE_ERR("object size bigger then buffer size"); resval = FPGA_EXCEPTION; goto out_destroy; } res = fpgaObjectRead(sec_object, (uint8_t *)(&name), 0, size, 0); if (res != FPGA_OK) { OPAE_ERR("Failed to Read object "); resval = res; goto out_destroy; } len = strnlen(name, FPGA_STR_SIZE - 1); strncpy(sysfs_name, name, len + 1); out_destroy: res = fpgaDestroyObject(&sec_object); if (res != FPGA_OK) { OPAE_ERR("Failed to Destroy Object"); resval = res; } return resval; } fpga_result print_sec_info(fpga_token token) { fpga_result res = FPGA_OK; fpga_object tcm_object; char name[FPGA_STR_SIZE] = { 0 }; res = fpgaTokenGetObject(token, SYSFS_TCM_GLOB, &tcm_object, FPGA_OBJECT_GLOB); if (res != FPGA_OK) { OPAE_MSG("Failed to get token Object"); return res; } if (read_sysfs(token, SYSFS_TCM_BMC_FWVERS, name) == FPGA_OK) printf("BMC FW Version: %s", name); else OPAE_MSG("Failed to Read BMC FW Version"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_BIP_VER, name) == FPGA_OK) printf("BIP Version: %s", name); else OPAE_MSG("Failed to Read BIP Version"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_FW_VER, name) == FPGA_OK) printf("TCM FW Version: %s", name); else OPAE_MSG("Failed to Read TCM FW Version"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_CRYPTO_VER, name) == FPGA_OK) printf("Crypto block Version: %s", name); else OPAE_MSG("Failed to Read Crypto block Version"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_SR_ROOT, name) == FPGA_OK) printf("FIM root entry hash: %s", name); else OPAE_MSG("Failed to Read FIM root entry hash"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_BMC_ROOT, name) == FPGA_OK) printf("BMC root entry hash: %s", name); else OPAE_MSG("Failed to Read TCM BMC root entry hash"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_PR_ROOT, name) == FPGA_OK) printf("PR root entry hash: %s", name); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_BMC_FLASH_COUNT, name) == FPGA_OK) printf("BMC flash update counter: %s", name); else OPAE_MSG("Failed to Read BMC flash update counter"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_QSPI_COUNT, name) == FPGA_OK) printf("User flash update counter: %s", name); else OPAE_MSG("Failed to Read User flash update counter"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_SR_CANCEL, name) == FPGA_OK) printf("FIM CSK IDs canceled : %s", strlen(name) > 1 ? name : "None\n"); else OPAE_MSG("Failed to Read FIM CSK IDs canceled"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_BMC_CANCEL, name) == FPGA_OK) printf("BMC CSK IDs canceled: %s", strlen(name) > 1 ? name : "None\n"); else OPAE_MSG("Failed to Read BMC CSK IDs canceled"); memset(name, 0, sizeof(name)); if (read_sysfs(token, SYSFS_TCM_PR_CANCEL, name) == FPGA_OK) printf("AFU CSK IDs canceled: %s", strlen(name) > 1 ? name : "None\n"); else OPAE_MSG("Failed to Read AFU CSK IDs canceled"); res = fpgaDestroyObject(&tcm_object); if (res != FPGA_OK) { OPAE_MSG("Failed to Destroy Object"); } return res; }