xref: /openbmc/fb-ipmi-oem/src/usb-dbg.cpp (revision 7ab87bbb)
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 = getService(*dbus, ipmi::selector::interface,
61                                          ipmi::selector::path);
62         Value variant = getDbusProperty(*dbus, service, ipmi::selector::path,
63                                         ipmi::selector::interface,
64                                         "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 = getService(*dbus, ipmi::selector::interface,
81                                          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 = "Phase" +
520                                                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 udbg_get_cri_sel(uint8_t, uint8_t page, uint8_t* next,
616                             uint8_t* count, uint8_t* buffer)
617 {
618     if (page == 1)
619     {
620         // initialize and clear frame
621         frame_sel.init(FRAME_BUFF_SIZE);
622         frame_sel.overwrite = 1;
623         frame_sel.max_page = 20;
624         snprintf(frame_sel.title, 32, "Cri SEL");
625 
626         static constexpr const auto depth = 0;
627         std::vector<std::string> paths;
628 
629         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
630 
631         auto mapperCall = dbus->new_method_call(
632             "xyz.openbmc_project.ObjectMapper",
633             "/xyz/openbmc_project/object_mapper",
634             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
635         static constexpr std::array<const char*, 1> interface = {
636             "xyz.openbmc_project.Logging.Entry"};
637         mapperCall.append("/", depth, interface);
638 
639         try
640         {
641             auto reply = dbus->call(mapperCall);
642             reply.read(paths);
643         }
644         catch (sdbusplus::exception_t& e)
645         {
646             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
647             return -1;
648         }
649 
650         std::string message;
651 
652         // Iterate each loggings
653         for (const auto& path : paths)
654         {
655             Value variant = ipmi::getDbusProperty(
656                 *dbus, "xyz.openbmc_project.Logging", path,
657                 "xyz.openbmc_project.Logging.Entry", "Severity");
658 
659             // Only filter critical sel
660             if (std::get<std::string>(variant) !=
661                 "xyz.openbmc_project.Logging.Entry.Level.Critical")
662             {
663                 continue;
664             }
665 
666             variant = ipmi::getDbusProperty(
667                 *dbus, "xyz.openbmc_project.Logging", path,
668                 "xyz.openbmc_project.Logging.Entry", "Message");
669             message = std::get<std::string>(variant);
670 
671             frame_sel.insert(message.c_str(), 0);
672         }
673     }
674     if (page > frame_sel.pages)
675     {
676         return -1;
677     }
678     int ret = frame_sel.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
679     if (ret < 0)
680     {
681         *count = 0;
682         return -1;
683     }
684     *count = (uint8_t)ret;
685 
686     if (page < frame_sel.pages)
687         *next = page + 1;
688     else
689         *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
690                       // last page
691 
692     return 0;
693 }
694 
695 static int udbg_get_cri_sensor(uint8_t, uint8_t page, uint8_t* next,
696                                uint8_t* count, uint8_t* buffer)
697 {
698     int ret;
699     double fvalue;
700     size_t pos = plat_get_fru_sel();
701 
702     if (page == 1)
703     {
704         // Only update frame data while getting page 1
705 
706         // initialize and clear frame
707         frame_snr.init(FRAME_BUFF_SIZE);
708         snprintf(frame_snr.title, 32, "CriSensor");
709 
710         nlohmann::json senObj;
711 
712         /* Get critical sensor names stored in json file */
713         std::ifstream file(JSON_SENSOR_NAMES_FILE);
714         if (file)
715         {
716             file >> senObj;
717             file.close();
718         }
719         else
720         {
721             phosphor::logging::log<phosphor::logging::level::ERR>(
722                 "Critical Sensor names file not found",
723                 phosphor::logging::entry("CRI_SENSOR_NAMES_FILE=%s",
724                                          JSON_SENSOR_NAMES_FILE));
725             return -1;
726         }
727 
728         /* Get sensors values for all critical sensors */
729         for (auto& j : senObj.items())
730         {
731             std::string senName = j.key();
732             auto val = j.value();
733 
734             if (senName[0] == '_')
735             {
736                 senName = std::to_string(pos) + senName;
737             }
738 
739             if (ipmi::storage::getSensorValue(senName, fvalue) == 0)
740             {
741                 std::stringstream ss;
742                 int prec = 0; // Default value
743 
744                 if (val.find("precision") != val.end())
745                     prec = val["precision"];
746 
747                 ss << std::fixed << std::setprecision(prec) << fvalue;
748 
749                 std::string senStr;
750                 if (val.find("short_name") != val.end())
751                     senStr = val["short_name"];
752                 else
753                     senStr = senName;
754 
755                 senStr += ss.str();
756 
757                 /* Get unit string for sensor and append in output */
758                 std::string unitStr;
759                 if (ipmi::storage::getSensorUnit(senName, unitStr) == 0)
760                     senStr += unitStr;
761 
762                 std::string thresholdStr;
763                 int ret = ipmi::storage::getSensorThreshold(senName,
764                                                             thresholdStr);
765                 if (ret < 0)
766                 {
767                     phosphor::logging::log<phosphor::logging::level::ERR>(
768                         "Error getting critical sensor threshold status",
769                         phosphor::logging::entry("CRI_SENSOR_NAME=%s",
770                                                  senName.c_str()));
771                     return -1;
772                 }
773                 if (thresholdStr.size() != 0)
774                 {
775                     senStr += ("/" + thresholdStr);
776                     std::string senStrWithBlinkAndInvertColor =
777                         ESC_ALT + senStr + ESC_RST;
778                     frame_snr.append(senStrWithBlinkAndInvertColor.c_str(), 0);
779                 }
780                 else
781                 {
782                     frame_snr.append(senStr.c_str(), 0);
783                 }
784             }
785             else
786             {
787                 phosphor::logging::log<phosphor::logging::level::INFO>(
788                     "Critical sensor not found",
789                     phosphor::logging::entry("CRI_SENSOR_NAME=%s",
790                                              senName.c_str()));
791             }
792         }
793 
794     } // End of update frame
795 
796     if (page > frame_snr.pages)
797     {
798         return -1;
799     }
800 
801     ret = frame_snr.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
802     if (ret < 0)
803     {
804         *count = 0;
805         return -1;
806     }
807     *count = (uint8_t)ret;
808 
809     if (page < frame_snr.pages)
810         *next = page + 1;
811     else
812         *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
813                       // last page
814 
815     return 0;
816 }
817 
818 static int getBiosVer(std::string& ver, size_t hostPosition)
819 {
820     nlohmann::json appObj;
821 
822     std::ifstream file(JSON_APP_DATA_FILE);
823     if (file)
824     {
825         file >> appObj;
826         file.close();
827         std::string version_key = KEY_SYSFW_VER + std::to_string(hostPosition);
828 
829         if (appObj.find(version_key) != appObj.end())
830         {
831             ver = appObj[version_key].get<std::string>();
832             return 0;
833         }
834     }
835 
836     return -1;
837 }
838 
839 int sendBicCmd(uint8_t netFn, uint8_t cmd, uint8_t bicAddr,
840                std::vector<uint8_t>& cmdData, std::vector<uint8_t>& respData)
841 {
842     static constexpr uint8_t lun = 0;
843 
844     auto bus = getSdBus();
845 
846     auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
847                                        "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
848                                        "org.openbmc.Ipmb", "sendRequest");
849     method.append(bicAddr, netFn, lun, cmd, cmdData);
850 
851     auto reply = bus->call(method);
852     if (reply.is_method_error())
853     {
854         phosphor::logging::log<phosphor::logging::level::ERR>(
855             "Error reading from BIC");
856         return -1;
857     }
858 
859     IpmbMethodType resp;
860     reply.read(resp);
861 
862     respData =
863         std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp));
864 
865     return 0;
866 }
867 
868 int sendMeCmd(uint8_t netFn, uint8_t cmd, std::vector<uint8_t>& cmdData,
869               std::vector<uint8_t>& respData)
870 {
871     auto bus = getSdBus();
872 
873     if (DEBUG)
874     {
875         std::cout << "ME NetFn:cmd " << (int)netFn << ":" << (int)cmd << "\n";
876         std::cout << "ME req data: ";
877         for (auto d : cmdData)
878         {
879             std::cout << d << " ";
880         }
881         std::cout << "\n";
882     }
883 
884     auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
885                                        "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
886                                        "org.openbmc.Ipmb", "sendRequest");
887     method.append(meAddress, netFn, lun, cmd, cmdData);
888 
889     auto reply = bus->call(method);
890     if (reply.is_method_error())
891     {
892         phosphor::logging::log<phosphor::logging::level::ERR>(
893             "Error reading from ME");
894         return -1;
895     }
896 
897     IpmbMethodType resp;
898     reply.read(resp);
899 
900     respData =
901         std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp));
902 
903     if (DEBUG)
904     {
905         std::cout << "ME resp data: ";
906         for (auto d : respData)
907         {
908             std::cout << d << " ";
909         }
910         std::cout << "\n";
911     }
912 
913     return 0;
914 }
915 
916 static int udbg_get_info_page(uint8_t, uint8_t page, uint8_t* next,
917                               uint8_t* count, uint8_t* buffer)
918 {
919     char line_buff[1000];
920     [[maybe_unused]] char* pres_dev = line_buff;
921     [[maybe_unused]] size_t pos = plat_get_fru_sel();
922     int ret;
923     std::string serialName = "SerialNumber";
924     std::string partName = "PartNumber";
925     std::string verDel = "VERSION=";
926     std::string verPath = "/etc/os-release";
927     size_t hostPosition = 0;
928     size_t maxPosition;
929 
930     if (page == 1)
931     {
932         // Only update frame data while getting page 1
933 
934         // initialize and clear frame
935         frame_info.init(FRAME_BUFF_SIZE);
936         snprintf(frame_info.title, 32, "SYS_Info");
937 
938         bool platform = isMultiHostPlatform();
939         if (platform == true)
940         {
941             hostPosition = plat_get_fru_sel();
942         }
943 
944         getMaxHostPosition(maxPosition);
945         if (hostPosition == BMC_POSITION || hostInstances == "0")
946         {
947             frame_info.append("FRU:spb", 0);
948         }
949         else if (hostPosition != BMC_POSITION && hostPosition <= maxPosition)
950         {
951             std::string data = "FRU:slot" + std::to_string(hostPosition);
952             frame_info.append(data.c_str(), 0);
953         }
954 
955         // FRU
956         std::string data;
957         frame_info.append("SN:", 0);
958         if (getFruData(data, serialName) != 0)
959         {
960             data = "Not Found";
961         }
962         frame_info.append(data.c_str(), 1);
963         frame_info.append("PN:", 0);
964         if (getFruData(data, partName) != 0)
965         {
966             data = "Not Found";
967         }
968         frame_info.append(data.c_str(), 1);
969 
970         // LAN
971         getNetworkData(3, line_buff);
972         frame_info.append("BMC_IP:", 0);
973         frame_info.append(line_buff, 1);
974         getNetworkData(59, line_buff);
975         frame_info.append("BMC_IPv6:", 0);
976         frame_info.append(line_buff, 1);
977 
978         // BMC ver
979         std::ifstream file(verPath);
980         if (file)
981         {
982             std::string line;
983             while (std::getline(file, line))
984             {
985                 if (line.find(verDel) != std::string::npos)
986                 {
987                     std::string bmcVer = line.substr(verDel.size());
988                     frame_info.append("BMC_FW_ver:", 0);
989                     frame_info.append(bmcVer.c_str(), 1);
990                     break;
991                 }
992             }
993         }
994 
995         if (hostPosition != BMC_POSITION)
996         {
997             // BIOS ver
998             std::string biosVer;
999             if (getBiosVer(biosVer, hostPosition) == 0)
1000             {
1001                 frame_info.append("BIOS_FW_ver:", 0);
1002                 frame_info.append(biosVer.c_str(), 1);
1003             }
1004         }
1005 
1006         /* TBD: Board ID needs implementation */
1007         // Board ID
1008 
1009         // Battery - Use Escape sequence
1010         frame_info.append("Battery:", 0);
1011         frame_info.append(ESC_BAT "     ", 1);
1012         // frame_info.append(&frame_info, esc_bat, 1);
1013 
1014         // MCU Version - Use Escape sequence
1015         frame_info.append("MCUbl_ver:", 0);
1016         frame_info.append(ESC_MCU_BL_VER, 1);
1017         frame_info.append("MCU_ver:", 0);
1018         frame_info.append(ESC_MCU_RUN_VER, 1);
1019 
1020         // Sys config present device
1021         if (hostPosition != BMC_POSITION)
1022         {
1023             frame_info.append("Sys Conf. info:", 0);
1024 
1025             // Dimm info
1026             std::vector<std::string> data;
1027             if (sysConfig(data, pos) == 0)
1028             {
1029                 for (auto& info : data)
1030                 {
1031                     frame_info.append(info.c_str(), 1);
1032                 }
1033             }
1034             else
1035             {
1036                 frame_info.append("Not Found", 1);
1037             }
1038 
1039             // Processor info
1040             std::string result;
1041             if (procInfo(result, pos) != 0)
1042             {
1043                 result = "Not Found";
1044             }
1045             frame_info.append(result.c_str(), 1);
1046         }
1047 
1048     } // End of update frame
1049 
1050     if (page > frame_info.pages)
1051     {
1052         return -1;
1053     }
1054 
1055     ret = frame_info.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
1056     if (ret < 0)
1057     {
1058         *count = 0;
1059         return -1;
1060     }
1061     *count = (uint8_t)ret;
1062 
1063     if (page < frame_info.pages)
1064         *next = page + 1;
1065     else
1066         *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
1067                       // last page
1068 
1069     return 0;
1070 }
1071 
1072 int plat_udbg_get_frame_data(uint8_t frame, uint8_t page, uint8_t* next,
1073                              uint8_t* count, uint8_t* buffer)
1074 {
1075     switch (frame)
1076     {
1077         case 1: // info_page
1078             return udbg_get_info_page(frame, page, next, count, buffer);
1079         case 2: // critical SEL
1080             return udbg_get_cri_sel(frame, page, next, count, buffer);
1081         case 3: // critical Sensor
1082             return udbg_get_cri_sensor(frame, page, next, count, buffer);
1083         default:
1084             return -1;
1085     }
1086 }
1087 
1088 static uint8_t panel_main(uint8_t item)
1089 {
1090     // Update item list when select item 0
1091     switch (item)
1092     {
1093         case 1:
1094             return panels[PANEL_BOOT_ORDER].select(0);
1095         case 2:
1096             return panels[PANEL_POWER_POLICY].select(0);
1097         default:
1098             return PANEL_MAIN;
1099     }
1100 }
1101 
1102 static uint8_t panel_boot_order(uint8_t selectedItemIndex)
1103 {
1104     static constexpr size_t sizeBootOrder = 6;
1105     static constexpr size_t bootValid = 0x80;
1106 
1107     std::vector<uint8_t> bootSeq;
1108 
1109     ctrl_panel& bootOrderPanel = panels[PANEL_BOOT_ORDER];
1110 
1111     size_t pos = plat_get_fru_sel();
1112 
1113     if (pos == FRU_ALL)
1114     {
1115         bootOrderPanel.item_num = 0;
1116         return PANEL_BOOT_ORDER;
1117     }
1118 
1119     auto [bootObjPath, hostName] = ipmi::boot::objPath(pos);
1120     ipmi::boot::getBootOrder(bootObjPath, bootSeq, hostName);
1121 
1122     uint8_t& bootMode = bootSeq.front();
1123 
1124     // One item is selected to set a new boot sequence.
1125     // The selected item become the first boot order.
1126     if (selectedItemIndex > 0 && selectedItemIndex < sizeBootOrder)
1127     {
1128         // Move the selected item to second element (the first one is boot mode)
1129         std::rotate(bootSeq.begin() + 1, bootSeq.begin() + selectedItemIndex,
1130                     bootSeq.begin() + selectedItemIndex + 1);
1131 
1132         bootMode |= bootValid;
1133         try
1134         {
1135             ipmi::boot::setBootOrder(bootObjPath, bootSeq, hostName);
1136         }
1137         catch (const std::exception& e)
1138         {
1139             lg2::error("Fail to set boot order : {ERROR}", "ERROR", e);
1140         }
1141 
1142         // refresh items
1143         return bootOrderPanel.select(0);
1144     }
1145 
1146     // '*': boot flags valid, BIOS has not yet read
1147     bootOrderPanel.item_str[0] = std::string("Boot Order") +
1148                                  ((bootMode & bootValid) ? "*" : "");
1149 
1150     static const std::unordered_map<uint8_t, const char*>
1151         bootOrderMappingTable = {
1152             {0x00, " USB device"}, {0x01, " Network v4"}, {0x02, " SATA HDD"},
1153             {0x03, " SATA-CDROM"}, {0x04, " Other"},      {0x09, " Network v6"},
1154         };
1155 
1156     size_t validItem = 0;
1157     for (size_t i = 1; i < sizeBootOrder; i++)
1158     {
1159         auto find = bootOrderMappingTable.find(bootSeq[i]);
1160         if (find == bootOrderMappingTable.end())
1161         {
1162             lg2::error("Unknown boot order : {BOOTORDER}", "BOOTORDER",
1163                        bootSeq[i]);
1164             break;
1165         }
1166 
1167         bootOrderPanel.item_str[i] = find->second;
1168 
1169         validItem++;
1170     }
1171 
1172     bootOrderPanel.item_num = validItem;
1173     return PANEL_BOOT_ORDER;
1174 }
1175 
1176 static uint8_t panel_power_policy(uint8_t)
1177 {
1178 /* To be cleaned */
1179 #if 0
1180     uint8_t buff[32] = {0};
1181     uint8_t res_len;
1182     size_t pos = plat_get_fru_sel();
1183     uint8_t policy;
1184     uint8_t pwr_policy_item_map[3] = {POWER_CFG_ON, POWER_CFG_LPS,
1185                                       POWER_CFG_OFF};
1186 
1187     if (pos != FRU_ALL)
1188     {
1189         if (item > 0 && item <= sizeof(pwr_policy_item_map))
1190         {
1191             policy = pwr_policy_item_map[item - 1];
1192             pal_set_power_restore_policy(pos, &policy, NULL);
1193         }
1194         pal_get_chassis_status(pos, NULL, buff, &res_len);
1195         policy = (((uint8_t)buff[0]) >> 5) & 0x7;
1196         snprintf(panels[PANEL_POWER_POLICY].item_str[1], 32, "%cPower On",
1197                  policy == POWER_CFG_ON ? '*' : ' ');
1198         snprintf(panels[PANEL_POWER_POLICY].item_str[2], 32, "%cLast State",
1199                  policy == POWER_CFG_LPS ? '*' : ' ');
1200         snprintf(panels[PANEL_POWER_POLICY].item_str[3], 32, "%cPower Off",
1201                  policy == POWER_CFG_OFF ? '*' : ' ');
1202         panels[PANEL_POWER_POLICY].item_num = 3;
1203     }
1204     else
1205     {
1206         panels[PANEL_POWER_POLICY].item_num = 0;
1207     }
1208 #endif
1209     return PANEL_POWER_POLICY;
1210 }
1211 
1212 ipmi_ret_t plat_udbg_control_panel(uint8_t panel, uint8_t operation,
1213                                    uint8_t item, uint8_t* count,
1214                                    uint8_t* buffer)
1215 {
1216     if (panel > panelNum || panel < PANEL_MAIN)
1217         return IPMI_CC_PARM_OUT_OF_RANGE;
1218 
1219     // No more item; End of item list
1220     if (item > panels[panel].item_num)
1221         return IPMI_CC_PARM_OUT_OF_RANGE;
1222 
1223     switch (operation)
1224     {
1225         case 0: // Get Description
1226             break;
1227         case 1: // Select item
1228             panel = panels[panel].select(item);
1229             item = 0;
1230             break;
1231         case 2: // Back
1232             panel = panels[panel].parent;
1233             item = 0;
1234             break;
1235         default:
1236             return IPMI_CC_PARM_OUT_OF_RANGE;
1237     }
1238 
1239     buffer[0] = panel;
1240     buffer[1] = item;
1241     buffer[2] = std::size(panels[panel].item_str[item]);
1242 
1243     if (buffer[2] > 0 && (buffer[2] + 3) < FRAME_PAGE_BUF_SIZE)
1244     {
1245         std::memcpy(&buffer[3], (panels[panel].item_str[item]).c_str(),
1246                     buffer[2]);
1247     }
1248     *count = buffer[2] + 3;
1249     return IPMI_CC_OK;
1250 }
1251 
1252 } // end of namespace ipmi
1253