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