xref: /openbmc/fb-ipmi-oem/src/usb-dbg.cpp (revision e9baaff1437783d916e18b19d32d3b1fcb58e01a)
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, size_t hostPosition)
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         std::string version_key = KEY_SYSFW_VER + std::to_string(hostPosition);
808 
809         if (appObj.find(version_key) != appObj.end())
810         {
811             ver = appObj[version_key].get<std::string>();
812             return 0;
813         }
814     }
815 
816     return -1;
817 }
818 
819 int sendBicCmd(uint8_t netFn, uint8_t cmd, uint8_t bicAddr,
820                std::vector<uint8_t>& cmdData, std::vector<uint8_t>& respData)
821 {
822     static constexpr uint8_t lun = 0;
823 
824     auto bus = getSdBus();
825 
826     auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
827                                        "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
828                                        "org.openbmc.Ipmb", "sendRequest");
829     method.append(bicAddr, netFn, lun, cmd, cmdData);
830 
831     auto reply = bus->call(method);
832     if (reply.is_method_error())
833     {
834         phosphor::logging::log<phosphor::logging::level::ERR>(
835             "Error reading from BIC");
836         return -1;
837     }
838 
839     IpmbMethodType resp;
840     reply.read(resp);
841 
842     respData =
843         std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp));
844 
845     return 0;
846 }
847 
848 int sendMeCmd(uint8_t netFn, uint8_t cmd, std::vector<uint8_t>& cmdData,
849               std::vector<uint8_t>& respData)
850 {
851     auto bus = getSdBus();
852 
853     if (DEBUG)
854     {
855         std::cout << "ME NetFn:cmd " << (int)netFn << ":" << (int)cmd << "\n";
856         std::cout << "ME req data: ";
857         for (auto d : cmdData)
858         {
859             std::cout << d << " ";
860         }
861         std::cout << "\n";
862     }
863 
864     auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
865                                        "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
866                                        "org.openbmc.Ipmb", "sendRequest");
867     method.append(meAddress, netFn, lun, cmd, cmdData);
868 
869     auto reply = bus->call(method);
870     if (reply.is_method_error())
871     {
872         phosphor::logging::log<phosphor::logging::level::ERR>(
873             "Error reading from ME");
874         return -1;
875     }
876 
877     IpmbMethodType resp;
878     reply.read(resp);
879 
880     respData =
881         std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp));
882 
883     if (DEBUG)
884     {
885         std::cout << "ME resp data: ";
886         for (auto d : respData)
887         {
888             std::cout << d << " ";
889         }
890         std::cout << "\n";
891     }
892 
893     return 0;
894 }
895 
896 #ifdef ME_SUPPORT
897 static int getMeStatus(std::string& status, size_t pos)
898 {
899     uint8_t cmd = 0x01;   // Get Device id command
900     uint8_t netFn = 0x06; // Netfn for APP
901     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
902     std::vector<uint8_t> cmdData;
903 
904     uint8_t meAddr = meAddress;
905     bool platform = isMultiHostPlatform();
906     if (platform == true)
907     {
908         meAddr = ((pos - 1) << 2);
909     }
910 
911     auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
912                                        "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
913                                        "org.openbmc.Ipmb", "sendRequest");
914     method.append(meAddr, netFn, lun, cmd, cmdData);
915 
916     auto reply = bus->call(method);
917     if (reply.is_method_error())
918     {
919         std::cerr << "Error reading from ME\n";
920         return -1;
921     }
922 
923     IpmbMethodType resp;
924     reply.read(resp);
925 
926     std::vector<uint8_t> data;
927     data = std::get<5>(resp);
928 
929     if (DEBUG)
930     {
931         std::cout << "ME Get ID: ";
932         for (size_t d : data)
933         {
934             std::cout << d << " ";
935         }
936         std::cout << "\n";
937     }
938 
939     if (data[2] & 0x80)
940         status = "recovery mode";
941     else
942         status = "operation mode";
943 
944     return 0;
945 }
946 #endif
947 
948 static int udbg_get_info_page(uint8_t, uint8_t page, uint8_t* next,
949                               uint8_t* count, uint8_t* buffer)
950 {
951     char line_buff[1000];
952     [[maybe_unused]] char* pres_dev = line_buff;
953     [[maybe_unused]] size_t pos = plat_get_fru_sel();
954     int ret;
955     std::string serialName = "SerialNumber";
956     std::string partName = "PartNumber";
957     std::string verDel = "VERSION=";
958     std::string verPath = "/etc/os-release";
959     size_t hostPosition;
960 
961     if (page == 1)
962     {
963         // Only update frame data while getting page 1
964 
965         // initialize and clear frame
966         frame_info.init(FRAME_BUFF_SIZE);
967         snprintf(frame_info.title, 32, "SYS_Info");
968 
969         bool platform = isMultiHostPlatform();
970         if (platform == true)
971         {
972             hostPosition = getSelectorPosition();
973         }
974 
975         if (hostPosition == BMC_POSITION || hostInstances == "0")
976         {
977             frame_info.append("FRU:spb", 0);
978         }
979         else if (hostPosition != BMC_POSITION &&
980                  hostPosition <= getMaxHostPosition())
981         {
982             std::string data = "FRU:slot" + std::to_string(hostPosition);
983             frame_info.append(data.c_str(), 0);
984         }
985 
986         // FRU
987         std::string data;
988         frame_info.append("SN:", 0);
989         if (getFruData(data, serialName) != 0)
990         {
991             data = "Not Found";
992         }
993         frame_info.append(data.c_str(), 1);
994         frame_info.append("PN:", 0);
995         if (getFruData(data, partName) != 0)
996         {
997             data = "Not Found";
998         }
999         frame_info.append(data.c_str(), 1);
1000 
1001         // LAN
1002         getNetworkData(3, line_buff);
1003         frame_info.append("BMC_IP:", 0);
1004         frame_info.append(line_buff, 1);
1005         getNetworkData(59, line_buff);
1006         frame_info.append("BMC_IPv6:", 0);
1007         frame_info.append(line_buff, 1);
1008 
1009         // BMC ver
1010         std::ifstream file(verPath);
1011         if (file)
1012         {
1013             std::string line;
1014             while (std::getline(file, line))
1015             {
1016                 if (line.find(verDel) != std::string::npos)
1017                 {
1018                     std::string bmcVer = line.substr(verDel.size());
1019                     frame_info.append("BMC_FW_ver:", 0);
1020                     frame_info.append(bmcVer.c_str(), 1);
1021                     break;
1022                 }
1023             }
1024         }
1025 
1026         if (hostPosition != BMC_POSITION)
1027         {
1028             // BIOS ver
1029             std::string biosVer;
1030             if (getBiosVer(biosVer, hostPosition) == 0)
1031             {
1032                 frame_info.append("BIOS_FW_ver:", 0);
1033                 frame_info.append(biosVer.c_str(), 1);
1034             }
1035 
1036 #ifdef ME_SUPPORT
1037             // ME status
1038             std::string meStatus;
1039             if (getMeStatus(meStatus, pos) != 0)
1040             {
1041                 phosphor::logging::log<phosphor::logging::level::WARNING>(
1042                     "Reading ME status failed");
1043                 meStatus = "unknown";
1044             }
1045             frame_info.append("ME_status:", 0);
1046             frame_info.append(meStatus.c_str(), 1);
1047 #endif
1048         }
1049 
1050         /* TBD: Board ID needs implementation */
1051         // Board ID
1052 
1053         // Battery - Use Escape sequence
1054         frame_info.append("Battery:", 0);
1055         frame_info.append(ESC_BAT "     ", 1);
1056         // frame_info.append(&frame_info, esc_bat, 1);
1057 
1058         // MCU Version - Use Escape sequence
1059         frame_info.append("MCUbl_ver:", 0);
1060         frame_info.append(ESC_MCU_BL_VER, 1);
1061         frame_info.append("MCU_ver:", 0);
1062         frame_info.append(ESC_MCU_RUN_VER, 1);
1063 
1064         // Sys config present device
1065         if (hostPosition != BMC_POSITION)
1066         {
1067             frame_info.append("Sys Conf. info:", 0);
1068 
1069             // Dimm info
1070             std::vector<std::string> data;
1071             sysConfig(data, pos);
1072             for (auto& info : data)
1073             {
1074                 frame_info.append(info.c_str(), 1);
1075             }
1076 
1077             // Processor info
1078             std::string result;
1079             procInfo(result, pos);
1080             frame_info.append(result.c_str(), 1);
1081         }
1082 
1083     } // End of update frame
1084 
1085     if (page > frame_info.pages)
1086     {
1087         return -1;
1088     }
1089 
1090     ret = frame_info.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
1091     if (ret < 0)
1092     {
1093         *count = 0;
1094         return -1;
1095     }
1096     *count = (uint8_t)ret;
1097 
1098     if (page < frame_info.pages)
1099         *next = page + 1;
1100     else
1101         *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
1102                       // last page
1103 
1104     return 0;
1105 }
1106 
1107 int plat_udbg_get_frame_data(uint8_t frame, uint8_t page, uint8_t* next,
1108                              uint8_t* count, uint8_t* buffer)
1109 {
1110     switch (frame)
1111     {
1112         case 1: // info_page
1113             return udbg_get_info_page(frame, page, next, count, buffer);
1114         case 2: // critical SEL
1115             return udbg_get_cri_sel(frame, page, next, count, buffer);
1116         case 3: // critical Sensor
1117             return udbg_get_cri_sensor(frame, page, next, count, buffer);
1118         default:
1119             return -1;
1120     }
1121 }
1122 
1123 static uint8_t panel_main(uint8_t item)
1124 {
1125     // Update item list when select item 0
1126     switch (item)
1127     {
1128         case 1:
1129             return panels[PANEL_BOOT_ORDER].select(0);
1130         case 2:
1131             return panels[PANEL_POWER_POLICY].select(0);
1132         default:
1133             return PANEL_MAIN;
1134     }
1135 }
1136 
1137 static uint8_t panel_boot_order(uint8_t)
1138 {
1139     /* To be implemented */
1140 #if 0
1141     int i;
1142     unsigned char buff[MAX_VALUE_LEN], pickup, len;
1143     size_t pos = plat_get_fru_sel();
1144     if (pos != FRU_ALL && pal_get_boot_order(pos, buff, buff, &len) == 0)
1145     {
1146         if (item > 0 && item < SIZE_BOOT_ORDER)
1147         {
1148             pickup = buff[item];
1149             while (item > 1)
1150             {
1151                 buff[item] = buff[item - 1];
1152                 item--;
1153             }
1154             buff[item] = pickup;
1155             buff[0] |= 0x80;
1156             pal_set_boot_order(pos, buff, buff, &len);
1157 
1158             // refresh items
1159             return panels[PANEL_BOOT_ORDER].select(0);
1160         }
1161 
1162         // '*': boot flags valid, BIOS has not yet read
1163         snprintf(panels[PANEL_BOOT_ORDER].item_str[0], 32, "Boot Order%c",
1164                  (buff[0] & 0x80) ? '*' : '\0');
1165 
1166         for (i = 1; i < SIZE_BOOT_ORDER; i++)
1167         {
1168             switch (buff[i])
1169             {
1170                 case 0x0:
1171                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1172                              " USB device");
1173                     break;
1174                 case 0x1:
1175                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1176                              " Network v4");
1177                     break;
1178                 case (0x1 | 0x8):
1179                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1180                              " Network v6");
1181                     break;
1182                 case 0x2:
1183                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1184                              " SATA HDD");
1185                     break;
1186                 case 0x3:
1187                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1188                              " SATA-CDROM");
1189                     break;
1190                 case 0x4:
1191                     snprintf(panels[PANEL_BOOT_ORDER].item_str[i], 32,
1192                              " Other");
1193                     break;
1194                 default:
1195                     panels[PANEL_BOOT_ORDER].item_str[i][0] = '\0';
1196                     break;
1197             }
1198         }
1199 
1200         // remove empty items
1201         for (i--;
1202              (strlen(panels[PANEL_BOOT_ORDER].item_str[i]) == 0) && (i > 0);
1203              i--)
1204             ;
1205 
1206         panels[PANEL_BOOT_ORDER].item_num = i;
1207     }
1208     else
1209     {
1210         panels[PANEL_BOOT_ORDER].item_num = 0;
1211     }
1212 #endif
1213     return PANEL_BOOT_ORDER;
1214 }
1215 
1216 static uint8_t panel_power_policy(uint8_t)
1217 {
1218 /* To be cleaned */
1219 #if 0
1220     uint8_t buff[32] = {0};
1221     uint8_t res_len;
1222     size_t pos = plat_get_fru_sel();
1223     uint8_t policy;
1224     uint8_t pwr_policy_item_map[3] = {POWER_CFG_ON, POWER_CFG_LPS,
1225                                       POWER_CFG_OFF};
1226 
1227     if (pos != FRU_ALL)
1228     {
1229         if (item > 0 && item <= sizeof(pwr_policy_item_map))
1230         {
1231             policy = pwr_policy_item_map[item - 1];
1232             pal_set_power_restore_policy(pos, &policy, NULL);
1233         }
1234         pal_get_chassis_status(pos, NULL, buff, &res_len);
1235         policy = (((uint8_t)buff[0]) >> 5) & 0x7;
1236         snprintf(panels[PANEL_POWER_POLICY].item_str[1], 32, "%cPower On",
1237                  policy == POWER_CFG_ON ? '*' : ' ');
1238         snprintf(panels[PANEL_POWER_POLICY].item_str[2], 32, "%cLast State",
1239                  policy == POWER_CFG_LPS ? '*' : ' ');
1240         snprintf(panels[PANEL_POWER_POLICY].item_str[3], 32, "%cPower Off",
1241                  policy == POWER_CFG_OFF ? '*' : ' ');
1242         panels[PANEL_POWER_POLICY].item_num = 3;
1243     }
1244     else
1245     {
1246         panels[PANEL_POWER_POLICY].item_num = 0;
1247     }
1248 #endif
1249     return PANEL_POWER_POLICY;
1250 }
1251 
1252 int plat_udbg_control_panel(uint8_t panel, uint8_t operation, uint8_t item,
1253                             uint8_t* count, uint8_t* buffer)
1254 {
1255     if (panel > panelNum || panel < PANEL_MAIN)
1256         return IPMI_CC_PARM_OUT_OF_RANGE;
1257 
1258     // No more item; End of item list
1259     if (item > panels[panel].item_num)
1260         return IPMI_CC_PARM_OUT_OF_RANGE;
1261 
1262     switch (operation)
1263     {
1264         case 0: // Get Description
1265             break;
1266         case 1: // Select item
1267             panel = panels[panel].select(item);
1268             item = 0;
1269             break;
1270         case 2: // Back
1271             panel = panels[panel].parent;
1272             item = 0;
1273             break;
1274         default:
1275             return IPMI_CC_PARM_OUT_OF_RANGE;
1276     }
1277 
1278     buffer[0] = panel;
1279     buffer[1] = item;
1280     buffer[2] = strlen(panels[panel].item_str[item]);
1281     if (buffer[2] > 0 && (buffer[2] + 3) < FRAME_PAGE_BUF_SIZE)
1282     {
1283         memcpy(&buffer[3], panels[panel].item_str[item], buffer[2]);
1284     }
1285     *count = buffer[2] + 3;
1286     return IPMI_CC_OK;
1287 }
1288 
1289 } // end of namespace ipmi
1290