xref: /openbmc/fb-ipmi-oem/src/usb-dbg.cpp (revision 2405ae98)
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 void getMaxHostPosition(size_t& maxPosition)
47 {
48     try
49     {
50         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
51         std::string service = getService(*dbus, ipmi::selector::interface,
52                                          ipmi::selector::path);
53         Value variant = getDbusProperty(*dbus, service, ipmi::selector::path,
54                                         ipmi::selector::interface,
55                                         "MaxPosition");
56         maxPosition = std::get<size_t>(variant);
57     }
58     catch (const std::exception& e)
59     {
60         lg2::error("Unable to get max host position - {MAXPOSITION}",
61                    "MAXPOSITION", maxPosition);
62         throw e;
63     }
64 }
65 
66 void getSelectorPosition(size_t& hostPosition)
67 {
68     try
69     {
70         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
71         std::string service = getService(*dbus, ipmi::selector::interface,
72                                          ipmi::selector::path);
73         Value variant = getDbusProperty(*dbus, service, ipmi::selector::path,
74                                         ipmi::selector::interface, "Position");
75         hostPosition = std::get<size_t>(variant);
76     }
77     catch (const std::exception& e)
78     {
79         lg2::error("Unable to get host position - {POSITION}", "POSITION",
80                    hostPosition);
81         throw e;
82     }
83 }
84 
85 static int panelNum = (sizeof(panels) / sizeof(struct ctrl_panel)) - 1;
86 
87 /* Returns the FRU the hand-switch is switched to. If it is switched to BMC
88  * it returns FRU_ALL. Note, if in err, it returns FRU_ALL */
89 static size_t plat_get_fru_sel()
90 {
91     size_t position;
92     bool platform = isMultiHostPlatform();
93     if (platform == true)
94     {
95         getSelectorPosition(position);
96         if (position == BMC_POSITION)
97         {
98             return FRU_ALL;
99         }
100     }
101     else
102     {
103         /* For Tiogapass it just return 1,
104          *  can modify to support more platform */
105         position = 1;
106     }
107     return position;
108 }
109 
110 // return 0 on seccuess
111 int frame::init(size_t size)
112 {
113     // Reset status
114     idx_head = idx_tail = 0;
115     lines = 0;
116     esc_sts = 0;
117     pages = 1;
118 
119     if (buf != NULL && max_size == size)
120     {
121         // reinit
122         return 0;
123     }
124 
125     if (buf != NULL && max_size != size)
126     {
127         delete[] buf;
128     }
129     // Initialize Configuration
130     title[0] = '\0';
131     buf = new char[size];
132     max_size = size;
133     max_page = size;
134     line_per_page = 7;
135     line_width = 16;
136     overwrite = 0;
137 
138     if (buf)
139         return 0;
140     else
141         return -1;
142 }
143 
144 // return 0 on seccuess
145 int frame::append(const char* string, int indent)
146 {
147     const size_t buf_size = 128;
148     char lbuf[buf_size];
149     char* ptr;
150     int ret;
151 
152     ret = parse(lbuf, buf_size, string, indent);
153 
154     if (ret < 0)
155         return ret;
156 
157     for (ptr = lbuf; *ptr != '\0'; ptr++)
158     {
159         if (isFull())
160         {
161             if (overwrite)
162             {
163                 if (buf[idx_head] == LINE_DELIMITER)
164                     lines--;
165                 idx_head = (idx_head + 1) % max_size;
166             }
167             else
168                 return -1;
169         }
170 
171         buf[idx_tail] = *ptr;
172         if (*ptr == LINE_DELIMITER)
173             lines++;
174 
175         idx_tail = (idx_tail + 1) % max_size;
176     }
177 
178     pages = (lines / line_per_page) + ((lines % line_per_page) ? 1 : 0);
179 
180     if (pages > max_page)
181         pages = max_page;
182 
183     return 0;
184 }
185 
186 // return 0 on seccuess
187 int frame::insert(const char* string, int indent)
188 {
189     const size_t buf_size = 128;
190     char lbuf[buf_size];
191     char* ptr;
192     int ret;
193     int i;
194 
195     ret = parse(lbuf, buf_size, string, indent);
196 
197     if (ret < 0)
198         return ret;
199 
200     for (i = strlen(lbuf) - 1; i >= 0; i--)
201     {
202         ptr = &lbuf[i];
203         if (isFull())
204         {
205             if (overwrite)
206             {
207                 idx_tail = (idx_tail + max_size - 1) % max_size;
208                 if (buf[idx_tail] == LINE_DELIMITER)
209                     lines--;
210             }
211             else
212                 return -1;
213         }
214 
215         idx_head = (idx_head + max_size - 1) % max_size;
216 
217         buf[idx_head] = *ptr;
218         if (*ptr == LINE_DELIMITER)
219             lines++;
220     }
221 
222     pages = (lines / line_per_page) + ((lines % line_per_page) ? 1 : 0);
223 
224     if (pages > max_page)
225         pages = max_page;
226 
227     return 0;
228 }
229 
230 // return page size
231 int frame::getPage(int page, char* page_buf, size_t page_buf_size)
232 {
233     int ret;
234     uint16_t line = 0;
235     uint16_t idx, len;
236 
237     if (buf == NULL)
238         return -1;
239 
240     // 1-based page
241     if (page > pages || page < 1)
242         return -1;
243 
244     if (page_buf == NULL || page_buf_size == 0)
245         return -1;
246 
247     ret = snprintf(page_buf, 17, "%-10s %02d/%02d", title, page, pages);
248     len = strlen(page_buf);
249     if (ret < 0)
250         return -1;
251 
252     line = 0;
253     idx = idx_head;
254     while (line < ((page - 1) * line_per_page) && idx != idx_tail)
255     {
256         if (buf[idx] == LINE_DELIMITER)
257             line++;
258         idx = (idx + 1) % max_size;
259     }
260 
261     while (line < ((page)*line_per_page) && idx != idx_tail)
262     {
263         if (buf[idx] == LINE_DELIMITER)
264         {
265             line++;
266         }
267         else
268         {
269             page_buf[len++] = buf[idx];
270             if (len == (page_buf_size - 1))
271             {
272                 break;
273             }
274         }
275         idx = (idx + 1) % max_size;
276     }
277 
278     return len;
279 }
280 
281 // return 1 for frame buffer full
282 int frame::isFull()
283 {
284     if (buf == NULL)
285         return -1;
286 
287     if ((idx_tail + 1) % max_size == idx_head)
288         return 1;
289     else
290         return 0;
291 }
292 
293 // return 1 for Escape Sequence
294 int frame::isEscSeq(char chr)
295 {
296     uint8_t curr_sts = esc_sts;
297 
298     if (esc_sts == 0 && (chr == 0x1b))
299         esc_sts = 1; // Escape Sequence
300     else if (esc_sts == 1 && (chr == 0x5b))
301         esc_sts = 2; // Control Sequence Introducer(CSI)
302     else if (esc_sts == 1 && (chr != 0x5b))
303         esc_sts = 0;
304     else if (esc_sts == 2 && (chr >= 0x40 && chr <= 0x7e))
305         esc_sts = 0;
306 
307     if (curr_sts || esc_sts)
308         return 1;
309     else
310         return 0;
311 }
312 
313 // return 0 on success
314 int frame::parse(char* lbuf, size_t buf_size, const char* input, int indent)
315 {
316     uint8_t pos, esc;
317     size_t i;
318     const char *in, *end;
319 
320     if (buf == NULL || input == NULL)
321         return -1;
322 
323     if (indent >= line_width || indent < 0)
324         return -1;
325 
326     in = input;
327     end = in + strlen(input);
328     pos = 0; // line position
329     esc = 0; // escape state
330     i = 0;   // buf index
331     while (in != end)
332     {
333         if (i >= buf_size)
334             break;
335 
336         if (pos < indent)
337         {
338             // fill indent
339             lbuf[i++] = ' ';
340             pos++;
341             continue;
342         }
343 
344         esc = isEscSeq(*in);
345 
346         if (!esc && pos == line_width)
347         {
348             lbuf[i++] = LINE_DELIMITER;
349             pos = 0;
350             continue;
351         }
352 
353         if (!esc)
354             pos++;
355 
356         // fill input data
357         lbuf[i++] = *(in++);
358     }
359 
360     // padding
361     while (pos <= line_width)
362     {
363         if (i >= buf_size)
364             break;
365         if (pos < line_width)
366             lbuf[i++] = ' ';
367         else
368             lbuf[i++] = LINE_DELIMITER;
369         pos++;
370     }
371 
372     // full
373     if (i >= buf_size)
374         return -1;
375 
376     lbuf[i++] = '\0';
377 
378     return 0;
379 }
380 
381 static int chk_cri_sel_update(uint8_t* cri_sel_up)
382 {
383     FILE* fp;
384     struct stat file_stat;
385     size_t pos = plat_get_fru_sel();
386     static uint8_t pre_pos = 0xff;
387 
388     fp = fopen("/mnt/data/cri_sel", "r");
389     if (fp)
390     {
391         if ((stat("/mnt/data/cri_sel", &file_stat) == 0) &&
392             (file_stat.st_mtime != frame_sel.mtime || pre_pos != pos))
393         {
394             *cri_sel_up = 1;
395         }
396         else
397         {
398             *cri_sel_up = 0;
399         }
400         fclose(fp);
401     }
402     else
403     {
404         if (frame_sel.buf == NULL || frame_sel.lines != 0 || pre_pos != pos)
405         {
406             *cri_sel_up = 1;
407         }
408         else
409         {
410             *cri_sel_up = 0;
411         }
412     }
413     pre_pos = pos;
414     return 0;
415 }
416 
417 int plat_udbg_get_frame_info(uint8_t* num)
418 {
419     *num = 3;
420     return 0;
421 }
422 
423 int plat_udbg_get_updated_frames(uint8_t* count, uint8_t* buffer)
424 {
425     uint8_t cri_sel_up = 0;
426     uint8_t info_page_up = 1;
427 
428     *count = 0;
429 
430     // info page update
431     if (info_page_up == 1)
432     {
433         buffer[*count] = 1;
434         *count += 1;
435     }
436 
437     // cri sel update
438     chk_cri_sel_update(&cri_sel_up);
439     if (cri_sel_up == 1)
440     {
441         buffer[*count] = 2;
442         *count += 1;
443     }
444 
445     // cri sensor update
446     buffer[*count] = 3;
447     *count += 1;
448 
449     return 0;
450 }
451 
452 int plat_udbg_get_post_desc(uint8_t index, uint8_t* next, uint8_t phase,
453                             uint8_t* end, uint8_t* length, uint8_t* buffer)
454 {
455     nlohmann::json postObj;
456     std::string postCode;
457 
458     /* Get post description data stored in json file */
459     std::ifstream file(JSON_POST_DATA_FILE);
460     if (file)
461     {
462         file >> postObj;
463         file.close();
464     }
465     else
466     {
467         phosphor::logging::log<phosphor::logging::level::ERR>(
468             "Post code description file not found",
469             phosphor::logging::entry("POST_CODE_FILE=%s", JSON_POST_DATA_FILE));
470         return -1;
471     }
472 
473     std::string phaseStr = "PhaseAny";
474     if (postObj.find(phaseStr) == postObj.end())
475     {
476         phaseStr = "Phase" + std::to_string(phase);
477     }
478 
479     if (postObj.find(phaseStr) == postObj.end())
480     {
481         phosphor::logging::log<phosphor::logging::level::ERR>(
482             "Post code phase not available",
483             phosphor::logging::entry("PHASE=%d", phase));
484         return -1;
485     }
486 
487     auto phaseObj = postObj[phaseStr];
488     int phaseSize = phaseObj.size();
489 
490     for (int i = 0; i < phaseSize; i++)
491     {
492         postCode = phaseObj[i][0];
493         if (index == stoul(postCode, nullptr, 16))
494         {
495             std::string postDesc = phaseObj[i][1];
496             *length = postDesc.size();
497             memcpy(buffer, postDesc.data(), *length);
498             buffer[*length] = '\0';
499 
500             if (phaseSize != i + 1)
501             {
502                 postCode = phaseObj[i + 1][0];
503                 *next = stoul(postCode, nullptr, 16);
504                 *end = 0;
505             }
506             else
507             {
508                 if (postObj.size() != phase)
509                 {
510                     std::string nextPhaseStr = "Phase" +
511                                                std::to_string(phase + 1);
512                     postCode = postObj[nextPhaseStr][0][0];
513                     *next = stoul(postCode, nullptr, 16);
514                     *end = 0;
515                 }
516                 else
517                 {
518                     *next = 0xff;
519                     *end = 1;
520                 }
521             }
522 
523             return 0;
524         }
525     }
526 
527     phosphor::logging::log<phosphor::logging::level::ERR>(
528         "Post code description data not available",
529         phosphor::logging::entry("PHASE_CODE=%d_0x%x", phase, index));
530     return -1;
531 }
532 
533 int plat_udbg_get_gpio_desc(uint8_t index, uint8_t* next, uint8_t* level,
534                             uint8_t* def, uint8_t* length, uint8_t* buffer)
535 {
536     nlohmann::json gpioObj;
537     std::string gpioPin;
538 
539     /* Get gpio data stored in json file */
540     std::ifstream file(JSON_GPIO_DATA_FILE);
541     if (file)
542     {
543         file >> gpioObj;
544         file.close();
545     }
546     else
547     {
548         phosphor::logging::log<phosphor::logging::level::ERR>(
549             "GPIO pin description file not found",
550             phosphor::logging::entry("GPIO_PIN_DETAILS_FILE=%s",
551                                      JSON_GPIO_DATA_FILE));
552         return -1;
553     }
554 
555     if (gpioObj.find(DEBUG_GPIO_KEY) == gpioObj.end())
556     {
557         phosphor::logging::log<phosphor::logging::level::ERR>(
558             "GPIO pin details not available",
559             phosphor::logging::entry("GPIO_JSON_KEY=%d", DEBUG_GPIO_KEY));
560         return -1;
561     }
562 
563     auto obj = gpioObj[DEBUG_GPIO_KEY];
564     int objSize = obj.size();
565 
566     for (int i = 0; i < objSize; i++)
567     {
568         if (obj[i].size() != GPIO_ARRAY_SIZE)
569         {
570             phosphor::logging::log<phosphor::logging::level::ERR>(
571                 "Size of gpio array is incorrect",
572                 phosphor::logging::entry("EXPECTED_SIZE=%d", GPIO_ARRAY_SIZE));
573             return -1;
574         }
575 
576         gpioPin = obj[i][GPIO_PIN_INDEX];
577         if (index == stoul(gpioPin, nullptr, 16))
578         {
579             if (objSize != i + 1)
580             {
581                 gpioPin = obj[i + 1][GPIO_PIN_INDEX];
582                 *next = stoul(gpioPin, nullptr, 16);
583             }
584             else
585             {
586                 *next = 0xff;
587             }
588 
589             *level = obj[i][GPIO_LEVEL_INDEX];
590             *def = obj[i][GPIO_DEF_INDEX];
591             std::string gpioDesc = obj[i][GPIO_DESC_INDEX];
592             *length = gpioDesc.size();
593             memcpy(buffer, gpioDesc.data(), *length);
594             buffer[*length] = '\0';
595 
596             return 0;
597         }
598     }
599 
600     phosphor::logging::log<phosphor::logging::level::ERR>(
601         "GPIO pin description data not available",
602         phosphor::logging::entry("GPIO_PIN=0x%x", index));
603     return -1;
604 }
605 
606 static int udbg_get_cri_sel(uint8_t, uint8_t page, uint8_t* next,
607                             uint8_t* count, uint8_t* buffer)
608 {
609     if (page == 1)
610     {
611         // initialize and clear frame
612         frame_sel.init(FRAME_BUFF_SIZE);
613         frame_sel.overwrite = 1;
614         frame_sel.max_page = 20;
615         snprintf(frame_sel.title, 32, "Cri SEL");
616 
617         static constexpr const auto depth = 0;
618         std::vector<std::string> paths;
619 
620         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
621 
622         auto mapperCall = dbus->new_method_call(
623             "xyz.openbmc_project.ObjectMapper",
624             "/xyz/openbmc_project/object_mapper",
625             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
626         static constexpr std::array<const char*, 1> interface = {
627             "xyz.openbmc_project.Logging.Entry"};
628         mapperCall.append("/", depth, interface);
629 
630         try
631         {
632             auto reply = dbus->call(mapperCall);
633             reply.read(paths);
634         }
635         catch (sdbusplus::exception_t& e)
636         {
637             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
638             return -1;
639         }
640 
641         std::string message;
642 
643         // Iterate each loggings
644         for (const auto& path : paths)
645         {
646             Value variant = ipmi::getDbusProperty(
647                 *dbus, "xyz.openbmc_project.Logging", path,
648                 "xyz.openbmc_project.Logging.Entry", "Severity");
649 
650             // Only filter critial sel
651             if (std::get<std::string>(variant) !=
652                 "xyz.openbmc_project.Logging.Entry.Level.Critical")
653             {
654                 continue;
655             }
656 
657             variant = ipmi::getDbusProperty(
658                 *dbus, "xyz.openbmc_project.Logging", path,
659                 "xyz.openbmc_project.Logging.Entry", "Message");
660             message = std::get<std::string>(variant);
661 
662             frame_sel.insert(message.c_str(), 0);
663         }
664     }
665     if (page > frame_sel.pages)
666     {
667         return -1;
668     }
669     int ret = frame_sel.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
670     if (ret < 0)
671     {
672         *count = 0;
673         return -1;
674     }
675     *count = (uint8_t)ret;
676 
677     if (page < frame_sel.pages)
678         *next = page + 1;
679     else
680         *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
681                       // last page
682 
683     return 0;
684 }
685 
686 static int udbg_get_cri_sensor(uint8_t, uint8_t page, uint8_t* next,
687                                uint8_t* count, uint8_t* buffer)
688 {
689     int ret;
690     double fvalue;
691     size_t pos = plat_get_fru_sel();
692 
693     if (page == 1)
694     {
695         // Only update frame data while getting page 1
696 
697         // initialize and clear frame
698         frame_snr.init(FRAME_BUFF_SIZE);
699         snprintf(frame_snr.title, 32, "CriSensor");
700 
701         nlohmann::json senObj;
702 
703         /* Get critical sensor names stored in json file */
704         std::ifstream file(JSON_SENSOR_NAMES_FILE);
705         if (file)
706         {
707             file >> senObj;
708             file.close();
709         }
710         else
711         {
712             phosphor::logging::log<phosphor::logging::level::ERR>(
713                 "Critical Sensor names file not found",
714                 phosphor::logging::entry("CRI_SENSOR_NAMES_FILE=%s",
715                                          JSON_SENSOR_NAMES_FILE));
716             return -1;
717         }
718 
719         /* Get sensors values for all critical sensors */
720         for (auto& j : senObj.items())
721         {
722             std::string senName = j.key();
723             auto val = j.value();
724 
725             if (senName[0] == '_')
726             {
727                 senName = std::to_string(pos) + senName;
728             }
729 
730             if (ipmi::storage::getSensorValue(senName, fvalue) == 0)
731             {
732                 std::stringstream ss;
733                 int prec = 0; // Default value
734 
735                 if (val.find("precision") != val.end())
736                     prec = val["precision"];
737 
738                 ss << std::fixed << std::setprecision(prec) << fvalue;
739 
740                 std::string senStr;
741                 if (val.find("short_name") != val.end())
742                     senStr = val["short_name"];
743                 else
744                     senStr = senName;
745 
746                 senStr += ss.str();
747 
748                 /* Get unit string for sensor and append in output */
749                 std::string unitStr;
750                 if (ipmi::storage::getSensorUnit(senName, unitStr) == 0)
751                     senStr += unitStr;
752 
753                 std::string thresholdStr;
754                 int ret = ipmi::storage::getSensorThreshold(senName,
755                                                             thresholdStr);
756                 if (ret < 0)
757                 {
758                     phosphor::logging::log<phosphor::logging::level::ERR>(
759                         "Error getting critical sensor threshold status",
760                         phosphor::logging::entry("CRI_SENSOR_NAME=%s",
761                                                  senName.c_str()));
762                     return -1;
763                 }
764                 if (thresholdStr.size() != 0)
765                 {
766                     senStr += ("/" + thresholdStr);
767                     std::string senStrWithBlinkAndInvertColor =
768                         ESC_ALT + senStr + ESC_RST;
769                     frame_snr.append(senStrWithBlinkAndInvertColor.c_str(), 0);
770                 }
771                 else
772                 {
773                     frame_snr.append(senStr.c_str(), 0);
774                 }
775             }
776             else
777             {
778                 phosphor::logging::log<phosphor::logging::level::INFO>(
779                     "Critical sensor not found",
780                     phosphor::logging::entry("CRI_SENSOR_NAME=%s",
781                                              senName.c_str()));
782             }
783         }
784 
785     } // End of update frame
786 
787     if (page > frame_snr.pages)
788     {
789         return -1;
790     }
791 
792     ret = frame_snr.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
793     if (ret < 0)
794     {
795         *count = 0;
796         return -1;
797     }
798     *count = (uint8_t)ret;
799 
800     if (page < frame_snr.pages)
801         *next = page + 1;
802     else
803         *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
804                       // last page
805 
806     return 0;
807 }
808 
809 static int getBiosVer(std::string& ver, size_t hostPosition)
810 {
811     nlohmann::json appObj;
812 
813     std::ifstream file(JSON_APP_DATA_FILE);
814     if (file)
815     {
816         file >> appObj;
817         file.close();
818         std::string version_key = KEY_SYSFW_VER + std::to_string(hostPosition);
819 
820         if (appObj.find(version_key) != appObj.end())
821         {
822             ver = appObj[version_key].get<std::string>();
823             return 0;
824         }
825     }
826 
827     return -1;
828 }
829 
830 int sendBicCmd(uint8_t netFn, uint8_t cmd, uint8_t bicAddr,
831                std::vector<uint8_t>& cmdData, std::vector<uint8_t>& respData)
832 {
833     static constexpr uint8_t lun = 0;
834 
835     auto bus = getSdBus();
836 
837     auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
838                                        "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
839                                        "org.openbmc.Ipmb", "sendRequest");
840     method.append(bicAddr, netFn, lun, cmd, cmdData);
841 
842     auto reply = bus->call(method);
843     if (reply.is_method_error())
844     {
845         phosphor::logging::log<phosphor::logging::level::ERR>(
846             "Error reading from BIC");
847         return -1;
848     }
849 
850     IpmbMethodType resp;
851     reply.read(resp);
852 
853     respData =
854         std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp));
855 
856     return 0;
857 }
858 
859 int sendMeCmd(uint8_t netFn, uint8_t cmd, std::vector<uint8_t>& cmdData,
860               std::vector<uint8_t>& respData)
861 {
862     auto bus = getSdBus();
863 
864     if (DEBUG)
865     {
866         std::cout << "ME NetFn:cmd " << (int)netFn << ":" << (int)cmd << "\n";
867         std::cout << "ME req data: ";
868         for (auto d : cmdData)
869         {
870             std::cout << d << " ";
871         }
872         std::cout << "\n";
873     }
874 
875     auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
876                                        "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
877                                        "org.openbmc.Ipmb", "sendRequest");
878     method.append(meAddress, netFn, lun, cmd, cmdData);
879 
880     auto reply = bus->call(method);
881     if (reply.is_method_error())
882     {
883         phosphor::logging::log<phosphor::logging::level::ERR>(
884             "Error reading from ME");
885         return -1;
886     }
887 
888     IpmbMethodType resp;
889     reply.read(resp);
890 
891     respData =
892         std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp));
893 
894     if (DEBUG)
895     {
896         std::cout << "ME resp data: ";
897         for (auto d : respData)
898         {
899             std::cout << d << " ";
900         }
901         std::cout << "\n";
902     }
903 
904     return 0;
905 }
906 
907 #ifdef ME_SUPPORT
908 static int getMeStatus(std::string& status, size_t pos)
909 {
910     uint8_t cmd = 0x01;   // Get Device id command
911     uint8_t netFn = 0x06; // Netfn for APP
912     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
913     std::vector<uint8_t> cmdData;
914 
915     uint8_t meAddr = meAddress;
916     bool platform = isMultiHostPlatform();
917     if (platform == true)
918     {
919         meAddr = ((pos - 1) << 2);
920     }
921 
922     auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
923                                        "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
924                                        "org.openbmc.Ipmb", "sendRequest");
925     method.append(meAddr, netFn, lun, cmd, cmdData);
926 
927     auto reply = bus->call(method);
928     if (reply.is_method_error())
929     {
930         std::cerr << "Error reading from ME\n";
931         return -1;
932     }
933 
934     IpmbMethodType resp;
935     reply.read(resp);
936 
937     std::vector<uint8_t> data;
938     data = std::get<5>(resp);
939 
940     if (DEBUG)
941     {
942         std::cout << "ME Get ID: ";
943         for (size_t d : data)
944         {
945             std::cout << d << " ";
946         }
947         std::cout << "\n";
948     }
949 
950     if (data[2] & 0x80)
951         status = "recovery mode";
952     else
953         status = "operation mode";
954 
955     return 0;
956 }
957 #endif
958 
959 static int udbg_get_info_page(uint8_t, uint8_t page, uint8_t* next,
960                               uint8_t* count, uint8_t* buffer)
961 {
962     char line_buff[1000];
963     [[maybe_unused]] char* pres_dev = line_buff;
964     [[maybe_unused]] size_t pos = plat_get_fru_sel();
965     int ret;
966     std::string serialName = "SerialNumber";
967     std::string partName = "PartNumber";
968     std::string verDel = "VERSION=";
969     std::string verPath = "/etc/os-release";
970     size_t hostPosition;
971     size_t maxPosition;
972 
973     if (page == 1)
974     {
975         // Only update frame data while getting page 1
976 
977         // initialize and clear frame
978         frame_info.init(FRAME_BUFF_SIZE);
979         snprintf(frame_info.title, 32, "SYS_Info");
980 
981         bool platform = isMultiHostPlatform();
982         if (platform == true)
983         {
984             hostPosition = plat_get_fru_sel();
985         }
986 
987         getMaxHostPosition(maxPosition);
988         if (hostPosition == BMC_POSITION || hostInstances == "0")
989         {
990             frame_info.append("FRU:spb", 0);
991         }
992         else if (hostPosition != BMC_POSITION && hostPosition <= maxPosition)
993         {
994             std::string data = "FRU:slot" + std::to_string(hostPosition);
995             frame_info.append(data.c_str(), 0);
996         }
997 
998         // FRU
999         std::string data;
1000         frame_info.append("SN:", 0);
1001         if (getFruData(data, serialName) != 0)
1002         {
1003             data = "Not Found";
1004         }
1005         frame_info.append(data.c_str(), 1);
1006         frame_info.append("PN:", 0);
1007         if (getFruData(data, partName) != 0)
1008         {
1009             data = "Not Found";
1010         }
1011         frame_info.append(data.c_str(), 1);
1012 
1013         // LAN
1014         getNetworkData(3, line_buff);
1015         frame_info.append("BMC_IP:", 0);
1016         frame_info.append(line_buff, 1);
1017         getNetworkData(59, line_buff);
1018         frame_info.append("BMC_IPv6:", 0);
1019         frame_info.append(line_buff, 1);
1020 
1021         // BMC ver
1022         std::ifstream file(verPath);
1023         if (file)
1024         {
1025             std::string line;
1026             while (std::getline(file, line))
1027             {
1028                 if (line.find(verDel) != std::string::npos)
1029                 {
1030                     std::string bmcVer = line.substr(verDel.size());
1031                     frame_info.append("BMC_FW_ver:", 0);
1032                     frame_info.append(bmcVer.c_str(), 1);
1033                     break;
1034                 }
1035             }
1036         }
1037 
1038         if (hostPosition != BMC_POSITION)
1039         {
1040             // BIOS ver
1041             std::string biosVer;
1042             if (getBiosVer(biosVer, hostPosition) == 0)
1043             {
1044                 frame_info.append("BIOS_FW_ver:", 0);
1045                 frame_info.append(biosVer.c_str(), 1);
1046             }
1047 
1048 #ifdef ME_SUPPORT
1049             // ME status
1050             std::string meStatus;
1051             if (getMeStatus(meStatus, pos) != 0)
1052             {
1053                 phosphor::logging::log<phosphor::logging::level::WARNING>(
1054                     "Reading ME status failed");
1055                 meStatus = "unknown";
1056             }
1057             frame_info.append("ME_status:", 0);
1058             frame_info.append(meStatus.c_str(), 1);
1059 #endif
1060         }
1061 
1062         /* TBD: Board ID needs implementation */
1063         // Board ID
1064 
1065         // Battery - Use Escape sequence
1066         frame_info.append("Battery:", 0);
1067         frame_info.append(ESC_BAT "     ", 1);
1068         // frame_info.append(&frame_info, esc_bat, 1);
1069 
1070         // MCU Version - Use Escape sequence
1071         frame_info.append("MCUbl_ver:", 0);
1072         frame_info.append(ESC_MCU_BL_VER, 1);
1073         frame_info.append("MCU_ver:", 0);
1074         frame_info.append(ESC_MCU_RUN_VER, 1);
1075 
1076         // Sys config present device
1077         if (hostPosition != BMC_POSITION)
1078         {
1079             frame_info.append("Sys Conf. info:", 0);
1080 
1081             // Dimm info
1082             std::vector<std::string> data;
1083             if (sysConfig(data, pos) == 0)
1084             {
1085                 for (auto& info : data)
1086                 {
1087                     frame_info.append(info.c_str(), 1);
1088                 }
1089             }
1090             else
1091             {
1092                 frame_info.append("Not Found", 1);
1093             }
1094 
1095             // Processor info
1096             std::string result;
1097             if (procInfo(result, pos) != 0)
1098             {
1099                 result = "Not Found";
1100             }
1101             frame_info.append(result.c_str(), 1);
1102         }
1103 
1104     } // End of update frame
1105 
1106     if (page > frame_info.pages)
1107     {
1108         return -1;
1109     }
1110 
1111     ret = frame_info.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
1112     if (ret < 0)
1113     {
1114         *count = 0;
1115         return -1;
1116     }
1117     *count = (uint8_t)ret;
1118 
1119     if (page < frame_info.pages)
1120         *next = page + 1;
1121     else
1122         *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
1123                       // last page
1124 
1125     return 0;
1126 }
1127 
1128 int plat_udbg_get_frame_data(uint8_t frame, uint8_t page, uint8_t* next,
1129                              uint8_t* count, uint8_t* buffer)
1130 {
1131     switch (frame)
1132     {
1133         case 1: // info_page
1134             return udbg_get_info_page(frame, page, next, count, buffer);
1135         case 2: // critical SEL
1136             return udbg_get_cri_sel(frame, page, next, count, buffer);
1137         case 3: // critical Sensor
1138             return udbg_get_cri_sensor(frame, page, next, count, buffer);
1139         default:
1140             return -1;
1141     }
1142 }
1143 
1144 static uint8_t panel_main(uint8_t item)
1145 {
1146     // Update item list when select item 0
1147     switch (item)
1148     {
1149         case 1:
1150             return panels[PANEL_BOOT_ORDER].select(0);
1151         case 2:
1152             return panels[PANEL_POWER_POLICY].select(0);
1153         default:
1154             return PANEL_MAIN;
1155     }
1156 }
1157 
1158 static uint8_t panel_boot_order(uint8_t)
1159 {
1160     /* To be implemented */
1161 #if 0
1162     int i;
1163     unsigned char buff[MAX_VALUE_LEN], pickup, len;
1164     size_t pos = plat_get_fru_sel();
1165     if (pos != FRU_ALL && pal_get_boot_order(pos, buff, buff, &len) == 0)
1166     {
1167         if (item > 0 && item < SIZE_BOOT_ORDER)
1168         {
1169             pickup = buff[item];
1170             while (item > 1)
1171             {
1172                 buff[item] = buff[item - 1];
1173                 item--;
1174             }
1175             buff[item] = pickup;
1176             buff[0] |= 0x80;
1177             pal_set_boot_order(pos, buff, buff, &len);
1178 
1179             // refresh items
1180             return panels[PANEL_BOOT_ORDER].select(0);
1181         }
1182 
1183         // '*': boot flags valid, BIOS has not yet read
1184         snprintf(panels[PANEL_BOOT_ORDER].item_str[0], 32, "Boot Order%c",
1185                  (buff[0] & 0x80) ? '*' : '\0');
1186 
1187         for (i = 1; i < SIZE_BOOT_ORDER; i++)
1188         {
1189             switch (buff[i])
1190             {
1191                 case 0x0:
1192                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1193                              " USB device");
1194                     break;
1195                 case 0x1:
1196                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1197                              " Network v4");
1198                     break;
1199                 case (0x1 | 0x8):
1200                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1201                              " Network v6");
1202                     break;
1203                 case 0x2:
1204                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1205                              " SATA HDD");
1206                     break;
1207                 case 0x3:
1208                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1209                              " SATA-CDROM");
1210                     break;
1211                 case 0x4:
1212                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1213                              " Other");
1214                     break;
1215                 default:
1216                     panels[PANEL_BOOT_ORDER].item_str[i][0] = '\0';
1217                     break;
1218             }
1219         }
1220 
1221         // remove empty items
1222         for (i--;
1223              (strlen(panels[PANEL_BOOT_ORDER].item_str[i]) == 0) && (i > 0);
1224              i--)
1225             ;
1226 
1227         panels[PANEL_BOOT_ORDER].item_num = i;
1228     }
1229     else
1230     {
1231         panels[PANEL_BOOT_ORDER].item_num = 0;
1232     }
1233 #endif
1234     return PANEL_BOOT_ORDER;
1235 }
1236 
1237 static uint8_t panel_power_policy(uint8_t)
1238 {
1239 /* To be cleaned */
1240 #if 0
1241     uint8_t buff[32] = {0};
1242     uint8_t res_len;
1243     size_t pos = plat_get_fru_sel();
1244     uint8_t policy;
1245     uint8_t pwr_policy_item_map[3] = {POWER_CFG_ON, POWER_CFG_LPS,
1246                                       POWER_CFG_OFF};
1247 
1248     if (pos != FRU_ALL)
1249     {
1250         if (item > 0 && item <= sizeof(pwr_policy_item_map))
1251         {
1252             policy = pwr_policy_item_map[item - 1];
1253             pal_set_power_restore_policy(pos, &policy, NULL);
1254         }
1255         pal_get_chassis_status(pos, NULL, buff, &res_len);
1256         policy = (((uint8_t)buff[0]) >> 5) & 0x7;
1257         snprintf(panels[PANEL_POWER_POLICY].item_str[1], 32, "%cPower On",
1258                  policy == POWER_CFG_ON ? '*' : ' ');
1259         snprintf(panels[PANEL_POWER_POLICY].item_str[2], 32, "%cLast State",
1260                  policy == POWER_CFG_LPS ? '*' : ' ');
1261         snprintf(panels[PANEL_POWER_POLICY].item_str[3], 32, "%cPower Off",
1262                  policy == POWER_CFG_OFF ? '*' : ' ');
1263         panels[PANEL_POWER_POLICY].item_num = 3;
1264     }
1265     else
1266     {
1267         panels[PANEL_POWER_POLICY].item_num = 0;
1268     }
1269 #endif
1270     return PANEL_POWER_POLICY;
1271 }
1272 
1273 int plat_udbg_control_panel(uint8_t panel, uint8_t operation, uint8_t item,
1274                             uint8_t* count, uint8_t* buffer)
1275 {
1276     if (panel > panelNum || panel < PANEL_MAIN)
1277         return IPMI_CC_PARM_OUT_OF_RANGE;
1278 
1279     // No more item; End of item list
1280     if (item > panels[panel].item_num)
1281         return IPMI_CC_PARM_OUT_OF_RANGE;
1282 
1283     switch (operation)
1284     {
1285         case 0: // Get Description
1286             break;
1287         case 1: // Select item
1288             panel = panels[panel].select(item);
1289             item = 0;
1290             break;
1291         case 2: // Back
1292             panel = panels[panel].parent;
1293             item = 0;
1294             break;
1295         default:
1296             return IPMI_CC_PARM_OUT_OF_RANGE;
1297     }
1298 
1299     buffer[0] = panel;
1300     buffer[1] = item;
1301     buffer[2] = strlen(panels[panel].item_str[item]);
1302     if (buffer[2] > 0 && (buffer[2] + 3) < FRAME_PAGE_BUF_SIZE)
1303     {
1304         memcpy(&buffer[3], panels[panel].item_str[item], buffer[2]);
1305     }
1306     *count = buffer[2] + 3;
1307     return IPMI_CC_OK;
1308 }
1309 
1310 } // end of namespace ipmi
1311