xref: /openbmc/phosphor-power/phosphor-power-sequencer/src/services.cpp (revision 3a11d6328d0bfdbd74bde8ae6d70909560f66030)
1  /**
2   * Copyright © 2024 IBM Corporation
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *     http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  #include "services.hpp"
18  
19  #include "types.hpp"
20  #include "utility.hpp"
21  
22  #include <sys/types.h> // for getpid()
23  #include <unistd.h>    // for getpid()
24  
25  #include <gpiod.hpp>
26  
27  #include <exception>
28  #include <variant>
29  
30  namespace phosphor::power::sequencer
31  {
32  
logError(const std::string & message,Entry::Level severity,std::map<std::string,std::string> & additionalData)33  void BMCServices::logError(const std::string& message, Entry::Level severity,
34                             std::map<std::string, std::string>& additionalData)
35  {
36      try
37      {
38          // Add PID to AdditionalData
39          additionalData.emplace("_PID", std::to_string(getpid()));
40  
41          // If severity is critical, set error as system terminating
42          if (severity == Entry::Level::Critical)
43          {
44              additionalData.emplace("SEVERITY_DETAIL", "SYSTEM_TERM");
45          }
46  
47          auto method = bus.new_method_call(
48              "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
49              "xyz.openbmc_project.Logging.Create", "Create");
50          method.append(message, severity, additionalData);
51          bus.call_noreply(method);
52      }
53      catch (const std::exception& e)
54      {
55          lg2::error("Unable to log error {ERROR}: {EXCEPTION}", "ERROR", message,
56                     "EXCEPTION", e);
57      }
58  }
59  
isPresent(const std::string & inventoryPath)60  bool BMCServices::isPresent(const std::string& inventoryPath)
61  {
62      // Initially assume hardware is not present
63      bool present{false};
64  
65      // Try to find cached presence value
66      auto it = presenceCache.find(inventoryPath);
67      if (it != presenceCache.end())
68      {
69          present = it->second;
70      }
71      else
72      {
73          // Get presence from D-Bus interface/property
74          try
75          {
76              util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
77                                INVENTORY_MGR_IFACE, bus, present);
78          }
79          catch (const sdbusplus::exception_t& e)
80          {
81              // If exception type is expected and indicates hardware not present
82              if (isExpectedException(e))
83              {
84                  present = false;
85              }
86              else
87              {
88                  // Re-throw unexpected exception
89                  throw;
90              }
91          }
92  
93          // Cache presence value
94          presenceCache[inventoryPath] = present;
95      }
96  
97      return present;
98  }
99  
getGPIOValues(const std::string & chipLabel)100  std::vector<int> BMCServices::getGPIOValues(const std::string& chipLabel)
101  {
102      // Set up the chip object
103      gpiod::chip chip{chipLabel, gpiod::chip::OPEN_BY_LABEL};
104      unsigned int numLines = chip.num_lines();
105      lg2::info(
106          "Reading GPIO values from chip {NAME} with label {LABEL} and {NUM_LINES} lines",
107          "NAME", chip.name(), "LABEL", chipLabel, "NUM_LINES", numLines);
108  
109      // Read GPIO values.  Work around libgpiod bulk line maximum by getting
110      // values from individual lines.
111      std::vector<int> values;
112      for (unsigned int offset = 0; offset < numLines; ++offset)
113      {
114          gpiod::line line = chip.get_line(offset);
115          line.request({"phosphor-power-control",
116                        gpiod::line_request::DIRECTION_INPUT, 0});
117          values.push_back(line.get_value());
118          line.release();
119      }
120      return values;
121  }
122  
isExpectedException(const sdbusplus::exception_t & e)123  bool BMCServices::isExpectedException(const sdbusplus::exception_t& e)
124  {
125      // Initially assume exception is not one of the expected types
126      bool isExpected{false};
127  
128      // If the D-Bus error name is set within the exception
129      if (e.name() != nullptr)
130      {
131          // Check if the error name is one of the expected values when hardware
132          // is not present.
133          //
134          // Sometimes the object path does not exist.  Sometimes the object path
135          // exists, but it does not implement the D-Bus interface that contains
136          // the present property.  Both of these cases result in exceptions.
137          //
138          // In the case where the interface is not implemented, the systemd
139          // documentation seems to indicate that the error name should be
140          // SD_BUS_ERROR_UNKNOWN_INTERFACE.  However, in OpenBMC the
141          // SD_BUS_ERROR_UNKNOWN_PROPERTY error name can occur.
142          std::string name = e.name();
143          if ((name == SD_BUS_ERROR_UNKNOWN_OBJECT) ||
144              (name == SD_BUS_ERROR_UNKNOWN_INTERFACE) ||
145              (name == SD_BUS_ERROR_UNKNOWN_PROPERTY))
146          {
147              isExpected = true;
148          }
149      }
150  
151      return isExpected;
152  }
153  
createBMCDump()154  void BMCServices::createBMCDump()
155  {
156      try
157      {
158          auto method = bus.new_method_call(
159              "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump/bmc",
160              "xyz.openbmc_project.Dump.Create", "CreateDump");
161          method.append(
162              std::vector<
163                  std::pair<std::string, std::variant<std::string, uint64_t>>>());
164          bus.call_noreply(method);
165      }
166      catch (const std::exception& e)
167      {
168          lg2::error("Unable to create BMC dump: {ERROR}", "ERROR", e);
169      }
170  }
171  
172  } // namespace phosphor::power::sequencer
173