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