meta-facebook/meta-grandcanyon/recipes-grandcanyon/iocd/files/iocd.c (707 lines of code) (raw):

/* * * Copyright 2021-present Facebook. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <fcntl.h> #include <errno.h> #include <syslog.h> #include <string.h> #include <pthread.h> #include <time.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <openbmc/ipmi.h> #include <openbmc/ipmb.h> #include <openbmc/pal.h> #include <openbmc/obmc-i2c.h> #include <openbmc/ipc.h> #include <facebook/fbgc_common.h> #include "iocd.h" pthread_mutex_t m_ioc = PTHREAD_MUTEX_INITIALIZER; static struct { uint8_t bus_id; uint8_t fru_num; bool check_pec; bool is_support_pec; char fru_name[MAX_FRU_CMD_STR]; char ioc_ver_key[MAX_KEY_LEN]; int i2c_write_failed_cnt; int i2c_fd; i2c_mslave_t *bmc_slave; } iocd_config = { .bus_id = 0, .fru_num = 0, .check_pec = false, .is_support_pec = true, .fru_name = {0}, .ioc_ver_key = {0}, .i2c_write_failed_cnt = 0, .i2c_fd = -1, .bmc_slave = NULL, }; static int parse_cmdline_args(int argc, char* const argv[]) { uint8_t i2c_bus = 0; if (argv == NULL) { syslog(LOG_WARNING, "%s() Fail to set iocd config due to NULL parameter", __func__); return -1; } i2c_bus = (uint8_t)strtoul(argv[1], NULL, 0); memset(iocd_config.fru_name, 0, sizeof(iocd_config.fru_name)); if (i2c_bus == I2C_T5IOC_BUS) { iocd_config.fru_num = FRU_SCC; } else if (i2c_bus == I2C_T5E1S0_T7IOC_BUS) { iocd_config.fru_num = FRU_E1S_IOCM; } else { syslog(LOG_WARNING, "%s() Fail to set iocd config due to wrong I2C bus:%d.", __func__, i2c_bus); return -1; } iocd_config.bus_id = i2c_bus; if (pal_get_fru_name(iocd_config.fru_num, iocd_config.fru_name) < 0) { syslog(LOG_WARNING, "%s() Fail to get fru%d name", __func__, iocd_config.fru_num); return -1; } return 0; } int mctp_i2c_write(uint8_t slave_addr, uint8_t *buf, uint8_t len) { int ret = 0; int i = 0; if (buf == NULL) { syslog(LOG_WARNING, "%s(): Failed to write i2c raw to bus:%d due to NULL parameter", __func__, iocd_config.bus_id); return -1; } for (i = 0; i < I2C_RETRIES_MAX; i++) { ret = i2c_rdwr_msg_transfer(iocd_config.i2c_fd, slave_addr, buf, len, NULL, 0); if (ret < 0) { msleep(100); continue; } else { break; } } if (ret < 0) { syslog(LOG_WARNING, "%s(): Failed to write i2c raw to bus:%d", __func__, iocd_config.bus_id); return -1; } return 0; } int mctp_i2c_read(uint8_t *buf, uint8_t *len) { int i = 0, read_len = 0; if ((buf == NULL) || (len == NULL)) { syslog(LOG_WARNING, "%s(): Failed to read i2c raw from bus:%d due to NULL parameters", __func__, iocd_config.bus_id); return -1; } for (i = 0; i < I2C_RETRIES_MAX; i++) { read_len = i2c_mslave_read(iocd_config.bmc_slave, buf, MCTP_MAX_READ_SIZE); if (read_len <= 0) { i2c_mslave_poll(iocd_config.bmc_slave, I2C_MSLAVE_POLL_TIME); /* 100 milliseconds */ continue; } else { break; } } if (read_len <= 0) { syslog(LOG_WARNING, "%s(): Failed to read i2c raw from bus:%d because response length is wrong: %d", __func__, iocd_config.bus_id, read_len); return -1; } else { *len = read_len; } return 0; } static int clear_slave_mqueue() { uint8_t buf[MCTP_MAX_READ_SIZE] = {0}; int read_len = 0; int i = 0; for (i = 0; i < I2C_RETRIES_MAX; i++) { read_len = i2c_mslave_read(iocd_config.bmc_slave, buf, MCTP_MAX_READ_SIZE); if (read_len <= 0) { i2c_mslave_poll(iocd_config.bmc_slave, I2C_MSLAVE_POLL_TIME); /* 100 milliseconds */ continue; } else { break; } } return 0; } static uint8_t crc8_calculate(uint16_t d) { const uint32_t poly_check = 0x1070 << 3; int i = 0; for (i = 0; i < 8; i++) { if (d & 0x8000) { d = d ^ poly_check; } d = d << 1; } return (uint8_t)(d >> 8); } /* Incremental CRC8 over count bytes in the array pointed to by p */ static int pec_calculate(uint8_t crc, uint8_t *p, size_t count) { int i = 0; if (p == NULL) { return -1; } for (i = 0; i < count; i++) { crc = crc8_calculate((crc ^ p[i]) << 8); } return (int)crc; } static int calculate_pec_byte(uint8_t *buf, size_t len, bool is_i2c_pec) { int ret = 0; uint8_t pec = 0, address = 0; if (buf == NULL) { return -1; } if (is_i2c_pec == true) { address = IOC_SLAVE_ADDRESS; ret = pec_calculate(0, &address, 1); if (ret < 0) { return -1; } pec = (uint8_t) ret; } return pec_calculate(pec, buf, len); } static int set_write_buf(uint8_t *write_buf, uint8_t tag, ioc_command command) { int pec = 0; uint8_t res_len = 0; struct mctp_smbus_header_tx smbus_header = {0}; struct mctp_hdr mctp_header = {0}; pkt_payload_hdr payload_header = {0}; size_t mctp_pkt_header_size = 0; bool is_i2c_pec = false; if (write_buf == NULL) { syslog(LOG_WARNING, "%s(): failed to set write buffer of %s due to NULL parameter.", __func__, ioc_command_map[command].command_name); return -1; } if (command >= ARRAY_SIZE(ioc_command_map)) { syslog(LOG_WARNING, "%s(): Failed to write i2c raw due to wrong command.", __func__); return -1; } memset(&smbus_header, 0, sizeof(smbus_header)); memset(&mctp_header, 0, sizeof(mctp_header)); memset(&payload_header, 0, sizeof(payload_header)); smbus_header.command_code = SMBUS_COMMAND_CODE; smbus_header.source_slave_address = (BMC_SLAVE_ADDR << 1) + 1; // Add 1 byte of Source Slave Address smbus_header.byte_count = (sizeof(mctp_header) + sizeof(payload_header) + 1); if (ioc_command_map[command].pkt_pl_hdr.is_need_request) { smbus_header.byte_count += (PKT_PAYLOAD_HDR_EXTRA_SIZE + ioc_command_map[command].pkt_pl_hdr.payload_len[0]); } mctp_header.ver = HEADER_VERSION; mctp_header.dest = 0x00; mctp_header.src = 0x00; mctp_header.flags_seq_tag = BRCM_MSG_TAG; if (iocd_config.is_support_pec == true) { // Add 1 byte of PEC smbus_header.byte_count += 1; payload_header.message_type = MESSAGE_TYPE_PEC; } else { payload_header.message_type = MESSAGE_TYPE; } payload_header.vendor_id[0] = 0x10; payload_header.vendor_id[1] = 0x00; payload_header.payload_id = ioc_command_map[command].pkt_pl_hdr.payload_id; payload_header.msg_seq_count[0] = 0x01; payload_header.msg_seq_count[1] = 0x00; payload_header.app_msg_tag = tag; memcpy(write_buf, &smbus_header, sizeof(smbus_header)); memcpy(write_buf + sizeof(smbus_header), &mctp_header, sizeof(mctp_header)); memcpy(write_buf + sizeof(smbus_header) + sizeof(mctp_header), &payload_header, sizeof(payload_header)); mctp_pkt_header_size = sizeof(smbus_header) + sizeof(mctp_header); res_len = mctp_pkt_header_size + sizeof(payload_header); switch (command) { case COMMAND_RESUME: case COMMAND_DONE: break; case VDM_RESET: case GET_IOC_TEMP: case GET_IOC_FW: memcpy(write_buf + res_len, &ioc_command_map[command].pkt_pl_hdr.opcode, PKT_PAYLOAD_HDR_EXTRA_SIZE); res_len += PKT_PAYLOAD_HDR_EXTRA_SIZE; if (command == GET_IOC_TEMP) { memcpy(write_buf + res_len, &get_ioc_temp_payload, sizeof(get_ioc_temp_payload)); res_len += sizeof(get_ioc_temp_payload); } else if (command == GET_IOC_FW) { memcpy(write_buf + res_len, &get_ioc_fw_version_payload, sizeof(get_ioc_fw_version_payload)); res_len += sizeof(get_ioc_fw_version_payload); } break; default: res_len = -1; break; } if ((res_len > 0) && (iocd_config.is_support_pec == true)) { // Calculate VDM PEC pec = calculate_pec_byte(&write_buf[mctp_pkt_header_size], (res_len - mctp_pkt_header_size), is_i2c_pec); if (pec < 0) { syslog(LOG_WARNING, "%s(): Failed to calculate the PEC of MCTP payload packet.", __func__); return -1; } write_buf[res_len] = (uint8_t)pec; res_len++; // Calculate I2C PEC is_i2c_pec = true; pec = calculate_pec_byte(write_buf, res_len, is_i2c_pec); if (pec < 0) { syslog(LOG_WARNING, "%s(): Failed to calculate the PEC of whole packet.", __func__); return -1; } write_buf[res_len] = (uint8_t)pec; res_len++; } return res_len; } int mctp_ioc_command(uint8_t slave_addr, uint8_t tag, ioc_command command, uint8_t *resp_buf, uint8_t *resp_len) { int ret = 0, write_len = 0, retry = 0; uint8_t write_buf[MCTP_MAX_WRITE_SIZE] = {0}; uint8_t read_buf[MCTP_MAX_READ_SIZE] = {0}; uint8_t read_len = 0, resp_pl = 0, excepted_pl = 0; if ((resp_buf == NULL) || (resp_len == NULL)) { syslog(LOG_WARNING, "%s(): Failed to write i2c raw due to NULL parameter.", __func__); return -1; } *resp_len = 0; if (command >= ARRAY_SIZE(ioc_command_map)) { syslog(LOG_WARNING, "%s(): Failed to write i2c raw to bus:%d due to wrong command.", __func__, iocd_config.bus_id); return -1; } write_len = set_write_buf(write_buf, tag, command); if (write_len < 0) { syslog(LOG_WARNING, "%s(): Failed to initialize write buffer.", __func__); return -1; } #ifdef DEBUG // Print write buffer data int i = 0; char temp[8] = {0}, log[MCTP_MAX_READ_SIZE*2] = {0}; for (i = 0; i < write_len; i++) { memset(temp, 0, sizeof(temp)); sprintf(temp, "%02X ", write_buf[i]); strcat(log, temp); } syslog(LOG_WARNING, "%s(): %s, write data to bus:%d, tag:%02X, write_len:%d, data: %s", __func__, ioc_command_map[command].command_name, iocd_config.bus_id, tag, write_len, log); #endif do { ret = mctp_i2c_write(slave_addr, write_buf, write_len); if (ret < 0) { syslog(LOG_WARNING, "%s(): failed to do '%s' on bus:%d because i2c write failed.", __func__, ioc_command_map[command].command_name, iocd_config.bus_id); return WRITE_I2C_RAW_FAILED; } memset(read_buf, 0, sizeof(read_buf)); ret = mctp_i2c_read(read_buf, &read_len); if ((ret < 0) || (read_len < MCTP_MIN_READ_SIZE)) { syslog(LOG_WARNING, "%s(): failed to do '%s' on bus:%d because i2c read failed.", __func__, ioc_command_map[command].command_name, iocd_config.bus_id); return -1; } // Clear the second package of the response of "command resume" if (command == COMMAND_RESUME) { if (clear_slave_mqueue() < 0) { syslog(LOG_WARNING, "%s(): Failed to clear bus:%d slave-mqueue", __func__, iocd_config.bus_id); return -1; } } // Retry if response is not ready. if (read_buf[RES_PAYLOAD_OFFSET] == RES_PL_CMD_RESP_NOT_READY) { msleep(500); } else { break; } } while (++retry < I2C_RETRIES_MAX); // Check respond payload ID if (command != VDM_RESET) { resp_pl = read_buf[RES_PAYLOAD_OFFSET]; excepted_pl = ioc_command_map[command].pkt_pl_hdr.res_payload_id; if (resp_pl != excepted_pl) { syslog(LOG_WARNING, "%s(): failed to do '%s' on bus:%d due to the wrong payload ID: %x, expected: %x", __func__, ioc_command_map[command].command_name, iocd_config.bus_id, resp_pl, excepted_pl); #ifdef DEBUG // Print read buffer data memset(log, 0, sizeof(log)); for (i = 0; i < read_len; i++) { memset(temp, 0, sizeof(temp)); sprintf(temp, "%02X ", read_buf[i]); strcat(log, temp); } syslog(LOG_WARNING, "%s(): %s, read data on bus:%d, tag:%02X, read_len:%d, data: %s", __func__, ioc_command_map[command].command_name, iocd_config.bus_id, tag, read_len, log); #endif return -1; } } memcpy(resp_buf, &read_buf, read_len); *resp_len = read_len; return 0; } static int check_ioc_support_pec(uint8_t slave_addr) { uint8_t tag = 0xFF, read_len = 0; uint8_t read_buf[MCTP_MAX_READ_SIZE] = {0}; int ret = 0; iocd_config.is_support_pec = true; // Clear slave mqueue if (clear_slave_mqueue() < 0) { syslog(LOG_WARNING, "%s(): Failed to clear bus:%d slave-mqueue", __func__, iocd_config.bus_id); return -1; } // Do "VDM reset" to check if IOC F/w could support PEC. ret = mctp_ioc_command(slave_addr, tag, VDM_RESET, read_buf, &read_len); if (ret < 0) { syslog(LOG_WARNING, "%s(): VDM reset failed, bus: %d", __func__, iocd_config.bus_id); return ret; } if (read_buf[RES_PAYLOAD_OFFSET] == RES_PL_PEC_FAILURE) { iocd_config.is_support_pec = false; } else if (read_buf[RES_PAYLOAD_OFFSET] == RES_PL_RESET_IN_PROGRESS) { // If the response status is "reset in progress", continue to do "command resume" and "command done". if (mctp_ioc_command(slave_addr, tag, COMMAND_RESUME, read_buf, &read_len) < 0) { iocd_config.is_support_pec = false; } if (mctp_ioc_command(slave_addr, tag, COMMAND_DONE, read_buf, &read_len) < 0) { iocd_config.is_support_pec = false; } } else if (read_buf[RES_PAYLOAD_OFFSET] != ioc_command_map[VDM_RESET].pkt_pl_hdr.res_payload_id) { syslog(LOG_WARNING, "%s(): Failed to check if %s IOC firmware could support PEC byte beacuse wrong payload ID: %02X", __func__, iocd_config.fru_name, read_buf[RES_PAYLOAD_OFFSET]); return -1; } if (iocd_config.is_support_pec == false) { syslog(LOG_WARNING, "%s(): %s IOC firmware doesn't support handling PEC byte, bus: %d", __func__, iocd_config.fru_name, iocd_config.bus_id); } iocd_config.check_pec = true; return 0; } static int vdm_reset(uint8_t slave_addr) { bool is_failed = false; uint8_t tag = 0xFF, read_len = 0; uint8_t read_buf[MCTP_MAX_READ_SIZE] = {0}; if (mctp_ioc_command(slave_addr, tag, VDM_RESET, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): VDM reset failed, bus: %d", __func__, iocd_config.bus_id); is_failed = true; } memset(read_buf, 0, sizeof(read_buf)); if (mctp_ioc_command(slave_addr, tag, COMMAND_RESUME, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): VDM reset failed because command resume failed, bus: %d", __func__, iocd_config.bus_id); is_failed = true; } memset(read_buf, 0, sizeof(read_buf)); if (mctp_ioc_command(slave_addr, tag, COMMAND_DONE, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): VDM reset failed because command done failed, bus: %d", __func__, iocd_config.bus_id); is_failed = true; } if (is_failed == true) { return -1; } return 0; } static int set_ioc_temp(int value) { char key[MAX_KEY_LEN] = {0}; char cache_value[MAX_VALUE_LEN] = {0}; memset(key, 0, sizeof(key)); memset(cache_value, 0, sizeof(cache_value)); if (iocd_config.bus_id == I2C_T5IOC_BUS) { snprintf(key, sizeof(key), "%s_ioc_sensor%d", iocd_config.fru_name, SCC_IOC_TEMP); } else if (iocd_config.bus_id == I2C_T5E1S0_T7IOC_BUS) { snprintf(key, sizeof(key), "%s_ioc_sensor%d", iocd_config.fru_name, IOCM_IOC_TEMP); } else { syslog(LOG_WARNING, "%s() Failed to set %s IOC temperature due to unknown i2c bus: %d", __func__, iocd_config.fru_name, iocd_config.bus_id); return -1; } if (value == READ_IOC_TEMP_FAILED) { snprintf(cache_value, sizeof(cache_value), "NA"); } else { snprintf(cache_value, sizeof(cache_value), "%d", value); } if (kv_set(key, cache_value, 0, 0) < 0) { syslog(LOG_WARNING, "%s() set %s IOC temperature failed. key = %s, value = %s.", __func__, iocd_config.fru_name, key, cache_value); return -1; } return 0; } static int reattach_i2c_device(char* slave_device_path) { // Detach IOC I2C device if (access(slave_device_path, F_OK) == 0) { if (i2c_delete_device(iocd_config.bus_id, DEVICE_ADDRESS) != 0) { syslog(LOG_ERR, "%s(): Failed to delete i2c device on bus:%d", __func__, iocd_config.bus_id); return -1; } } if (iocd_config.i2c_fd >= 0) { close(iocd_config.i2c_fd); } if (iocd_config.bmc_slave != NULL) { if (i2c_mslave_close(iocd_config.bmc_slave) < 0){ syslog(LOG_ERR, "%s(): Failed to close slave device on bus:%d", __func__, iocd_config.bus_id); return -1; } } // Attach IOC I2C device if (i2c_add_device(iocd_config.bus_id, DEVICE_ADDRESS, DEVICE_NAME) < 0) { syslog(LOG_ERR, "%s(): Failed to add i2c device on bus:%d", __func__, iocd_config.bus_id); return -1; } iocd_config.i2c_fd = i2c_cdev_slave_open(iocd_config.bus_id, IOC_SLAVE_ADDRESS, I2C_SLAVE); if (iocd_config.i2c_fd < 0) { syslog(LOG_ERR, "%s(): Failed to open i2c device on bus:%d", __func__, iocd_config.bus_id); return -1; } iocd_config.bmc_slave = i2c_mslave_open(iocd_config.bus_id, BMC_SLAVE_ADDR); if (iocd_config.bmc_slave == NULL) { syslog(LOG_ERR, "%s(): Failed to open bmc as slave on bus:%d", __func__, iocd_config.bus_id); return -1; } return 0; } static void * get_ioc_temp() { int value = 0, ret = 0; bool is_failed = true; uint8_t tag = 0x01, read_len = 0; uint8_t read_buf[MCTP_MAX_READ_SIZE] = {0}; char slave_dev_path[MAX_FILE_PATH] = {0}; snprintf(slave_dev_path, sizeof(slave_dev_path), IOC_SLAVE_QUEUE, iocd_config.bus_id, DEVICE_ADDRESS); if (access(slave_dev_path, F_OK) == -1) { if (i2c_add_device(iocd_config.bus_id, DEVICE_ADDRESS, DEVICE_NAME) < 0) { syslog(LOG_WARNING, "%s(): Failed to add i2c device on bus:%d", __func__, iocd_config.bus_id); goto cleanup; } } iocd_config.i2c_fd = i2c_cdev_slave_open(iocd_config.bus_id, IOC_SLAVE_ADDRESS, I2C_SLAVE); if (iocd_config.i2c_fd < 0) { syslog(LOG_WARNING, "%s(): Failed to open i2c device on bus:%d", __func__, iocd_config.bus_id); goto cleanup; } iocd_config.bmc_slave = i2c_mslave_open(iocd_config.bus_id, BMC_SLAVE_ADDR); if (iocd_config.bmc_slave == NULL) { syslog(LOG_WARNING, "%s(): Failed to open bmc as slave on bus:%d", __func__, iocd_config.bus_id); goto cleanup; } snprintf(iocd_config.ioc_ver_key, sizeof(iocd_config.ioc_ver_key), "ioc_%d_fw_ver", iocd_config.bus_id); kv_set(iocd_config.ioc_ver_key, "", 0, 0); while (1) { // Check IOC is ready to be access pthread_mutex_lock(&m_ioc); while (1) { if (pal_is_ioc_ready(iocd_config.bus_id) == true) { if (iocd_config.check_pec == false) { ret = check_ioc_support_pec(IOC_SLAVE_ADDRESS); if (ret < 0) { is_failed = true; set_ioc_temp(READ_IOC_TEMP_FAILED); kv_set(iocd_config.ioc_ver_key, "", 0, 0); // Reattach i2c devcie if i2c write failed 2 times if (ret == WRITE_I2C_RAW_FAILED) { if (iocd_config.i2c_write_failed_cnt >= 2) { syslog(LOG_WARNING, "%s(): Failed to do i2c write, reattach i2c device on bus:%d", __func__, iocd_config.bus_id); if (reattach_i2c_device(slave_dev_path) < 0) { syslog(LOG_ERR, "%s(): Failed to reattach i2c device on bus:%d", __func__, iocd_config.bus_id); } else { iocd_config.i2c_write_failed_cnt = 0; } } else { iocd_config.i2c_write_failed_cnt++; } } sleep (10); continue; } } break; } is_failed = true; set_ioc_temp(READ_IOC_TEMP_FAILED); kv_set(iocd_config.ioc_ver_key, "", 0, 0); sleep(1); } if (is_failed == true) { if (vdm_reset(IOC_SLAVE_ADDRESS) < 0) { syslog(LOG_WARNING, "%s(): failed to get %s IOC temperature because VDM reset failed", __func__, iocd_config.fru_name); pthread_mutex_unlock(&m_ioc); iocd_config.check_pec = false; sleep (1); continue; } tag = 0x01; is_failed = false; iocd_config.i2c_write_failed_cnt = 0; } value = 0; memset(read_buf, 0, sizeof(read_buf)); if (mctp_ioc_command(IOC_SLAVE_ADDRESS, tag, GET_IOC_TEMP, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): failed to get %s IOC temperature", __func__, iocd_config.fru_name); is_failed = true; } memset(read_buf, 0, sizeof(read_buf)); if (mctp_ioc_command(IOC_SLAVE_ADDRESS, tag, COMMAND_RESUME, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): failed to get %s IOC temperature because command resume failed", __func__, iocd_config.fru_name); is_failed = true; } if ((is_failed == false) && (read_buf[RES_NUM_SENSOR_OFFSET] == 0x01) && (read_buf[RES_IOC_TEMP_VALID_OFFSET] == 0x01)) { value = (int)read_buf[RES_IOC_TEMP_OFFSET]; } else { syslog(LOG_WARNING, "%s(): failed to get the correct %s IOC temperature", __func__, iocd_config.fru_name); } memset(read_buf, 0, sizeof(read_buf)); if (mctp_ioc_command(IOC_SLAVE_ADDRESS, tag, COMMAND_DONE, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): failed to get %s IOC temperature because command done failed", __func__, iocd_config.fru_name); is_failed = true; } pthread_mutex_unlock(&m_ioc); if (is_failed == false) { set_ioc_temp(value); } else { iocd_config.check_pec = false; set_ioc_temp(READ_IOC_TEMP_FAILED); } tag += 1; if (tag == 0xFF) { tag = 0x01; } sleep (2); } cleanup: if (access(slave_dev_path, F_OK) == 0) { i2c_delete_device(iocd_config.bus_id, DEVICE_ADDRESS); } if (iocd_config.i2c_fd >= 0) { close(iocd_config.i2c_fd); } if (iocd_config.bmc_slave != NULL) { i2c_mslave_close(iocd_config.bmc_slave); } return NULL; } int get_ioc_fw_ver(uint8_t slave_addr, uint8_t *res_buf, uint8_t *res_len) { bool is_failed = false; uint8_t tag = 0x01, read_len = 0; uint8_t read_buf[MCTP_MAX_READ_SIZE] = {0}; uint8_t ioc_fw_ver[IOC_FW_VER_SIZE] = {0}; char ioc_fw_ver_str[MAX_VALUE_LEN] = {0}; if ((res_buf == NULL) || (res_len == NULL)) { syslog(LOG_WARNING, "%s(): Failed to get %s IOC firmware version due to NULL parameter.", __func__, iocd_config.fru_name); return -1; } if (iocd_config.check_pec == false) { syslog(LOG_WARNING, "%s(): Failed to get %s IOC firmware version because IOC firmware is not ready.", __func__, iocd_config.fru_name); return -1; } if (pal_get_cached_value(iocd_config.ioc_ver_key, ioc_fw_ver_str) != 0) { syslog(LOG_WARNING, "%s(): Failed to get %s IOC firmware version due to kv get failed.", __func__, iocd_config.fru_name); return -1; } // Get IOC firmware version from IOC if the cache is empty if (strncmp(ioc_fw_ver_str, "", sizeof(ioc_fw_ver_str)) == 0) { pthread_mutex_lock(&m_ioc); if (vdm_reset(slave_addr) < 0) { syslog(LOG_WARNING, "%s(): failed to get %s IOC firmware version because VDM reset failed", __func__, iocd_config.fru_name); pthread_mutex_unlock(&m_ioc); return -1; } memset(read_buf, 0, sizeof(read_buf)); if (mctp_ioc_command(slave_addr, tag, GET_IOC_FW, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): failed to get %s IOC firmware version", __func__, iocd_config.fru_name); is_failed = true; } memset(read_buf, 0, sizeof(read_buf)); if (mctp_ioc_command(slave_addr, tag, COMMAND_RESUME, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): failed to get %s IOC firmware version because command resume failed", __func__, iocd_config.fru_name); is_failed = true; } if ((is_failed == false) && (read_len >= ioc_command_map[GET_IOC_FW].pkt_pl_hdr.response_len[0])) { memcpy(ioc_fw_ver, &read_buf[RES_IOC_FW_VER_OFFSET], sizeof(ioc_fw_ver)); } else { syslog(LOG_WARNING, "%s(): failed to get the correct %s IOC firmware version.", __func__, iocd_config.fru_name); is_failed = true; } memset(read_buf, 0, sizeof(read_buf)); if (mctp_ioc_command(slave_addr, tag, COMMAND_DONE, read_buf, &read_len) < 0) { syslog(LOG_WARNING, "%s(): failed to get %s IOC firmware version because command done failed", __func__, iocd_config.fru_name); is_failed = true; } pthread_mutex_unlock(&m_ioc); if (is_failed == true) { *res_len = 0; return -1; } snprintf(ioc_fw_ver_str, sizeof(ioc_fw_ver_str), "%d.%d%d%d.%02d-0000", ioc_fw_ver[7], ioc_fw_ver[6], ioc_fw_ver[5], ioc_fw_ver[4], ioc_fw_ver[0]); if (pal_set_cached_value(iocd_config.ioc_ver_key, ioc_fw_ver_str) < 0) { return -1; } } res_buf[0] = IOC_FW_VER_SIZE; *res_len = 1; return 0; } int conn_handler(client_t *cli) { uint8_t req_buf[MCTP_MAX_WRITE_SIZE] = {0}; uint8_t res_buf[MCTP_MAX_READ_SIZE] = {0}; size_t req_len = IOC_FW_VER_SIZE, res_len = 0; memset(req_buf, 0, sizeof(req_buf)); memset(res_buf, 0, sizeof(res_buf)); if (cli == NULL) { syslog(LOG_WARNING, "%s(): get %s IOC firmware version failed due to NULL parameter", __func__, iocd_config.fru_name); return -1; } if (ipc_recv_req(cli, req_buf, &req_len, TIMEOUT_IOC)) { syslog(LOG_WARNING, "%s(): get %s IOC firmware version failed because IPC recvive failed", __func__, iocd_config.fru_name); return -1; } if (get_ioc_fw_ver(IOC_SLAVE_ADDRESS, res_buf, (uint8_t*)&res_len) < 0) { syslog(LOG_WARNING, "%s(): get %s IOC firmware version failed", __func__, iocd_config.fru_name); return -1; } if (res_len == 0) { syslog(LOG_WARNING, "%s(): get %s IOC firmware version failed due to the wrong response length", __func__, iocd_config.fru_name); return -1; } if(ipc_send_resp(cli, res_buf, res_len)) { syslog(LOG_WARNING, "%s(): get %s IOC firmware version failed because IPC send failed", __func__, iocd_config.fru_name); return -1; } return 0; } static void run_iocd() { char ioc_socket[MAX_SOCK_PATH_SIZE] = {0}; pthread_t tid_get_ioc_temp, tid_get_ioc_fw_ver; int ret_get_ioc_temp = 0, ret_get_ioc_fw = 0; // Create thread for getting IOC temperature if (pthread_create(&tid_get_ioc_temp, NULL, get_ioc_temp, (void *)&iocd_config.bus_id) < 0) { syslog(LOG_WARNING, "Failed to create thread for getting %s IOC temperature error", iocd_config.fru_name); ret_get_ioc_temp = -1; } // Create IPC socket to receive and send IOC firmware version to fw-util. memset(ioc_socket, 0, sizeof(ioc_socket)); snprintf(ioc_socket, sizeof(ioc_socket), SOCK_PATH_IOC, iocd_config.fru_num); if (ipc_start_svc(ioc_socket, conn_handler, MAX_REQUESTS, NULL, &tid_get_ioc_fw_ver) < 0) { syslog(LOG_WARNING, "Failed to create ipc socket for %s IOC firmware version", iocd_config.fru_name); ret_get_ioc_fw = -1; } if (ret_get_ioc_temp == 0) { pthread_join(tid_get_ioc_temp, NULL); } if (ret_get_ioc_fw == 0) { pthread_join(tid_get_ioc_fw_ver, NULL); } } int main (int argc, char * const argv[]) { int rc = 0, pid_file = 0; char pid_file_path[MAX_PID_PATH_SIZE] = {0}; // Parse command line arguments. if (parse_cmdline_args(argc, argv) != 0) { syslog(LOG_ERR, "Fail to execute iocd because fail to set up IOC Daemon config"); return -1; } memset(pid_file_path,0, sizeof(pid_file_path)); snprintf(pid_file_path, sizeof(pid_file_path), "/var/run/iocd_%d.pid", iocd_config.bus_id); pid_file = open(pid_file_path, O_CREAT | O_RDWR, 0666); rc = pal_flock_retry(pid_file); if (rc < 0) { if (EWOULDBLOCK == errno) { printf("Another iocd_%d instance is running...\n", iocd_config.bus_id); } else { syslog(LOG_ERR, "Fail to execute iocd_%d because %s", iocd_config.bus_id, strerror(errno)); } pal_unflock_retry(pid_file); close(pid_file); exit(EXIT_FAILURE); } else { syslog(LOG_INFO, "IOC bus:%d daemon started", iocd_config.bus_id); run_iocd(); } pal_unflock_retry(pid_file); close(pid_file); return 0; }