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 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 uint8_t param = req[0]; 317 uint8_t req_len = req.size(); 318 std::optional<size_t> hostId = findHost(ctx->hostIdx); 319 320 if (!hostId) 321 { 322 phosphor::logging::log<phosphor::logging::level::ERR>( 323 "Invalid Host Id received"); 324 return ipmi::responseInvalidCommand(); 325 } 326 327 switch (param) 328 { 329 case SYS_INFO_PARAM_SET_IN_PROG: 330 sysInfoParams.set_in_prog = req[1]; 331 break; 332 case SYS_INFO_PARAM_SYSFW_VER: 333 { 334 memcpy(sysInfoParams.sysfw_ver, &req[1], SIZE_SYSFW_VER); 335 std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId); 336 if (platSetSysFWVer(sysInfoParams.sysfw_ver, version_key)) 337 return ipmi::responseSystemInfoParamterNotSupportCommand(); 338 break; 339 } 340 case SYS_INFO_PARAM_SYS_NAME: 341 memcpy(sysInfoParams.sys_name, &req[1], SIZE_SYS_NAME); 342 break; 343 case SYS_INFO_PARAM_PRI_OS_NAME: 344 memcpy(sysInfoParams.pri_os_name, &req[1], SIZE_OS_NAME); 345 break; 346 case SYS_INFO_PARAM_PRESENT_OS_NAME: 347 memcpy(sysInfoParams.present_os_name, &req[1], SIZE_OS_NAME); 348 break; 349 case SYS_INFO_PARAM_PRESENT_OS_VER: 350 memcpy(sysInfoParams.present_os_ver, &req[1], SIZE_OS_VER); 351 break; 352 case SYS_INFO_PARAM_BMC_URL: 353 memcpy(sysInfoParams.bmc_url, &req[1], SIZE_BMC_URL); 354 break; 355 case SYS_INFO_PARAM_OS_HV_URL: 356 memcpy(sysInfoParams.os_hv_url, &req[1], SIZE_OS_HV_URL); 357 break; 358 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST: 359 memcpy(sysInfoParams.bios_current_boot_list, &req[1], req_len); 360 appData[KEY_BIOS_BOOT_LEN] = req_len; 361 flush_app_data(); 362 break; 363 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE: 364 if (SIZE_BIOS_FIXED_BOOT_DEVICE != req_len) 365 break; 366 memcpy(sysInfoParams.bios_fixed_boot_device, &req[1], 367 SIZE_BIOS_FIXED_BOOT_DEVICE); 368 break; 369 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING: 370 if (SIZE_BIOS_RSTR_DFLT_SETTING != req_len) 371 break; 372 memcpy(sysInfoParams.bios_rstr_dflt_setting, &req[1], 373 SIZE_BIOS_RSTR_DFLT_SETTING); 374 break; 375 case SYS_INFO_PARAM_LAST_BOOT_TIME: 376 if (SIZE_LAST_BOOT_TIME != req_len) 377 break; 378 memcpy(sysInfoParams.last_boot_time, &req[1], SIZE_LAST_BOOT_TIME); 379 break; 380 default: 381 return ipmi::responseSystemInfoParamterNotSupportCommand(); 382 break; 383 } 384 385 return ipmi::responseSuccess(); 386 } 387 388 //---------------------------------------------------------------------- 389 // Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS) 390 //---------------------------------------------------------------------- 391 ipmi::RspType<std::vector<uint8_t>> 392 ipmiAppGetSysInfoParams(ipmi::Context::ptr ctx, uint8_t, uint8_t param, 393 uint8_t, uint8_t) 394 { 395 int len; 396 std::vector<uint8_t> respData; 397 respData.push_back(1); // Parameter revision 398 399 std::optional<size_t> hostId = findHost(ctx->hostIdx); 400 401 if (!hostId) 402 { 403 phosphor::logging::log<phosphor::logging::level::ERR>( 404 "Invalid Host Id received"); 405 return ipmi::responseInvalidCommand(); 406 } 407 408 switch (param) 409 { 410 case SYS_INFO_PARAM_SET_IN_PROG: 411 respData.push_back(sysInfoParams.set_in_prog); 412 break; 413 case SYS_INFO_PARAM_SYSFW_VER: 414 { 415 std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId); 416 if ((platGetSysFWVer(respData, version_key)) < 0) 417 return ipmi::responseSystemInfoParamterNotSupportCommand(); 418 break; 419 } 420 case SYS_INFO_PARAM_SYS_NAME: 421 respData.insert(respData.end(), std::begin(sysInfoParams.sys_name), 422 std::end(sysInfoParams.sys_name)); 423 break; 424 case SYS_INFO_PARAM_PRI_OS_NAME: 425 respData.insert(respData.end(), 426 std::begin(sysInfoParams.pri_os_name), 427 std::end(sysInfoParams.pri_os_name)); 428 break; 429 case SYS_INFO_PARAM_PRESENT_OS_NAME: 430 respData.insert(respData.end(), 431 std::begin(sysInfoParams.present_os_name), 432 std::end(sysInfoParams.present_os_name)); 433 break; 434 case SYS_INFO_PARAM_PRESENT_OS_VER: 435 respData.insert(respData.end(), 436 std::begin(sysInfoParams.present_os_ver), 437 std::end(sysInfoParams.present_os_ver)); 438 break; 439 case SYS_INFO_PARAM_BMC_URL: 440 respData.insert(respData.end(), std::begin(sysInfoParams.bmc_url), 441 std::end(sysInfoParams.bmc_url)); 442 break; 443 case SYS_INFO_PARAM_OS_HV_URL: 444 respData.insert(respData.end(), std::begin(sysInfoParams.os_hv_url), 445 std::end(sysInfoParams.os_hv_url)); 446 break; 447 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST: 448 len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>(); 449 respData.insert(respData.end(), 450 std::begin(sysInfoParams.bios_current_boot_list), 451 std::begin(sysInfoParams.bios_current_boot_list) + 452 len); 453 break; 454 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE: 455 respData.insert(respData.end(), 456 std::begin(sysInfoParams.bios_fixed_boot_device), 457 std::end(sysInfoParams.bios_fixed_boot_device)); 458 break; 459 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING: 460 respData.insert(respData.end(), 461 std::begin(sysInfoParams.bios_rstr_dflt_setting), 462 std::end(sysInfoParams.bios_rstr_dflt_setting)); 463 break; 464 case SYS_INFO_PARAM_LAST_BOOT_TIME: 465 respData.insert(respData.end(), 466 std::begin(sysInfoParams.last_boot_time), 467 std::end(sysInfoParams.last_boot_time)); 468 break; 469 default: 470 return ipmi::responseSystemInfoParamterNotSupportCommand(); 471 break; 472 } 473 474 return ipmi::responseSuccess(respData); 475 } 476 477 void registerAPPFunctions() 478 { 479 /* Get App data stored in json file */ 480 std::ifstream file(JSON_APP_DATA_FILE); 481 if (file) 482 { 483 file >> appData; 484 file.close(); 485 } 486 487 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL, 488 ipmiAppGetSTResults, 489 PRIVILEGE_USER); // Get Self Test Results 490 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL, 491 ipmiAppMfrTestOn, 492 PRIVILEGE_USER); // Manufacturing Test On 493 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL, 494 ipmiAppSetGlobalEnables, 495 PRIVILEGE_USER); // Set Global Enables 496 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL, 497 ipmiAppGetGlobalEnables, 498 PRIVILEGE_USER); // Get Global Enables 499 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL, 500 ipmiAppClearMsgFlags, 501 PRIVILEGE_USER); // Clear Message flags 502 #if BIC_ENABLED 503 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 504 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User, 505 ipmiAppGetSysGUID); 506 #else 507 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL, 508 ipmiAppGetSysGUID, 509 PRIVILEGE_USER); // Get System GUID 510 #endif 511 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 512 ipmi::app::cmdSetSystemInfoParameters, 513 ipmi::Privilege::User, ipmiAppSetSysInfoParams); 514 515 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 516 ipmi::app::cmdGetSystemInfoParameters, 517 ipmi::Privilege::User, ipmiAppGetSysInfoParams); 518 return; 519 } 520 521 } // namespace ipmi 522