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 
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 
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 
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 
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 
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