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