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 #include "handler.hpp" 15 16 #include "bm_config.h" 17 18 #include "bmc_mode_enum.hpp" 19 #include "errors.hpp" 20 #include "handler_impl.hpp" 21 #include "util.hpp" 22 23 #include <fcntl.h> 24 #include <ipmid/api.h> 25 #include <mtd/mtd-abi.h> 26 #include <mtd/mtd-user.h> 27 #include <sys/ioctl.h> 28 #include <unistd.h> 29 30 #include <nlohmann/json.hpp> 31 #include <phosphor-logging/elog-errors.hpp> 32 #include <phosphor-logging/log.hpp> 33 #include <sdbusplus/bus.hpp> 34 #include <xyz/openbmc_project/Common/error.hpp> 35 36 #include <cinttypes> 37 #include <cstdio> 38 #include <filesystem> 39 #include <fstream> 40 #include <map> 41 #include <sstream> 42 #include <string> 43 #include <string_view> 44 #include <tuple> 45 #include <variant> 46 47 #ifndef NCSI_IF_NAME 48 #define NCSI_IF_NAME eth0 49 #endif 50 51 // To deal with receiving a string without quotes. 52 #define QUOTE(name) #name 53 #define STR(macro) QUOTE(macro) 54 #define NCSI_IF_NAME_STR STR(NCSI_IF_NAME) 55 56 namespace ipmi 57 { 58 std::uint8_t getChannelByName(const std::string& chName); 59 } 60 61 namespace google 62 { 63 namespace ipmi 64 { 65 namespace fs = std::filesystem; 66 using Json = nlohmann::json; 67 using namespace phosphor::logging; 68 using InternalFailure = 69 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 70 71 static constexpr auto bmDriveCleaningFlagPath = "/run/bm-drive-cleaning.flag"; 72 static constexpr auto bmDriveCleaningDoneFlagPath = 73 "/run/bm-drive-cleaning-done.flag"; 74 static constexpr auto bmDriveCleaningDoneAckFlagPath = 75 "/run/bm-drive-cleaning-done-ack.flag"; 76 77 uint8_t isBmcInBareMetalMode() 78 { 79 #if BARE_METAL 80 return static_cast<uint8_t>(BmcMode::BM_MODE); 81 #else 82 std::error_code ec; 83 84 if (fs::exists(bmDriveCleaningDoneAckFlagPath, ec)) 85 { 86 std::fprintf( 87 stderr, 88 "%s exists so we acked cleaning done and must be in BM mode\n", 89 bmDriveCleaningDoneAckFlagPath); 90 return static_cast<uint8_t>(BmcMode::BM_MODE); 91 } 92 93 if (fs::exists(bmDriveCleaningDoneFlagPath, ec)) 94 { 95 fs::rename(bmDriveCleaningDoneFlagPath, bmDriveCleaningDoneAckFlagPath, 96 ec); 97 std::fprintf( 98 stderr, 99 "%s exists so we just finished cleaning and must be in BM mode\n", 100 bmDriveCleaningDoneFlagPath); 101 return static_cast<uint8_t>(BmcMode::BM_MODE); 102 } 103 104 if (fs::exists(BM_SIGNAL_PATH, ec)) 105 { 106 if (!fs::exists(bmDriveCleaningFlagPath, ec)) 107 { 108 std::ofstream ofs; 109 ofs.open(bmDriveCleaningFlagPath, std::ofstream::out); 110 ofs.close(); 111 } 112 113 std::fprintf( 114 stderr, 115 "%s exists and no done/ack flag, we must be in BM cleaning mode\n", 116 BM_SIGNAL_PATH); 117 return static_cast<uint8_t>(BmcMode::BM_CLEANING_MODE); 118 } 119 120 std::fprintf( 121 stderr, 122 "Unable to find any BM state files so we must not be in BM mode\n"); 123 return static_cast<uint8_t>(BmcMode::NON_BM_MODE); 124 #endif 125 } 126 127 uint8_t Handler::getBmcMode() 128 { 129 // BM_CLEANING_MODE is not implemented yet 130 return isBmcInBareMetalMode(); 131 } 132 133 std::tuple<std::uint8_t, std::string> 134 Handler::getEthDetails(std::string intf) const 135 { 136 if (intf.empty()) 137 { 138 intf = NCSI_IF_NAME_STR; 139 } 140 return std::make_tuple(::ipmi::getChannelByName(intf), std::move(intf)); 141 } 142 143 std::int64_t Handler::getRxPackets(const std::string& name) const 144 { 145 std::ostringstream opath; 146 opath << "/sys/class/net/" << name << "/statistics/rx_packets"; 147 std::string path = opath.str(); 148 149 // Minor sanity & security check (of course, I'm less certain if unicode 150 // comes into play here. 151 // 152 // Basically you can't easily inject ../ or /../ into the path below. 153 if (name.find("/") != std::string::npos) 154 { 155 std::fprintf(stderr, "Invalid or illegal name: '%s'\n", name.c_str()); 156 throw IpmiException(::ipmi::ccInvalidFieldRequest); 157 } 158 159 std::error_code ec; 160 if (!fs::exists(path, ec)) 161 { 162 std::fprintf(stderr, "Path: '%s' doesn't exist.\n", path.c_str()); 163 throw IpmiException(::ipmi::ccInvalidFieldRequest); 164 } 165 // We're uninterested in the state of ec. 166 167 int64_t count = 0; 168 std::ifstream ifs; 169 ifs.exceptions(std::ifstream::failbit); 170 try 171 { 172 ifs.open(path); 173 ifs >> count; 174 } 175 catch (std::ios_base::failure& fail) 176 { 177 throw IpmiException(::ipmi::ccUnspecifiedError); 178 } 179 180 return count; 181 } 182 183 VersionTuple Handler::getCpldVersion(unsigned int id) const 184 { 185 std::ostringstream opath; 186 opath << "/run/cpld" << id << ".version"; 187 // Check for file 188 189 std::error_code ec; 190 if (!fs::exists(opath.str(), ec)) 191 { 192 std::fprintf(stderr, "Path: '%s' doesn't exist.\n", 193 opath.str().c_str()); 194 throw IpmiException(::ipmi::ccInvalidFieldRequest); 195 } 196 // We're uninterested in the state of ec. 197 198 // If file exists, read. 199 std::ifstream ifs; 200 ifs.exceptions(std::ifstream::failbit); 201 std::string value; 202 try 203 { 204 ifs.open(opath.str()); 205 ifs >> value; 206 } 207 catch (std::ios_base::failure& fail) 208 { 209 throw IpmiException(::ipmi::ccUnspecifiedError); 210 } 211 212 // If value parses as expected, return version. 213 VersionTuple version = std::make_tuple(0, 0, 0, 0); 214 215 int num_fields = std::sscanf(value.c_str(), 216 "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8, 217 &std::get<0>(version), &std::get<1>(version), 218 &std::get<2>(version), &std::get<3>(version)); 219 if (num_fields == 0) 220 { 221 std::fprintf(stderr, "Invalid version.\n"); 222 throw IpmiException(::ipmi::ccUnspecifiedError); 223 } 224 225 return version; 226 } 227 228 static constexpr auto TIME_DELAY_FILENAME = "/run/psu_timedelay"; 229 static constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 230 static constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1"; 231 static constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 232 static constexpr auto PSU_HARDRESET_TARGET = "gbmc-psu-hardreset.target"; 233 234 void Handler::psuResetDelay(std::uint32_t delay) const 235 { 236 std::ofstream ofs; 237 ofs.open(TIME_DELAY_FILENAME, std::ofstream::out); 238 if (!ofs.good()) 239 { 240 std::fprintf(stderr, "Unable to open file for output.\n"); 241 throw IpmiException(::ipmi::ccUnspecifiedError); 242 } 243 244 ofs << "PSU_HARDRESET_DELAY=" << delay << std::endl; 245 if (ofs.fail()) 246 { 247 std::fprintf(stderr, "Write failed\n"); 248 ofs.close(); 249 throw IpmiException(::ipmi::ccUnspecifiedError); 250 } 251 252 // Write succeeded, please continue. 253 ofs.flush(); 254 ofs.close(); 255 256 auto bus = sdbusplus::bus::new_default(); 257 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT, 258 SYSTEMD_INTERFACE, "StartUnit"); 259 260 method.append(PSU_HARDRESET_TARGET); 261 method.append("replace"); 262 263 try 264 { 265 bus.call_noreply(method); 266 } 267 catch (const sdbusplus::exception::SdBusError& ex) 268 { 269 log<level::ERR>("Failed to call PSU hard reset"); 270 throw IpmiException(::ipmi::ccUnspecifiedError); 271 } 272 } 273 274 static constexpr auto RESET_ON_SHUTDOWN_FILENAME = "/run/powercycle_on_s5"; 275 276 void Handler::psuResetOnShutdown() const 277 { 278 std::ofstream ofs; 279 ofs.open(RESET_ON_SHUTDOWN_FILENAME, std::ofstream::out); 280 if (!ofs.good()) 281 { 282 std::fprintf(stderr, "Unable to open file for output.\n"); 283 throw IpmiException(::ipmi::ccUnspecifiedError); 284 } 285 ofs.close(); 286 } 287 288 uint32_t Handler::getFlashSize() 289 { 290 mtd_info_t info; 291 int fd = open("/dev/mtd0", O_RDONLY); 292 int err = ioctl(fd, MEMGETINFO, &info); 293 close(fd); 294 295 if (err) 296 { 297 throw IpmiException(::ipmi::ccUnspecifiedError); 298 } 299 return info.size; 300 } 301 302 std::string Handler::getEntityName(std::uint8_t id, std::uint8_t instance) 303 { 304 // Check if we support this Entity ID. 305 auto it = _entityIdToName.find(id); 306 if (it == _entityIdToName.end()) 307 { 308 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", id)); 309 throw IpmiException(::ipmi::ccInvalidFieldRequest); 310 } 311 312 std::string entityName; 313 try 314 { 315 // Parse the JSON config file. 316 if (!_entityConfigParsed) 317 { 318 _entityConfig = parseConfig(_configFile); 319 _entityConfigParsed = true; 320 } 321 322 // Find the "entity id:entity instance" mapping to entity name. 323 entityName = readNameFromConfig(it->second, instance, _entityConfig); 324 if (entityName.empty()) 325 { 326 throw IpmiException(::ipmi::ccInvalidFieldRequest); 327 } 328 } 329 catch (InternalFailure& e) 330 { 331 throw IpmiException(::ipmi::ccUnspecifiedError); 332 } 333 334 return entityName; 335 } 336 337 std::string Handler::getMachineName() 338 { 339 const char* path = "/etc/os-release"; 340 std::ifstream ifs(path); 341 if (ifs.fail()) 342 { 343 std::fprintf(stderr, "Failed to open: %s\n", path); 344 throw IpmiException(::ipmi::ccUnspecifiedError); 345 } 346 347 std::string line; 348 while (true) 349 { 350 std::getline(ifs, line); 351 if (ifs.eof()) 352 { 353 std::fprintf(stderr, "Failed to find OPENBMC_TARGET_MACHINE: %s\n", 354 path); 355 throw IpmiException(::ipmi::ccInvalidCommand); 356 } 357 if (ifs.fail()) 358 { 359 std::fprintf(stderr, "Failed to read: %s\n", path); 360 throw IpmiException(::ipmi::ccUnspecifiedError); 361 } 362 std::string_view lineView(line); 363 constexpr std::string_view prefix = "OPENBMC_TARGET_MACHINE="; 364 if (lineView.substr(0, prefix.size()) != prefix) 365 { 366 continue; 367 } 368 lineView.remove_prefix(prefix.size()); 369 lineView.remove_prefix( 370 std::min(lineView.find_first_not_of('"'), lineView.size())); 371 lineView.remove_suffix( 372 lineView.size() - 1 - 373 std::min(lineView.find_last_not_of('"'), lineView.size() - 1)); 374 return std::string(lineView); 375 } 376 } 377 378 static constexpr auto HOST_TIME_DELAY_FILENAME = "/run/host_poweroff_delay"; 379 static constexpr auto HOST_POWEROFF_TARGET = "gbmc-host-poweroff.target"; 380 381 void Handler::hostPowerOffDelay(std::uint32_t delay) const 382 { 383 // Set time delay 384 std::ofstream ofs; 385 ofs.open(HOST_TIME_DELAY_FILENAME, std::ofstream::out); 386 if (!ofs.good()) 387 { 388 std::fprintf(stderr, "Unable to open file for output.\n"); 389 throw IpmiException(::ipmi::ccUnspecifiedError); 390 } 391 392 ofs << "HOST_POWEROFF_DELAY=" << delay << std::endl; 393 ofs.close(); 394 if (ofs.fail()) 395 { 396 std::fprintf(stderr, "Write failed\n"); 397 throw IpmiException(::ipmi::ccUnspecifiedError); 398 } 399 400 // Write succeeded, please continue. 401 auto bus = sdbusplus::bus::new_default(); 402 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT, 403 SYSTEMD_INTERFACE, "StartUnit"); 404 405 method.append(HOST_POWEROFF_TARGET); 406 method.append("replace"); 407 408 try 409 { 410 bus.call_noreply(method); 411 } 412 catch (const sdbusplus::exception::SdBusError& ex) 413 { 414 log<level::ERR>("Failed to call Power Off", 415 entry("WHAT=%s", ex.what())); 416 throw IpmiException(::ipmi::ccUnspecifiedError); 417 } 418 } 419 420 std::string readNameFromConfig(const std::string& type, uint8_t instance, 421 const Json& config) 422 { 423 static const std::vector<Json> empty{}; 424 std::vector<Json> readings = config.value(type, empty); 425 std::string name = ""; 426 427 for (const auto& j : readings) 428 { 429 uint8_t instanceNum = j.value("instance", 0); 430 // Not the instance we're interested in 431 if (instanceNum != instance) 432 { 433 continue; 434 } 435 436 // Found the instance we're interested in 437 name = j.value("name", ""); 438 439 break; 440 } 441 442 return name; 443 } 444 445 void Handler::buildI2cPcieMapping() 446 { 447 _pcie_i2c_map = buildPcieMap(); 448 } 449 450 size_t Handler::getI2cPcieMappingSize() const 451 { 452 return _pcie_i2c_map.size(); 453 } 454 455 std::tuple<std::uint32_t, std::string> 456 Handler::getI2cEntry(unsigned int entry) const 457 { 458 return _pcie_i2c_map[entry]; 459 } 460 461 namespace 462 { 463 464 static constexpr std::string_view ACCEL_OOB_ROOT = "/com/google/customAccel/"; 465 static constexpr char ACCEL_OOB_SERVICE[] = "com.google.custom_accel"; 466 static constexpr char ACCEL_OOB_INTERFACE[] = "com.google.custom_accel.BAR"; 467 468 // C type for "a{oa{sa{sv}}}" from DBus.ObjectManager::GetManagedObjects() 469 using AnyType = std::variant<std::string, uint8_t, uint32_t, uint64_t>; 470 using AnyTypeList = std::vector<std::pair<std::string, AnyType>>; 471 using NamedArrayOfAnyTypeLists = 472 std::vector<std::pair<std::string, AnyTypeList>>; 473 using ArrayOfObjectPathsAndTieredAnyTypeLists = std::vector< 474 std::pair<sdbusplus::message::object_path, NamedArrayOfAnyTypeLists>>; 475 476 } // namespace 477 478 sdbusplus::bus_t Handler::getDbus() const 479 { 480 return sdbusplus::bus::new_default(); 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() != static_cast<uint8_t>(BmcMode::BM_MODE)) 678 { 679 return; 680 } 681 682 log<level::INFO>("LinuxBootDone: Disabling IPMI"); 683 684 // Start the bare metal active systemd target. 685 auto bus = sdbusplus::bus::new_default(); 686 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT, 687 SYSTEMD_INTERFACE, "StartUnit"); 688 689 method.append(BARE_METAL_TARGET); 690 method.append("replace"); 691 692 try 693 { 694 bus.call_noreply(method); 695 } 696 catch (const sdbusplus::exception::SdBusError& ex) 697 { 698 log<level::ERR>("Failed to start bare metal active systemd target", 699 entry("WHAT=%s", ex.what())); 700 throw IpmiException(::ipmi::ccUnspecifiedError); 701 } 702 } 703 704 } // namespace ipmi 705 } // namespace google 706