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