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