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