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