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