xref: /openbmc/fb-ipmi-oem/src/usb-dbg.cpp (revision 3add478ba5b825e2d800a5b6b8a98d409110e42e)
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 <commandutils.hpp>
18  #include <usb-dbg.hpp>
19  
20  namespace ipmi
21  {
22  
23  ipmi_ret_t getNetworkData(uint8_t lan_param, char* data);
24  std::string getMotherBoardFruName();
25  int8_t getFruData(std::string& serial, std::string& name);
26  int8_t sysConfig(std::vector<std::string>& data, size_t pos);
27  int8_t procInfo(std::string& result, size_t pos);
28  
29  bool isMultiHostPlatform();
30  
31  /* Declare Host Selector interface and path */
32  namespace selector
33  {
34  const std::string path = "/xyz/openbmc_project/Chassis/Buttons/HostSelector";
35  const std::string interface =
36      "xyz.openbmc_project.Chassis.Buttons.HostSelector";
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  int getSensorThreshold(std::string&, std::string&);
45  } // namespace storage
46  
47  namespace boot
48  {
49  std::tuple<std::string, std::string> objPath(size_t id);
50  void setBootOrder(std::string bootObjPath, const std::vector<uint8_t>& bootSeq,
51                    std::string hostName);
52  void getBootOrder(std::string bootObjPath, std::vector<uint8_t>& bootSeq,
53                    std::string hostName);
54  } // namespace boot
55  
getMaxHostPosition(size_t & maxPosition)56  void getMaxHostPosition(size_t& maxPosition)
57  {
58      try
59      {
60          std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
61          std::string service =
62              getService(*dbus, ipmi::selector::interface, ipmi::selector::path);
63          Value variant =
64              getDbusProperty(*dbus, service, ipmi::selector::path,
65                              ipmi::selector::interface, "MaxPosition");
66          maxPosition = std::get<size_t>(variant);
67      }
68      catch (const std::exception& e)
69      {
70          lg2::error("Unable to get max host position - {MAXPOSITION}",
71                     "MAXPOSITION", maxPosition);
72          throw e;
73      }
74  }
75  
getSelectorPosition(size_t & hostPosition)76  void getSelectorPosition(size_t& hostPosition)
77  {
78      try
79      {
80          std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
81          std::string service =
82              getService(*dbus, ipmi::selector::interface, ipmi::selector::path);
83          Value variant = getDbusProperty(*dbus, service, ipmi::selector::path,
84                                          ipmi::selector::interface, "Position");
85          hostPosition = std::get<size_t>(variant);
86      }
87      catch (const std::exception& e)
88      {
89          lg2::error("Unable to get host position - {POSITION}", "POSITION",
90                     hostPosition);
91          throw e;
92      }
93  }
94  
95  static int panelNum = (sizeof(panels) / sizeof(struct ctrl_panel)) - 1;
96  
97  /* Returns the FRU the hand-switch is switched to. If it is switched to BMC
98   * it returns FRU_ALL. Note, if in err, it returns FRU_ALL */
plat_get_fru_sel()99  static size_t plat_get_fru_sel()
100  {
101      size_t position;
102      bool platform = isMultiHostPlatform();
103      if (platform == true)
104      {
105          getSelectorPosition(position);
106          if (position == BMC_POSITION)
107          {
108              return FRU_ALL;
109          }
110      }
111      else
112      {
113          /* For Tiogapass it just return 1,
114           *  can modify to support more platform */
115          position = 1;
116      }
117      return position;
118  }
119  
120  // return 0 on seccuess
init(size_t size)121  void frame::init(size_t size)
122  {
123      // Reset status
124      idx_head = idx_tail = 0;
125      lines = 0;
126      esc_sts = 0;
127      pages = 1;
128  
129      if (buf != nullptr && max_size == size)
130      {
131          return;
132      }
133  
134      if (buf != nullptr && max_size != size)
135      {
136          delete[] buf;
137      }
138      // Initialize Configuration
139      title[0] = '\0';
140      buf = new char[size];
141      max_size = size;
142      max_page = size;
143      line_per_page = 7;
144      line_width = 16;
145      overwrite = false;
146  
147      return;
148  }
149  
150  // return 0 on seccuess
append(const std::string & str,size_t indent)151  void frame::append(const std::string& str, size_t indent)
152  {
153      for (auto ch : parse(str, indent))
154      {
155          if (isFull())
156          {
157              if (overwrite)
158              {
159                  if (buf[idx_head] == LINE_DELIMITER)
160                      lines--;
161                  idx_head = (idx_head + 1) % max_size;
162              }
163              else
164              {
165                  throw std::overflow_error("No room in buffer");
166              }
167          }
168  
169          buf[idx_tail] = ch;
170          if (ch == LINE_DELIMITER)
171              lines++;
172  
173          idx_tail = (idx_tail + 1) % max_size;
174      }
175  
176      pages = (lines / line_per_page) + ((lines % line_per_page) ? 1 : 0);
177  
178      if (pages > max_page)
179          pages = max_page;
180  
181      return;
182  }
183  
184  // return page size
getPage(size_t page,char * page_buf,size_t page_buf_size)185  int frame::getPage(size_t page, char* page_buf, size_t page_buf_size)
186  {
187      int ret;
188      uint16_t line = 0;
189      uint16_t idx, len;
190  
191      if (buf == nullptr)
192          return -1;
193  
194      // 1-based page
195      if (page > pages || page < 1)
196          return -1;
197  
198      if (page_buf == nullptr || page_buf_size == 0)
199          return -1;
200  
201      ret = snprintf(page_buf, 17, "%-10s %02zd/%02zd", title, page, pages);
202      len = strlen(page_buf);
203      if (ret < 0)
204          return -1;
205  
206      line = 0;
207      idx = idx_head;
208      while (line < ((page - 1) * line_per_page) && idx != idx_tail)
209      {
210          if (buf[idx] == LINE_DELIMITER)
211              line++;
212          idx = (idx + 1) % max_size;
213      }
214  
215      while (line < ((page)*line_per_page) && idx != idx_tail)
216      {
217          if (buf[idx] == LINE_DELIMITER)
218          {
219              line++;
220          }
221          else
222          {
223              page_buf[len++] = buf[idx];
224              if (len == (page_buf_size - 1))
225              {
226                  break;
227              }
228          }
229          idx = (idx + 1) % max_size;
230      }
231  
232      return len;
233  }
234  
isFull() const235  bool frame::isFull() const
236  {
237      if (buf == nullptr)
238          return true;
239  
240      if ((idx_tail + 1) % max_size == idx_head)
241          return true;
242      else
243          return false;
244  }
245  
246  // return 1 for Escape Sequence
isEscSeq(char chr)247  bool frame::isEscSeq(char chr)
248  {
249      uint8_t curr_sts = esc_sts;
250  
251      if (esc_sts == 0 && (chr == 0x1b))
252          esc_sts = 1; // Escape Sequence
253      else if (esc_sts == 1 && (chr == 0x5b))
254          esc_sts = 2; // Control Sequence Introducer(CSI)
255      else if (esc_sts == 1 && (chr != 0x5b))
256          esc_sts = 0;
257      else if (esc_sts == 2 && (chr >= 0x40 && chr <= 0x7e))
258          esc_sts = 0;
259  
260      if (curr_sts || esc_sts)
261          return true;
262      else
263          return false;
264  }
265  
266  // return 0 on success
parse(const std::string & input,size_t indent)267  auto frame::parse(const std::string& input, size_t indent) -> std::string
268  {
269      if (indent > line_width)
270          return {};
271  
272      std::string result;
273      size_t linepos = 0;
274  
275      for (auto ch : input)
276      {
277          if (linepos == 0)
278          {
279              result.append(indent, ' ');
280              linepos = indent;
281          }
282  
283          // Insert character.
284          result.push_back(ch);
285  
286          if (!isEscSeq(ch))
287          {
288              // Check if new line is needed.
289              if (++linepos == line_width)
290              {
291                  result.push_back(LINE_DELIMITER);
292                  linepos = 0;
293              }
294          }
295      }
296  
297      // Fill out remaining line.
298      result.append(line_width - linepos, ' ');
299      result.push_back(LINE_DELIMITER);
300  
301      return result;
302  }
303  
chk_cri_sel_update(uint8_t * cri_sel_up)304  static int chk_cri_sel_update(uint8_t* cri_sel_up)
305  {
306      FILE* fp;
307      struct stat file_stat;
308      size_t pos = plat_get_fru_sel();
309      static uint8_t pre_pos = 0xff;
310  
311      fp = fopen("/mnt/data/cri_sel", "r");
312      if (fp)
313      {
314          if ((stat("/mnt/data/cri_sel", &file_stat) == 0) &&
315              (file_stat.st_mtime != frame_sel.mtime || pre_pos != pos))
316          {
317              *cri_sel_up = 1;
318          }
319          else
320          {
321              *cri_sel_up = 0;
322          }
323          fclose(fp);
324      }
325      else
326      {
327          if (frame_sel.buf == nullptr || frame_sel.lines != 0 || pre_pos != pos)
328          {
329              *cri_sel_up = 1;
330          }
331          else
332          {
333              *cri_sel_up = 0;
334          }
335      }
336      pre_pos = pos;
337      return 0;
338  }
339  
plat_udbg_get_frame_info(uint8_t * num)340  int plat_udbg_get_frame_info(uint8_t* num)
341  {
342      *num = 3;
343      return 0;
344  }
345  
plat_udbg_get_updated_frames(uint8_t * count,uint8_t * buffer)346  int plat_udbg_get_updated_frames(uint8_t* count, uint8_t* buffer)
347  {
348      uint8_t cri_sel_up = 0;
349      uint8_t info_page_up = 1;
350  
351      *count = 0;
352  
353      // info page update
354      if (info_page_up == 1)
355      {
356          buffer[*count] = 1;
357          *count += 1;
358      }
359  
360      // cri sel update
361      chk_cri_sel_update(&cri_sel_up);
362      if (cri_sel_up == 1)
363      {
364          buffer[*count] = 2;
365          *count += 1;
366      }
367  
368      // cri sensor update
369      buffer[*count] = 3;
370      *count += 1;
371  
372      return 0;
373  }
374  
plat_udbg_get_post_desc(uint8_t index,uint8_t * next,uint8_t phase,uint8_t * end,uint8_t * length,uint8_t * buffer)375  int plat_udbg_get_post_desc(uint8_t index, uint8_t* next, uint8_t phase,
376                              uint8_t* end, uint8_t* length, uint8_t* buffer)
377  {
378      nlohmann::json postObj;
379      std::string postCode;
380  
381      /* Get post description data stored in json file */
382      std::ifstream file(JSON_POST_DATA_FILE);
383      if (file)
384      {
385          file >> postObj;
386          file.close();
387      }
388      else
389      {
390          phosphor::logging::log<phosphor::logging::level::ERR>(
391              "Post code description file not found",
392              phosphor::logging::entry("POST_CODE_FILE=%s", JSON_POST_DATA_FILE));
393          return -1;
394      }
395  
396      std::string phaseStr = "PhaseAny";
397      if (postObj.find(phaseStr) == postObj.end())
398      {
399          phaseStr = "Phase" + std::to_string(phase);
400      }
401  
402      if (postObj.find(phaseStr) == postObj.end())
403      {
404          phosphor::logging::log<phosphor::logging::level::ERR>(
405              "Post code phase not available",
406              phosphor::logging::entry("PHASE=%d", phase));
407          return -1;
408      }
409  
410      auto phaseObj = postObj[phaseStr];
411      int phaseSize = phaseObj.size();
412  
413      for (int i = 0; i < phaseSize; i++)
414      {
415          postCode = phaseObj[i][0];
416          if (index == stoul(postCode, nullptr, 16))
417          {
418              std::string postDesc = phaseObj[i][1];
419              *length = postDesc.size();
420              memcpy(buffer, postDesc.data(), *length);
421              buffer[*length] = '\0';
422  
423              if (phaseSize != i + 1)
424              {
425                  postCode = phaseObj[i + 1][0];
426                  *next = stoul(postCode, nullptr, 16);
427                  *end = 0;
428              }
429              else
430              {
431                  if (postObj.size() != phase)
432                  {
433                      std::string nextPhaseStr =
434                          "Phase" + std::to_string(phase + 1);
435                      postCode = postObj[nextPhaseStr][0][0];
436                      *next = stoul(postCode, nullptr, 16);
437                      *end = 0;
438                  }
439                  else
440                  {
441                      *next = 0xff;
442                      *end = 1;
443                  }
444              }
445  
446              return 0;
447          }
448      }
449  
450      phosphor::logging::log<phosphor::logging::level::ERR>(
451          "Post code description data not available",
452          phosphor::logging::entry("PHASE_CODE=%d_0x%x", phase, index));
453      return -1;
454  }
455  
plat_udbg_get_gpio_desc(uint8_t index,uint8_t * next,uint8_t * level,uint8_t * def,uint8_t * length,uint8_t * buffer)456  int plat_udbg_get_gpio_desc(uint8_t index, uint8_t* next, uint8_t* level,
457                              uint8_t* def, uint8_t* length, uint8_t* buffer)
458  {
459      nlohmann::json gpioObj;
460      std::string gpioPin;
461  
462      /* Get gpio data stored in json file */
463      std::ifstream file(JSON_GPIO_DATA_FILE);
464      if (file)
465      {
466          file >> gpioObj;
467          file.close();
468      }
469      else
470      {
471          phosphor::logging::log<phosphor::logging::level::ERR>(
472              "GPIO pin description file not found",
473              phosphor::logging::entry("GPIO_PIN_DETAILS_FILE=%s",
474                                       JSON_GPIO_DATA_FILE));
475          return -1;
476      }
477  
478      if (gpioObj.find(DEBUG_GPIO_KEY) == gpioObj.end())
479      {
480          phosphor::logging::log<phosphor::logging::level::ERR>(
481              "GPIO pin details not available",
482              phosphor::logging::entry("GPIO_JSON_KEY=%d", DEBUG_GPIO_KEY));
483          return -1;
484      }
485  
486      auto obj = gpioObj[DEBUG_GPIO_KEY];
487      int objSize = obj.size();
488  
489      for (int i = 0; i < objSize; i++)
490      {
491          if (obj[i].size() != GPIO_ARRAY_SIZE)
492          {
493              phosphor::logging::log<phosphor::logging::level::ERR>(
494                  "Size of gpio array is incorrect",
495                  phosphor::logging::entry("EXPECTED_SIZE=%d", GPIO_ARRAY_SIZE));
496              return -1;
497          }
498  
499          gpioPin = obj[i][GPIO_PIN_INDEX];
500          if (index == stoul(gpioPin, nullptr, 16))
501          {
502              if (objSize != i + 1)
503              {
504                  gpioPin = obj[i + 1][GPIO_PIN_INDEX];
505                  *next = stoul(gpioPin, nullptr, 16);
506              }
507              else
508              {
509                  *next = 0xff;
510              }
511  
512              *level = obj[i][GPIO_LEVEL_INDEX];
513              *def = obj[i][GPIO_DEF_INDEX];
514              std::string gpioDesc = obj[i][GPIO_DESC_INDEX];
515              *length = gpioDesc.size();
516              memcpy(buffer, gpioDesc.data(), *length);
517              buffer[*length] = '\0';
518  
519              return 0;
520          }
521      }
522  
523      phosphor::logging::log<phosphor::logging::level::ERR>(
524          "GPIO pin description data not available",
525          phosphor::logging::entry("GPIO_PIN=0x%x", index));
526      return -1;
527  }
528  
getBiosVer(std::string & ver,size_t hostPosition)529  static int getBiosVer(std::string& ver, size_t hostPosition)
530  {
531      nlohmann::json appObj;
532  
533      std::ifstream file(JSON_APP_DATA_FILE);
534      if (file)
535      {
536          file >> appObj;
537          file.close();
538          std::string version_key = KEY_SYSFW_VER + std::to_string(hostPosition);
539  
540          if (appObj.find(version_key) != appObj.end())
541          {
542              ver = appObj[version_key].get<std::string>();
543              return 0;
544          }
545      }
546  
547      return -1;
548  }
549  
sendBicCmd(uint8_t netFn,uint8_t cmd,uint8_t bicAddr,std::vector<uint8_t> & cmdData,std::vector<uint8_t> & respData)550  int sendBicCmd(uint8_t netFn, uint8_t cmd, uint8_t bicAddr,
551                 std::vector<uint8_t>& cmdData, std::vector<uint8_t>& respData)
552  {
553      static constexpr uint8_t lun = 0;
554  
555      auto bus = getSdBus();
556  
557      auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
558                                         "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
559                                         "org.openbmc.Ipmb", "sendRequest");
560      method.append(bicAddr, netFn, lun, cmd, cmdData);
561  
562      auto reply = bus->call(method);
563      if (reply.is_method_error())
564      {
565          phosphor::logging::log<phosphor::logging::level::ERR>(
566              "Error reading from BIC");
567          return -1;
568      }
569  
570      IpmbMethodType resp;
571      reply.read(resp);
572  
573      respData =
574          std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp));
575  
576      return 0;
577  }
578  
sendMeCmd(uint8_t netFn,uint8_t cmd,std::vector<uint8_t> & cmdData,std::vector<uint8_t> & respData)579  int sendMeCmd(uint8_t netFn, uint8_t cmd, std::vector<uint8_t>& cmdData,
580                std::vector<uint8_t>& respData)
581  {
582      auto bus = getSdBus();
583  
584      if (DEBUG)
585      {
586          std::cout << "ME NetFn:cmd " << (int)netFn << ":" << (int)cmd << "\n";
587          std::cout << "ME req data: ";
588          for (auto d : cmdData)
589          {
590              std::cout << d << " ";
591          }
592          std::cout << "\n";
593      }
594  
595      auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
596                                         "/xyz/openbmc_project/Ipmi/Channel/Ipmb",
597                                         "org.openbmc.Ipmb", "sendRequest");
598      method.append(meAddress, netFn, lun, cmd, cmdData);
599  
600      auto reply = bus->call(method);
601      if (reply.is_method_error())
602      {
603          phosphor::logging::log<phosphor::logging::level::ERR>(
604              "Error reading from ME");
605          return -1;
606      }
607  
608      IpmbMethodType resp;
609      reply.read(resp);
610  
611      respData =
612          std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp));
613  
614      if (DEBUG)
615      {
616          std::cout << "ME resp data: ";
617          for (auto d : respData)
618          {
619              std::cout << d << " ";
620          }
621          std::cout << "\n";
622      }
623  
624      return 0;
625  }
626  
udbg_get_info_page(uint8_t,uint8_t page,uint8_t * next,uint8_t * count,uint8_t * buffer)627  static int udbg_get_info_page(uint8_t, uint8_t page, uint8_t* next,
628                                uint8_t* count, uint8_t* buffer)
629  {
630      char line_buff[1000];
631      [[maybe_unused]] char* pres_dev = line_buff;
632      [[maybe_unused]] size_t pos = plat_get_fru_sel();
633      int ret;
634      std::string serialName = "SerialNumber";
635      std::string partName = "PartNumber";
636      std::string verDel = "VERSION=";
637      std::string verPath = "/etc/os-release";
638      size_t hostPosition = 0;
639      size_t maxPosition;
640  
641      if (page == 1)
642      {
643          // Only update frame data while getting page 1
644  
645          // initialize and clear frame
646          frame_info.init();
647          snprintf(frame_info.title, 32, "SYS_Info");
648  
649          bool platform = isMultiHostPlatform();
650          if (platform == true)
651          {
652              hostPosition = plat_get_fru_sel();
653          }
654  
655          getMaxHostPosition(maxPosition);
656          if (hostPosition == BMC_POSITION || hostInstances == "0")
657          {
658              std::string data = "FRU:" + getMotherBoardFruName();
659              frame_info.append(data);
660          }
661          else if (hostPosition != BMC_POSITION && hostPosition <= maxPosition)
662          {
663              std::string data = "FRU:slot" + std::to_string(hostPosition);
664              frame_info.append(data);
665          }
666  
667          // FRU
668          std::string data;
669          frame_info.append("SN:");
670          if (getFruData(data, serialName) != 0)
671          {
672              data = "Not Found";
673          }
674          frame_info.append(data, 1);
675          frame_info.append("PN:");
676          if (getFruData(data, partName) != 0)
677          {
678              data = "Not Found";
679          }
680          frame_info.append(data, 1);
681  
682          // LAN
683          getNetworkData(3, line_buff);
684          frame_info.append("BMC_IP:");
685          frame_info.append(line_buff, 1);
686          getNetworkData(59, line_buff);
687          frame_info.append("BMC_IPv6:");
688          frame_info.append(line_buff, 1);
689  
690          // BMC ver
691          std::ifstream file(verPath);
692          if (file)
693          {
694              std::string line;
695              while (std::getline(file, line))
696              {
697                  if (line.find(verDel) != std::string::npos)
698                  {
699                      std::string bmcVer = line.substr(verDel.size());
700                      frame_info.append("BMC_FW_ver:");
701                      frame_info.append(bmcVer, 1);
702                      break;
703                  }
704              }
705          }
706  
707          if (hostPosition != BMC_POSITION || hostInstances == "0")
708          {
709              // BIOS ver
710              std::string biosVer;
711              if (getBiosVer(biosVer, hostPosition) == 0)
712              {
713                  frame_info.append("BIOS_FW_ver:");
714                  frame_info.append(biosVer, 1);
715              }
716          }
717  
718          /* TBD: Board ID needs implementation */
719          // Board ID
720  
721          // Battery - Use Escape sequence
722          frame_info.append("Battery:");
723          frame_info.append(ESC_BAT "     ", 1);
724          // frame_info.append(&frame_info, esc_bat, 1);
725  
726          // MCU Version - Use Escape sequence
727          frame_info.append("MCUbl_ver:");
728          frame_info.append(ESC_MCU_BL_VER, 1);
729          frame_info.append("MCU_ver:");
730          frame_info.append(ESC_MCU_RUN_VER, 1);
731  
732          // Sys config present device
733          if (hostPosition != BMC_POSITION)
734          {
735              frame_info.append("Sys Conf. info:");
736  
737              // Dimm info
738              std::vector<std::string> data;
739              if (sysConfig(data, pos) == 0)
740              {
741                  for (auto& info : data)
742                  {
743                      frame_info.append(info, 1);
744                  }
745              }
746              else
747              {
748                  frame_info.append("Not Found", 1);
749              }
750  
751              // Processor info
752              std::string result;
753              if (procInfo(result, pos) != 0)
754              {
755                  result = "Not Found";
756              }
757              frame_info.append(result, 1);
758          }
759  
760      } // End of update frame
761  
762      if (page > frame_info.pages)
763      {
764          return -1;
765      }
766  
767      ret = frame_info.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
768      if (ret < 0)
769      {
770          *count = 0;
771          return -1;
772      }
773      *count = (uint8_t)ret;
774  
775      if (page < frame_info.pages)
776          *next = page + 1;
777      else
778          *next = 0xFF; // Set the value of next to 0xFF to indicate this is the
779                        // last page
780  
781      return 0;
782  }
783  
udbg_get_postcode(uint8_t,uint8_t page,uint8_t * next,uint8_t * count,uint8_t * buffer)784  static int udbg_get_postcode(uint8_t, uint8_t page, uint8_t* next,
785                               uint8_t* count, uint8_t* buffer)
786  {
787      // up to 70 codes can be displayed on 10 pages
788      static constexpr size_t maxPostcodes = 70;
789  
790      if (page == 1)
791      {
792          // Initialize and clear frame (example initialization)
793          frame_postcode.init();
794          snprintf(frame_postcode.title, 32, "POST CODE");
795          frame_postcode.max_page = 10;
796  
797          // Synchronously get D-Bus connection
798          auto bus = sdbusplus::bus::new_default();
799  
800          // Build D-Bus method call
801          auto method = bus.new_method_call(
802              "xyz.openbmc_project.State.Boot.PostCode0",  // Target service name
803              "/xyz/openbmc_project/State/Boot/PostCode0", // Object path
804              "xyz.openbmc_project.State.Boot.PostCode",   // Interface name
805              "GetPostCodes");                             // Method name
806  
807          method.append(uint16_t(1)); // Add method parameter, assuming it's page
808  
809          try
810          {
811              auto reply = bus.call(method); // Send synchronous method call
812  
813              // Read postcode value
814              std::vector<std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>>
815                  postcodes;
816              reply.read(postcodes);
817  
818              // retrieve the latest postcodes
819              size_t numEntries = std::min(maxPostcodes, postcodes.size());
820              auto range = std::ranges::subrange(postcodes.rbegin(),
821                                                 postcodes.rbegin() + numEntries);
822              for (const auto& [code, extra] : range)
823              {
824                  std::string result;
825                  result.reserve(2 * code.size());
826                  for (const auto& byte : code)
827                  {
828                      result += std::format("{:02X}", byte);
829                  }
830  
831                  frame_postcode.append(result);
832                  if (frame_postcode.lines >= maxPostcodes)
833                  {
834                      break;
835                  }
836              }
837          }
838          catch (const std::exception& e)
839          {
840              // Handle exceptions
841              std::cerr << "Error retrieving postcodes: " << e.what()
842                        << std::endl;
843              return -1;
844          }
845      }
846  
847      if (page > frame_postcode.pages)
848      {
849          return -1;
850      }
851  
852      int ret = frame_postcode.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE);
853      if (ret < 0)
854      {
855          *count = 0;
856          return -1;
857      }
858      *count = (uint8_t)ret;
859  
860      if (page < frame_postcode.pages)
861          *next = page + 1;
862      else
863          *next = 0xFF; // Set next to 0xFF to indicate last page
864      return 0;
865  }
866  
plat_udbg_get_frame_data(uint8_t frame,uint8_t page,uint8_t * next,uint8_t * count,uint8_t * buffer)867  int plat_udbg_get_frame_data(uint8_t frame, uint8_t page, uint8_t* next,
868                               uint8_t* count, uint8_t* buffer)
869  {
870      switch (frame)
871      {
872          case 1: // info_page
873              return udbg_get_info_page(frame, page, next, count, buffer);
874          case 2: // Extra Post Code
875              return udbg_get_postcode(frame, page, next, count, buffer);
876          default:
877              return -1;
878      }
879  }
880  
panel_main(size_t item)881  static panel panel_main(size_t item)
882  {
883      // Update item list when select item 0
884      switch (item)
885      {
886          case 1:
887              return panels[std::to_underlying(panel::BOOT_ORDER)].select(0);
888          case 2:
889              return panels[std::to_underlying(panel::POWER_POLICY)].select(0);
890          default:
891              return panel::MAIN;
892      }
893  }
894  
panel_boot_order(size_t selectedItemIndex)895  static panel panel_boot_order(size_t selectedItemIndex)
896  {
897      static constexpr size_t sizeBootOrder = 6;
898      static constexpr size_t bootValid = 0x80;
899  
900      std::vector<uint8_t> bootSeq;
901  
902      ctrl_panel& bootOrderPanel = panels[std::to_underlying(panel::BOOT_ORDER)];
903  
904      size_t pos = plat_get_fru_sel();
905  
906      if (pos == FRU_ALL)
907      {
908          bootOrderPanel.item_num = 0;
909          return panel::BOOT_ORDER;
910      }
911  
912      auto [bootObjPath, hostName] = ipmi::boot::objPath(pos);
913      ipmi::boot::getBootOrder(bootObjPath, bootSeq, hostName);
914  
915      uint8_t& bootMode = bootSeq.front();
916  
917      // One item is selected to set a new boot sequence.
918      // The selected item become the first boot order.
919      if (selectedItemIndex > 0 && selectedItemIndex < sizeBootOrder)
920      {
921          // Move the selected item to second element (the first one is boot mode)
922          std::rotate(bootSeq.begin() + 1, bootSeq.begin() + selectedItemIndex,
923                      bootSeq.begin() + selectedItemIndex + 1);
924  
925          bootMode |= bootValid;
926          try
927          {
928              ipmi::boot::setBootOrder(bootObjPath, bootSeq, hostName);
929          }
930          catch (const std::exception& e)
931          {
932              lg2::error("Fail to set boot order : {ERROR}", "ERROR", e);
933          }
934  
935          // refresh items
936          return bootOrderPanel.select(0);
937      }
938  
939      // '*': boot flags valid, BIOS has not yet read
940      bootOrderPanel.item_str[0] =
941          std::string("Boot Order") + ((bootMode & bootValid) ? "*" : "");
942  
943      static const std::unordered_map<uint8_t, const char*>
944          bootOrderMappingTable = {
945              {0x00, " USB device"}, {0x01, " Network v4"}, {0x02, " SATA HDD"},
946              {0x03, " SATA-CDROM"}, {0x04, " Other"},      {0x09, " Network v6"},
947          };
948  
949      size_t validItem = 0;
950      for (size_t i = 1; i < sizeBootOrder; i++)
951      {
952          auto find = bootOrderMappingTable.find(bootSeq[i]);
953          if (find == bootOrderMappingTable.end())
954          {
955              lg2::error("Unknown boot order : {BOOTORDER}", "BOOTORDER",
956                         bootSeq[i]);
957              break;
958          }
959  
960          bootOrderPanel.item_str[i] = find->second;
961  
962          validItem++;
963      }
964  
965      bootOrderPanel.item_num = validItem;
966      return panel::BOOT_ORDER;
967  }
968  
panel_power_policy(size_t)969  static panel panel_power_policy(size_t)
970  {
971  /* To be cleaned */
972  #if 0
973      uint8_t buff[32] = {0};
974      uint8_t res_len;
975      size_t pos = plat_get_fru_sel();
976      uint8_t policy;
977      uint8_t pwr_policy_item_map[3] = {POWER_CFG_ON, POWER_CFG_LPS,
978                                        POWER_CFG_OFF};
979  
980      if (pos != FRU_ALL)
981      {
982          if (item > 0 && item <= sizeof(pwr_policy_item_map))
983          {
984              policy = pwr_policy_item_map[item - 1];
985              pal_set_power_restore_policy(pos, &policy, nullptr);
986          }
987          pal_get_chassis_status(pos, nullptr, buff, &res_len);
988          policy = (((uint8_t)buff[0]) >> 5) & 0x7;
989          snprintf(panels[PANEL_POWER_POLICY].item_str[1], 32, "%cPower On",
990                   policy == POWER_CFG_ON ? '*' : ' ');
991          snprintf(panels[PANEL_POWER_POLICY].item_str[2], 32, "%cLast State",
992                   policy == POWER_CFG_LPS ? '*' : ' ');
993          snprintf(panels[PANEL_POWER_POLICY].item_str[3], 32, "%cPower Off",
994                   policy == POWER_CFG_OFF ? '*' : ' ');
995          panels[PANEL_POWER_POLICY].item_num = 3;
996      }
997      else
998      {
999          panels[PANEL_POWER_POLICY].item_num = 0;
1000      }
1001  #endif
1002      return panel::POWER_POLICY;
1003  }
1004  
plat_udbg_control_panel(uint8_t cur_panel,uint8_t operation,uint8_t item,uint8_t * count,uint8_t * buffer)1005  ipmi_ret_t plat_udbg_control_panel(uint8_t cur_panel, uint8_t operation,
1006                                     uint8_t item, uint8_t* count,
1007                                     uint8_t* buffer)
1008  {
1009      if (cur_panel > panelNum || cur_panel < std::to_underlying(panel::MAIN))
1010          return IPMI_CC_PARM_OUT_OF_RANGE;
1011  
1012      // No more item; End of item list
1013      if (item > panels[cur_panel].item_num)
1014          return IPMI_CC_PARM_OUT_OF_RANGE;
1015  
1016      switch (operation)
1017      {
1018          case 0: // Get Description
1019              break;
1020          case 1: // Select item
1021              cur_panel = std::to_underlying(panels[cur_panel].select(item));
1022              item = 0;
1023              break;
1024          case 2: // Back
1025              cur_panel = std::to_underlying(panels[cur_panel].parent);
1026              item = 0;
1027              break;
1028          default:
1029              return IPMI_CC_PARM_OUT_OF_RANGE;
1030      }
1031  
1032      buffer[0] = cur_panel;
1033      buffer[1] = item;
1034      buffer[2] = std::size(panels[cur_panel].item_str[item]);
1035  
1036      if (buffer[2] > 0 && (buffer[2] + 3u) < FRAME_PAGE_BUF_SIZE)
1037      {
1038          std::memcpy(&buffer[3], (panels[cur_panel].item_str[item]).c_str(),
1039                      buffer[2]);
1040      }
1041      *count = buffer[2] + 3;
1042      return IPMI_CC_OK;
1043  }
1044  
1045  } // end of namespace ipmi
1046