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
responseSystemInfoParamterNotSupportCommand()54 static inline auto responseSystemInfoParamterNotSupportCommand()
55 {
56 return response(IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED);
57 }
58
printGUID(uint8_t * guid,off_t offset)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
getGUID(off_t offset,uint8_t * guid)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
getSystemGUID(uint8_t * guid)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 //----------------------------------------------------------------------
ipmiAppGetSTResults(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)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 //----------------------------------------------------------------------
ipmiAppMfrTestOn(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)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 //----------------------------------------------------------------------
ipmiAppSetGlobalEnables(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)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 //----------------------------------------------------------------------
ipmiAppGetGlobalEnables(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)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 //----------------------------------------------------------------------
ipmiAppClearMsgFlags(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)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>>
ipmiAppGetSysGUID(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)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
ipmiAppGetSysGUID(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)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
flush_app_data()260 void flush_app_data()
261 {
262 std::ofstream file(JSON_APP_DATA_FILE);
263 file << appData;
264 file.close();
265 return;
266 }
267
platSetSysFWVer(uint8_t * ver,const std::string key)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
platGetSysFWVer(std::vector<uint8_t> & respData,const std::string key)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,std::vector<uint8_t> req)327 ipmiAppSetSysInfoParams(ipmi::Context::ptr ctx, 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 //----------------------------------------------------------------------
ipmiAppGetSysInfoParams(ipmi::Context::ptr ctx,uint8_t,uint8_t param,uint8_t,uint8_t)404 ipmi::RspType<std::vector<uint8_t>> ipmiAppGetSysInfoParams(
405 ipmi::Context::ptr ctx, uint8_t, uint8_t param, uint8_t, uint8_t)
406 {
407 int len;
408 std::vector<uint8_t> respData;
409 respData.push_back(1); // Parameter revision
410
411 std::optional<size_t> hostId = findHost(ctx->hostIdx);
412
413 if (!hostId)
414 {
415 phosphor::logging::log<phosphor::logging::level::ERR>(
416 "Invalid Host Id received");
417 return ipmi::responseInvalidCommand();
418 }
419
420 switch (param)
421 {
422 case SYS_INFO_PARAM_SET_IN_PROG:
423 respData.push_back(sysInfoParams.set_in_prog);
424 break;
425 case SYS_INFO_PARAM_SYSFW_VER:
426 {
427 std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId);
428 if ((platGetSysFWVer(respData, version_key)) < 0)
429 return ipmi::responseSystemInfoParamterNotSupportCommand();
430 break;
431 }
432 case SYS_INFO_PARAM_SYS_NAME:
433 respData.insert(respData.end(), std::begin(sysInfoParams.sys_name),
434 std::end(sysInfoParams.sys_name));
435 break;
436 case SYS_INFO_PARAM_PRI_OS_NAME:
437 respData.insert(respData.end(),
438 std::begin(sysInfoParams.pri_os_name),
439 std::end(sysInfoParams.pri_os_name));
440 break;
441 case SYS_INFO_PARAM_PRESENT_OS_NAME:
442 respData.insert(respData.end(),
443 std::begin(sysInfoParams.present_os_name),
444 std::end(sysInfoParams.present_os_name));
445 break;
446 case SYS_INFO_PARAM_PRESENT_OS_VER:
447 respData.insert(respData.end(),
448 std::begin(sysInfoParams.present_os_ver),
449 std::end(sysInfoParams.present_os_ver));
450 break;
451 case SYS_INFO_PARAM_BMC_URL:
452 respData.insert(respData.end(), std::begin(sysInfoParams.bmc_url),
453 std::end(sysInfoParams.bmc_url));
454 break;
455 case SYS_INFO_PARAM_OS_HV_URL:
456 respData.insert(respData.end(), std::begin(sysInfoParams.os_hv_url),
457 std::end(sysInfoParams.os_hv_url));
458 break;
459 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
460 len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>();
461 respData.insert(
462 respData.end(),
463 std::begin(sysInfoParams.bios_current_boot_list),
464 std::begin(sysInfoParams.bios_current_boot_list) + len);
465 break;
466 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
467 respData.insert(respData.end(),
468 std::begin(sysInfoParams.bios_fixed_boot_device),
469 std::end(sysInfoParams.bios_fixed_boot_device));
470 break;
471 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
472 respData.insert(respData.end(),
473 std::begin(sysInfoParams.bios_rstr_dflt_setting),
474 std::end(sysInfoParams.bios_rstr_dflt_setting));
475 break;
476 case SYS_INFO_PARAM_LAST_BOOT_TIME:
477 respData.insert(respData.end(),
478 std::begin(sysInfoParams.last_boot_time),
479 std::end(sysInfoParams.last_boot_time));
480 break;
481 default:
482 return ipmi::responseSystemInfoParamterNotSupportCommand();
483 break;
484 }
485
486 return ipmi::responseSuccess(respData);
487 }
488
registerAPPFunctions()489 void registerAPPFunctions()
490 {
491 /* Get App data stored in json file */
492 std::ifstream file(JSON_APP_DATA_FILE);
493 if (file)
494 {
495 file >> appData;
496 file.close();
497 }
498
499 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL,
500 ipmiAppGetSTResults,
501 PRIVILEGE_USER); // Get Self Test Results
502 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL,
503 ipmiAppMfrTestOn,
504 PRIVILEGE_USER); // Manufacturing Test On
505 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL,
506 ipmiAppSetGlobalEnables,
507 PRIVILEGE_USER); // Set Global Enables
508 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL,
509 ipmiAppGetGlobalEnables,
510 PRIVILEGE_USER); // Get Global Enables
511 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL,
512 ipmiAppClearMsgFlags,
513 PRIVILEGE_USER); // Clear Message flags
514 #if BIC_ENABLED
515 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
516 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
517 ipmiAppGetSysGUID);
518 #else
519 ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL,
520 ipmiAppGetSysGUID,
521 PRIVILEGE_USER); // Get System GUID
522 #endif
523 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
524 ipmi::app::cmdSetSystemInfoParameters,
525 ipmi::Privilege::User, ipmiAppSetSysInfoParams);
526
527 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
528 ipmi::app::cmdGetSystemInfoParameters,
529 ipmi::Privilege::User, ipmiAppGetSysInfoParams);
530 return;
531 }
532
533 } // namespace ipmi
534