1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "handler.hpp" 16 17 #include "bm_config.h" 18 19 #include "bmc_mode_enum.hpp" 20 #include "errors.hpp" 21 #include "handler_impl.hpp" 22 #include "util.hpp" 23 24 #include <fcntl.h> 25 #include <ipmid/api.h> 26 #include <mtd/mtd-abi.h> 27 #include <mtd/mtd-user.h> 28 #include <sys/ioctl.h> 29 #include <unistd.h> 30 31 #include <nlohmann/json.hpp> 32 #include <phosphor-logging/elog-errors.hpp> 33 #include <phosphor-logging/log.hpp> 34 #include <sdbusplus/bus.hpp> 35 #include <stdplus/print.hpp> 36 #include <xyz/openbmc_project/Common/error.hpp> 37 38 #include <cinttypes> 39 #include <cstdio> 40 #include <filesystem> 41 #include <fstream> 42 #include <map> 43 #include <sstream> 44 #include <string> 45 #include <string_view> 46 #include <tuple> 47 #include <variant> 48 49 #ifndef NCSI_IF_NAME 50 #define NCSI_IF_NAME eth0 51 #endif 52 53 // To deal with receiving a string without quotes. 54 #define QUOTE(name) #name 55 #define STR(macro) QUOTE(macro) 56 #define NCSI_IF_NAME_STR STR(NCSI_IF_NAME) 57 58 namespace ipmi 59 { 60 std::uint8_t getChannelByName(const std::string& chName); 61 } 62 63 namespace google 64 { 65 namespace ipmi 66 { 67 using Json = nlohmann::json; 68 using namespace phosphor::logging; 69 using InternalFailure = 70 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 71 using Value = std::variant<double>; 72 73 uint8_t isBmcInBareMetalMode(const std::unique_ptr<FileSystemInterface>& fs) 74 { 75 #if BARE_METAL 76 return static_cast<uint8_t>(BmcMode::BM_MODE); 77 #else 78 std::error_code ec; 79 80 if (fs->exists(bmDriveCleaningDoneAckFlagPath, ec)) 81 { 82 stdplus::print( 83 stderr, 84 "{} exists so we acked cleaning done and must be in BM mode\n", 85 bmDriveCleaningDoneAckFlagPath); 86 return static_cast<uint8_t>(BmcMode::BM_MODE); 87 } 88 89 if (fs->exists(bmDriveCleaningDoneFlagPath, ec)) 90 { 91 fs->rename(bmDriveCleaningDoneFlagPath, bmDriveCleaningDoneAckFlagPath, 92 ec); 93 stdplus::print( 94 stderr, 95 "{} exists so we just finished cleaning and must be in BM mode\n", 96 bmDriveCleaningDoneFlagPath); 97 return static_cast<uint8_t>(BmcMode::BM_MODE); 98 } 99 100 if (fs->exists(BM_SIGNAL_PATH, ec)) 101 { 102 if (!fs->exists(bmDriveCleaningFlagPath, ec)) 103 { 104 fs->create(bmDriveCleaningFlagPath); 105 } 106 107 stdplus::print( 108 stderr, 109 "{} exists and no done/ack flag, we must be in BM cleaning mode\n", 110 BM_SIGNAL_PATH); 111 return static_cast<uint8_t>(BmcMode::BM_CLEANING_MODE); 112 } 113 114 stdplus::print( 115 stderr, 116 "Unable to find any BM state files so we must not be in BM mode\n"); 117 return static_cast<uint8_t>(BmcMode::NON_BM_MODE); 118 #endif 119 } 120 121 uint8_t Handler::getBmcMode() 122 { 123 // BM_CLEANING_MODE is not implemented yet 124 return isBmcInBareMetalMode(this->getFs()); 125 } 126 127 std::tuple<std::uint8_t, std::string> 128 Handler::getEthDetails(std::string intf) const 129 { 130 if (intf.empty()) 131 { 132 intf = NCSI_IF_NAME_STR; 133 } 134 return std::make_tuple(::ipmi::getChannelByName(intf), std::move(intf)); 135 } 136 137 std::int64_t Handler::getRxPackets(const std::string& name) const 138 { 139 std::ostringstream opath; 140 opath << "/sys/class/net/" << name << "/statistics/rx_packets"; 141 std::string path = opath.str(); 142 143 // Minor sanity & security check (of course, I'm less certain if unicode 144 // comes into play here. 145 // 146 // Basically you can't easily inject ../ or /../ into the path below. 147 if (name.find("/") != std::string::npos) 148 { 149 stdplus::print(stderr, "Invalid or illegal name: '{}'\n", name); 150 throw IpmiException(::ipmi::ccInvalidFieldRequest); 151 } 152 153 std::error_code ec; 154 if (!this->getFs()->exists(path, ec)) 155 { 156 stdplus::print(stderr, "Path: '{}' doesn't exist.\n", path); 157 throw IpmiException(::ipmi::ccInvalidFieldRequest); 158 } 159 // We're uninterested in the state of ec. 160 161 int64_t count = 0; 162 std::ifstream ifs; 163 ifs.exceptions(std::ifstream::failbit); 164 try 165 { 166 ifs.open(path); 167 ifs >> count; 168 } 169 catch (std::ios_base::failure& fail) 170 { 171 throw IpmiException(::ipmi::ccUnspecifiedError); 172 } 173 174 return count; 175 } 176 177 VersionTuple Handler::getCpldVersion(unsigned int id) const 178 { 179 std::ostringstream opath; 180 opath << "/run/cpld" << id << ".version"; 181 // Check for file 182 183 std::error_code ec; 184 if (!this->getFs()->exists(opath.str(), ec)) 185 { 186 stdplus::print(stderr, "Path: '{}' doesn't exist.\n", opath.str()); 187 throw IpmiException(::ipmi::ccInvalidFieldRequest); 188 } 189 // We're uninterested in the state of ec. 190 191 // If file exists, read. 192 std::ifstream ifs; 193 ifs.exceptions(std::ifstream::failbit); 194 std::string value; 195 try 196 { 197 ifs.open(opath.str()); 198 ifs >> value; 199 } 200 catch (std::ios_base::failure& fail) 201 { 202 throw IpmiException(::ipmi::ccUnspecifiedError); 203 } 204 205 // If value parses as expected, return version. 206 VersionTuple version = std::make_tuple(0, 0, 0, 0); 207 208 int num_fields = std::sscanf(value.c_str(), 209 "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8, 210 &std::get<0>(version), &std::get<1>(version), 211 &std::get<2>(version), &std::get<3>(version)); 212 if (num_fields == 0) 213 { 214 stdplus::print(stderr, "Invalid version.\n"); 215 throw IpmiException(::ipmi::ccUnspecifiedError); 216 } 217 218 return version; 219 } 220 221 static constexpr auto TIME_DELAY_FILENAME = "/run/psu_timedelay"; 222 static constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 223 static constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1"; 224 static constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 225 static constexpr auto PSU_HARDRESET_TARGET = "gbmc-psu-hardreset.target"; 226 227 void Handler::psuResetDelay(std::uint32_t delay) const 228 { 229 std::ofstream ofs; 230 ofs.open(TIME_DELAY_FILENAME, std::ofstream::out); 231 if (!ofs.good()) 232 { 233 stdplus::print(stderr, "Unable to open file for output.\n"); 234 throw IpmiException(::ipmi::ccUnspecifiedError); 235 } 236 237 ofs << "PSU_HARDRESET_DELAY=" << delay << std::endl; 238 if (ofs.fail()) 239 { 240 stdplus::print(stderr, "Write failed\n"); 241 ofs.close(); 242 throw IpmiException(::ipmi::ccUnspecifiedError); 243 } 244 245 // Write succeeded, please continue. 246 ofs.flush(); 247 ofs.close(); 248 249 auto bus = sdbusplus::bus::new_default(); 250 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT, 251 SYSTEMD_INTERFACE, "StartUnit"); 252 253 method.append(PSU_HARDRESET_TARGET); 254 method.append("replace"); 255 256 try 257 { 258 bus.call_noreply(method); 259 } 260 catch (const sdbusplus::exception::SdBusError& ex) 261 { 262 log<level::ERR>("Failed to call PSU hard reset"); 263 throw IpmiException(::ipmi::ccUnspecifiedError); 264 } 265 } 266 267 static constexpr auto RESET_ON_SHUTDOWN_FILENAME = "/run/powercycle_on_s5"; 268 269 void Handler::psuResetOnShutdown() const 270 { 271 std::ofstream ofs; 272 ofs.open(RESET_ON_SHUTDOWN_FILENAME, std::ofstream::out); 273 if (!ofs.good()) 274 { 275 stdplus::print(stderr, "Unable to open file for output.\n"); 276 throw IpmiException(::ipmi::ccUnspecifiedError); 277 } 278 ofs.close(); 279 } 280 281 uint32_t Handler::getFlashSize() 282 { 283 mtd_info_t info; 284 int fd = open("/dev/mtd0", O_RDONLY); 285 int err = ioctl(fd, MEMGETINFO, &info); 286 close(fd); 287 288 if (err) 289 { 290 throw IpmiException(::ipmi::ccUnspecifiedError); 291 } 292 return info.size; 293 } 294 295 std::string Handler::getEntityName(std::uint8_t id, std::uint8_t instance) 296 { 297 // Check if we support this Entity ID. 298 auto it = _entityIdToName.find(id); 299 if (it == _entityIdToName.end()) 300 { 301 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", id)); 302 throw IpmiException(::ipmi::ccInvalidFieldRequest); 303 } 304 305 std::string entityName; 306 try 307 { 308 // Parse the JSON config file. 309 if (!_entityConfigParsed) 310 { 311 _entityConfig = parseConfig(_configFile); 312 _entityConfigParsed = true; 313 } 314 315 // Find the "entity id:entity instance" mapping to entity name. 316 entityName = readNameFromConfig(it->second, instance, _entityConfig); 317 if (entityName.empty()) 318 { 319 throw IpmiException(::ipmi::ccInvalidFieldRequest); 320 } 321 } 322 catch (InternalFailure& e) 323 { 324 throw IpmiException(::ipmi::ccUnspecifiedError); 325 } 326 327 return entityName; 328 } 329 330 std::string Handler::getMachineName() 331 { 332 const char* path = "/etc/os-release"; 333 std::ifstream ifs(path); 334 if (ifs.fail()) 335 { 336 stdplus::print(stderr, "Failed to open: {}\n", path); 337 throw IpmiException(::ipmi::ccUnspecifiedError); 338 } 339 340 std::string line; 341 while (true) 342 { 343 std::getline(ifs, line); 344 if (ifs.eof()) 345 { 346 stdplus::print(stderr, 347 "Failed to find OPENBMC_TARGET_MACHINE: {}\n", path); 348 throw IpmiException(::ipmi::ccInvalidCommand); 349 } 350 if (ifs.fail()) 351 { 352 stdplus::print(stderr, "Failed to read: {}\n", path); 353 throw IpmiException(::ipmi::ccUnspecifiedError); 354 } 355 std::string_view lineView(line); 356 constexpr std::string_view prefix = "OPENBMC_TARGET_MACHINE="; 357 if (lineView.substr(0, prefix.size()) != prefix) 358 { 359 continue; 360 } 361 lineView.remove_prefix(prefix.size()); 362 lineView.remove_prefix( 363 std::min(lineView.find_first_not_of('"'), lineView.size())); 364 lineView.remove_suffix( 365 lineView.size() - 1 - 366 std::min(lineView.find_last_not_of('"'), lineView.size() - 1)); 367 return std::string(lineView); 368 } 369 } 370 371 static constexpr auto HOST_TIME_DELAY_FILENAME = "/run/host_poweroff_delay"; 372 static constexpr auto HOST_POWEROFF_TARGET = "gbmc-host-poweroff.target"; 373 374 void Handler::hostPowerOffDelay(std::uint32_t delay) const 375 { 376 // Set time delay 377 std::ofstream ofs; 378 ofs.open(HOST_TIME_DELAY_FILENAME, std::ofstream::out); 379 if (!ofs.good()) 380 { 381 stdplus::print(stderr, "Unable to open file for output.\n"); 382 throw IpmiException(::ipmi::ccUnspecifiedError); 383 } 384 385 ofs << "HOST_POWEROFF_DELAY=" << delay << std::endl; 386 ofs.close(); 387 if (ofs.fail()) 388 { 389 stdplus::print(stderr, "Write failed\n"); 390 throw IpmiException(::ipmi::ccUnspecifiedError); 391 } 392 393 // Write succeeded, please continue. 394 auto bus = sdbusplus::bus::new_default(); 395 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT, 396 SYSTEMD_INTERFACE, "StartUnit"); 397 398 method.append(HOST_POWEROFF_TARGET); 399 method.append("replace"); 400 401 try 402 { 403 bus.call_noreply(method); 404 } 405 catch (const sdbusplus::exception::SdBusError& ex) 406 { 407 log<level::ERR>("Failed to call Power Off", 408 entry("WHAT=%s", ex.what())); 409 throw IpmiException(::ipmi::ccUnspecifiedError); 410 } 411 } 412 413 std::string readNameFromConfig(const std::string& type, uint8_t instance, 414 const Json& config) 415 { 416 static const std::vector<Json> empty{}; 417 std::vector<Json> readings = config.value(type, empty); 418 std::string name = ""; 419 420 for (const auto& j : readings) 421 { 422 uint8_t instanceNum = j.value("instance", 0); 423 // Not the instance we're interested in 424 if (instanceNum != instance) 425 { 426 continue; 427 } 428 429 // Found the instance we're interested in 430 name = j.value("name", ""); 431 432 break; 433 } 434 435 return name; 436 } 437 438 void Handler::buildI2cPcieMapping() 439 { 440 _pcie_i2c_map = buildPcieMap(); 441 } 442 443 size_t Handler::getI2cPcieMappingSize() const 444 { 445 return _pcie_i2c_map.size(); 446 } 447 448 std::tuple<std::uint32_t, std::string> 449 Handler::getI2cEntry(unsigned int entry) const 450 { 451 return _pcie_i2c_map[entry]; 452 } 453 454 namespace 455 { 456 457 static constexpr std::string_view ACCEL_OOB_ROOT = "/com/google/customAccel/"; 458 static constexpr char ACCEL_OOB_SERVICE[] = "com.google.custom_accel"; 459 static constexpr char ACCEL_OOB_INTERFACE[] = "com.google.custom_accel.BAR"; 460 461 // C type for "a{oa{sa{sv}}}" from DBus.ObjectManager::GetManagedObjects() 462 using AnyType = std::variant<std::string, uint8_t, uint32_t, uint64_t>; 463 using AnyTypeList = std::vector<std::pair<std::string, AnyType>>; 464 using NamedArrayOfAnyTypeLists = 465 std::vector<std::pair<std::string, AnyTypeList>>; 466 using ArrayOfObjectPathsAndTieredAnyTypeLists = std::vector< 467 std::pair<sdbusplus::message::object_path, NamedArrayOfAnyTypeLists>>; 468 469 } // namespace 470 471 sdbusplus::bus_t Handler::getDbus() const 472 { 473 return sdbusplus::bus::new_default(); 474 } 475 476 const std::unique_ptr<FileSystemInterface>& Handler::getFs() const 477 { 478 return this->fsPtr; 479 } 480 481 uint32_t Handler::accelOobDeviceCount() const 482 { 483 ArrayOfObjectPathsAndTieredAnyTypeLists data; 484 485 try 486 { 487 auto bus = getDbus(); 488 auto method = bus.new_method_call(ACCEL_OOB_SERVICE, "/", 489 "org.freedesktop.DBus.ObjectManager", 490 "GetManagedObjects"); 491 bus.call(method).read(data); 492 } 493 catch (const sdbusplus::exception::SdBusError& ex) 494 { 495 log<level::ERR>( 496 "Failed to call GetManagedObjects on com.google.custom_accel", 497 entry("WHAT=%s", ex.what())); 498 throw IpmiException(::ipmi::ccUnspecifiedError); 499 } 500 501 return data.size(); 502 } 503 504 std::string Handler::accelOobDeviceName(size_t index) const 505 { 506 ArrayOfObjectPathsAndTieredAnyTypeLists data; 507 508 try 509 { 510 auto bus = getDbus(); 511 auto method = bus.new_method_call(ACCEL_OOB_SERVICE, "/", 512 "org.freedesktop.DBus.ObjectManager", 513 "GetManagedObjects"); 514 bus.call(method).read(data); 515 } 516 catch (const sdbusplus::exception::SdBusError& ex) 517 { 518 log<level::ERR>( 519 "Failed to call GetManagedObjects on com.google.custom_accel", 520 entry("WHAT=%s", ex.what())); 521 throw IpmiException(::ipmi::ccUnspecifiedError); 522 } 523 524 if (index >= data.size()) 525 { 526 log<level::WARNING>( 527 "Requested index is larger than the number of entries.", 528 entry("INDEX=%zu", index), entry("NUM_NAMES=%zu", data.size())); 529 throw IpmiException(::ipmi::ccParmOutOfRange); 530 } 531 532 std::string_view name(data[index].first.str); 533 if (!name.starts_with(ACCEL_OOB_ROOT)) 534 { 535 throw IpmiException(::ipmi::ccInvalidCommand); 536 } 537 name.remove_prefix(ACCEL_OOB_ROOT.length()); 538 return std::string(name); 539 } 540 541 uint64_t Handler::accelOobRead(std::string_view name, uint64_t address, 542 uint8_t num_bytes) const 543 { 544 static constexpr char ACCEL_OOB_METHOD[] = "Read"; 545 546 std::string object_name(ACCEL_OOB_ROOT); 547 object_name.append(name); 548 549 auto bus = getDbus(); 550 auto method = bus.new_method_call(ACCEL_OOB_SERVICE, object_name.c_str(), 551 ACCEL_OOB_INTERFACE, ACCEL_OOB_METHOD); 552 method.append(address, static_cast<uint64_t>(num_bytes)); 553 554 std::vector<uint8_t> bytes; 555 556 try 557 { 558 bus.call(method).read(bytes); 559 } 560 catch (const sdbusplus::exception::SdBusError& ex) 561 { 562 log<level::ERR>("Failed to call Read on com.google.custom_accel", 563 entry("WHAT=%s", ex.what()), 564 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE), 565 entry("DBUS_OBJECT=%s", object_name.c_str()), 566 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE), 567 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD), 568 entry("DBUS_ARG_ADDRESS=%016llx", address), 569 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes)); 570 throw IpmiException(::ipmi::ccUnspecifiedError); 571 } 572 573 if (bytes.size() < num_bytes) 574 { 575 log<level::ERR>( 576 "Call to Read on com.google.custom_accel didn't return the expected" 577 " number of bytes.", 578 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE), 579 entry("DBUS_OBJECT=%s", object_name.c_str()), 580 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE), 581 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD), 582 entry("DBUS_ARG_ADDRESS=%016llx", address), 583 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes), 584 entry("DBUS_RETURN_SIZE=%zu", bytes.size())); 585 throw IpmiException(::ipmi::ccUnspecifiedError); 586 } 587 588 if (bytes.size() > sizeof(uint64_t)) 589 { 590 log<level::ERR>( 591 "Call to Read on com.google.custom_accel returned more than 8B.", 592 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE), 593 entry("DBUS_OBJECT=%s", object_name.c_str()), 594 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE), 595 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD), 596 entry("DBUS_ARG_ADDRESS=%016llx", address), 597 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes), 598 entry("DBUS_RETURN_SIZE=%zu", bytes.size())); 599 throw IpmiException(::ipmi::ccReqDataTruncated); 600 } 601 602 uint64_t data = 0; 603 for (size_t i = 0; i < num_bytes; ++i) 604 { 605 data = (data << 8) | bytes[i]; 606 } 607 608 return data; 609 } 610 611 void Handler::accelOobWrite(std::string_view name, uint64_t address, 612 uint8_t num_bytes, uint64_t data) const 613 { 614 static constexpr std::string_view ACCEL_OOB_METHOD = "Write"; 615 616 std::string object_name(ACCEL_OOB_ROOT); 617 object_name.append(name); 618 619 if (num_bytes > sizeof(data)) 620 { 621 log<level::ERR>( 622 "Call to Write on com.google.custom_accel requested more than 8B.", 623 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE), 624 entry("DBUS_OBJECT=%s", object_name.c_str()), 625 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE), 626 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD.data()), 627 entry("DBUS_ARG_ADDRESS=%016llx", address), 628 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes), 629 entry("DBUS_ARG_DATA=%016llx", data)); 630 throw IpmiException(::ipmi::ccParmOutOfRange); 631 } 632 633 std::vector<uint8_t> bytes; 634 bytes.reserve(num_bytes); 635 for (size_t i = 0; i < num_bytes; ++i) 636 { 637 bytes.emplace_back(data & 0xff); 638 data >>= 8; 639 } 640 641 try 642 { 643 auto bus = getDbus(); 644 auto method = 645 bus.new_method_call(ACCEL_OOB_SERVICE, object_name.c_str(), 646 ACCEL_OOB_INTERFACE, ACCEL_OOB_METHOD.data()); 647 method.append(address, bytes); 648 bus.call_noreply(method); 649 } 650 catch (const sdbusplus::exception::SdBusError& ex) 651 { 652 log<level::ERR>("Failed to call Write on com.google.custom_accel", 653 entry("WHAT=%s", ex.what()), 654 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE), 655 entry("DBUS_OBJECT=%s", object_name.c_str()), 656 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE), 657 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD.data()), 658 entry("DBUS_ARG_ADDRESS=%016llx", address), 659 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes), 660 entry("DBUS_ARG_DATA=%016llx", data)); 661 throw IpmiException(::ipmi::ccUnspecifiedError); 662 } 663 } 664 665 std::vector<uint8_t> Handler::pcieBifurcation(uint8_t index) 666 { 667 return bifurcationHelper.get().getBifurcation(index).value_or( 668 std::vector<uint8_t>{}); 669 } 670 671 static constexpr auto BARE_METAL_TARGET = "gbmc-bare-metal-active.target"; 672 673 void Handler::linuxBootDone() const 674 { 675 if (isBmcInBareMetalMode(this->fsPtr) != 676 static_cast<uint8_t>(BmcMode::BM_MODE)) 677 { 678 return; 679 } 680 681 log<level::INFO>("LinuxBootDone: Disabling IPMI"); 682 683 // Start the bare metal active systemd target. 684 auto bus = sdbusplus::bus::new_default(); 685 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT, 686 SYSTEMD_INTERFACE, "StartUnit"); 687 688 method.append(BARE_METAL_TARGET); 689 method.append("replace"); 690 691 try 692 { 693 bus.call_noreply(method); 694 } 695 catch (const sdbusplus::exception::SdBusError& ex) 696 { 697 log<level::ERR>("Failed to start bare metal active systemd target", 698 entry("WHAT=%s", ex.what())); 699 throw IpmiException(::ipmi::ccUnspecifiedError); 700 } 701 } 702 703 static constexpr char ACCEL_POWER_SERVICE[] = "xyz.openbmc_project.AccelPower"; 704 static constexpr char ACCEL_POWER_PATH_PREFIX[] = 705 "/xyz/openbmc_project/control/accel_power_"; 706 static constexpr char POWER_MODE_IFC[] = 707 "xyz.openbmc_project.Control.Power.Mode"; 708 709 void Handler::accelSetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id, 710 uint8_t settings_id, uint16_t value) const 711 { 712 int vrSettingsReq; 713 boost::system::error_code ec; 714 if (_vrSettingsMap.find(settings_id) == _vrSettingsMap.end()) 715 { 716 log<level::ERR>("Settings ID is not supported", 717 entry("settings_id=%d", settings_id)); 718 throw IpmiException(::ipmi::ccParmOutOfRange); 719 } 720 721 vrSettingsReq = static_cast<int>(settings_id | value << 8); 722 std::string object_name( 723 std::format("{}{}", ACCEL_POWER_PATH_PREFIX, chip_id)); 724 725 std::variant<int> val = vrSettingsReq; 726 ctx->bus->yield_method_call(ctx->yield, ec, ACCEL_POWER_SERVICE, 727 object_name.c_str(), 728 "org.freedesktop.DBus.Properties", "Set", 729 POWER_MODE_IFC, "PowerMode", val); 730 if (ec) 731 { 732 log<level::ERR>("Failed to set PowerMode property"); 733 throw IpmiException(::ipmi::ccUnspecifiedError); 734 } 735 } 736 737 static constexpr char EXTERNAL_SENSOR_SERVICE[] = 738 "xyz.openbmc_project.ExternalSensor"; 739 static constexpr char EXTERNAL_SENSOR_PATH_PREFIX[] = 740 "/xyz/openbmc_project/sensors/power/"; 741 static constexpr char SENSOR_VALUE_IFC[] = "xyz.openbmc_project.Sensor.Value"; 742 743 uint16_t Handler::accelGetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id, 744 uint8_t settings_id) const 745 { 746 Value value; 747 boost::system::error_code ec; 748 std::string object_name(std::format("{}{}{}", EXTERNAL_SENSOR_PATH_PREFIX, 749 _vrSettingsMap.at(settings_id), 750 chip_id)); 751 752 if (_vrSettingsMap.find(settings_id) == _vrSettingsMap.end()) 753 { 754 log<level::ERR>("Settings ID is not supported", 755 entry("settings_id=%d", settings_id)); 756 throw IpmiException(::ipmi::ccParmOutOfRange); 757 } 758 759 value = ctx->bus->yield_method_call<std::variant<double>>( 760 ctx->yield, ec, EXTERNAL_SENSOR_SERVICE, object_name.c_str(), 761 "org.freedesktop.DBus.Properties", "Get", SENSOR_VALUE_IFC, "Value"); 762 if (ec) 763 { 764 log<level::ERR>("accelGetVrSettings: Failed to call GetObject "); 765 throw IpmiException(::ipmi::ccUnspecifiedError); 766 } 767 768 return static_cast<uint16_t>(std::get<double>(value)); 769 } 770 } // namespace ipmi 771 } // namespace google 772