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