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