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 <fcntl.h> 19 #include <ipmid/api.h> 20 #include <sys/stat.h> 21 #include <unistd.h> 22 23 #include <appcommands.hpp> 24 #include <commandutils.hpp> 25 #include <ipmid/api-types.hpp> 26 #include <ipmid/api.hpp> 27 #include <nlohmann/json.hpp> 28 #include <phosphor-logging/log.hpp> 29 #include <sdbusplus/message/types.hpp> 30 31 #include <fstream> 32 #include <iomanip> 33 #include <iostream> 34 #include <sstream> 35 36 namespace ipmi 37 { 38 39 static void registerAPPFunctions() __attribute__((constructor)); 40 static constexpr size_t GUID_SIZE = 16; 41 // TODO Make offset and location runtime configurable to ensure we 42 // can make each define their own locations. 43 static constexpr off_t OFFSET_SYS_GUID = 0x17F0; 44 static constexpr const char* FRU_EEPROM = "/sys/bus/i2c/devices/6-0054/eeprom"; 45 46 // TODO: Need to store this info after identifying proper storage 47 static uint8_t globEna = 0x09; 48 static SysInfoParam sysInfoParams; 49 nlohmann::json appData __attribute__((init_priority(101))); 50 51 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&, 52 std::vector<uint8_t>&); 53 54 static inline auto responseSystemInfoParamterNotSupportCommand() 55 { 56 return response(IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED); 57 } 58 59 void printGUID(uint8_t* guid, off_t offset) 60 { 61 std::cout << "Read GUID from offset : " << offset << " :\n"; 62 for (size_t i = 0; i < GUID_SIZE; i++) 63 { 64 int data = guid[i]; 65 std::cout << std::hex << data << " "; 66 } 67 std::cout << std::endl; 68 } 69 70 int getGUID(off_t offset, uint8_t* guid) 71 { 72 int fd = -1; 73 ssize_t bytes_rd; 74 int ret = 0; 75 std::string eepromPath = FRU_EEPROM; 76 77 // find the eeprom path of MB FRU 78 auto device = getMbFruDevice(); 79 if (device) 80 { 81 auto [bus, address] = *device; 82 std::stringstream ss; 83 ss << "/sys/bus/i2c/devices/" << static_cast<int>(bus) << "-" 84 << std::setw(4) << std::setfill('0') << std::hex 85 << static_cast<int>(address) << "/eeprom"; 86 eepromPath = ss.str(); 87 } 88 89 errno = 0; 90 91 // Check if file is present 92 if (access(eepromPath.c_str(), F_OK) == -1) 93 { 94 std::cerr << "Unable to access: " << eepromPath << std::endl; 95 return errno; 96 } 97 98 // Open the file 99 fd = open(eepromPath.c_str(), O_RDONLY); 100 if (fd == -1) 101 { 102 std::cerr << "Unable to open: " << eepromPath << std::endl; 103 return errno; 104 } 105 106 // seek to the offset 107 lseek(fd, offset, SEEK_SET); 108 109 // Read bytes from location 110 bytes_rd = read(fd, guid, GUID_SIZE); 111 if (bytes_rd != GUID_SIZE) 112 { 113 phosphor::logging::log<phosphor::logging::level::ERR>( 114 "GUID read data from EEPROM failed"); 115 ret = errno; 116 } 117 else 118 { 119 printGUID(guid, offset); 120 } 121 close(fd); 122 return ret; 123 } 124 125 int getSystemGUID(uint8_t* guid) 126 { 127 return getGUID(OFFSET_SYS_GUID, guid); 128 } 129 130 //---------------------------------------------------------------------- 131 // Get Self Test Results (IPMI/Section 20.4) (CMD_APP_GET_SELFTEST_RESULTS) 132 //---------------------------------------------------------------------- 133 ipmi_ret_t ipmiAppGetSTResults(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, 134 ipmi_response_t response, 135 ipmi_data_len_t data_len, ipmi_context_t) 136 { 137 uint8_t* res = reinterpret_cast<uint8_t*>(response); 138 139 // TODO: Following data needs to be updated based on self-test results 140 *res++ = 0x55; // Self-Test result 141 *res++ = 0x00; // Extra error info in case of failure 142 143 *data_len = 2; 144 145 return IPMI_CC_OK; 146 } 147 148 //---------------------------------------------------------------------- 149 // Manufacturing Test On (IPMI/Section 20.5) (CMD_APP_MFR_TEST_ON) 150 //---------------------------------------------------------------------- 151 ipmi_ret_t ipmiAppMfrTestOn(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 152 ipmi_response_t, ipmi_data_len_t data_len, 153 ipmi_context_t) 154 { 155 uint8_t* req = reinterpret_cast<uint8_t*>(request); 156 std::string mfrTest = "sled-cycle"; 157 ipmi_ret_t rc = IPMI_CC_OK; 158 159 if (!memcmp(req, mfrTest.data(), mfrTest.length()) && 160 (*data_len == mfrTest.length())) 161 { 162 /* sled-cycle the BMC */ 163 auto ret = system("/usr/sbin/power-util sled-cycle"); 164 if (ret) 165 { 166 rc = IPMI_CC_UNSPECIFIED_ERROR; 167 } 168 } 169 else 170 { 171 rc = IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; 172 } 173 174 *data_len = 0; 175 176 return rc; 177 } 178 179 //---------------------------------------------------------------------- 180 // Set Global Enables (CMD_APP_SET_GLOBAL_ENABLES) 181 //---------------------------------------------------------------------- 182 ipmi_ret_t ipmiAppSetGlobalEnables(ipmi_netfn_t, ipmi_cmd_t, 183 ipmi_request_t request, ipmi_response_t, 184 ipmi_data_len_t data_len, ipmi_context_t) 185 { 186 uint8_t* req = reinterpret_cast<uint8_t*>(request); 187 188 globEna = *req; 189 *data_len = 0; 190 191 return IPMI_CC_OK; 192 } 193 194 //---------------------------------------------------------------------- 195 // Get Global Enables (CMD_APP_GET_GLOBAL_ENABLES) 196 //---------------------------------------------------------------------- 197 ipmi_ret_t ipmiAppGetGlobalEnables(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, 198 ipmi_response_t response, 199 ipmi_data_len_t data_len, ipmi_context_t) 200 { 201 uint8_t* res = reinterpret_cast<uint8_t*>(response); 202 203 *data_len = 1; 204 *res++ = globEna; 205 206 return IPMI_CC_OK; 207 } 208 209 //---------------------------------------------------------------------- 210 // Clear Message flags (IPMI/Section 22.3) (CMD_APP_CLEAR_MESSAGE_FLAGS) 211 //---------------------------------------------------------------------- 212 ipmi_ret_t ipmiAppClearMsgFlags(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, 213 ipmi_response_t, ipmi_data_len_t data_len, 214 ipmi_context_t) 215 { 216 // Do Nothing and just return success 217 *data_len = 0; 218 219 return IPMI_CC_OK; 220 } 221 222 //---------------------------------------------------------------------- 223 // Get System GUID (CMD_APP_GET_SYS_GUID) 224 //---------------------------------------------------------------------- 225 #if BIC_ENABLED 226 ipmi::RspType<std::vector<uint8_t>> 227 ipmiAppGetSysGUID(ipmi::Context::ptr ctx, std::vector<uint8_t> reqData) 228 229 { 230 std::vector<uint8_t> respData; 231 232 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; 233 234 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData)) 235 return ipmi::responseUnspecifiedError(); 236 237 return ipmi::responseSuccess(respData); 238 } 239 240 #else 241 ipmi_ret_t ipmiAppGetSysGUID(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, 242 ipmi_response_t response, ipmi_data_len_t data_len, 243 ipmi_context_t) 244 { 245 uint8_t* res = reinterpret_cast<uint8_t*>(response); 246 if (getSystemGUID(res)) 247 { 248 return IPMI_CC_UNSPECIFIED_ERROR; 249 } 250 *data_len = GUID_SIZE; 251 return IPMI_CC_OK; 252 } 253 254 #endif 255 256 //---------------------------------------------------------------------- 257 // Platform specific functions for storing app data 258 //---------------------------------------------------------------------- 259 260 void flush_app_data() 261 { 262 std::ofstream file(JSON_APP_DATA_FILE); 263 file << appData; 264 file.close(); 265 return; 266 } 267 268 static int platSetSysFWVer(uint8_t* ver, const std::string key) 269 { 270 std::stringstream ss; 271 int i; 272 273 /* TODO: implement byte 1: Set selector 274 * byte 2: encodeing, currently only supported 275 * ASCII which is value 0, UTF and unicode are 276 * not supported yet. 277 */ 278 if (ver[1] & 0x0f) 279 return -1; 280 281 for (i = 3; i < 3 + ver[2]; i++) 282 { 283 ss << (char)ver[i]; 284 } 285 286 appData[key] = ss.str(); 287 flush_app_data(); 288 289 return 0; 290 } 291 292 static int platGetSysFWVer(std::vector<uint8_t>& respData, 293 const std::string key) 294 { 295 int len = -1; 296 297 if (!appData.contains(std::string(key))) 298 { 299 return -1; 300 } 301 std::string str = appData[key].get<std::string>(); 302 303 respData.push_back(0); // byte 1: Set selector not supported 304 respData.push_back(0); // byte 2: Only ASCII supported 305 306 len = str.length(); 307 respData.push_back(len); // byte 3: Size of version 308 309 for (auto c : str) 310 { 311 respData.push_back(c); 312 } 313 314 // Remaining byte fill to 0 315 for (int i = 0; i < SIZE_SYSFW_VER - (len + 3); i++) 316 { 317 respData.push_back(0); 318 } 319 320 return (len + 3); 321 } 322 323 //---------------------------------------------------------------------- 324 // Set Sys Info Params (IPMI/Sec 22.14a) (CMD_APP_SET_SYS_INFO_PARAMS) 325 //---------------------------------------------------------------------- 326 ipmi::RspType<uint8_t> ipmiAppSetSysInfoParams(ipmi::Context::ptr ctx, 327 std::vector<uint8_t> req) 328 { 329 uint8_t param = req[0]; 330 uint8_t req_len = req.size(); 331 std::optional<size_t> hostId = findHost(ctx->hostIdx); 332 333 if (!hostId) 334 { 335 phosphor::logging::log<phosphor::logging::level::ERR>( 336 "Invalid Host Id received"); 337 return ipmi::responseInvalidCommand(); 338 } 339 340 switch (param) 341 { 342 case SYS_INFO_PARAM_SET_IN_PROG: 343 sysInfoParams.set_in_prog = req[1]; 344 break; 345 case SYS_INFO_PARAM_SYSFW_VER: 346 { 347 memcpy(sysInfoParams.sysfw_ver, &req[1], SIZE_SYSFW_VER); 348 std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId); 349 if (platSetSysFWVer(sysInfoParams.sysfw_ver, version_key)) 350 return ipmi::responseSystemInfoParamterNotSupportCommand(); 351 break; 352 } 353 case SYS_INFO_PARAM_SYS_NAME: 354 memcpy(sysInfoParams.sys_name, &req[1], SIZE_SYS_NAME); 355 break; 356 case SYS_INFO_PARAM_PRI_OS_NAME: 357 memcpy(sysInfoParams.pri_os_name, &req[1], SIZE_OS_NAME); 358 break; 359 case SYS_INFO_PARAM_PRESENT_OS_NAME: 360 memcpy(sysInfoParams.present_os_name, &req[1], SIZE_OS_NAME); 361 break; 362 case SYS_INFO_PARAM_PRESENT_OS_VER: 363 memcpy(sysInfoParams.present_os_ver, &req[1], SIZE_OS_VER); 364 break; 365 case SYS_INFO_PARAM_BMC_URL: 366 memcpy(sysInfoParams.bmc_url, &req[1], SIZE_BMC_URL); 367 break; 368 case SYS_INFO_PARAM_OS_HV_URL: 369 memcpy(sysInfoParams.os_hv_url, &req[1], SIZE_OS_HV_URL); 370 break; 371 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST: 372 memcpy(sysInfoParams.bios_current_boot_list, &req[1], req_len); 373 appData[KEY_BIOS_BOOT_LEN] = req_len; 374 flush_app_data(); 375 break; 376 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE: 377 if (SIZE_BIOS_FIXED_BOOT_DEVICE != req_len) 378 break; 379 memcpy(sysInfoParams.bios_fixed_boot_device, &req[1], 380 SIZE_BIOS_FIXED_BOOT_DEVICE); 381 break; 382 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING: 383 if (SIZE_BIOS_RSTR_DFLT_SETTING != req_len) 384 break; 385 memcpy(sysInfoParams.bios_rstr_dflt_setting, &req[1], 386 SIZE_BIOS_RSTR_DFLT_SETTING); 387 break; 388 case SYS_INFO_PARAM_LAST_BOOT_TIME: 389 if (SIZE_LAST_BOOT_TIME != req_len) 390 break; 391 memcpy(sysInfoParams.last_boot_time, &req[1], SIZE_LAST_BOOT_TIME); 392 break; 393 default: 394 return ipmi::responseSystemInfoParamterNotSupportCommand(); 395 break; 396 } 397 398 return ipmi::responseSuccess(); 399 } 400 401 //---------------------------------------------------------------------- 402 // Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS) 403 //---------------------------------------------------------------------- 404 ipmi::RspType<std::vector<uint8_t>> 405 ipmiAppGetSysInfoParams(ipmi::Context::ptr ctx, uint8_t, uint8_t param, 406 uint8_t, uint8_t) 407 { 408 int len; 409 std::vector<uint8_t> respData; 410 respData.push_back(1); // Parameter revision 411 412 std::optional<size_t> hostId = findHost(ctx->hostIdx); 413 414 if (!hostId) 415 { 416 phosphor::logging::log<phosphor::logging::level::ERR>( 417 "Invalid Host Id received"); 418 return ipmi::responseInvalidCommand(); 419 } 420 421 switch (param) 422 { 423 case SYS_INFO_PARAM_SET_IN_PROG: 424 respData.push_back(sysInfoParams.set_in_prog); 425 break; 426 case SYS_INFO_PARAM_SYSFW_VER: 427 { 428 std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId); 429 if ((platGetSysFWVer(respData, version_key)) < 0) 430 return ipmi::responseSystemInfoParamterNotSupportCommand(); 431 break; 432 } 433 case SYS_INFO_PARAM_SYS_NAME: 434 respData.insert(respData.end(), std::begin(sysInfoParams.sys_name), 435 std::end(sysInfoParams.sys_name)); 436 break; 437 case SYS_INFO_PARAM_PRI_OS_NAME: 438 respData.insert(respData.end(), 439 std::begin(sysInfoParams.pri_os_name), 440 std::end(sysInfoParams.pri_os_name)); 441 break; 442 case SYS_INFO_PARAM_PRESENT_OS_NAME: 443 respData.insert(respData.end(), 444 std::begin(sysInfoParams.present_os_name), 445 std::end(sysInfoParams.present_os_name)); 446 break; 447 case SYS_INFO_PARAM_PRESENT_OS_VER: 448 respData.insert(respData.end(), 449 std::begin(sysInfoParams.present_os_ver), 450 std::end(sysInfoParams.present_os_ver)); 451 break; 452 case SYS_INFO_PARAM_BMC_URL: 453 respData.insert(respData.end(), std::begin(sysInfoParams.bmc_url), 454 std::end(sysInfoParams.bmc_url)); 455 break; 456 case SYS_INFO_PARAM_OS_HV_URL: 457 respData.insert(respData.end(), std::begin(sysInfoParams.os_hv_url), 458 std::end(sysInfoParams.os_hv_url)); 459 break; 460 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST: 461 len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>(); 462 respData.insert(respData.end(), 463 std::begin(sysInfoParams.bios_current_boot_list), 464 std::begin(sysInfoParams.bios_current_boot_list) + 465 len); 466 break; 467 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE: 468 respData.insert(respData.end(), 469 std::begin(sysInfoParams.bios_fixed_boot_device), 470 std::end(sysInfoParams.bios_fixed_boot_device)); 471 break; 472 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING: 473 respData.insert(respData.end(), 474 std::begin(sysInfoParams.bios_rstr_dflt_setting), 475 std::end(sysInfoParams.bios_rstr_dflt_setting)); 476 break; 477 case SYS_INFO_PARAM_LAST_BOOT_TIME: 478 respData.insert(respData.end(), 479 std::begin(sysInfoParams.last_boot_time), 480 std::end(sysInfoParams.last_boot_time)); 481 break; 482 default: 483 return ipmi::responseSystemInfoParamterNotSupportCommand(); 484 break; 485 } 486 487 return ipmi::responseSuccess(respData); 488 } 489 490 void registerAPPFunctions() 491 { 492 /* Get App data stored in json file */ 493 std::ifstream file(JSON_APP_DATA_FILE); 494 if (file) 495 { 496 file >> appData; 497 file.close(); 498 } 499 500 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL, 501 ipmiAppGetSTResults, 502 PRIVILEGE_USER); // Get Self Test Results 503 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL, 504 ipmiAppMfrTestOn, 505 PRIVILEGE_USER); // Manufacturing Test On 506 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL, 507 ipmiAppSetGlobalEnables, 508 PRIVILEGE_USER); // Set Global Enables 509 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL, 510 ipmiAppGetGlobalEnables, 511 PRIVILEGE_USER); // Get Global Enables 512 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL, 513 ipmiAppClearMsgFlags, 514 PRIVILEGE_USER); // Clear Message flags 515 #if BIC_ENABLED 516 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 517 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User, 518 ipmiAppGetSysGUID); 519 #else 520 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL, 521 ipmiAppGetSysGUID, 522 PRIVILEGE_USER); // Get System GUID 523 #endif 524 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 525 ipmi::app::cmdSetSystemInfoParameters, 526 ipmi::Privilege::User, ipmiAppSetSysInfoParams); 527 528 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 529 ipmi::app::cmdGetSystemInfoParameters, 530 ipmi::Privilege::User, ipmiAppGetSysInfoParams); 531 return; 532 } 533 534 } // namespace ipmi 535