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 <format>
32 #include <fstream>
33 #include <iomanip>
34 #include <iostream>
35 #include <sstream>
36
37 namespace ipmi
38 {
39
40 static void registerAPPFunctions() __attribute__((constructor));
41 static constexpr size_t GUID_SIZE = 16;
42 // TODO Make offset and location runtime configurable to ensure we
43 // can make each define their own locations.
44 static constexpr off_t OFFSET_SYS_GUID = 0x17F0;
45 static constexpr const char* FRU_EEPROM = "/sys/bus/i2c/devices/6-0054/eeprom";
46
47 // TODO: Need to store this info after identifying proper storage
48 static uint8_t globEna = 0x09;
49 static SysInfoParam sysInfoParams;
50 nlohmann::json appData __attribute__((init_priority(101)));
51
52 // Custom completion codes can be defined in individual modules for command
53 // specific errors in the 0x80-0xBE range
54 constexpr ipmi::Cc ccSystemInfoParameterNotSupported = 0x80;
55
56 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&,
57 std::vector<uint8_t>&);
58
responseSystemInfoParamterNotSupportCommand()59 static inline auto responseSystemInfoParamterNotSupportCommand()
60 {
61 return response(ccSystemInfoParameterNotSupported);
62 }
63
printGUID(uint8_t * guid,off_t offset)64 void printGUID(uint8_t* guid, off_t offset)
65 {
66 std::cout << "Read GUID from offset : " << offset << " :\n";
67 for (size_t i = 0; i < GUID_SIZE; i++)
68 {
69 int data = guid[i];
70 std::cout << std::hex << data << " ";
71 }
72 std::cout << std::endl;
73 }
74
getGUID(off_t offset,uint8_t * guid)75 int getGUID(off_t offset, uint8_t* guid)
76 {
77 int fd = -1;
78 ssize_t bytes_rd;
79 int ret = 0;
80 std::string eepromPath = FRU_EEPROM;
81
82 // find the eeprom path of MB FRU
83 auto device = getMbFruDevice();
84 if (device)
85 {
86 auto [bus, address] = *device;
87 std::stringstream ss;
88 ss << "/sys/bus/i2c/devices/" << static_cast<int>(bus) << "-"
89 << std::setw(4) << std::setfill('0') << std::hex
90 << static_cast<int>(address) << "/eeprom";
91 eepromPath = ss.str();
92 }
93
94 errno = 0;
95
96 // Check if file is present
97 if (access(eepromPath.c_str(), F_OK) == -1)
98 {
99 std::cerr << "Unable to access: " << eepromPath << std::endl;
100 return errno;
101 }
102
103 // Open the file
104 fd = open(eepromPath.c_str(), O_RDONLY);
105 if (fd == -1)
106 {
107 std::cerr << "Unable to open: " << eepromPath << std::endl;
108 return errno;
109 }
110
111 // seek to the offset
112 lseek(fd, offset, SEEK_SET);
113
114 // Read bytes from location
115 bytes_rd = read(fd, guid, GUID_SIZE);
116 if (bytes_rd != GUID_SIZE)
117 {
118 phosphor::logging::log<phosphor::logging::level::ERR>(
119 "GUID read data from EEPROM failed");
120 ret = errno;
121 }
122 else
123 {
124 printGUID(guid, offset);
125 }
126 close(fd);
127 return ret;
128 }
129
getSystemGUID(uint8_t * guid)130 int getSystemGUID(uint8_t* guid)
131 {
132 return getGUID(OFFSET_SYS_GUID, guid);
133 }
134
135 //----------------------------------------------------------------------
136 // Get Self Test Results (IPMI/Section 20.4) (CMD_APP_GET_SELFTEST_RESULTS)
137 //----------------------------------------------------------------------
ipmiAppGetSTResults(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)138 ipmi_ret_t ipmiAppGetSTResults(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
139 ipmi_response_t response,
140 ipmi_data_len_t data_len, ipmi_context_t)
141 {
142 uint8_t* res = reinterpret_cast<uint8_t*>(response);
143
144 // TODO: Following data needs to be updated based on self-test results
145 *res++ = 0x55; // Self-Test result
146 *res++ = 0x00; // Extra error info in case of failure
147
148 *data_len = 2;
149
150 return ipmi::ccSuccess;
151 }
152
153 //----------------------------------------------------------------------
154 // Manufacturing Test On (IPMI/Section 20.5) (CMD_APP_MFR_TEST_ON)
155 //----------------------------------------------------------------------
ipmiAppMfrTestOn(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)156 ipmi_ret_t ipmiAppMfrTestOn(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request,
157 ipmi_response_t, ipmi_data_len_t data_len,
158 ipmi_context_t)
159 {
160 uint8_t* req = reinterpret_cast<uint8_t*>(request);
161 std::string mfrTest = "sled-cycle";
162 ipmi_ret_t rc = ipmi::ccSuccess;
163
164 if (!memcmp(req, mfrTest.data(), mfrTest.length()) &&
165 (*data_len == mfrTest.length()))
166 {
167 /* sled-cycle the BMC */
168 auto ret = system("/usr/sbin/power-util sled-cycle");
169 if (ret)
170 {
171 rc = ipmi::ccUnspecifiedError;
172 }
173 }
174 else
175 {
176 rc = ccSystemInfoParameterNotSupported;
177 }
178
179 *data_len = 0;
180
181 return rc;
182 }
183
184 //----------------------------------------------------------------------
185 // Set Global Enables (CMD_APP_SET_GLOBAL_ENABLES)
186 //----------------------------------------------------------------------
ipmiAppSetGlobalEnables(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)187 ipmi_ret_t ipmiAppSetGlobalEnables(ipmi_netfn_t, ipmi_cmd_t,
188 ipmi_request_t request, ipmi_response_t,
189 ipmi_data_len_t data_len, ipmi_context_t)
190 {
191 uint8_t* req = reinterpret_cast<uint8_t*>(request);
192
193 globEna = *req;
194 *data_len = 0;
195
196 return ipmi::ccSuccess;
197 }
198
199 //----------------------------------------------------------------------
200 // Get Global Enables (CMD_APP_GET_GLOBAL_ENABLES)
201 //----------------------------------------------------------------------
ipmiAppGetGlobalEnables(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)202 ipmi_ret_t ipmiAppGetGlobalEnables(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
203 ipmi_response_t response,
204 ipmi_data_len_t data_len, ipmi_context_t)
205 {
206 uint8_t* res = reinterpret_cast<uint8_t*>(response);
207
208 *data_len = 1;
209 *res++ = globEna;
210
211 return ipmi::ccSuccess;
212 }
213
214 //----------------------------------------------------------------------
215 // Clear Message flags (IPMI/Section 22.3) (CMD_APP_CLEAR_MESSAGE_FLAGS)
216 //----------------------------------------------------------------------
ipmiAppClearMsgFlags(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)217 ipmi_ret_t ipmiAppClearMsgFlags(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
218 ipmi_response_t, ipmi_data_len_t data_len,
219 ipmi_context_t)
220 {
221 // Do Nothing and just return success
222 *data_len = 0;
223
224 return ipmi::ccSuccess;
225 }
226
227 //----------------------------------------------------------------------
228 // Get System GUID (CMD_APP_GET_SYS_GUID)
229 //----------------------------------------------------------------------
230 #if BIC_ENABLED
ipmiAppGetSysGUID(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)231 ipmi::RspType<std::vector<uint8_t>> ipmiAppGetSysGUID(
232 ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
233
234 {
235 std::vector<uint8_t> respData;
236
237 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
238
239 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData))
240 return ipmi::responseUnspecifiedError();
241
242 return ipmi::responseSuccess(respData);
243 }
244
245 #else
ipmiAppGetSysGUID(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)246 ipmi_ret_t ipmiAppGetSysGUID(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
247 ipmi_response_t response, ipmi_data_len_t data_len,
248 ipmi_context_t)
249 {
250 uint8_t* res = reinterpret_cast<uint8_t*>(response);
251 if (getSystemGUID(res))
252 {
253 return ipmi::ccUnspecifiedError;
254 }
255 *data_len = GUID_SIZE;
256 return ipmi::ccSuccess;
257 }
258
259 #endif
260
261 //----------------------------------------------------------------------
262 // Platform specific functions for storing app data
263 //----------------------------------------------------------------------
264
flush_app_data()265 void flush_app_data()
266 {
267 std::ofstream file(JSON_APP_DATA_FILE);
268 file << appData;
269 file.close();
270 return;
271 }
272
platSetSysFWVer(uint8_t * ver,const size_t hostId)273 static int platSetSysFWVer(uint8_t* ver, const size_t hostId)
274 {
275 std::stringstream ss;
276 int i;
277
278 /* TODO: implement byte 1: Set selector
279 * byte 2: encodeing, currently only supported
280 * ASCII which is value 0, UTF and unicode are
281 * not supported yet.
282 */
283 if (ver[1] & 0x0f)
284 return -1;
285
286 for (i = 3; i < 3 + ver[2]; i++)
287 {
288 ss << (char)ver[i];
289 }
290
291 /* Save to legacy sysfw version file for backward compatibility */
292 appData[KEY_SYSFW_VER + std::to_string(hostId)] = ss.str();
293 flush_app_data();
294
295 auto sysfwVersionFile = std::format(SYSFW_VER_FILE, hostId);
296 std::ofstream file(sysfwVersionFile);
297
298 if (!file)
299 {
300 phosphor::logging::log<phosphor::logging::level::ERR>(
301 "Failed to open system firmware version file for writing",
302 phosphor::logging::entry("FILE=%s", sysfwVersionFile.c_str()));
303 return -1;
304 }
305
306 file << ss.str();
307 file.close();
308
309 return 0;
310 }
311
platGetSysFWVer(std::vector<uint8_t> & respData,const size_t hostId)312 static int platGetSysFWVer(std::vector<uint8_t>& respData, const size_t hostId)
313 {
314 constexpr size_t headerSize = 3; // selector + encoding + version size
315
316 std::string sysfwVersionFile = std::format(SYSFW_VER_FILE, hostId);
317 std::ifstream file(sysfwVersionFile);
318
319 if (!file)
320 {
321 phosphor::logging::log<phosphor::logging::level::ERR>(
322 "Failed to open system firmware version file for reading",
323 phosphor::logging::entry("FILE=%s", sysfwVersionFile.c_str()));
324 return -1;
325 }
326
327 std::string version;
328 std::getline(file, version);
329 file.close();
330
331 // Truncate if longer than allowed
332 if (version.size() > SIZE_SYSFW_VER - headerSize)
333 {
334 version.resize(SIZE_SYSFW_VER - headerSize);
335 }
336
337 respData.push_back(0); // Byte 1: set selector not supported
338 respData.push_back(0); // Byte 2: only ASCII supported
339 respData.push_back(version.size()); // Byte 3: length of version
340
341 for (auto c : version)
342 {
343 respData.push_back(c);
344 }
345
346 // Remaining byte fill to 0
347 for (size_t i = 0; i < SIZE_SYSFW_VER - (version.size() + headerSize); i++)
348 {
349 respData.push_back(0);
350 }
351
352 return (version.size() + headerSize);
353 }
354
355 //----------------------------------------------------------------------
356 // Set Sys Info Params (IPMI/Sec 22.14a) (CMD_APP_SET_SYS_INFO_PARAMS)
357 //----------------------------------------------------------------------
ipmiAppSetSysInfoParams(ipmi::Context::ptr ctx,std::vector<uint8_t> req)358 ipmi::RspType<uint8_t> ipmiAppSetSysInfoParams(ipmi::Context::ptr ctx,
359 std::vector<uint8_t> req)
360 {
361 uint8_t param = req[0];
362 uint8_t req_len = req.size();
363 std::optional<size_t> hostId = findHost(ctx->hostIdx);
364
365 if (!hostId)
366 {
367 phosphor::logging::log<phosphor::logging::level::ERR>(
368 "Invalid Host Id received");
369 return ipmi::responseInvalidCommand();
370 }
371
372 switch (param)
373 {
374 case SYS_INFO_PARAM_SET_IN_PROG:
375 sysInfoParams.set_in_prog = req[1];
376 break;
377 case SYS_INFO_PARAM_SYSFW_VER:
378 {
379 memcpy(sysInfoParams.sysfw_ver, &req[1], SIZE_SYSFW_VER);
380 if (platSetSysFWVer(sysInfoParams.sysfw_ver, *hostId))
381 return ipmi::responseSystemInfoParamterNotSupportCommand();
382 break;
383 }
384 case SYS_INFO_PARAM_SYS_NAME:
385 memcpy(sysInfoParams.sys_name, &req[1], SIZE_SYS_NAME);
386 break;
387 case SYS_INFO_PARAM_PRI_OS_NAME:
388 memcpy(sysInfoParams.pri_os_name, &req[1], SIZE_OS_NAME);
389 break;
390 case SYS_INFO_PARAM_PRESENT_OS_NAME:
391 memcpy(sysInfoParams.present_os_name, &req[1], SIZE_OS_NAME);
392 break;
393 case SYS_INFO_PARAM_PRESENT_OS_VER:
394 memcpy(sysInfoParams.present_os_ver, &req[1], SIZE_OS_VER);
395 break;
396 case SYS_INFO_PARAM_BMC_URL:
397 memcpy(sysInfoParams.bmc_url, &req[1], SIZE_BMC_URL);
398 break;
399 case SYS_INFO_PARAM_OS_HV_URL:
400 memcpy(sysInfoParams.os_hv_url, &req[1], SIZE_OS_HV_URL);
401 break;
402 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
403 memcpy(sysInfoParams.bios_current_boot_list, &req[1], req_len);
404 appData[KEY_BIOS_BOOT_LEN] = req_len;
405 flush_app_data();
406 break;
407 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
408 if (SIZE_BIOS_FIXED_BOOT_DEVICE != req_len)
409 break;
410 memcpy(sysInfoParams.bios_fixed_boot_device, &req[1],
411 SIZE_BIOS_FIXED_BOOT_DEVICE);
412 break;
413 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
414 if (SIZE_BIOS_RSTR_DFLT_SETTING != req_len)
415 break;
416 memcpy(sysInfoParams.bios_rstr_dflt_setting, &req[1],
417 SIZE_BIOS_RSTR_DFLT_SETTING);
418 break;
419 case SYS_INFO_PARAM_LAST_BOOT_TIME:
420 if (SIZE_LAST_BOOT_TIME != req_len)
421 break;
422 memcpy(sysInfoParams.last_boot_time, &req[1], SIZE_LAST_BOOT_TIME);
423 break;
424 default:
425 return ipmi::responseSystemInfoParamterNotSupportCommand();
426 break;
427 }
428
429 return ipmi::responseSuccess();
430 }
431
432 //----------------------------------------------------------------------
433 // Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS)
434 //----------------------------------------------------------------------
ipmiAppGetSysInfoParams(ipmi::Context::ptr ctx,uint8_t,uint8_t param,uint8_t,uint8_t)435 ipmi::RspType<std::vector<uint8_t>> ipmiAppGetSysInfoParams(
436 ipmi::Context::ptr ctx, uint8_t, uint8_t param, uint8_t, uint8_t)
437 {
438 int len;
439 std::vector<uint8_t> respData;
440 respData.push_back(1); // Parameter revision
441
442 std::optional<size_t> hostId = findHost(ctx->hostIdx);
443
444 if (!hostId)
445 {
446 phosphor::logging::log<phosphor::logging::level::ERR>(
447 "Invalid Host Id received");
448 return ipmi::responseInvalidCommand();
449 }
450
451 switch (param)
452 {
453 case SYS_INFO_PARAM_SET_IN_PROG:
454 respData.push_back(sysInfoParams.set_in_prog);
455 break;
456 case SYS_INFO_PARAM_SYSFW_VER:
457 {
458 if ((platGetSysFWVer(respData, *hostId)) < 0)
459 return ipmi::responseSystemInfoParamterNotSupportCommand();
460 break;
461 }
462 case SYS_INFO_PARAM_SYS_NAME:
463 respData.insert(respData.end(), std::begin(sysInfoParams.sys_name),
464 std::end(sysInfoParams.sys_name));
465 break;
466 case SYS_INFO_PARAM_PRI_OS_NAME:
467 respData.insert(respData.end(),
468 std::begin(sysInfoParams.pri_os_name),
469 std::end(sysInfoParams.pri_os_name));
470 break;
471 case SYS_INFO_PARAM_PRESENT_OS_NAME:
472 respData.insert(respData.end(),
473 std::begin(sysInfoParams.present_os_name),
474 std::end(sysInfoParams.present_os_name));
475 break;
476 case SYS_INFO_PARAM_PRESENT_OS_VER:
477 respData.insert(respData.end(),
478 std::begin(sysInfoParams.present_os_ver),
479 std::end(sysInfoParams.present_os_ver));
480 break;
481 case SYS_INFO_PARAM_BMC_URL:
482 respData.insert(respData.end(), std::begin(sysInfoParams.bmc_url),
483 std::end(sysInfoParams.bmc_url));
484 break;
485 case SYS_INFO_PARAM_OS_HV_URL:
486 respData.insert(respData.end(), std::begin(sysInfoParams.os_hv_url),
487 std::end(sysInfoParams.os_hv_url));
488 break;
489 case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
490 len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>();
491 respData.insert(
492 respData.end(),
493 std::begin(sysInfoParams.bios_current_boot_list),
494 std::begin(sysInfoParams.bios_current_boot_list) + len);
495 break;
496 case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
497 respData.insert(respData.end(),
498 std::begin(sysInfoParams.bios_fixed_boot_device),
499 std::end(sysInfoParams.bios_fixed_boot_device));
500 break;
501 case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
502 respData.insert(respData.end(),
503 std::begin(sysInfoParams.bios_rstr_dflt_setting),
504 std::end(sysInfoParams.bios_rstr_dflt_setting));
505 break;
506 case SYS_INFO_PARAM_LAST_BOOT_TIME:
507 respData.insert(respData.end(),
508 std::begin(sysInfoParams.last_boot_time),
509 std::end(sysInfoParams.last_boot_time));
510 break;
511 default:
512 return ipmi::responseSystemInfoParamterNotSupportCommand();
513 break;
514 }
515
516 return ipmi::responseSuccess(respData);
517 }
518
registerAPPFunctions()519 void registerAPPFunctions()
520 {
521 /* Get App data stored in json file */
522 std::ifstream file(JSON_APP_DATA_FILE);
523 if (file)
524 {
525 file >> appData;
526 file.close();
527 }
528
529 ipmiPrintAndRegister(ipmi::netFnApp, CMD_APP_GET_SELFTEST_RESULTS, NULL,
530 ipmiAppGetSTResults,
531 PRIVILEGE_USER); // Get Self Test Results
532 ipmiPrintAndRegister(ipmi::netFnApp, CMD_APP_MFR_TEST_ON, NULL,
533 ipmiAppMfrTestOn,
534 PRIVILEGE_USER); // Manufacturing Test On
535 ipmiPrintAndRegister(ipmi::netFnApp, CMD_APP_SET_GLOBAL_ENABLES, NULL,
536 ipmiAppSetGlobalEnables,
537 PRIVILEGE_USER); // Set Global Enables
538 ipmiPrintAndRegister(ipmi::netFnApp, CMD_APP_GET_GLOBAL_ENABLES, NULL,
539 ipmiAppGetGlobalEnables,
540 PRIVILEGE_USER); // Get Global Enables
541 ipmiPrintAndRegister(ipmi::netFnApp, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL,
542 ipmiAppClearMsgFlags,
543 PRIVILEGE_USER); // Clear Message flags
544 #if BIC_ENABLED
545 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
546 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
547 ipmiAppGetSysGUID);
548 #else
549 ipmiPrintAndRegister(ipmi::netFnApp, CMD_APP_GET_SYS_GUID, NULL,
550 ipmiAppGetSysGUID,
551 PRIVILEGE_USER); // Get System GUID
552 #endif
553 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
554 ipmi::app::cmdSetSystemInfoParameters,
555 ipmi::Privilege::User, ipmiAppSetSysInfoParams);
556
557 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
558 ipmi::app::cmdGetSystemInfoParameters,
559 ipmi::Privilege::User, ipmiAppGetSysInfoParams);
560 return;
561 }
562
563 } // namespace ipmi
564