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