xref: /openbmc/fb-ipmi-oem/src/usb-dbg.cpp (revision 90903803458a09ea1929cd9745903cd032c5050a)
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