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 = getService(*dbus, ipmi::selector::interface, 61 ipmi::selector::path); 62 Value variant = getDbusProperty(*dbus, service, ipmi::selector::path, 63 ipmi::selector::interface, 64 "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 = getService(*dbus, ipmi::selector::interface, 81 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 = "Phase" + 520 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 udbg_get_cri_sel(uint8_t, uint8_t page, uint8_t* next, 616 uint8_t* count, uint8_t* buffer) 617 { 618 if (page == 1) 619 { 620 // initialize and clear frame 621 frame_sel.init(FRAME_BUFF_SIZE); 622 frame_sel.overwrite = 1; 623 frame_sel.max_page = 20; 624 snprintf(frame_sel.title, 32, "Cri SEL"); 625 626 static constexpr const auto depth = 0; 627 std::vector<std::string> paths; 628 629 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 630 631 auto mapperCall = dbus->new_method_call( 632 "xyz.openbmc_project.ObjectMapper", 633 "/xyz/openbmc_project/object_mapper", 634 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths"); 635 static constexpr std::array<const char*, 1> interface = { 636 "xyz.openbmc_project.Logging.Entry"}; 637 mapperCall.append("/", depth, interface); 638 639 try 640 { 641 auto reply = dbus->call(mapperCall); 642 reply.read(paths); 643 } 644 catch (sdbusplus::exception_t& e) 645 { 646 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 647 return -1; 648 } 649 650 std::string message; 651 652 // Iterate each loggings 653 for (const auto& path : paths) 654 { 655 Value variant = ipmi::getDbusProperty( 656 *dbus, "xyz.openbmc_project.Logging", path, 657 "xyz.openbmc_project.Logging.Entry", "Severity"); 658 659 // Only filter critial sel 660 if (std::get<std::string>(variant) != 661 "xyz.openbmc_project.Logging.Entry.Level.Critical") 662 { 663 continue; 664 } 665 666 variant = ipmi::getDbusProperty( 667 *dbus, "xyz.openbmc_project.Logging", path, 668 "xyz.openbmc_project.Logging.Entry", "Message"); 669 message = std::get<std::string>(variant); 670 671 frame_sel.insert(message.c_str(), 0); 672 } 673 } 674 if (page > frame_sel.pages) 675 { 676 return -1; 677 } 678 int ret = frame_sel.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE); 679 if (ret < 0) 680 { 681 *count = 0; 682 return -1; 683 } 684 *count = (uint8_t)ret; 685 686 if (page < frame_sel.pages) 687 *next = page + 1; 688 else 689 *next = 0xFF; // Set the value of next to 0xFF to indicate this is the 690 // last page 691 692 return 0; 693 } 694 695 static int udbg_get_cri_sensor(uint8_t, uint8_t page, uint8_t* next, 696 uint8_t* count, uint8_t* buffer) 697 { 698 int ret; 699 double fvalue; 700 size_t pos = plat_get_fru_sel(); 701 702 if (page == 1) 703 { 704 // Only update frame data while getting page 1 705 706 // initialize and clear frame 707 frame_snr.init(FRAME_BUFF_SIZE); 708 snprintf(frame_snr.title, 32, "CriSensor"); 709 710 nlohmann::json senObj; 711 712 /* Get critical sensor names stored in json file */ 713 std::ifstream file(JSON_SENSOR_NAMES_FILE); 714 if (file) 715 { 716 file >> senObj; 717 file.close(); 718 } 719 else 720 { 721 phosphor::logging::log<phosphor::logging::level::ERR>( 722 "Critical Sensor names file not found", 723 phosphor::logging::entry("CRI_SENSOR_NAMES_FILE=%s", 724 JSON_SENSOR_NAMES_FILE)); 725 return -1; 726 } 727 728 /* Get sensors values for all critical sensors */ 729 for (auto& j : senObj.items()) 730 { 731 std::string senName = j.key(); 732 auto val = j.value(); 733 734 if (senName[0] == '_') 735 { 736 senName = std::to_string(pos) + senName; 737 } 738 739 if (ipmi::storage::getSensorValue(senName, fvalue) == 0) 740 { 741 std::stringstream ss; 742 int prec = 0; // Default value 743 744 if (val.find("precision") != val.end()) 745 prec = val["precision"]; 746 747 ss << std::fixed << std::setprecision(prec) << fvalue; 748 749 std::string senStr; 750 if (val.find("short_name") != val.end()) 751 senStr = val["short_name"]; 752 else 753 senStr = senName; 754 755 senStr += ss.str(); 756 757 /* Get unit string for sensor and append in output */ 758 std::string unitStr; 759 if (ipmi::storage::getSensorUnit(senName, unitStr) == 0) 760 senStr += unitStr; 761 762 std::string thresholdStr; 763 int ret = ipmi::storage::getSensorThreshold(senName, 764 thresholdStr); 765 if (ret < 0) 766 { 767 phosphor::logging::log<phosphor::logging::level::ERR>( 768 "Error getting critical sensor threshold status", 769 phosphor::logging::entry("CRI_SENSOR_NAME=%s", 770 senName.c_str())); 771 return -1; 772 } 773 if (thresholdStr.size() != 0) 774 { 775 senStr += ("/" + thresholdStr); 776 std::string senStrWithBlinkAndInvertColor = 777 ESC_ALT + senStr + ESC_RST; 778 frame_snr.append(senStrWithBlinkAndInvertColor.c_str(), 0); 779 } 780 else 781 { 782 frame_snr.append(senStr.c_str(), 0); 783 } 784 } 785 else 786 { 787 phosphor::logging::log<phosphor::logging::level::INFO>( 788 "Critical sensor not found", 789 phosphor::logging::entry("CRI_SENSOR_NAME=%s", 790 senName.c_str())); 791 } 792 } 793 794 } // End of update frame 795 796 if (page > frame_snr.pages) 797 { 798 return -1; 799 } 800 801 ret = frame_snr.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE); 802 if (ret < 0) 803 { 804 *count = 0; 805 return -1; 806 } 807 *count = (uint8_t)ret; 808 809 if (page < frame_snr.pages) 810 *next = page + 1; 811 else 812 *next = 0xFF; // Set the value of next to 0xFF to indicate this is the 813 // last page 814 815 return 0; 816 } 817 818 static int getBiosVer(std::string& ver, size_t hostPosition) 819 { 820 nlohmann::json appObj; 821 822 std::ifstream file(JSON_APP_DATA_FILE); 823 if (file) 824 { 825 file >> appObj; 826 file.close(); 827 std::string version_key = KEY_SYSFW_VER + std::to_string(hostPosition); 828 829 if (appObj.find(version_key) != appObj.end()) 830 { 831 ver = appObj[version_key].get<std::string>(); 832 return 0; 833 } 834 } 835 836 return -1; 837 } 838 839 int sendBicCmd(uint8_t netFn, uint8_t cmd, uint8_t bicAddr, 840 std::vector<uint8_t>& cmdData, std::vector<uint8_t>& respData) 841 { 842 static constexpr uint8_t lun = 0; 843 844 auto bus = getSdBus(); 845 846 auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb", 847 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", 848 "org.openbmc.Ipmb", "sendRequest"); 849 method.append(bicAddr, netFn, lun, cmd, cmdData); 850 851 auto reply = bus->call(method); 852 if (reply.is_method_error()) 853 { 854 phosphor::logging::log<phosphor::logging::level::ERR>( 855 "Error reading from BIC"); 856 return -1; 857 } 858 859 IpmbMethodType resp; 860 reply.read(resp); 861 862 respData = 863 std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp)); 864 865 return 0; 866 } 867 868 int sendMeCmd(uint8_t netFn, uint8_t cmd, std::vector<uint8_t>& cmdData, 869 std::vector<uint8_t>& respData) 870 { 871 auto bus = getSdBus(); 872 873 if (DEBUG) 874 { 875 std::cout << "ME NetFn:cmd " << (int)netFn << ":" << (int)cmd << "\n"; 876 std::cout << "ME req data: "; 877 for (auto d : cmdData) 878 { 879 std::cout << d << " "; 880 } 881 std::cout << "\n"; 882 } 883 884 auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb", 885 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", 886 "org.openbmc.Ipmb", "sendRequest"); 887 method.append(meAddress, netFn, lun, cmd, cmdData); 888 889 auto reply = bus->call(method); 890 if (reply.is_method_error()) 891 { 892 phosphor::logging::log<phosphor::logging::level::ERR>( 893 "Error reading from ME"); 894 return -1; 895 } 896 897 IpmbMethodType resp; 898 reply.read(resp); 899 900 respData = 901 std::move(std::get<std::remove_reference_t<decltype(respData)>>(resp)); 902 903 if (DEBUG) 904 { 905 std::cout << "ME resp data: "; 906 for (auto d : respData) 907 { 908 std::cout << d << " "; 909 } 910 std::cout << "\n"; 911 } 912 913 return 0; 914 } 915 916 static int udbg_get_info_page(uint8_t, uint8_t page, uint8_t* next, 917 uint8_t* count, uint8_t* buffer) 918 { 919 char line_buff[1000]; 920 [[maybe_unused]] char* pres_dev = line_buff; 921 [[maybe_unused]] size_t pos = plat_get_fru_sel(); 922 int ret; 923 std::string serialName = "SerialNumber"; 924 std::string partName = "PartNumber"; 925 std::string verDel = "VERSION="; 926 std::string verPath = "/etc/os-release"; 927 size_t hostPosition = 0; 928 size_t maxPosition; 929 930 if (page == 1) 931 { 932 // Only update frame data while getting page 1 933 934 // initialize and clear frame 935 frame_info.init(FRAME_BUFF_SIZE); 936 snprintf(frame_info.title, 32, "SYS_Info"); 937 938 bool platform = isMultiHostPlatform(); 939 if (platform == true) 940 { 941 hostPosition = plat_get_fru_sel(); 942 } 943 944 getMaxHostPosition(maxPosition); 945 if (hostPosition == BMC_POSITION || hostInstances == "0") 946 { 947 frame_info.append("FRU:spb", 0); 948 } 949 else if (hostPosition != BMC_POSITION && hostPosition <= maxPosition) 950 { 951 std::string data = "FRU:slot" + std::to_string(hostPosition); 952 frame_info.append(data.c_str(), 0); 953 } 954 955 // FRU 956 std::string data; 957 frame_info.append("SN:", 0); 958 if (getFruData(data, serialName) != 0) 959 { 960 data = "Not Found"; 961 } 962 frame_info.append(data.c_str(), 1); 963 frame_info.append("PN:", 0); 964 if (getFruData(data, partName) != 0) 965 { 966 data = "Not Found"; 967 } 968 frame_info.append(data.c_str(), 1); 969 970 // LAN 971 getNetworkData(3, line_buff); 972 frame_info.append("BMC_IP:", 0); 973 frame_info.append(line_buff, 1); 974 getNetworkData(59, line_buff); 975 frame_info.append("BMC_IPv6:", 0); 976 frame_info.append(line_buff, 1); 977 978 // BMC ver 979 std::ifstream file(verPath); 980 if (file) 981 { 982 std::string line; 983 while (std::getline(file, line)) 984 { 985 if (line.find(verDel) != std::string::npos) 986 { 987 std::string bmcVer = line.substr(verDel.size()); 988 frame_info.append("BMC_FW_ver:", 0); 989 frame_info.append(bmcVer.c_str(), 1); 990 break; 991 } 992 } 993 } 994 995 if (hostPosition != BMC_POSITION) 996 { 997 // BIOS ver 998 std::string biosVer; 999 if (getBiosVer(biosVer, hostPosition) == 0) 1000 { 1001 frame_info.append("BIOS_FW_ver:", 0); 1002 frame_info.append(biosVer.c_str(), 1); 1003 } 1004 } 1005 1006 /* TBD: Board ID needs implementation */ 1007 // Board ID 1008 1009 // Battery - Use Escape sequence 1010 frame_info.append("Battery:", 0); 1011 frame_info.append(ESC_BAT " ", 1); 1012 // frame_info.append(&frame_info, esc_bat, 1); 1013 1014 // MCU Version - Use Escape sequence 1015 frame_info.append("MCUbl_ver:", 0); 1016 frame_info.append(ESC_MCU_BL_VER, 1); 1017 frame_info.append("MCU_ver:", 0); 1018 frame_info.append(ESC_MCU_RUN_VER, 1); 1019 1020 // Sys config present device 1021 if (hostPosition != BMC_POSITION) 1022 { 1023 frame_info.append("Sys Conf. info:", 0); 1024 1025 // Dimm info 1026 std::vector<std::string> data; 1027 if (sysConfig(data, pos) == 0) 1028 { 1029 for (auto& info : data) 1030 { 1031 frame_info.append(info.c_str(), 1); 1032 } 1033 } 1034 else 1035 { 1036 frame_info.append("Not Found", 1); 1037 } 1038 1039 // Processor info 1040 std::string result; 1041 if (procInfo(result, pos) != 0) 1042 { 1043 result = "Not Found"; 1044 } 1045 frame_info.append(result.c_str(), 1); 1046 } 1047 1048 } // End of update frame 1049 1050 if (page > frame_info.pages) 1051 { 1052 return -1; 1053 } 1054 1055 ret = frame_info.getPage(page, (char*)buffer, FRAME_PAGE_BUF_SIZE); 1056 if (ret < 0) 1057 { 1058 *count = 0; 1059 return -1; 1060 } 1061 *count = (uint8_t)ret; 1062 1063 if (page < frame_info.pages) 1064 *next = page + 1; 1065 else 1066 *next = 0xFF; // Set the value of next to 0xFF to indicate this is the 1067 // last page 1068 1069 return 0; 1070 } 1071 1072 int plat_udbg_get_frame_data(uint8_t frame, uint8_t page, uint8_t* next, 1073 uint8_t* count, uint8_t* buffer) 1074 { 1075 switch (frame) 1076 { 1077 case 1: // info_page 1078 return udbg_get_info_page(frame, page, next, count, buffer); 1079 case 2: // critical SEL 1080 return udbg_get_cri_sel(frame, page, next, count, buffer); 1081 case 3: // critical Sensor 1082 return udbg_get_cri_sensor(frame, page, next, count, buffer); 1083 default: 1084 return -1; 1085 } 1086 } 1087 1088 static uint8_t panel_main(uint8_t item) 1089 { 1090 // Update item list when select item 0 1091 switch (item) 1092 { 1093 case 1: 1094 return panels[PANEL_BOOT_ORDER].select(0); 1095 case 2: 1096 return panels[PANEL_POWER_POLICY].select(0); 1097 default: 1098 return PANEL_MAIN; 1099 } 1100 } 1101 1102 static uint8_t panel_boot_order(uint8_t selectedItemIndex) 1103 { 1104 static constexpr size_t sizeBootOrder = 6; 1105 static constexpr size_t bootValid = 0x80; 1106 1107 std::vector<uint8_t> bootSeq; 1108 1109 ctrl_panel& bootOrderPanel = panels[PANEL_BOOT_ORDER]; 1110 1111 size_t pos = plat_get_fru_sel(); 1112 1113 if (pos == FRU_ALL) 1114 { 1115 bootOrderPanel.item_num = 0; 1116 return PANEL_BOOT_ORDER; 1117 } 1118 1119 auto [bootObjPath, hostName] = ipmi::boot::objPath(pos); 1120 ipmi::boot::getBootOrder(bootObjPath, bootSeq, hostName); 1121 1122 uint8_t& bootMode = bootSeq.front(); 1123 1124 // One item is selected to set a new boot sequence. 1125 // The selected item become the first boot order. 1126 if (selectedItemIndex > 0 && selectedItemIndex < sizeBootOrder) 1127 { 1128 // Move the selected item to second element (the first one is boot mode) 1129 std::rotate(bootSeq.begin() + 1, bootSeq.begin() + selectedItemIndex, 1130 bootSeq.begin() + selectedItemIndex + 1); 1131 1132 bootMode |= bootValid; 1133 try 1134 { 1135 ipmi::boot::setBootOrder(bootObjPath, bootSeq, hostName); 1136 } 1137 catch (const std::exception& e) 1138 { 1139 lg2::error("Fail to set boot order : {ERROR}", "ERROR", e); 1140 } 1141 1142 // refresh items 1143 return bootOrderPanel.select(0); 1144 } 1145 1146 // '*': boot flags valid, BIOS has not yet read 1147 bootOrderPanel.item_str[0] = std::string("Boot Order") + 1148 ((bootMode & bootValid) ? "*" : ""); 1149 1150 static const std::unordered_map<uint8_t, const char*> 1151 bootOrderMappingTable = { 1152 {0x00, " USB device"}, {0x01, " Network v4"}, {0x02, " SATA HDD"}, 1153 {0x03, " SATA-CDROM"}, {0x04, " Other"}, {0x09, " Network v6"}, 1154 }; 1155 1156 size_t validItem = 0; 1157 for (size_t i = 1; i < sizeBootOrder; i++) 1158 { 1159 auto find = bootOrderMappingTable.find(bootSeq[i]); 1160 if (find == bootOrderMappingTable.end()) 1161 { 1162 lg2::error("Unknown boot order : {BOOTORDER}", "BOOTORDER", 1163 bootSeq[i]); 1164 break; 1165 } 1166 1167 bootOrderPanel.item_str[i] = find->second; 1168 1169 validItem++; 1170 } 1171 1172 bootOrderPanel.item_num = validItem; 1173 return PANEL_BOOT_ORDER; 1174 } 1175 1176 static uint8_t panel_power_policy(uint8_t) 1177 { 1178 /* To be cleaned */ 1179 #if 0 1180 uint8_t buff[32] = {0}; 1181 uint8_t res_len; 1182 size_t pos = plat_get_fru_sel(); 1183 uint8_t policy; 1184 uint8_t pwr_policy_item_map[3] = {POWER_CFG_ON, POWER_CFG_LPS, 1185 POWER_CFG_OFF}; 1186 1187 if (pos != FRU_ALL) 1188 { 1189 if (item > 0 && item <= sizeof(pwr_policy_item_map)) 1190 { 1191 policy = pwr_policy_item_map[item - 1]; 1192 pal_set_power_restore_policy(pos, &policy, NULL); 1193 } 1194 pal_get_chassis_status(pos, NULL, buff, &res_len); 1195 policy = (((uint8_t)buff[0]) >> 5) & 0x7; 1196 snprintf(panels[PANEL_POWER_POLICY].item_str[1], 32, "%cPower On", 1197 policy == POWER_CFG_ON ? '*' : ' '); 1198 snprintf(panels[PANEL_POWER_POLICY].item_str[2], 32, "%cLast State", 1199 policy == POWER_CFG_LPS ? '*' : ' '); 1200 snprintf(panels[PANEL_POWER_POLICY].item_str[3], 32, "%cPower Off", 1201 policy == POWER_CFG_OFF ? '*' : ' '); 1202 panels[PANEL_POWER_POLICY].item_num = 3; 1203 } 1204 else 1205 { 1206 panels[PANEL_POWER_POLICY].item_num = 0; 1207 } 1208 #endif 1209 return PANEL_POWER_POLICY; 1210 } 1211 1212 int plat_udbg_control_panel(uint8_t panel, uint8_t operation, uint8_t item, 1213 uint8_t* count, uint8_t* buffer) 1214 { 1215 if (panel > panelNum || panel < PANEL_MAIN) 1216 return IPMI_CC_PARM_OUT_OF_RANGE; 1217 1218 // No more item; End of item list 1219 if (item > panels[panel].item_num) 1220 return IPMI_CC_PARM_OUT_OF_RANGE; 1221 1222 switch (operation) 1223 { 1224 case 0: // Get Description 1225 break; 1226 case 1: // Select item 1227 panel = panels[panel].select(item); 1228 item = 0; 1229 break; 1230 case 2: // Back 1231 panel = panels[panel].parent; 1232 item = 0; 1233 break; 1234 default: 1235 return IPMI_CC_PARM_OUT_OF_RANGE; 1236 } 1237 1238 buffer[0] = panel; 1239 buffer[1] = item; 1240 buffer[2] = std::size(panels[panel].item_str[item]); 1241 1242 if (buffer[2] > 0 && (buffer[2] + 3) < FRAME_PAGE_BUF_SIZE) 1243 { 1244 std::memcpy(&buffer[3], (panels[panel].item_str[item]).c_str(), 1245 buffer[2]); 1246 } 1247 *count = buffer[2] + 3; 1248 return IPMI_CC_OK; 1249 } 1250 1251 } // end of namespace ipmi 1252