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