xref: /openbmc/google-ipmi-sys/handler.cpp (revision 8c0094e4)
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 "bm_instance.hpp"
20 #include "bmc_mode_enum.hpp"
21 #include "errors.hpp"
22 #include "handler_impl.hpp"
23 #include "util.hpp"
24 
25 #include <fcntl.h>
26 #include <ipmid/api.h>
27 #include <mtd/mtd-abi.h>
28 #include <mtd/mtd-user.h>
29 #include <sys/ioctl.h>
30 #include <unistd.h>
31 
32 #include <nlohmann/json.hpp>
33 #include <phosphor-logging/elog-errors.hpp>
34 #include <phosphor-logging/log.hpp>
35 #include <sdbusplus/bus.hpp>
36 #include <stdplus/print.hpp>
37 #include <xyz/openbmc_project/Common/error.hpp>
38 
39 #include <cinttypes>
40 #include <cstdio>
41 #include <filesystem>
42 #include <fstream>
43 #include <iomanip>
44 #include <map>
45 #include <sstream>
46 #include <string>
47 #include <string_view>
48 #include <tuple>
49 #include <variant>
50 
51 #ifndef NCSI_IF_NAME
52 #define NCSI_IF_NAME eth0
53 #endif
54 
55 // To deal with receiving a string without quotes.
56 #define QUOTE(name) #name
57 #define STR(macro) QUOTE(macro)
58 #define NCSI_IF_NAME_STR STR(NCSI_IF_NAME)
59 
60 namespace ipmi
61 {
62 std::uint8_t getChannelByName(const std::string& chName);
63 }
64 
65 namespace google
66 {
67 namespace ipmi
68 {
69 using Json = nlohmann::json;
70 using namespace phosphor::logging;
71 using InternalFailure =
72     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
73 using Value = std::variant<double>;
74 
isBmcInBareMetalMode(const std::unique_ptr<FileSystemInterface> & fs)75 uint8_t isBmcInBareMetalMode(const std::unique_ptr<FileSystemInterface>& fs)
76 {
77 #if BARE_METAL
78     return static_cast<uint8_t>(BmcMode::BM_MODE);
79 #else
80     std::error_code ec;
81 
82     if (fs->exists(bmDriveCleaningDoneAckFlagPath, ec))
83     {
84         stdplus::print(
85             stderr,
86             "{} exists so we acked cleaning done and must be in BM mode\n",
87             bmDriveCleaningDoneAckFlagPath);
88         return static_cast<uint8_t>(BmcMode::BM_MODE);
89     }
90 
91     if (fs->exists(bmDriveCleaningDoneFlagPath, ec))
92     {
93         fs->rename(bmDriveCleaningDoneFlagPath, bmDriveCleaningDoneAckFlagPath,
94                    ec);
95         stdplus::print(
96             stderr,
97             "{} exists so we just finished cleaning and must be in BM mode\n",
98             bmDriveCleaningDoneFlagPath);
99         return static_cast<uint8_t>(BmcMode::BM_MODE);
100     }
101 
102     if (fs->exists(BM_SIGNAL_PATH, ec))
103     {
104         if (!fs->exists(bmDriveCleaningFlagPath, ec))
105         {
106             fs->create(bmDriveCleaningFlagPath);
107         }
108 
109         stdplus::print(
110             stderr,
111             "{} exists and no done/ack flag, we must be in BM cleaning mode\n",
112             BM_SIGNAL_PATH);
113         return static_cast<uint8_t>(BmcMode::BM_CLEANING_MODE);
114     }
115 
116     stdplus::print(
117         stderr,
118         "Unable to find any BM state files so we must not be in BM mode\n");
119     return static_cast<uint8_t>(BmcMode::NON_BM_MODE);
120 #endif
121 }
122 
getBmcMode()123 uint8_t Handler::getBmcMode()
124 {
125     // BM_CLEANING_MODE is not implemented yet
126     return isBmcInBareMetalMode(this->getFs());
127 }
128 
129 std::tuple<std::uint8_t, std::string>
getEthDetails(std::string intf) const130     Handler::getEthDetails(std::string intf) const
131 {
132     if (intf.empty())
133     {
134         intf = NCSI_IF_NAME_STR;
135     }
136     return std::make_tuple(::ipmi::getChannelByName(intf), std::move(intf));
137 }
138 
getRxPackets(const std::string & name) const139 std::int64_t Handler::getRxPackets(const std::string& name) const
140 {
141     std::ostringstream opath;
142     opath << "/sys/class/net/" << name << "/statistics/rx_packets";
143     std::string path = opath.str();
144 
145     // Minor sanity & security check (of course, I'm less certain if unicode
146     // comes into play here.
147     //
148     // Basically you can't easily inject ../ or /../ into the path below.
149     if (name.find("/") != std::string::npos)
150     {
151         stdplus::print(stderr, "Invalid or illegal name: '{}'\n", name);
152         throw IpmiException(::ipmi::ccInvalidFieldRequest);
153     }
154 
155     std::error_code ec;
156     if (!this->getFs()->exists(path, ec))
157     {
158         stdplus::print(stderr, "Path: '{}' doesn't exist.\n", path);
159         throw IpmiException(::ipmi::ccInvalidFieldRequest);
160     }
161     // We're uninterested in the state of ec.
162 
163     int64_t count = 0;
164     std::ifstream ifs;
165     ifs.exceptions(std::ifstream::failbit);
166     try
167     {
168         ifs.open(path);
169         ifs >> count;
170     }
171     catch (std::ios_base::failure& fail)
172     {
173         throw IpmiException(::ipmi::ccUnspecifiedError);
174     }
175 
176     return count;
177 }
178 
getCpldVersion(unsigned int id) const179 VersionTuple Handler::getCpldVersion(unsigned int id) const
180 {
181     std::ostringstream opath;
182     opath << "/run/cpld" << id << ".version";
183     // Check for file
184 
185     std::error_code ec;
186     if (!this->getFs()->exists(opath.str(), ec))
187     {
188         stdplus::print(stderr, "Path: '{}' doesn't exist.\n", opath.str());
189         throw IpmiException(::ipmi::ccInvalidFieldRequest);
190     }
191     // We're uninterested in the state of ec.
192 
193     // If file exists, read.
194     std::ifstream ifs;
195     ifs.exceptions(std::ifstream::failbit);
196     std::string value;
197     try
198     {
199         ifs.open(opath.str());
200         ifs >> value;
201     }
202     catch (std::ios_base::failure& fail)
203     {
204         throw IpmiException(::ipmi::ccUnspecifiedError);
205     }
206 
207     // If value parses as expected, return version.
208     VersionTuple version = std::make_tuple(0, 0, 0, 0);
209 
210     int num_fields =
211         std::sscanf(value.c_str(), "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8,
212                     &std::get<0>(version), &std::get<1>(version),
213                     &std::get<2>(version), &std::get<3>(version));
214     if (num_fields == 0)
215     {
216         stdplus::print(stderr, "Invalid version.\n");
217         throw IpmiException(::ipmi::ccUnspecifiedError);
218     }
219 
220     return version;
221 }
222 
223 static constexpr auto TIME_DELAY_FILENAME = "/run/psu_timedelay";
224 static constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
225 static constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
226 static constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
227 static constexpr auto PSU_HARDRESET_TARGET = "gbmc-psu-hardreset.target";
228 
psuResetDelay(std::uint32_t delay) const229 void Handler::psuResetDelay(std::uint32_t delay) const
230 {
231     std::ofstream ofs;
232     ofs.open(TIME_DELAY_FILENAME, std::ofstream::out);
233     if (!ofs.good())
234     {
235         stdplus::print(stderr, "Unable to open file for output.\n");
236         throw IpmiException(::ipmi::ccUnspecifiedError);
237     }
238 
239     ofs << "PSU_HARDRESET_DELAY=" << delay << std::endl;
240     if (ofs.fail())
241     {
242         stdplus::print(stderr, "Write failed\n");
243         ofs.close();
244         throw IpmiException(::ipmi::ccUnspecifiedError);
245     }
246 
247     // Write succeeded, please continue.
248     ofs.flush();
249     ofs.close();
250 
251     auto bus = sdbusplus::bus::new_default();
252     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
253                                       SYSTEMD_INTERFACE, "StartUnit");
254 
255     method.append(PSU_HARDRESET_TARGET);
256     method.append("replace");
257 
258     try
259     {
260         bus.call_noreply(method);
261     }
262     catch (const sdbusplus::exception::SdBusError& ex)
263     {
264         log<level::ERR>("Failed to call PSU hard reset");
265         throw IpmiException(::ipmi::ccUnspecifiedError);
266     }
267 }
268 
269 static constexpr auto RESET_ON_SHUTDOWN_FILENAME = "/run/powercycle_on_s5";
270 
psuResetOnShutdown() const271 void Handler::psuResetOnShutdown() const
272 {
273     std::ofstream ofs;
274     ofs.open(RESET_ON_SHUTDOWN_FILENAME, std::ofstream::out);
275     if (!ofs.good())
276     {
277         stdplus::print(stderr, "Unable to open file for output.\n");
278         throw IpmiException(::ipmi::ccUnspecifiedError);
279     }
280     ofs.close();
281 }
282 
getFlashSize()283 uint32_t Handler::getFlashSize()
284 {
285     mtd_info_t info;
286     int fd = open("/dev/mtd0", O_RDONLY);
287     int err = ioctl(fd, MEMGETINFO, &info);
288     close(fd);
289 
290     if (err)
291     {
292         throw IpmiException(::ipmi::ccUnspecifiedError);
293     }
294     return info.size;
295 }
296 
getEntityName(std::uint8_t id,std::uint8_t instance)297 std::string Handler::getEntityName(std::uint8_t id, std::uint8_t instance)
298 {
299     // Check if we support this Entity ID.
300     auto it = _entityIdToName.find(id);
301     if (it == _entityIdToName.end())
302     {
303         log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", id));
304         throw IpmiException(::ipmi::ccInvalidFieldRequest);
305     }
306 
307     std::string entityName;
308     try
309     {
310         // Parse the JSON config file.
311         if (!_entityConfigParsed)
312         {
313             _entityConfig = parseConfig(_configFile);
314             _entityConfigParsed = true;
315         }
316 
317         // Find the "entity id:entity instance" mapping to entity name.
318         entityName = readNameFromConfig(it->second, instance, _entityConfig);
319         if (entityName.empty())
320         {
321             throw IpmiException(::ipmi::ccInvalidFieldRequest);
322         }
323     }
324     catch (InternalFailure& e)
325     {
326         throw IpmiException(::ipmi::ccUnspecifiedError);
327     }
328 
329     return entityName;
330 }
331 
getMachineName()332 std::string Handler::getMachineName()
333 {
334     const char* path = "/etc/os-release";
335     std::ifstream ifs(path);
336     if (ifs.fail())
337     {
338         stdplus::print(stderr, "Failed to open: {}\n", path);
339         throw IpmiException(::ipmi::ccUnspecifiedError);
340     }
341 
342     std::string line;
343     while (true)
344     {
345         std::getline(ifs, line);
346         if (ifs.eof())
347         {
348             stdplus::print(stderr,
349                            "Failed to find OPENBMC_TARGET_MACHINE: {}\n", path);
350             throw IpmiException(::ipmi::ccInvalidCommand);
351         }
352         if (ifs.fail())
353         {
354             stdplus::print(stderr, "Failed to read: {}\n", path);
355             throw IpmiException(::ipmi::ccUnspecifiedError);
356         }
357         std::string_view lineView(line);
358         constexpr std::string_view prefix = "OPENBMC_TARGET_MACHINE=";
359         if (lineView.substr(0, prefix.size()) != prefix)
360         {
361             continue;
362         }
363         lineView.remove_prefix(prefix.size());
364         lineView.remove_prefix(
365             std::min(lineView.find_first_not_of('"'), lineView.size()));
366         lineView.remove_suffix(
367             lineView.size() - 1 -
368             std::min(lineView.find_last_not_of('"'), lineView.size() - 1));
369         return std::string(lineView);
370     }
371 }
372 
373 static constexpr auto HOST_TIME_DELAY_FILENAME = "/run/host_poweroff_delay";
374 static constexpr auto HOST_POWEROFF_TARGET = "gbmc-host-poweroff.target";
375 
hostPowerOffDelay(std::uint32_t delay) const376 void Handler::hostPowerOffDelay(std::uint32_t delay) const
377 {
378     // Set time delay
379     std::ofstream ofs;
380     ofs.open(HOST_TIME_DELAY_FILENAME, std::ofstream::out);
381     if (!ofs.good())
382     {
383         stdplus::print(stderr, "Unable to open file for output.\n");
384         throw IpmiException(::ipmi::ccUnspecifiedError);
385     }
386 
387     ofs << "HOST_POWEROFF_DELAY=" << delay << std::endl;
388     ofs.close();
389     if (ofs.fail())
390     {
391         stdplus::print(stderr, "Write failed\n");
392         throw IpmiException(::ipmi::ccUnspecifiedError);
393     }
394 
395     // Write succeeded, please continue.
396     auto bus = sdbusplus::bus::new_default();
397     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
398                                       SYSTEMD_INTERFACE, "StartUnit");
399 
400     method.append(HOST_POWEROFF_TARGET);
401     method.append("replace");
402 
403     try
404     {
405         bus.call_noreply(method);
406     }
407     catch (const sdbusplus::exception::SdBusError& ex)
408     {
409         log<level::ERR>("Failed to call Power Off",
410                         entry("WHAT=%s", ex.what()));
411         throw IpmiException(::ipmi::ccUnspecifiedError);
412     }
413 }
414 
readNameFromConfig(const std::string & type,uint8_t instance,const Json & config)415 std::string readNameFromConfig(const std::string& type, uint8_t instance,
416                                const Json& config)
417 {
418     static const std::vector<Json> empty{};
419     std::vector<Json> readings = config.value(type, empty);
420     std::string name = "";
421 
422     for (const auto& j : readings)
423     {
424         uint8_t instanceNum = j.value("instance", 0);
425         // Not the instance we're interested in
426         if (instanceNum != instance)
427         {
428             continue;
429         }
430 
431         // Found the instance we're interested in
432         name = j.value("name", "");
433 
434         break;
435     }
436 
437     return name;
438 }
439 
buildI2cPcieMapping()440 void Handler::buildI2cPcieMapping()
441 {
442     _pcie_i2c_map = buildPcieMap();
443 }
444 
getI2cPcieMappingSize() const445 size_t Handler::getI2cPcieMappingSize() const
446 {
447     return _pcie_i2c_map.size();
448 }
449 
450 std::tuple<std::uint32_t, std::string>
getI2cEntry(unsigned int entry) const451     Handler::getI2cEntry(unsigned int entry) const
452 {
453     return _pcie_i2c_map[entry];
454 }
455 
456 namespace
457 {
458 
459 static constexpr std::string_view ACCEL_OOB_ROOT = "/com/google/customAccel/";
460 static constexpr char ACCEL_OOB_SERVICE[] = "com.google.custom_accel";
461 static constexpr char ACCEL_OOB_INTERFACE[] = "com.google.custom_accel.BAR";
462 
463 // C type for "a{oa{sa{sv}}}" from DBus.ObjectManager::GetManagedObjects()
464 using AnyType = std::variant<std::string, uint8_t, uint32_t, uint64_t>;
465 using AnyTypeList = std::vector<std::pair<std::string, AnyType>>;
466 using NamedArrayOfAnyTypeLists =
467     std::vector<std::pair<std::string, AnyTypeList>>;
468 using ArrayOfObjectPathsAndTieredAnyTypeLists = std::vector<
469     std::pair<sdbusplus::message::object_path, NamedArrayOfAnyTypeLists>>;
470 
471 } // namespace
472 
getDbus() const473 sdbusplus::bus_t Handler::getDbus() const
474 {
475     return sdbusplus::bus::new_default();
476 }
477 
getFs() const478 const std::unique_ptr<FileSystemInterface>& Handler::getFs() const
479 {
480     return this->fsPtr;
481 }
482 
accelOobDeviceCount() const483 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 
accelOobDeviceName(size_t index) const506 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 
accelOobRead(std::string_view name,uint64_t address,uint8_t num_bytes) const543 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 
accelOobWrite(std::string_view name,uint64_t address,uint8_t num_bytes,uint64_t data) const613 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 
pcieBifurcation(uint8_t index)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 
linuxBootDone() const675 void Handler::linuxBootDone() const
676 {
677     if (isBmcInBareMetalMode(this->fsPtr) !=
678         static_cast<uint8_t>(BmcMode::BM_MODE))
679     {
680         return;
681     }
682 
683     log<level::INFO>("LinuxBootDone: Disabling IPMI");
684 
685     // Start the bare metal active systemd target.
686     auto bus = sdbusplus::bus::new_default();
687     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
688                                       SYSTEMD_INTERFACE, "StartUnit");
689 
690     method.append(BARE_METAL_TARGET);
691     method.append("replace");
692 
693     try
694     {
695         bus.call_noreply(method);
696     }
697     catch (const sdbusplus::exception::SdBusError& ex)
698     {
699         log<level::ERR>("Failed to start bare metal active systemd target",
700                         entry("WHAT=%s", ex.what()));
701         throw IpmiException(::ipmi::ccUnspecifiedError);
702     }
703 }
704 
705 static constexpr char ACCEL_POWER_SERVICE[] = "xyz.openbmc_project.AccelPower";
706 static constexpr char ACCEL_POWER_PATH_PREFIX[] =
707     "/xyz/openbmc_project/control/accel_power_";
708 static constexpr char POWER_MODE_IFC[] =
709     "xyz.openbmc_project.Control.Power.Mode";
710 
accelSetVrSettings(::ipmi::Context::ptr ctx,uint8_t chip_id,uint8_t settings_id,uint16_t value) const711 void Handler::accelSetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
712                                  uint8_t settings_id, uint16_t value) const
713 {
714     int vrSettingsReq;
715     boost::system::error_code ec;
716     if (_vrSettingsMap.find(settings_id) == _vrSettingsMap.end())
717     {
718         log<level::ERR>("Settings ID is not supported",
719                         entry("settings_id=%d", settings_id));
720         throw IpmiException(::ipmi::ccParmOutOfRange);
721     }
722 
723     vrSettingsReq = static_cast<int>(settings_id | value << 8);
724     std::string object_name(
725         std::format("{}{}", ACCEL_POWER_PATH_PREFIX, chip_id));
726 
727     std::variant<int> val = vrSettingsReq;
728     ctx->bus->yield_method_call(
729         ctx->yield, ec, ACCEL_POWER_SERVICE, object_name.c_str(),
730         "org.freedesktop.DBus.Properties", "Set", POWER_MODE_IFC, "PowerMode",
731         val);
732     if (ec)
733     {
734         log<level::ERR>("Failed to set PowerMode property");
735         throw IpmiException(::ipmi::ccUnspecifiedError);
736     }
737 }
738 
739 static constexpr char EXTERNAL_SENSOR_SERVICE[] =
740     "xyz.openbmc_project.ExternalSensor";
741 static constexpr char EXTERNAL_SENSOR_PATH_PREFIX[] =
742     "/xyz/openbmc_project/sensors/power/";
743 static constexpr char SENSOR_VALUE_IFC[] = "xyz.openbmc_project.Sensor.Value";
744 
accelGetVrSettings(::ipmi::Context::ptr ctx,uint8_t chip_id,uint8_t settings_id) const745 uint16_t Handler::accelGetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
746                                      uint8_t settings_id) const
747 {
748     Value value;
749     boost::system::error_code ec;
750     std::string object_name(
751         std::format("{}{}{}", EXTERNAL_SENSOR_PATH_PREFIX,
752                     _vrSettingsMap.at(settings_id), chip_id));
753 
754     if (_vrSettingsMap.find(settings_id) == _vrSettingsMap.end())
755     {
756         log<level::ERR>("Settings ID is not supported",
757                         entry("settings_id=%d", settings_id));
758         throw IpmiException(::ipmi::ccParmOutOfRange);
759     }
760 
761     value = ctx->bus->yield_method_call<std::variant<double>>(
762         ctx->yield, ec, EXTERNAL_SENSOR_SERVICE, object_name.c_str(),
763         "org.freedesktop.DBus.Properties", "Get", SENSOR_VALUE_IFC, "Value");
764     if (ec)
765     {
766         log<level::ERR>("accelGetVrSettings: Failed to call GetObject ");
767         throw IpmiException(::ipmi::ccUnspecifiedError);
768     }
769 
770     return static_cast<uint16_t>(std::get<double>(value));
771 }
772 
getBMInstanceProperty(uint8_t propertyType) const773 std::string Handler::getBMInstanceProperty(uint8_t propertyType) const
774 {
775     std::string propertyTypeString;
776     if (auto it = bmInstanceTypeStringMap.find(propertyType);
777         it == bmInstanceTypeStringMap.end())
778     {
779         stdplus::print(stderr, "PropertyType: '{}' is invalid.\n",
780                        propertyType);
781         throw IpmiException(::ipmi::ccInvalidFieldRequest);
782     }
783     else
784     {
785         propertyTypeString = it->second;
786     }
787     std::string opath = std::format("/run/bm-instance/{}", propertyTypeString);
788     // Check for file
789 
790     std::error_code ec;
791     // TODO(brandonkim@google.com): Fix this to use stdplus::ManagedFd
792     if (!this->getFs()->exists(opath, ec))
793     {
794         stdplus::print(stderr, "Path: '{}' doesn't exist.\n", opath);
795         throw IpmiException(::ipmi::ccInvalidFieldRequest);
796     }
797 
798     std::ifstream ifs;
799     ifs.exceptions(std::ifstream::failbit);
800     std::string property;
801     try
802     {
803         ifs.open(opath);
804         std::getline(ifs, property);
805     }
806     catch (std::ios_base::failure& fail)
807     {
808         stdplus::print(stderr, "Failed to read: '{}'.\n", opath);
809         throw IpmiException(::ipmi::ccUnspecifiedError);
810     }
811     return property;
812 }
813 
814 } // namespace ipmi
815 } // namespace google
816