1 /*
2 * Copyright (c) 2018-present Facebook. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <commandutils.hpp>
18 #include <usb-dbg.hpp>
19
20 namespace ipmi
21 {
22
23 ipmi_ret_t getNetworkData(uint8_t lan_param, char* data);
24 std::string getMotherBoardFruName();
25 int8_t getFruData(std::string& serial, std::string& name);
26 int8_t sysConfig(std::vector<std::string>& data, size_t pos);
27 int8_t procInfo(std::string& result, size_t pos);
28
29 bool isMultiHostPlatform();
30
31 /* Declare Host Selector interface and path */
32 namespace selector
33 {
34 const std::string path = "/xyz/openbmc_project/Chassis/Buttons/HostSelector";
35 const std::string interface =
36 "xyz.openbmc_project.Chassis.Buttons.HostSelector";
37 } // namespace selector
38
39 /* Declare storage functions used here */
40 namespace storage
41 {
42 int getSensorValue(std::string&, double&);
43 int getSensorUnit(std::string&, std::string&);
44 int getSensorThreshold(std::string&, std::string&);
45 } // namespace storage
46
47 namespace boot
48 {
49 std::tuple<std::string, std::string> objPath(size_t id);
50 void setBootOrder(std::string bootObjPath, const std::vector<uint8_t>& bootSeq,
51 std::string hostName);
52 void getBootOrder(std::string bootObjPath, std::vector<uint8_t>& bootSeq,
53 std::string hostName);
54 } // namespace boot
55
getMaxHostPosition(size_t & maxPosition)56 void getMaxHostPosition(size_t& maxPosition)
57 {
58 try
59 {
60 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
61 std::string service =
62 getService(*dbus, ipmi::selector::interface, ipmi::selector::path);
63 Value variant =
64 getDbusProperty(*dbus, service, ipmi::selector::path,
65 ipmi::selector::interface, "MaxPosition");
66 maxPosition = std::get<size_t>(variant);
67 }
68 catch (const std::exception& e)
69 {
70 lg2::error("Unable to get max host position - {MAXPOSITION}",
71 "MAXPOSITION", maxPosition);
72 throw e;
73 }
74 }
75
getSelectorPosition(size_t & hostPosition)76 void getSelectorPosition(size_t& hostPosition)
77 {
78 try
79 {
80 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
81 std::string service =
82 getService(*dbus, ipmi::selector::interface, ipmi::selector::path);
83 Value variant = getDbusProperty(*dbus, service, ipmi::selector::path,
84 ipmi::selector::interface, "Position");
85 hostPosition = std::get<size_t>(variant);
86 }
87 catch (const std::exception& e)
88 {
89 lg2::error("Unable to get host position - {POSITION}", "POSITION",
90 hostPosition);
91 throw e;
92 }
93 }
94
95 static int panelNum = (sizeof(panels) / sizeof(struct ctrl_panel)) - 1;
96
97 /* Returns the FRU the hand-switch is switched to. If it is switched to BMC
98 * it returns FRU_ALL. Note, if in err, it returns FRU_ALL */
plat_get_fru_sel()99 static size_t plat_get_fru_sel()
100 {
101 size_t position;
102 bool platform = isMultiHostPlatform();
103 if (platform == true)
104 {
105 getSelectorPosition(position);
106 if (position == BMC_POSITION)
107 {
108 return FRU_ALL;
109 }
110 }
111 else
112 {
113 /* For Tiogapass it just return 1,
114 * can modify to support more platform */
115 position = 1;
116 }
117 return position;
118 }
119
120 // return 0 on seccuess
init(size_t size)121 void frame::init(size_t size)
122 {
123 // Reset status
124 idx_head = idx_tail = 0;
125 lines = 0;
126 esc_sts = 0;
127 pages = 1;
128
129 if (buf != nullptr && max_size == size)
130 {
131 return;
132 }
133
134 if (buf != nullptr && max_size != size)
135 {
136 delete[] buf;
137 }
138 // Initialize Configuration
139 title[0] = '\0';
140 buf = new char[size];
141 max_size = size;
142 max_page = size;
143 line_per_page = 7;
144 line_width = 16;
145 overwrite = false;
146
147 return;
148 }
149
150 // return 0 on seccuess
append(const std::string & str,size_t indent)151 void frame::append(const std::string& str, size_t indent)
152 {
153 for (auto ch : parse(str, indent))
154 {
155 if (isFull())
156 {
157 if (overwrite)
158 {
159 if (buf[idx_head] == LINE_DELIMITER)
160 lines--;
161 idx_head = (idx_head + 1) % max_size;
162 }
163 else
164 {
165 throw std::overflow_error("No room in buffer");
166 }
167 }
168
169 buf[idx_tail] = ch;
170 if (ch == LINE_DELIMITER)
171 lines++;
172
173 idx_tail = (idx_tail + 1) % max_size;
174 }
175
176 pages = (lines / line_per_page) + ((lines % line_per_page) ? 1 : 0);
177
178 if (pages > max_page)
179 pages = max_page;
180
181 return;
182 }
183
184 // return page size
getPage(size_t page,char * page_buf,size_t page_buf_size)185 int frame::getPage(size_t page, char* page_buf, size_t page_buf_size)
186 {
187 int ret;
188 uint16_t line = 0;
189 uint16_t idx, len;
190
191 if (buf == nullptr)
192 return -1;
193
194 // 1-based page
195 if (page > pages || page < 1)
196 return -1;
197
198 if (page_buf == nullptr || page_buf_size == 0)
199 return -1;
200
201 ret = snprintf(page_buf, 17, "%-10s %02zd/%02zd", title, page, pages);
202 len = strlen(page_buf);
203 if (ret < 0)
204 return -1;
205
206 line = 0;
207 idx = idx_head;
208 while (line < ((page - 1) * line_per_page) && idx != idx_tail)
209 {
210 if (buf[idx] == LINE_DELIMITER)
211 line++;
212 idx = (idx + 1) % max_size;
213 }
214
215 while (line < ((page)*line_per_page) && idx != idx_tail)
216 {
217 if (buf[idx] == LINE_DELIMITER)
218 {
219 line++;
220 }
221 else
222 {
223 page_buf[len++] = buf[idx];
224 if (len == (page_buf_size - 1))
225 {
226 break;
227 }
228 }
229 idx = (idx + 1) % max_size;
230 }
231
232 return len;
233 }
234
isFull() const235 bool frame::isFull() const
236 {
237 if (buf == nullptr)
238 return true;
239
240 if ((idx_tail + 1) % max_size == idx_head)
241 return true;
242 else
243 return false;
244 }
245
246 // return 1 for Escape Sequence
isEscSeq(char chr)247 bool frame::isEscSeq(char chr)
248 {
249 uint8_t curr_sts = esc_sts;
250
251 if (esc_sts == 0 && (chr == 0x1b))
252 esc_sts = 1; // Escape Sequence
253 else if (esc_sts == 1 && (chr == 0x5b))
254 esc_sts = 2; // Control Sequence Introducer(CSI)
255 else if (esc_sts == 1 && (chr != 0x5b))
256 esc_sts = 0;
257 else if (esc_sts == 2 && (chr >= 0x40 && chr <= 0x7e))
258 esc_sts = 0;
259
260 if (curr_sts || esc_sts)
261 return true;
262 else
263 return false;
264 }
265
266 // return 0 on success
parse(const std::string & input,size_t indent)267 auto frame::parse(const std::string& input, size_t indent) -> std::string
268 {
269 if (indent > line_width)
270 return {};
271
272 std::string result;
273 size_t linepos = 0;
274
275 for (auto ch : input)
276 {
277 if (linepos == 0)
278 {
279 result.append(indent, ' ');
280 linepos = indent;
281 }
282
283 // Insert character.
284 result.push_back(ch);
285
286 if (!isEscSeq(ch))
287 {
288 // Check if new line is needed.
289 if (++linepos == line_width)
290 {
291 result.push_back(LINE_DELIMITER);
292 linepos = 0;
293 }
294 }
295 }
296
297 // Fill out remaining line.
298 result.append(line_width - linepos, ' ');
299 result.push_back(LINE_DELIMITER);
300
301 return result;
302 }
303
chk_cri_sel_update(uint8_t * cri_sel_up)304 static int chk_cri_sel_update(uint8_t* cri_sel_up)
305 {
306 FILE* fp;
307 struct stat file_stat;
308 size_t pos = plat_get_fru_sel();
309 static uint8_t pre_pos = 0xff;
310
311 fp = fopen("/mnt/data/cri_sel", "r");
312 if (fp)
313 {
314 if ((stat("/mnt/data/cri_sel", &file_stat) == 0) &&
315 (file_stat.st_mtime != frame_sel.mtime || pre_pos != pos))
316 {
317 *cri_sel_up = 1;
318 }
319 else
320 {
321 *cri_sel_up = 0;
322 }
323 fclose(fp);
324 }
325 else
326 {
327 if (frame_sel.buf == nullptr || frame_sel.lines != 0 || pre_pos != pos)
328 {
329 *cri_sel_up = 1;
330 }
331 else
332 {
333 *cri_sel_up = 0;
334 }
335 }
336 pre_pos = pos;
337 return 0;
338 }
339
plat_udbg_get_frame_info(uint8_t * num)340 int plat_udbg_get_frame_info(uint8_t* num)
341 {
342 *num = 3;
343 return 0;
344 }
345
plat_udbg_get_updated_frames(uint8_t * count,uint8_t * buffer)346 int plat_udbg_get_updated_frames(uint8_t* count, uint8_t* buffer)
347 {
348 uint8_t cri_sel_up = 0;
349 uint8_t info_page_up = 1;
350
351 *count = 0;
352
353 // info page update
354 if (info_page_up == 1)
355 {
356 buffer[*count] = 1;
357 *count += 1;
358 }
359
360 // cri sel update
361 chk_cri_sel_update(&cri_sel_up);
362 if (cri_sel_up == 1)
363 {
364 buffer[*count] = 2;
365 *count += 1;
366 }
367
368 // cri sensor update
369 buffer[*count] = 3;
370 *count += 1;
371
372 return 0;
373 }
374
plat_udbg_get_post_desc(uint8_t index,uint8_t * next,uint8_t phase,uint8_t * end,uint8_t * length,uint8_t * buffer)375 int plat_udbg_get_post_desc(uint8_t index, uint8_t* next, uint8_t phase,
376 uint8_t* end, uint8_t* length, uint8_t* buffer)
377 {
378 nlohmann::json postObj;
379 std::string postCode;
380
381 /* Get post description data stored in json file */
382 std::ifstream file(JSON_POST_DATA_FILE);
383 if (file)
384 {
385 file >> postObj;
386 file.close();
387 }
388 else
389 {
390 phosphor::logging::log<phosphor::logging::level::ERR>(
391 "Post code description file not found",
392 phosphor::logging::entry("POST_CODE_FILE=%s", JSON_POST_DATA_FILE));
393 return -1;
394 }
395
396 std::string phaseStr = "PhaseAny";
397 if (postObj.find(phaseStr) == postObj.end())
398 {
399 phaseStr = "Phase" + std::to_string(phase);
400 }
401
402 if (postObj.find(phaseStr) == postObj.end())
403 {
404 phosphor::logging::log<phosphor::logging::level::ERR>(
405 "Post code phase not available",
406 phosphor::logging::entry("PHASE=%d", phase));
407 return -1;
408 }
409
410 auto phaseObj = postObj[phaseStr];
411 int phaseSize = phaseObj.size();
412
413 for (int i = 0; i < phaseSize; i++)
414 {
415 postCode = phaseObj[i][0];
416 if (index == stoul(postCode, nullptr, 16))
417 {
418 std::string postDesc = phaseObj[i][1];
419 *length = postDesc.size();
420 memcpy(buffer, postDesc.data(), *length);
421 buffer[*length] = '\0';
422
423 if (phaseSize != i + 1)
424 {
425 postCode = phaseObj[i + 1][0];
426 *next = stoul(postCode, nullptr, 16);
427 *end = 0;
428 }
429 else
430 {
431 if (postObj.size() != phase)
432 {
433 std::string nextPhaseStr =
434 "Phase" + std::to_string(phase + 1);
435 postCode = postObj[nextPhaseStr][0][0];
436 *next = stoul(postCode, nullptr, 16);
437 *end = 0;
438 }
439 else
440 {
441 *next = 0xff;
442 *end = 1;
443 }
444 }
445
446 return 0;
447 }
448 }
449
450 phosphor::logging::log<phosphor::logging::level::ERR>(
451 "Post code description data not available",
452 phosphor::logging::entry("PHASE_CODE=%d_0x%x", phase, index));
453 return -1;
454 }
455
plat_udbg_get_gpio_desc(uint8_t index,uint8_t * next,uint8_t * level,uint8_t * def,uint8_t * length,uint8_t * buffer)456 int plat_udbg_get_gpio_desc(uint8_t index, uint8_t* next, uint8_t* level,
457 uint8_t* def, uint8_t* length, uint8_t* buffer)
458 {
459 nlohmann::json gpioObj;
460 std::string gpioPin;
461
462 /* Get gpio data stored in json file */
463 std::ifstream file(JSON_GPIO_DATA_FILE);
464 if (file)
465 {
466 file >> gpioObj;
467 file.close();
468 }
469 else
470 {
471 phosphor::logging::log<phosphor::logging::level::ERR>(
472 "GPIO pin description file not found",
473 phosphor::logging::entry("GPIO_PIN_DETAILS_FILE=%s",
474 JSON_GPIO_DATA_FILE));
475 return -1;
476 }
477
478 if (gpioObj.find(DEBUG_GPIO_KEY) == gpioObj.end())
479 {
480 phosphor::logging::log<phosphor::logging::level::ERR>(
481 "GPIO pin details not available",
482 phosphor::logging::entry("GPIO_JSON_KEY=%d", DEBUG_GPIO_KEY));
483 return -1;
484 }
485
486 auto obj = gpioObj[DEBUG_GPIO_KEY];
487 int objSize = obj.size();
488
489 for (int i = 0; i < objSize; i++)
490 {
491 if (obj[i].size() != GPIO_ARRAY_SIZE)
492 {
493 phosphor::logging::log<phosphor::logging::level::ERR>(
494 "Size of gpio array is incorrect",
495 phosphor::logging::entry("EXPECTED_SIZE=%d", GPIO_ARRAY_SIZE));
496 return -1;
497 }
498
499 gpioPin = obj[i][GPIO_PIN_INDEX];
500 if (index == stoul(gpioPin, nullptr, 16))
501 {
502 if (objSize != i + 1)
503 {
504 gpioPin = obj[i + 1][GPIO_PIN_INDEX];
505 *next = stoul(gpioPin, nullptr, 16);
506 }
507 else
508 {
509 *next = 0xff;
510 }
511
512 *level = obj[i][GPIO_LEVEL_INDEX];
513 *def = obj[i][GPIO_DEF_INDEX];
514 std::string gpioDesc = obj[i][GPIO_DESC_INDEX];
515 *length = gpioDesc.size();
516 memcpy(buffer, gpioDesc.data(), *length);
517 buffer[*length] = '\0';
518
519 return 0;
520 }
521 }
522
523 phosphor::logging::log<phosphor::logging::level::ERR>(
524 "GPIO pin description data not available",
525 phosphor::logging::entry("GPIO_PIN=0x%x", index));
526 return -1;
527 }
528
getBiosVer(std::string & ver,size_t hostPosition)529 static int getBiosVer(std::string& ver, size_t hostPosition)
530 {
531 std::string sysfwVersionFile = std::format(SYSFW_VER_FILE, hostPosition);
532 std::ifstream file(sysfwVersionFile);
533 if (!file)
534 {
535 phosphor::logging::log<phosphor::logging::level::ERR>(
536 "Failed to open system firmware version file",
537 phosphor::logging::entry("FILE=%s", sysfwVersionFile.c_str()));
538 return -1;
539 }
540
541 std::getline(file, ver);
542 file.close();
543
544 return 0;
545 }
546
sendBicCmd(uint8_t netFn,uint8_t cmd,uint8_t bicAddr,std::vector<uint8_t> & cmdData,std::vector<uint8_t> & respData)547 int sendBicCmd(uint8_t netFn, uint8_t cmd, uint8_t bicAddr,
548 std::vector<uint8_t>& cmdData, std::vector<uint8_t>& respData)
549 {
550 static constexpr uint8_t lun = 0;
551
552 auto bus = getSdBus();
553
554 auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
555 "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
556 "org.openbmc.Ipmb", "sendRequest");
557 method.append(bicAddr, netFn, lun, cmd, cmdData);
558
559 try
560 {
561 auto reply = bus->call(method);
562
563 auto resp = reply.unpack<IpmbMethodType>();
564
565 respData = std::move(
566 std::get<std::remove_reference_t<decltype(respData)>>(resp));
567 }
568 catch (const sdbusplus::exception_t& e)
569 {
570 phosphor::logging::log<phosphor::logging::level::ERR>(
571 "Error reading from BIC");
572 return -1;
573 }
574
575 return 0;
576 }
577
sendMeCmd(uint8_t netFn,uint8_t cmd,std::vector<uint8_t> & cmdData,std::vector<uint8_t> & respData)578 int sendMeCmd(uint8_t netFn, uint8_t cmd, std::vector<uint8_t>& cmdData,
579 std::vector<uint8_t>& respData)
580 {
581 auto bus = getSdBus();
582
583 if (DEBUG)
584 {
585 std::cout << "ME NetFn:cmd " << (int)netFn << ":" << (int)cmd << "\n";
586 std::cout << "ME req data: ";
587 for (auto d : cmdData)
588 {
589 std::cout << d << " ";
590 }
591 std::cout << "\n";
592 }
593
594 auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
595 "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
596 "org.openbmc.Ipmb", "sendRequest");
597 method.append(meAddress, netFn, lun, cmd, cmdData);
598
599 try
600 {
601 auto reply = bus->call(method);
602
603 auto resp = reply.unpack<IpmbMethodType>();
604
605 respData = std::move(
606 std::get<std::remove_reference_t<decltype(respData)>>(resp));
607 }
608 catch (const sdbusplus::exception_t& e)
609 {
610 phosphor::logging::log<phosphor::logging::level::ERR>(
611 "Error reading from ME");
612 return -1;
613 }
614
615 if (DEBUG)
616 {
617 std::cout << "ME resp data: ";
618 for (auto d : respData)
619 {
620 std::cout << d << " ";
621 }
622 std::cout << "\n";
623 }
624
625 return 0;
626 }
627
udbg_get_info_page(uint8_t,uint8_t page,uint8_t * next,uint8_t * count,uint8_t * buffer)628 static int udbg_get_info_page(uint8_t, uint8_t page, uint8_t* next,
629 uint8_t* count, uint8_t* buffer)
630 {
631 char line_buff[1000];
632 [[maybe_unused]] char* pres_dev = line_buff;
633 [[maybe_unused]] size_t pos = plat_get_fru_sel();
634 int ret;
635 std::string serialName = "SerialNumber";
636 std::string partName = "PartNumber";
637 std::string verDel = "VERSION=";
638 std::string verPath = "/etc/os-release";
639 size_t hostPosition = 0;
640 size_t maxPosition;
641
642 if (page == 1)
643 {
644 // Only update frame data while getting page 1
645
646 // initialize and clear frame
647 frame_info.init();
648 snprintf(frame_info.title, 32, "SYS_Info");
649
650 bool platform = isMultiHostPlatform();
651 if (platform == true)
652 {
653 hostPosition = plat_get_fru_sel();
654 }
655
656 getMaxHostPosition(maxPosition);
657 std::string data;
658 if (hostPosition == BMC_POSITION || hostInstances == "0")
659 {
660 data = "FRU:" + getMotherBoardFruName();
661 }
662 else if (hostPosition != BMC_POSITION && hostPosition <= maxPosition)
663 {
664 if (getMotherBoardFruName() != "")
665 {
666 data = "FRU:" + getMotherBoardFruName();
667 }
668 else
669 {
670 data = "FRU:slot" + std::to_string(hostPosition);
671 }
672 }
673 frame_info.append(data);
674
675 // FRU
676 frame_info.append("SN:");
677 if (getFruData(data, serialName) != 0)
678 {
679 data = "Not Found";
680 }
681 frame_info.append(data, 1);
682 frame_info.append("PN:");
683 if (getFruData(data, partName) != 0)
684 {
685 data = "Not Found";
686 }
687 frame_info.append(data, 1);
688
689 // LAN
690 getNetworkData(3, line_buff);
691 frame_info.append("BMC_IP:");
692 frame_info.append(line_buff, 1);
693 getNetworkData(59, line_buff);
694 frame_info.append("BMC_IPv6:");
695 frame_info.append(line_buff, 1);
696
697 // BMC ver
698 std::ifstream file(verPath);
699 if (file)
700 {
701 std::string line;
702 while (std::getline(file, line))
703 {
704 if (line.find(verDel) != std::string::npos)
705 {
706 std::string bmcVer = line.substr(verDel.size());
707 frame_info.append("BMC_FW_ver:");
708 frame_info.append(bmcVer, 1);
709 break;
710 }
711 }
712 }
713
714 if (hostPosition != BMC_POSITION || hostInstances == "0")
715 {
716 // BIOS ver
717 std::string biosVer;
718 if (getBiosVer(biosVer, hostPosition) == 0)
719 {
720 frame_info.append("BIOS_FW_ver:");
721 frame_info.append(biosVer, 1);
722 }
723 }
724
725 /* TBD: Board ID needs implementation */
726 // Board ID
727
728 // Battery - Use Escape sequence
729 frame_info.append("Battery:");
730 frame_info.append(ESC_BAT " ", 1);
731 // frame_info.append(&frame_info, esc_bat, 1);
732
733 // MCU Version - Use Escape sequence
734 frame_info.append("MCUbl_ver:");
735 frame_info.append(ESC_MCU_BL_VER, 1);
736 frame_info.append("MCU_ver:");
737 frame_info.append(ESC_MCU_RUN_VER, 1);
738
739 // Sys config present device
740 if (hostPosition != BMC_POSITION)
741 {
742 frame_info.append("Sys Conf. info:");
743
744 // Dimm info
745 std::vector<std::string> data;
746 if (sysConfig(data, pos) == 0)
747 {
748 for (auto& info : data)
749 {
750 frame_info.append(info, 1);
751 }
752 }
753 else
754 {
755 frame_info.append("Not Found", 1);
756 }
757
758 // Processor info
759 std::string result;
760 if (procInfo(result, pos) != 0)
761 {
762 result = "Not Found";
763 }
764 frame_info.append(result, 1);
765 }
766
767 } // End of update frame
768
769 if (page > frame_info.pages)
770 {
771 return -1;
772 }
773
774 ret = frame_info.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
775 if (ret < 0)
776 {
777 *count = 0;
778 return -1;
779 }
780 *count = (uint8_t)ret;
781
782 if (page < frame_info.pages)
783 *next = page + 1;
784 else
785 *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
786 // last page
787
788 return 0;
789 }
790
udbg_get_postcode(uint8_t,uint8_t page,uint8_t * next,uint8_t * count,uint8_t * buffer)791 static int udbg_get_postcode(uint8_t, uint8_t page, uint8_t* next,
792 uint8_t* count, uint8_t* buffer)
793 {
794 // up to 70 codes can be displayed on 10 pages
795 static constexpr size_t maxPostcodes = 70;
796 bool platform = isMultiHostPlatform();
797 size_t hostPosition = 0;
798
799 if (page == 1)
800 {
801 // Initialize and clear frame (example initialization)
802 frame_postcode.init();
803 snprintf(frame_postcode.title, 32, "POST CODE");
804 frame_postcode.max_page = 10;
805
806 if (platform)
807 getSelectorPosition(hostPosition);
808
809 // Synchronously get D-Bus connection
810 auto bus = sdbusplus::bus::new_default();
811 std::string serviceName =
812 BOOT_POSTCODE_SERVICE + std::to_string(hostPosition);
813 std::string objectPath =
814 BOOT_POSTCODE_OBJECTPATH + std::to_string(hostPosition);
815
816 // Build D-Bus method call
817 auto method = bus.new_method_call(
818 serviceName.c_str(), // Target service name
819 objectPath.c_str(), // Object path
820 BOOT_POSTCODE_INTERFACE, // Interface name
821 "GetPostCodes"); // Method name
822
823 method.append(uint16_t(1)); // Add method parameter, assuming it's pag
824 try
825 {
826 auto reply = bus.call(method); // Send synchronous method call
827
828 // Read postcode value
829 auto postcodes = reply.unpack<std::vector<
830 std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>>>();
831
832 // retrieve the latest postcodes
833 size_t numEntries = std::min(maxPostcodes, postcodes.size());
834 auto range = std::ranges::subrange(postcodes.rbegin(),
835 postcodes.rbegin() + numEntries);
836 for (const auto& [code, extra] : range)
837 {
838 std::string result;
839 result.reserve(2 * code.size());
840 for (const auto& byte : code)
841 {
842 result += std::format("{:02X}", byte);
843 }
844
845 frame_postcode.append(result);
846 if (frame_postcode.lines >= maxPostcodes)
847 {
848 break;
849 }
850 }
851 }
852 catch (const std::exception& e)
853 {
854 // Handle exceptions
855 std::cerr << "Error retrieving postcodes: " << e.what()
856 << std::endl;
857 return -1;
858 }
859 }
860
861 if (page > frame_postcode.pages)
862 {
863 return -1;
864 }
865
866 int ret = frame_postcode.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
867 if (ret < 0)
868 {
869 *count = 0;
870 return -1;
871 }
872 *count = (uint8_t)ret;
873
874 if (page < frame_postcode.pages)
875 *next = page + 1;
876 else
877 *next = 0xFF; // Set next to 0xFF to indicate last page
878 return 0;
879 }
880
plat_udbg_get_frame_data(uint8_t frame,uint8_t page,uint8_t * next,uint8_t * count,uint8_t * buffer)881 int plat_udbg_get_frame_data(uint8_t frame, uint8_t page, uint8_t* next,
882 uint8_t* count, uint8_t* buffer)
883 {
884 switch (frame)
885 {
886 case 1: // info_page
887 return udbg_get_info_page(frame, page, next, count, buffer);
888 case 2: // Extra Post Code
889 return udbg_get_postcode(frame, page, next, count, buffer);
890 default:
891 return -1;
892 }
893 }
894
panel_main(size_t item)895 static panel panel_main(size_t item)
896 {
897 // Update item list when select item 0
898 switch (item)
899 {
900 case 1:
901 return panels[std::to_underlying(panel::BOOT_ORDER)].select(0);
902 case 2:
903 return panels[std::to_underlying(panel::POWER_POLICY)].select(0);
904 default:
905 return panel::MAIN;
906 }
907 }
908
panel_boot_order(size_t selectedItemIndex)909 static panel panel_boot_order(size_t selectedItemIndex)
910 {
911 static constexpr size_t sizeBootOrder = 6;
912 static constexpr size_t bootValid = 0x80;
913
914 std::vector<uint8_t> bootSeq;
915
916 ctrl_panel& bootOrderPanel = panels[std::to_underlying(panel::BOOT_ORDER)];
917
918 size_t pos = plat_get_fru_sel();
919
920 if (pos == FRU_ALL)
921 {
922 bootOrderPanel.item_num = 0;
923 return panel::BOOT_ORDER;
924 }
925
926 auto [bootObjPath, hostName] = ipmi::boot::objPath(pos);
927 ipmi::boot::getBootOrder(bootObjPath, bootSeq, hostName);
928
929 uint8_t& bootMode = bootSeq.front();
930
931 // One item is selected to set a new boot sequence.
932 // The selected item become the first boot order.
933 if (selectedItemIndex > 0 && selectedItemIndex < sizeBootOrder)
934 {
935 // Move the selected item to second element (the first one is boot mode)
936 std::rotate(bootSeq.begin() + 1, bootSeq.begin() + selectedItemIndex,
937 bootSeq.begin() + selectedItemIndex + 1);
938
939 bootMode |= bootValid;
940 try
941 {
942 ipmi::boot::setBootOrder(bootObjPath, bootSeq, hostName);
943 }
944 catch (const std::exception& e)
945 {
946 lg2::error("Fail to set boot order : {ERROR}", "ERROR", e);
947 }
948
949 // refresh items
950 return bootOrderPanel.select(0);
951 }
952
953 // '*': boot flags valid, BIOS has not yet read
954 bootOrderPanel.item_str[0] =
955 std::string("Boot Order") + ((bootMode & bootValid) ? "*" : "");
956
957 static const std::unordered_map<uint8_t, const char*>
958 bootOrderMappingTable = {
959 {0x00, " USB device"}, {0x01, " Network v4"}, {0x02, " SATA HDD"},
960 {0x03, " SATA-CDROM"}, {0x04, " Other"}, {0x09, " Network v6"},
961 };
962
963 size_t validItem = 0;
964 for (size_t i = 1; i < sizeBootOrder; i++)
965 {
966 auto find = bootOrderMappingTable.find(bootSeq[i]);
967 if (find == bootOrderMappingTable.end())
968 {
969 lg2::error("Unknown boot order : {BOOTORDER}", "BOOTORDER",
970 bootSeq[i]);
971 break;
972 }
973
974 bootOrderPanel.item_str[i] = find->second;
975
976 validItem++;
977 }
978
979 bootOrderPanel.item_num = validItem;
980 return panel::BOOT_ORDER;
981 }
982
panel_power_policy(size_t)983 static panel panel_power_policy(size_t)
984 {
985 /* To be cleaned */
986 #if 0
987 uint8_t buff[32] = {0};
988 uint8_t res_len;
989 size_t pos = plat_get_fru_sel();
990 uint8_t policy;
991 uint8_t pwr_policy_item_map[3] = {POWER_CFG_ON, POWER_CFG_LPS,
992 POWER_CFG_OFF};
993
994 if (pos != FRU_ALL)
995 {
996 if (item > 0 && item <= sizeof(pwr_policy_item_map))
997 {
998 policy = pwr_policy_item_map[item - 1];
999 pal_set_power_restore_policy(pos, &policy, nullptr);
1000 }
1001 pal_get_chassis_status(pos, nullptr, buff, &res_len);
1002 policy = (((uint8_t)buff[0]) >> 5) & 0x7;
1003 snprintf(panels[PANEL_POWER_POLICY].item_str[1], 32, "%cPower On",
1004 policy == POWER_CFG_ON ? '*' : ' ');
1005 snprintf(panels[PANEL_POWER_POLICY].item_str[2], 32, "%cLast State",
1006 policy == POWER_CFG_LPS ? '*' : ' ');
1007 snprintf(panels[PANEL_POWER_POLICY].item_str[3], 32, "%cPower Off",
1008 policy == POWER_CFG_OFF ? '*' : ' ');
1009 panels[PANEL_POWER_POLICY].item_num = 3;
1010 }
1011 else
1012 {
1013 panels[PANEL_POWER_POLICY].item_num = 0;
1014 }
1015 #endif
1016 return panel::POWER_POLICY;
1017 }
1018
plat_udbg_control_panel(uint8_t cur_panel,uint8_t operation,uint8_t item,uint8_t * count,uint8_t * buffer)1019 ipmi_ret_t plat_udbg_control_panel(uint8_t cur_panel, uint8_t operation,
1020 uint8_t item, uint8_t* count,
1021 uint8_t* buffer)
1022 {
1023 if (cur_panel > panelNum || cur_panel < std::to_underlying(panel::MAIN))
1024 return ipmi::ccParmOutOfRange;
1025
1026 // No more item; End of item list
1027 if (item > panels[cur_panel].item_num)
1028 return ipmi::ccParmOutOfRange;
1029
1030 switch (operation)
1031 {
1032 case 0: // Get Description
1033 break;
1034 case 1: // Select item
1035 cur_panel = std::to_underlying(panels[cur_panel].select(item));
1036 item = 0;
1037 break;
1038 case 2: // Back
1039 cur_panel = std::to_underlying(panels[cur_panel].parent);
1040 item = 0;
1041 break;
1042 default:
1043 return ipmi::ccParmOutOfRange;
1044 }
1045
1046 buffer[0] = cur_panel;
1047 buffer[1] = item;
1048 buffer[2] = std::size(panels[cur_panel].item_str[item]);
1049
1050 if (buffer[2] > 0 && (buffer[2] + 3u) < FRAME_PAGE_BUF_SIZE)
1051 {
1052 std::memcpy(&buffer[3], (panels[cur_panel].item_str[item]).c_str(),
1053 buffer[2]);
1054 }
1055 *count = buffer[2] + 3;
1056 return ipmi::ccSuccess;
1057 }
1058
1059 } // end of namespace ipmi
1060