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