1 /* 2 * Copyright (c) 2018 Intel Corporation. 3 * Copyright (c) 2018-present Facebook. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 #include <ipmid/api.h> 19 20 #include <sys/stat.h> 21 #include <fcntl.h> 22 #include <unistd.h> 23 #include <nlohmann/json.hpp> 24 #include <commandutils.hpp> 25 #include <iostream> 26 #include <iomanip> 27 #include <sstream> 28 #include <fstream> 29 #include <phosphor-logging/log.hpp> 30 #include <sdbusplus/message/types.hpp> 31 #include <appcommands.hpp> 32 33 namespace ipmi 34 { 35 36 static void registerAPPFunctions() __attribute__((constructor)); 37 static constexpr size_t GUID_SIZE = 16; 38 // TODO Make offset and location runtime configurable to ensure we 39 // can make each define their own locations. 40 static constexpr off_t OFFSET_SYS_GUID = 0x17F0; 41 static constexpr const char *FRU_EEPROM = "/sys/bus/i2c/devices/6-0054/eeprom"; 42 43 // TODO: Need to store this info after identifying proper storage 44 static uint8_t globEna = 0x09; 45 static SysInfoParam sysInfoParams; 46 nlohmann::json appData __attribute__((init_priority(101))); 47 48 void printGUID(uint8_t *guid, off_t offset) 49 { 50 std::cout << "Read GUID from offset : " << offset << " :\n"; 51 for (int i = 0; i < GUID_SIZE; i++) 52 { 53 int data = guid[i]; 54 std::cout << std::hex << data << " "; 55 } 56 std::cout << std::endl; 57 } 58 59 int getGUID(off_t offset, uint8_t *guid) 60 { 61 int fd = -1; 62 ssize_t bytes_rd; 63 int ret = 0; 64 65 errno = 0; 66 67 // Check if file is present 68 if (access(FRU_EEPROM, F_OK) == -1) 69 { 70 std::cerr << "Unable to access: " << FRU_EEPROM << std::endl; 71 return errno; 72 } 73 74 // Open the file 75 fd = open(FRU_EEPROM, O_RDONLY); 76 if (fd == -1) 77 { 78 std::cerr << "Unable to open: " << FRU_EEPROM << std::endl; 79 return errno; 80 } 81 82 // seek to the offset 83 lseek(fd, offset, SEEK_SET); 84 85 // Read bytes from location 86 bytes_rd = read(fd, guid, GUID_SIZE); 87 if (bytes_rd != GUID_SIZE) 88 { 89 phosphor::logging::log<phosphor::logging::level::ERR>( 90 "GUID read data from EEPROM failed"); 91 ret = errno; 92 } 93 else 94 { 95 printGUID(guid, offset); 96 } 97 close(fd); 98 return ret; 99 } 100 101 int getSystemGUID(uint8_t *guid) 102 { 103 return getGUID(OFFSET_SYS_GUID, guid); 104 } 105 106 //---------------------------------------------------------------------- 107 // Get Self Test Results (IPMI/Section 20.4) (CMD_APP_GET_SELFTEST_RESULTS) 108 //---------------------------------------------------------------------- 109 ipmi_ret_t ipmiAppGetSTResults(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 110 ipmi_request_t request, ipmi_response_t response, 111 ipmi_data_len_t data_len, ipmi_context_t context) 112 { 113 uint8_t *res = reinterpret_cast<uint8_t *>(response); 114 115 // TODO: Following data needs to be updated based on self-test results 116 *res++ = 0x55; // Self-Test result 117 *res++ = 0x00; // Extra error info in case of failure 118 119 *data_len = 2; 120 121 return IPMI_CC_OK; 122 } 123 124 //---------------------------------------------------------------------- 125 // Manufacturing Test On (IPMI/Section 20.5) (CMD_APP_MFR_TEST_ON) 126 //---------------------------------------------------------------------- 127 ipmi_ret_t ipmiAppMfrTestOn(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 128 ipmi_request_t request, ipmi_response_t response, 129 ipmi_data_len_t data_len, ipmi_context_t context) 130 { 131 uint8_t *req = reinterpret_cast<uint8_t *>(request); 132 std::string mfrTest = "sled-cycle"; 133 134 if (!memcmp(req, mfrTest.data(), mfrTest.length()) && 135 (*data_len == mfrTest.length())) 136 { 137 /* sled-cycle the BMC */ 138 system("/usr/sbin/power-util sled-cycle"); 139 } 140 else 141 { 142 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; 143 } 144 145 *data_len = 0; 146 147 return IPMI_CC_OK; 148 } 149 150 //---------------------------------------------------------------------- 151 // Set Global Enables (CMD_APP_SET_GLOBAL_ENABLES) 152 //---------------------------------------------------------------------- 153 ipmi_ret_t ipmiAppSetGlobalEnables(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 154 ipmi_request_t request, 155 ipmi_response_t response, 156 ipmi_data_len_t data_len, 157 ipmi_context_t context) 158 { 159 uint8_t *req = reinterpret_cast<uint8_t *>(request); 160 161 globEna = *req; 162 *data_len = 0; 163 164 return IPMI_CC_OK; 165 } 166 167 //---------------------------------------------------------------------- 168 // Get Global Enables (CMD_APP_GET_GLOBAL_ENABLES) 169 //---------------------------------------------------------------------- 170 ipmi_ret_t ipmiAppGetGlobalEnables(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 171 ipmi_request_t request, 172 ipmi_response_t response, 173 ipmi_data_len_t data_len, 174 ipmi_context_t context) 175 { 176 uint8_t *res = reinterpret_cast<uint8_t *>(response); 177 178 *data_len = 1; 179 *res++ = globEna; 180 181 return IPMI_CC_OK; 182 } 183 184 //---------------------------------------------------------------------- 185 // Clear Message flags (IPMI/Section 22.3) (CMD_APP_CLEAR_MESSAGE_FLAGS) 186 //---------------------------------------------------------------------- 187 ipmi_ret_t ipmiAppClearMsgFlags(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 188 ipmi_request_t request, 189 ipmi_response_t response, 190 ipmi_data_len_t data_len, 191 ipmi_context_t context) 192 { 193 // Do Nothing and just return success 194 *data_len = 0; 195 196 return IPMI_CC_OK; 197 } 198 199 //---------------------------------------------------------------------- 200 // Get System GUID (CMD_APP_GET_SYS_GUID) 201 //---------------------------------------------------------------------- 202 ipmi_ret_t ipmiAppGetSysGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 203 ipmi_request_t request, ipmi_response_t response, 204 ipmi_data_len_t data_len, ipmi_context_t context) 205 { 206 uint8_t *res = reinterpret_cast<uint8_t *>(response); 207 if (getSystemGUID(res)) 208 { 209 return IPMI_CC_UNSPECIFIED_ERROR; 210 } 211 *data_len = GUID_SIZE; 212 return IPMI_CC_OK; 213 } 214 215 //---------------------------------------------------------------------- 216 // Platform specific functions for storing app data 217 //---------------------------------------------------------------------- 218 219 void flush_app_data() 220 { 221 std::ofstream file(JSON_APP_DATA_FILE); 222 file << appData; 223 file.close(); 224 return; 225 } 226 227 static int platSetSysFWVer(uint8_t *ver) 228 { 229 std::stringstream ss; 230 int i; 231 232 /* TODO: implement byte 1: Set selector 233 * byte 2: encodeing, currently only supported 234 * ASCII which is value 0, UTF and unicode are 235 * not supported yet. 236 */ 237 if (ver[1] & 0x0f) 238 return -1; 239 240 for (i = 3; i < 3 + ver[2]; i++) 241 { 242 ss << (char)ver[i]; 243 } 244 245 appData[KEY_SYSFW_VER] = ss.str(); 246 flush_app_data(); 247 248 return 0; 249 } 250 251 static int platGetSysFWVer(uint8_t *ver) 252 { 253 std::string str = appData[KEY_SYSFW_VER].get<std::string>(); 254 int len; 255 256 *ver++ = 0; // byte 1: Set selector not supported 257 *ver++ = 0; // byte 2: Only ASCII supported 258 259 len = str.length(); 260 *ver++ = len; 261 memcpy(ver, str.data(), len); 262 263 return (len + 3); 264 } 265 266 //---------------------------------------------------------------------- 267 // Set Sys Info Params (IPMI/Sec 22.14a) (CMD_APP_SET_SYS_INFO_PARAMS) 268 //---------------------------------------------------------------------- 269 ipmi_ret_t ipmiAppSetSysInfoParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 270 ipmi_request_t request, 271 ipmi_response_t response, 272 ipmi_data_len_t data_len, 273 ipmi_context_t context) 274 { 275 uint8_t *req = reinterpret_cast<uint8_t *>(request); 276 277 uint8_t param = req[0]; 278 uint8_t req_len = *data_len; 279 280 *data_len = 0; 281 282 switch (param) 283 { 284 case SYS_INFO_PARAM_SET_IN_PROG: 285 sysInfoParams.set_in_prog = req[1]; 286 break; 287 case SYS_INFO_PARAM_SYSFW_VER: 288 memcpy(sysInfoParams.sysfw_ver, &req[1], SIZE_SYSFW_VER); 289 if (platSetSysFWVer(sysInfoParams.sysfw_ver)) 290 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; 291 break; 292 case SYS_INFO_PARAM_SYS_NAME: 293 memcpy(sysInfoParams.sys_name, &req[1], SIZE_SYS_NAME); 294 break; 295 case SYS_INFO_PARAM_PRI_OS_NAME: 296 memcpy(sysInfoParams.pri_os_name, &req[1], SIZE_OS_NAME); 297 break; 298 case SYS_INFO_PARAM_PRESENT_OS_NAME: 299 memcpy(sysInfoParams.present_os_name, &req[1], SIZE_OS_NAME); 300 break; 301 case SYS_INFO_PARAM_PRESENT_OS_VER: 302 memcpy(sysInfoParams.present_os_ver, &req[1], SIZE_OS_VER); 303 break; 304 case SYS_INFO_PARAM_BMC_URL: 305 memcpy(sysInfoParams.bmc_url, &req[1], SIZE_BMC_URL); 306 break; 307 case SYS_INFO_PARAM_OS_HV_URL: 308 memcpy(sysInfoParams.os_hv_url, &req[1], SIZE_OS_HV_URL); 309 break; 310 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST: 311 memcpy(sysInfoParams.bios_current_boot_list, &req[1], req_len); 312 appData[KEY_BIOS_BOOT_LEN] = req_len; 313 flush_app_data(); 314 break; 315 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE: 316 if (SIZE_BIOS_FIXED_BOOT_DEVICE != req_len) 317 break; 318 memcpy(sysInfoParams.bios_fixed_boot_device, &req[1], 319 SIZE_BIOS_FIXED_BOOT_DEVICE); 320 break; 321 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING: 322 if (SIZE_BIOS_RSTR_DFLT_SETTING != req_len) 323 break; 324 memcpy(sysInfoParams.bios_rstr_dflt_setting, &req[1], 325 SIZE_BIOS_RSTR_DFLT_SETTING); 326 break; 327 case SYS_INFO_PARAM_LAST_BOOT_TIME: 328 if (SIZE_LAST_BOOT_TIME != req_len) 329 break; 330 memcpy(sysInfoParams.last_boot_time, &req[1], SIZE_LAST_BOOT_TIME); 331 break; 332 default: 333 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; 334 break; 335 } 336 337 return IPMI_CC_OK; 338 } 339 340 //---------------------------------------------------------------------- 341 // Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS) 342 //---------------------------------------------------------------------- 343 ipmi_ret_t ipmiAppGetSysInfoParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 344 ipmi_request_t request, 345 ipmi_response_t response, 346 ipmi_data_len_t data_len, 347 ipmi_context_t context) 348 { 349 uint8_t *req = reinterpret_cast<uint8_t *>(request); 350 uint8_t *res = reinterpret_cast<uint8_t *>(response); 351 352 uint8_t param = req[1]; 353 uint8_t len; 354 355 *res++ = 1; // Parameter revision 356 *data_len = 1; 357 358 switch (param) 359 { 360 case SYS_INFO_PARAM_SET_IN_PROG: 361 *res++ = sysInfoParams.set_in_prog; 362 *data_len += 1; 363 break; 364 case SYS_INFO_PARAM_SYSFW_VER: 365 if ((len = platGetSysFWVer(res)) < 0) 366 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; 367 *data_len += SIZE_SYSFW_VER; 368 break; 369 case SYS_INFO_PARAM_SYS_NAME: 370 memcpy(res, sysInfoParams.sys_name, SIZE_SYS_NAME); 371 *data_len += SIZE_SYS_NAME; 372 break; 373 case SYS_INFO_PARAM_PRI_OS_NAME: 374 memcpy(res, sysInfoParams.pri_os_name, SIZE_OS_NAME); 375 *data_len += SIZE_OS_NAME; 376 break; 377 case SYS_INFO_PARAM_PRESENT_OS_NAME: 378 memcpy(res, sysInfoParams.present_os_name, SIZE_OS_NAME); 379 *data_len += SIZE_OS_NAME; 380 break; 381 case SYS_INFO_PARAM_PRESENT_OS_VER: 382 memcpy(res, sysInfoParams.present_os_ver, SIZE_OS_VER); 383 *data_len += SIZE_OS_VER; 384 break; 385 case SYS_INFO_PARAM_BMC_URL: 386 memcpy(res, sysInfoParams.bmc_url, SIZE_BMC_URL); 387 *data_len += SIZE_BMC_URL; 388 break; 389 case SYS_INFO_PARAM_OS_HV_URL: 390 memcpy(res, sysInfoParams.os_hv_url, SIZE_OS_HV_URL); 391 *data_len += SIZE_OS_HV_URL; 392 break; 393 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST: 394 len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>(); 395 memcpy(res, sysInfoParams.bios_current_boot_list, len); 396 *data_len += len; 397 break; 398 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE: 399 memcpy(res, sysInfoParams.bios_fixed_boot_device, 400 SIZE_BIOS_FIXED_BOOT_DEVICE); 401 *data_len += SIZE_BIOS_FIXED_BOOT_DEVICE; 402 break; 403 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING: 404 memcpy(res, sysInfoParams.bios_rstr_dflt_setting, 405 SIZE_BIOS_RSTR_DFLT_SETTING); 406 *data_len += SIZE_BIOS_RSTR_DFLT_SETTING; 407 break; 408 case SYS_INFO_PARAM_LAST_BOOT_TIME: 409 memcpy(res, sysInfoParams.last_boot_time, SIZE_LAST_BOOT_TIME); 410 *data_len += SIZE_LAST_BOOT_TIME; 411 break; 412 default: 413 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; 414 break; 415 } 416 return IPMI_CC_OK; 417 } 418 419 void registerAPPFunctions() 420 { 421 /* Get App data stored in json file */ 422 std::ifstream file(JSON_APP_DATA_FILE); 423 if (file) 424 { 425 file >> appData; 426 file.close(); 427 } 428 429 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL, 430 ipmiAppGetSTResults, 431 PRIVILEGE_USER); // Get Self Test Results 432 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL, 433 ipmiAppMfrTestOn, 434 PRIVILEGE_USER); // Manufacturing Test On 435 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL, 436 ipmiAppSetGlobalEnables, 437 PRIVILEGE_USER); // Set Global Enables 438 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL, 439 ipmiAppGetGlobalEnables, 440 PRIVILEGE_USER); // Get Global Enables 441 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL, 442 ipmiAppClearMsgFlags, 443 PRIVILEGE_USER); // Clear Message flags 444 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL, 445 ipmiAppGetSysGUID, 446 PRIVILEGE_USER); // Get System GUID 447 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_SYS_INFO_PARAMS, NULL, 448 ipmiAppSetSysInfoParams, 449 PRIVILEGE_USER); // Set Sys Info Params 450 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_INFO_PARAMS, NULL, 451 ipmiAppGetSysInfoParams, 452 PRIVILEGE_USER); // Get Sys Info Params 453 return; 454 } 455 456 } // namespace ipmi 457