meta-facebook/meta-grandcanyon/recipes-grandcanyon/me-util/files/me-util.c (363 lines of code) (raw):

/* * me-util * * 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. */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <syslog.h> #include <stdint.h> #include <pthread.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <openbmc/ipmi.h> #include <openbmc/pal.h> #define MAX_CMD_LENGTH 64 #define MAX_ME_READ_BUFFER 1024 #define COUNT_ARGC_UTIL_FRU 2 const uint32_t NM_INTEL_MANUFANCTURER_ID = 0x000157; const uint32_t NM_POLICY_CORRECT_TIME_LIMIT = 0x000003e8; const uint16_t NM_POLICY_TRIGGER_LIMIT = 0x0000; const uint16_t NM_POLICY_STATISTICS_PERIOD = 0x0001; #define NM_STATISTICS_MODE_GLOBAL_POWER 0x01 #define NM_STATISTICS_DOMAIN_ID 0x00 #define NM_STATISTICS_POLICY_ID 0x00 #define NM_POLICY_DOMAIN_ID 0x00 #define NM_POLICY_POLICY_ID 0x02 #define NM_POLICY_POLICY_TYPE 0xb0 #define NM_POLICY_POLICY_EXCEPTION_ACTION 0x00 #define NM_POLICY_ENABLE_FLAG 0x05 enum { ME_ENABLE_DISABLE_NODE_MANAGER_POLICY = 0xC0, ME_SET_NODE_MANAGER_POLICY = 0xC1, ME_GET_NODE_MANAGER_STATISTICS= 0xC8, }; typedef struct { ipmi_req_t_common_header header; uint8_t intel_manufacturer_id[3]; uint8_t mode; uint8_t domain_id; uint8_t plicy_id; } me_get_nm_statistics_req; typedef struct { uint8_t cc; uint8_t intel_manufacturer_id[3]; uint8_t statistics_data[8]; uint8_t timestamp[4]; uint8_t statistics_period[4]; uint8_t domain_id; } me_get_nm_statistics_res; typedef struct { ipmi_req_t_common_header header; uint8_t intel_manufacturer_id[3]; uint8_t domain_id; uint8_t policy_id; uint8_t policy_type; uint8_t policy_exception_actions; uint8_t policy_target_limit[2]; uint8_t correction_time_limit[4]; uint8_t policy_trigger_limit[2]; uint8_t statistics_period[2]; } me_set_nm_policy_req; typedef struct { ipmi_req_t_common_header header; uint8_t intel_manufacturer_id[3]; uint8_t policy_enable_disable_flag; uint8_t domain_id; uint8_t policy_id; } me_enable_disable_nm_policy_req; static void print_usage_help(void) { printf("Usage: me-util server <[0..n]data_bytes_to_send>\n"); printf("Usage: me-util server <cmd>\n"); printf(" cmd list:\n"); printf(" --get_dev_id Get device ID\n"); printf(" --file <file> Input commands from file\n"); printf(" --get_nm_power_statistics Get node manager power statistics\n"); printf(" --set_nm_power_policy <limit power> Set node manager power policy\n"); } static int me_get_dev_id() { int ret = 0; ipmi_req_t_common_header req = {0}; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t rlen = 0; me_get_dev_id_res *res = (me_get_dev_id_res *)rbuf; req.netfn_lun = IPMI_NETFN_SHIFT(NETFN_APP_REQ); req.cmd = CMD_APP_GET_DEVICE_ID; ret = bic_me_xmit((uint8_t *)(&req), sizeof(ipmi_req_t_common_header), (uint8_t *)res, &rlen); if (ret < 0 ) { printf("ME transmission failed\n"); return ret; } if (res->cc != CC_SUCCESS) { printf("Fail to get device id: Completion Code: %02X\n", res->cc); return -1; } if (rlen != sizeof(me_get_dev_id_res)) { printf("Invalid response length of get device id: %d, expected: %d\n", rlen, sizeof(me_get_dev_id_res)); return -1; } printf("Device ID: %02x\n", res->ipmi_dev_id.dev_id); printf("Device Revision: %02x\n", res->ipmi_dev_id.dev_rev); printf("Firmware Revision: %02x %02x\n", res->ipmi_dev_id.fw_rev1, res->ipmi_dev_id.fw_rev2); printf("IPMI Version: %02x\n", res->ipmi_dev_id.ipmi_ver); printf("Additional Device Support: %02x\n", res->ipmi_dev_id.dev_support); printf("Manufacturer ID: %02x %02x %02x\n", res->ipmi_dev_id.mfg_id[0], res->ipmi_dev_id.mfg_id[1], res->ipmi_dev_id.mfg_id[2]); printf("Product ID: %02x %02x\n", res->ipmi_dev_id.prod_id[0], res->ipmi_dev_id.prod_id[1]); printf("Aux Firmware Revision: %02x %02x %02x %02x\n", res->ipmi_dev_id.aux_fw_rev[0], res->ipmi_dev_id.aux_fw_rev[1], res->ipmi_dev_id.aux_fw_rev[2], res->ipmi_dev_id.aux_fw_rev[3]); return ret; } static int me_get_nm_power_statistic() { int ret = 0; me_get_nm_statistics_req req; me_get_nm_statistics_res res; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t tlen = 0, rlen = 0; uint32_t timestamp = 0; char time[MAX_VALUE_LEN] = {0}; struct tm ts = {0}; memset(&req, 0, sizeof(me_get_nm_statistics_req)); req.header.netfn_lun = IPMI_NETFN_SHIFT(NETFN_NM_REQ); req.header.cmd = ME_GET_NODE_MANAGER_STATISTICS; memcpy(&req.intel_manufacturer_id, (uint8_t *) &NM_INTEL_MANUFANCTURER_ID, sizeof(req.intel_manufacturer_id)); req.mode = NM_STATISTICS_MODE_GLOBAL_POWER; req.domain_id = NM_STATISTICS_DOMAIN_ID; req.plicy_id = NM_STATISTICS_POLICY_ID; tlen = sizeof(me_get_nm_statistics_req); ret = bic_me_xmit((uint8_t *)(&req), tlen, (uint8_t *)rbuf, &rlen); if (ret < 0) { printf("%s(): ME transmission failed\n", __func__); return ret; } if (rlen == sizeof(me_get_nm_statistics_res)) { memset(&res, 0, rlen); memcpy(&res, rbuf, rlen); } else { printf("Invalid response length of get node manager statistic: %d, expected: %zu\n", rlen, sizeof(me_get_nm_statistics_res)); return -1; } if (res.cc != CC_SUCCESS) { printf("Fail to get node manager statistic: Completion Code: %02X\n", res.cc); return -1; } timestamp |= res.timestamp[0]; timestamp |= res.timestamp[1] << 8; timestamp |= res.timestamp[2] << 16; timestamp |= res.timestamp[3] << 24; snprintf(time, sizeof(time), "%u", timestamp); memset(&ts, 0, sizeof(struct tm)); strptime(time, "%s", &ts); memset(&time, 0, sizeof(time)); strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", &ts); printf("Manufacturer ID: %02X %02X %02X\n", res.intel_manufacturer_id[0], res.intel_manufacturer_id[1], res.intel_manufacturer_id[2]); printf("Statistics Power data:\n"); printf(" Current Value: %3d Watts\n", (res.statistics_data[1] << 8) + res.statistics_data[0]); printf(" Minimum Value: %3d Watts\n", (res.statistics_data[3] << 8) + res.statistics_data[2]); printf(" Maximum Value: %3d Watts\n", (res.statistics_data[5] << 8) + res.statistics_data[4]); printf(" Average Value: %3d Watts\n", (res.statistics_data[7] << 8) + res.statistics_data[6]); printf("Timestamp: %s\n", time); printf("Statistics Reporting Period: %02X %02X %02X %02X\n", res.statistics_period[0], res.statistics_period[1], res.statistics_period[2], res.statistics_period[3]); printf("Domain ID | Policy State: %02X\n", res.domain_id); return ret; } static int me_set_nm_power_policy(char *limit_power) { me_set_nm_policy_req set_req; me_enable_disable_nm_policy_req enable_req; uint16_t power = 0; uint8_t rbuf[MAX_IPMB_RES_LEN] = {0}; uint8_t tlen = 0, rlen = 0; int ret = 0; if (limit_power == NULL) { printf("Fail to set node manager policy because parameter *limit_power is NULL\n"); return -1; } if ((atoi(limit_power) >= 0) && (atoi(limit_power) < 0xFFFF)) { power = atoi(limit_power); } else { printf("Fail to set node manager policy because limit: %s out of range 0~0xFFFF\n", limit_power); return -1; } // set node manager policy config memset(&set_req, 0, sizeof(me_set_nm_policy_req)); set_req.header.netfn_lun = IPMI_NETFN_SHIFT(NETFN_NM_REQ); set_req.header.cmd = ME_SET_NODE_MANAGER_POLICY; memcpy(&set_req.intel_manufacturer_id, (uint8_t *) &NM_INTEL_MANUFANCTURER_ID, sizeof(set_req.intel_manufacturer_id)); set_req.domain_id = NM_POLICY_DOMAIN_ID; set_req.policy_id = NM_POLICY_POLICY_ID; set_req.policy_type = NM_POLICY_POLICY_TYPE; set_req.policy_exception_actions = NM_POLICY_POLICY_EXCEPTION_ACTION; memcpy(&set_req.policy_target_limit, &power, sizeof(set_req.policy_target_limit)); memcpy(&set_req.correction_time_limit, (uint8_t *) &NM_POLICY_CORRECT_TIME_LIMIT, sizeof(set_req.correction_time_limit)); memcpy(&set_req.policy_trigger_limit, (uint8_t *) &NM_POLICY_TRIGGER_LIMIT, sizeof(set_req.policy_trigger_limit)); memcpy(&set_req.statistics_period, (uint8_t *) &NM_POLICY_STATISTICS_PERIOD, sizeof(set_req.statistics_period)); tlen = sizeof(me_set_nm_policy_req); ret = bic_me_xmit((uint8_t *)(&set_req), tlen, (uint8_t *)rbuf, &rlen); if (ret < 0) { printf("%s(): ME transmission failed cmd:%d\n", __func__, ME_SET_NODE_MANAGER_POLICY); return ret; } if (rbuf[0] != CC_SUCCESS) { printf("Fail to set node manager policy config: Completion Code: %02X\n", rbuf[0]); return -1; } // enable node manager policy memset(&enable_req, 0, sizeof(me_enable_disable_nm_policy_req)); enable_req.header.netfn_lun = IPMI_NETFN_SHIFT(NETFN_NM_REQ); enable_req.header.cmd = ME_ENABLE_DISABLE_NODE_MANAGER_POLICY; memcpy(&enable_req.intel_manufacturer_id, (uint8_t *) &NM_INTEL_MANUFANCTURER_ID, sizeof(enable_req.intel_manufacturer_id)); enable_req.policy_enable_disable_flag = NM_POLICY_ENABLE_FLAG; enable_req.domain_id = NM_POLICY_DOMAIN_ID; enable_req.policy_id = NM_POLICY_POLICY_ID; tlen = sizeof(me_enable_disable_nm_policy_req); ret = bic_me_xmit((uint8_t *)(&enable_req), tlen, (uint8_t *)rbuf, &rlen); if (ret < 0) { printf("%s(): ME transmission failed cmd:%d\n", __func__, ME_SET_NODE_MANAGER_POLICY); return ret; } if (rbuf[0] != CC_SUCCESS) { printf("Fail to enable node manager policy config: Completion Code: %02X\n", rbuf[0]); return -1; } return ret; } static int process_command(int cmd_length, char **cmd_buf) { int i = 0, ret = 0; uint8_t tbuf[MAX_IPMB_REQ_LEN] = {0}; uint8_t rbuf[MAX_IPMB_RES_LEN + 2] = {0}; uint8_t tlen = 0; uint8_t rlen = 0; me_xmit_res *res = (me_xmit_res *)rbuf; if (cmd_length > MAX_IPMB_REQ_LEN) { printf("Command length(%d) is out of IPMI Spec.(%d)\n", cmd_length, MAX_IPMB_REQ_LEN); return -1; } if (cmd_buf == NULL) { printf("Command buffer is NULL, abort\n"); return -1; } for (i = 0; i < cmd_length; i++) { tbuf[tlen++] = (uint8_t)strtoul(cmd_buf[i], NULL, 0); } ret = bic_me_xmit(tbuf, tlen, rbuf, &rlen); if (ret < 0) { printf("ME transmission failed\n"); return ret; } if (res->cc != CC_SUCCESS) { printf("Completion Code: %02X \n", res->cc); return -1; } if (rlen == 0) { return ret; } if (rlen > MAX_IPMB_RES_LEN) { printf("Response data length(%u) is out of IPMI Spec. (%d)\n", rlen, MAX_IPMB_RES_LEN); return -1; } for (i = 0; i < (rlen - 1); i++) { printf("%02X ", res->data[i]); } printf("\n"); return ret; } static int process_file(char *path) { FILE *fp = NULL; int cmd_length = 0; char buf[MAX_ME_READ_BUFFER] = {0}; char *str = NULL, *next = NULL, *del=" \n"; char *cmd_buf[MAX_CMD_LENGTH] = {NULL}; if (path == NULL) { printf("%s Invalid parameter: path is null\n", __func__); return -1; } if ((fp = fopen(path, "r")) == NULL) { syslog(LOG_WARNING, "Failed to open %s", path); return -1; } while (fgets(buf, sizeof(buf), fp) != NULL) { str = strtok_r(buf, del, &next); for (cmd_length = 0; cmd_length < MAX_CMD_LENGTH && str; cmd_length++, str = strtok_r(NULL, del, &next)) { if (str[0] == '#') { break; } if (cmd_length == 0 && strcmp(str, "echo") == 0) { if ((*next) != 0) { printf("%s", next); } else { printf("\n"); } break; } cmd_buf[cmd_length] = str; } if (cmd_length < 1) { continue; } process_command(cmd_length, cmd_buf); } fclose(fp); return 0; } static int do_cmds(char *cmd) { int ret = -1; if (cmd == NULL) { printf("Invalid parameter: cmd is null\n"); return ret; } if ((strcmp(cmd, "--get_dev_id") == 0)) { return me_get_dev_id(); } else if (strcmp(cmd, "--get_nm_power_statistics") == 0) { return me_get_nm_power_statistic(); } else { print_usage_help(); } return ret; } int main(int argc, char **argv) { uint8_t present_status = 0; int cmd_length = 0; if (argc < 3) { goto err_exit; } if (strcmp(argv[1], "server") != 0) { goto err_exit; } if (pal_is_fru_prsnt(FRU_SERVER, &present_status) < 0) { printf("Fail to get %s present status\n", argv[1]); } if (present_status == FRU_ABSENT) { printf("%s is not present!\n", argv[1]); return -1; } if ((strcmp(argv[2], "--file") == 0)) { if (argc < 4) { goto err_exit; } return process_file(argv[3]); } else if (strcmp(argv[2], "--set_nm_power_policy") == 0) { if (argc < 4) { goto err_exit; } return me_set_nm_power_policy(argv[3]); } else if ((start_with(argv[2], "--") == true)) { return do_cmds(argv[2]); } else { if (argc < 4) { goto err_exit; } cmd_length = argc - COUNT_ARGC_UTIL_FRU; return process_command(cmd_length, (argv + COUNT_ARGC_UTIL_FRU)); } err_exit: print_usage_help(); return -1; }