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