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