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