xref: /openbmc/google-ipmi-sys/handler.cpp (revision 8d618532)
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 
72 uint8_t isBmcInBareMetalMode(const std::unique_ptr<FileSystemInterface>& fs)
73 {
74 #if BARE_METAL
75     return static_cast<uint8_t>(BmcMode::BM_MODE);
76 #else
77     std::error_code ec;
78 
79     if (fs->exists(bmDriveCleaningDoneAckFlagPath, ec))
80     {
81         stdplus::print(
82             stderr,
83             "{} exists so we acked cleaning done and must be in BM mode\n",
84             bmDriveCleaningDoneAckFlagPath);
85         return static_cast<uint8_t>(BmcMode::BM_MODE);
86     }
87 
88     if (fs->exists(bmDriveCleaningDoneFlagPath, ec))
89     {
90         fs->rename(bmDriveCleaningDoneFlagPath, bmDriveCleaningDoneAckFlagPath,
91                    ec);
92         stdplus::print(
93             stderr,
94             "{} exists so we just finished cleaning and must be in BM mode\n",
95             bmDriveCleaningDoneFlagPath);
96         return static_cast<uint8_t>(BmcMode::BM_MODE);
97     }
98 
99     if (fs->exists(BM_SIGNAL_PATH, ec))
100     {
101         if (!fs->exists(bmDriveCleaningFlagPath, ec))
102         {
103             fs->create(bmDriveCleaningFlagPath);
104         }
105 
106         stdplus::print(
107             stderr,
108             "{} exists and no done/ack flag, we must be in BM cleaning mode\n",
109             BM_SIGNAL_PATH);
110         return static_cast<uint8_t>(BmcMode::BM_CLEANING_MODE);
111     }
112 
113     stdplus::print(
114         stderr,
115         "Unable to find any BM state files so we must not be in BM mode\n");
116     return static_cast<uint8_t>(BmcMode::NON_BM_MODE);
117 #endif
118 }
119 
120 uint8_t Handler::getBmcMode()
121 {
122     // BM_CLEANING_MODE is not implemented yet
123     return isBmcInBareMetalMode(this->getFs());
124 }
125 
126 std::tuple<std::uint8_t, std::string>
127     Handler::getEthDetails(std::string intf) const
128 {
129     if (intf.empty())
130     {
131         intf = NCSI_IF_NAME_STR;
132     }
133     return std::make_tuple(::ipmi::getChannelByName(intf), std::move(intf));
134 }
135 
136 std::int64_t Handler::getRxPackets(const std::string& name) const
137 {
138     std::ostringstream opath;
139     opath << "/sys/class/net/" << name << "/statistics/rx_packets";
140     std::string path = opath.str();
141 
142     // Minor sanity & security check (of course, I'm less certain if unicode
143     // comes into play here.
144     //
145     // Basically you can't easily inject ../ or /../ into the path below.
146     if (name.find("/") != std::string::npos)
147     {
148         stdplus::print(stderr, "Invalid or illegal name: '{}'\n", name);
149         throw IpmiException(::ipmi::ccInvalidFieldRequest);
150     }
151 
152     std::error_code ec;
153     if (!this->getFs()->exists(path, ec))
154     {
155         stdplus::print(stderr, "Path: '{}' doesn't exist.\n", path);
156         throw IpmiException(::ipmi::ccInvalidFieldRequest);
157     }
158     // We're uninterested in the state of ec.
159 
160     int64_t count = 0;
161     std::ifstream ifs;
162     ifs.exceptions(std::ifstream::failbit);
163     try
164     {
165         ifs.open(path);
166         ifs >> count;
167     }
168     catch (std::ios_base::failure& fail)
169     {
170         throw IpmiException(::ipmi::ccUnspecifiedError);
171     }
172 
173     return count;
174 }
175 
176 VersionTuple Handler::getCpldVersion(unsigned int id) const
177 {
178     std::ostringstream opath;
179     opath << "/run/cpld" << id << ".version";
180     // Check for file
181 
182     std::error_code ec;
183     if (!this->getFs()->exists(opath.str(), ec))
184     {
185         stdplus::print(stderr, "Path: '{}' doesn't exist.\n", opath.str());
186         throw IpmiException(::ipmi::ccInvalidFieldRequest);
187     }
188     // We're uninterested in the state of ec.
189 
190     // If file exists, read.
191     std::ifstream ifs;
192     ifs.exceptions(std::ifstream::failbit);
193     std::string value;
194     try
195     {
196         ifs.open(opath.str());
197         ifs >> value;
198     }
199     catch (std::ios_base::failure& fail)
200     {
201         throw IpmiException(::ipmi::ccUnspecifiedError);
202     }
203 
204     // If value parses as expected, return version.
205     VersionTuple version = std::make_tuple(0, 0, 0, 0);
206 
207     int num_fields = std::sscanf(value.c_str(),
208                                  "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8,
209                                  &std::get<0>(version), &std::get<1>(version),
210                                  &std::get<2>(version), &std::get<3>(version));
211     if (num_fields == 0)
212     {
213         stdplus::print(stderr, "Invalid version.\n");
214         throw IpmiException(::ipmi::ccUnspecifiedError);
215     }
216 
217     return version;
218 }
219 
220 static constexpr auto TIME_DELAY_FILENAME = "/run/psu_timedelay";
221 static constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
222 static constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
223 static constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
224 static constexpr auto PSU_HARDRESET_TARGET = "gbmc-psu-hardreset.target";
225 
226 void Handler::psuResetDelay(std::uint32_t delay) const
227 {
228     std::ofstream ofs;
229     ofs.open(TIME_DELAY_FILENAME, std::ofstream::out);
230     if (!ofs.good())
231     {
232         stdplus::print(stderr, "Unable to open file for output.\n");
233         throw IpmiException(::ipmi::ccUnspecifiedError);
234     }
235 
236     ofs << "PSU_HARDRESET_DELAY=" << delay << std::endl;
237     if (ofs.fail())
238     {
239         stdplus::print(stderr, "Write failed\n");
240         ofs.close();
241         throw IpmiException(::ipmi::ccUnspecifiedError);
242     }
243 
244     // Write succeeded, please continue.
245     ofs.flush();
246     ofs.close();
247 
248     auto bus = sdbusplus::bus::new_default();
249     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
250                                       SYSTEMD_INTERFACE, "StartUnit");
251 
252     method.append(PSU_HARDRESET_TARGET);
253     method.append("replace");
254 
255     try
256     {
257         bus.call_noreply(method);
258     }
259     catch (const sdbusplus::exception::SdBusError& ex)
260     {
261         log<level::ERR>("Failed to call PSU hard reset");
262         throw IpmiException(::ipmi::ccUnspecifiedError);
263     }
264 }
265 
266 static constexpr auto RESET_ON_SHUTDOWN_FILENAME = "/run/powercycle_on_s5";
267 
268 void Handler::psuResetOnShutdown() const
269 {
270     std::ofstream ofs;
271     ofs.open(RESET_ON_SHUTDOWN_FILENAME, std::ofstream::out);
272     if (!ofs.good())
273     {
274         stdplus::print(stderr, "Unable to open file for output.\n");
275         throw IpmiException(::ipmi::ccUnspecifiedError);
276     }
277     ofs.close();
278 }
279 
280 uint32_t Handler::getFlashSize()
281 {
282     mtd_info_t info;
283     int fd = open("/dev/mtd0", O_RDONLY);
284     int err = ioctl(fd, MEMGETINFO, &info);
285     close(fd);
286 
287     if (err)
288     {
289         throw IpmiException(::ipmi::ccUnspecifiedError);
290     }
291     return info.size;
292 }
293 
294 std::string Handler::getEntityName(std::uint8_t id, std::uint8_t instance)
295 {
296     // Check if we support this Entity ID.
297     auto it = _entityIdToName.find(id);
298     if (it == _entityIdToName.end())
299     {
300         log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", id));
301         throw IpmiException(::ipmi::ccInvalidFieldRequest);
302     }
303 
304     std::string entityName;
305     try
306     {
307         // Parse the JSON config file.
308         if (!_entityConfigParsed)
309         {
310             _entityConfig = parseConfig(_configFile);
311             _entityConfigParsed = true;
312         }
313 
314         // Find the "entity id:entity instance" mapping to entity name.
315         entityName = readNameFromConfig(it->second, instance, _entityConfig);
316         if (entityName.empty())
317         {
318             throw IpmiException(::ipmi::ccInvalidFieldRequest);
319         }
320     }
321     catch (InternalFailure& e)
322     {
323         throw IpmiException(::ipmi::ccUnspecifiedError);
324     }
325 
326     return entityName;
327 }
328 
329 std::string Handler::getMachineName()
330 {
331     const char* path = "/etc/os-release";
332     std::ifstream ifs(path);
333     if (ifs.fail())
334     {
335         stdplus::print(stderr, "Failed to open: {}\n", path);
336         throw IpmiException(::ipmi::ccUnspecifiedError);
337     }
338 
339     std::string line;
340     while (true)
341     {
342         std::getline(ifs, line);
343         if (ifs.eof())
344         {
345             stdplus::print(stderr,
346                            "Failed to find OPENBMC_TARGET_MACHINE: {}\n", path);
347             throw IpmiException(::ipmi::ccInvalidCommand);
348         }
349         if (ifs.fail())
350         {
351             stdplus::print(stderr, "Failed to read: {}\n", path);
352             throw IpmiException(::ipmi::ccUnspecifiedError);
353         }
354         std::string_view lineView(line);
355         constexpr std::string_view prefix = "OPENBMC_TARGET_MACHINE=";
356         if (lineView.substr(0, prefix.size()) != prefix)
357         {
358             continue;
359         }
360         lineView.remove_prefix(prefix.size());
361         lineView.remove_prefix(
362             std::min(lineView.find_first_not_of('"'), lineView.size()));
363         lineView.remove_suffix(
364             lineView.size() - 1 -
365             std::min(lineView.find_last_not_of('"'), lineView.size() - 1));
366         return std::string(lineView);
367     }
368 }
369 
370 static constexpr auto HOST_TIME_DELAY_FILENAME = "/run/host_poweroff_delay";
371 static constexpr auto HOST_POWEROFF_TARGET = "gbmc-host-poweroff.target";
372 
373 void Handler::hostPowerOffDelay(std::uint32_t delay) const
374 {
375     // Set time delay
376     std::ofstream ofs;
377     ofs.open(HOST_TIME_DELAY_FILENAME, std::ofstream::out);
378     if (!ofs.good())
379     {
380         stdplus::print(stderr, "Unable to open file for output.\n");
381         throw IpmiException(::ipmi::ccUnspecifiedError);
382     }
383 
384     ofs << "HOST_POWEROFF_DELAY=" << delay << std::endl;
385     ofs.close();
386     if (ofs.fail())
387     {
388         stdplus::print(stderr, "Write failed\n");
389         throw IpmiException(::ipmi::ccUnspecifiedError);
390     }
391 
392     // Write succeeded, please continue.
393     auto bus = sdbusplus::bus::new_default();
394     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
395                                       SYSTEMD_INTERFACE, "StartUnit");
396 
397     method.append(HOST_POWEROFF_TARGET);
398     method.append("replace");
399 
400     try
401     {
402         bus.call_noreply(method);
403     }
404     catch (const sdbusplus::exception::SdBusError& ex)
405     {
406         log<level::ERR>("Failed to call Power Off",
407                         entry("WHAT=%s", ex.what()));
408         throw IpmiException(::ipmi::ccUnspecifiedError);
409     }
410 }
411 
412 std::string readNameFromConfig(const std::string& type, uint8_t instance,
413                                const Json& config)
414 {
415     static const std::vector<Json> empty{};
416     std::vector<Json> readings = config.value(type, empty);
417     std::string name = "";
418 
419     for (const auto& j : readings)
420     {
421         uint8_t instanceNum = j.value("instance", 0);
422         // Not the instance we're interested in
423         if (instanceNum != instance)
424         {
425             continue;
426         }
427 
428         // Found the instance we're interested in
429         name = j.value("name", "");
430 
431         break;
432     }
433 
434     return name;
435 }
436 
437 void Handler::buildI2cPcieMapping()
438 {
439     _pcie_i2c_map = buildPcieMap();
440 }
441 
442 size_t Handler::getI2cPcieMappingSize() const
443 {
444     return _pcie_i2c_map.size();
445 }
446 
447 std::tuple<std::uint32_t, std::string>
448     Handler::getI2cEntry(unsigned int entry) const
449 {
450     return _pcie_i2c_map[entry];
451 }
452 
453 namespace
454 {
455 
456 static constexpr std::string_view ACCEL_OOB_ROOT = "/com/google/customAccel/";
457 static constexpr char ACCEL_OOB_SERVICE[] = "com.google.custom_accel";
458 static constexpr char ACCEL_OOB_INTERFACE[] = "com.google.custom_accel.BAR";
459 
460 // C type for "a{oa{sa{sv}}}" from DBus.ObjectManager::GetManagedObjects()
461 using AnyType = std::variant<std::string, uint8_t, uint32_t, uint64_t>;
462 using AnyTypeList = std::vector<std::pair<std::string, AnyType>>;
463 using NamedArrayOfAnyTypeLists =
464     std::vector<std::pair<std::string, AnyTypeList>>;
465 using ArrayOfObjectPathsAndTieredAnyTypeLists = std::vector<
466     std::pair<sdbusplus::message::object_path, NamedArrayOfAnyTypeLists>>;
467 
468 } // namespace
469 
470 sdbusplus::bus_t Handler::getDbus() const
471 {
472     return sdbusplus::bus::new_default();
473 }
474 
475 const std::unique_ptr<FileSystemInterface>& Handler::getFs() const
476 {
477     return this->fsPtr;
478 }
479 
480 uint32_t Handler::accelOobDeviceCount() const
481 {
482     ArrayOfObjectPathsAndTieredAnyTypeLists data;
483 
484     try
485     {
486         auto bus = getDbus();
487         auto method = bus.new_method_call(ACCEL_OOB_SERVICE, "/",
488                                           "org.freedesktop.DBus.ObjectManager",
489                                           "GetManagedObjects");
490         bus.call(method).read(data);
491     }
492     catch (const sdbusplus::exception::SdBusError& ex)
493     {
494         log<level::ERR>(
495             "Failed to call GetManagedObjects on com.google.custom_accel",
496             entry("WHAT=%s", ex.what()));
497         throw IpmiException(::ipmi::ccUnspecifiedError);
498     }
499 
500     return data.size();
501 }
502 
503 std::string Handler::accelOobDeviceName(size_t index) const
504 {
505     ArrayOfObjectPathsAndTieredAnyTypeLists data;
506 
507     try
508     {
509         auto bus = getDbus();
510         auto method = bus.new_method_call(ACCEL_OOB_SERVICE, "/",
511                                           "org.freedesktop.DBus.ObjectManager",
512                                           "GetManagedObjects");
513         bus.call(method).read(data);
514     }
515     catch (const sdbusplus::exception::SdBusError& ex)
516     {
517         log<level::ERR>(
518             "Failed to call GetManagedObjects on com.google.custom_accel",
519             entry("WHAT=%s", ex.what()));
520         throw IpmiException(::ipmi::ccUnspecifiedError);
521     }
522 
523     if (index >= data.size())
524     {
525         log<level::WARNING>(
526             "Requested index is larger than the number of entries.",
527             entry("INDEX=%zu", index), entry("NUM_NAMES=%zu", data.size()));
528         throw IpmiException(::ipmi::ccParmOutOfRange);
529     }
530 
531     std::string_view name(data[index].first.str);
532     if (!name.starts_with(ACCEL_OOB_ROOT))
533     {
534         throw IpmiException(::ipmi::ccInvalidCommand);
535     }
536     name.remove_prefix(ACCEL_OOB_ROOT.length());
537     return std::string(name);
538 }
539 
540 uint64_t Handler::accelOobRead(std::string_view name, uint64_t address,
541                                uint8_t num_bytes) const
542 {
543     static constexpr char ACCEL_OOB_METHOD[] = "Read";
544 
545     std::string object_name(ACCEL_OOB_ROOT);
546     object_name.append(name);
547 
548     auto bus = getDbus();
549     auto method = bus.new_method_call(ACCEL_OOB_SERVICE, object_name.c_str(),
550                                       ACCEL_OOB_INTERFACE, ACCEL_OOB_METHOD);
551     method.append(address, static_cast<uint64_t>(num_bytes));
552 
553     std::vector<uint8_t> bytes;
554 
555     try
556     {
557         bus.call(method).read(bytes);
558     }
559     catch (const sdbusplus::exception::SdBusError& ex)
560     {
561         log<level::ERR>("Failed to call Read on com.google.custom_accel",
562                         entry("WHAT=%s", ex.what()),
563                         entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
564                         entry("DBUS_OBJECT=%s", object_name.c_str()),
565                         entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
566                         entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
567                         entry("DBUS_ARG_ADDRESS=%016llx", address),
568                         entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes));
569         throw IpmiException(::ipmi::ccUnspecifiedError);
570     }
571 
572     if (bytes.size() < num_bytes)
573     {
574         log<level::ERR>(
575             "Call to Read on com.google.custom_accel didn't return the expected"
576             " number of bytes.",
577             entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
578             entry("DBUS_OBJECT=%s", object_name.c_str()),
579             entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
580             entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
581             entry("DBUS_ARG_ADDRESS=%016llx", address),
582             entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
583             entry("DBUS_RETURN_SIZE=%zu", bytes.size()));
584         throw IpmiException(::ipmi::ccUnspecifiedError);
585     }
586 
587     if (bytes.size() > sizeof(uint64_t))
588     {
589         log<level::ERR>(
590             "Call to Read on com.google.custom_accel returned more than 8B.",
591             entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
592             entry("DBUS_OBJECT=%s", object_name.c_str()),
593             entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
594             entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
595             entry("DBUS_ARG_ADDRESS=%016llx", address),
596             entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
597             entry("DBUS_RETURN_SIZE=%zu", bytes.size()));
598         throw IpmiException(::ipmi::ccReqDataTruncated);
599     }
600 
601     uint64_t data = 0;
602     for (size_t i = 0; i < num_bytes; ++i)
603     {
604         data = (data << 8) | bytes[i];
605     }
606 
607     return data;
608 }
609 
610 void Handler::accelOobWrite(std::string_view name, uint64_t address,
611                             uint8_t num_bytes, uint64_t data) const
612 {
613     static constexpr std::string_view ACCEL_OOB_METHOD = "Write";
614 
615     std::string object_name(ACCEL_OOB_ROOT);
616     object_name.append(name);
617 
618     if (num_bytes > sizeof(data))
619     {
620         log<level::ERR>(
621             "Call to Write on com.google.custom_accel requested more than 8B.",
622             entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
623             entry("DBUS_OBJECT=%s", object_name.c_str()),
624             entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
625             entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD.data()),
626             entry("DBUS_ARG_ADDRESS=%016llx", address),
627             entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
628             entry("DBUS_ARG_DATA=%016llx", data));
629         throw IpmiException(::ipmi::ccParmOutOfRange);
630     }
631 
632     std::vector<uint8_t> bytes;
633     bytes.reserve(num_bytes);
634     for (size_t i = 0; i < num_bytes; ++i)
635     {
636         bytes.emplace_back(data & 0xff);
637         data >>= 8;
638     }
639 
640     try
641     {
642         auto bus = getDbus();
643         auto method =
644             bus.new_method_call(ACCEL_OOB_SERVICE, object_name.c_str(),
645                                 ACCEL_OOB_INTERFACE, ACCEL_OOB_METHOD.data());
646         method.append(address, bytes);
647         bus.call_noreply(method);
648     }
649     catch (const sdbusplus::exception::SdBusError& ex)
650     {
651         log<level::ERR>("Failed to call Write on com.google.custom_accel",
652                         entry("WHAT=%s", ex.what()),
653                         entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
654                         entry("DBUS_OBJECT=%s", object_name.c_str()),
655                         entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
656                         entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD.data()),
657                         entry("DBUS_ARG_ADDRESS=%016llx", address),
658                         entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
659                         entry("DBUS_ARG_DATA=%016llx", data));
660         throw IpmiException(::ipmi::ccUnspecifiedError);
661     }
662 }
663 
664 std::vector<uint8_t> Handler::pcieBifurcation(uint8_t index)
665 {
666     return bifurcationHelper.get().getBifurcation(index).value_or(
667         std::vector<uint8_t>{});
668 }
669 
670 static constexpr auto BARE_METAL_TARGET = "gbmc-bare-metal-active.target";
671 
672 void Handler::linuxBootDone() const
673 {
674     if (isBmcInBareMetalMode(this->fsPtr) !=
675         static_cast<uint8_t>(BmcMode::BM_MODE))
676     {
677         return;
678     }
679 
680     log<level::INFO>("LinuxBootDone: Disabling IPMI");
681 
682     // Start the bare metal active systemd target.
683     auto bus = sdbusplus::bus::new_default();
684     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
685                                       SYSTEMD_INTERFACE, "StartUnit");
686 
687     method.append(BARE_METAL_TARGET);
688     method.append("replace");
689 
690     try
691     {
692         bus.call_noreply(method);
693     }
694     catch (const sdbusplus::exception::SdBusError& ex)
695     {
696         log<level::ERR>("Failed to start bare metal active systemd target",
697                         entry("WHAT=%s", ex.what()));
698         throw IpmiException(::ipmi::ccUnspecifiedError);
699     }
700 }
701 
702 } // namespace ipmi
703 } // namespace google
704