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 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 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 */ 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 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 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 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 235 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 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 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 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 340 int plat_udbg_get_frame_info(uint8_t* num) 341 { 342 *num = 3; 343 return 0; 344 } 345 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 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 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 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 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 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 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) 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 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 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 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 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 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 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