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