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 
29 namespace phosphor::power::sequencer
30 {
31 
32 void BMCServices::logError(const std::string& message, Entry::Level severity,
33                            std::map<std::string, std::string>& additionalData)
34 {
35     try
36     {
37         // Add PID to AdditionalData
38         additionalData.emplace("_PID", std::to_string(getpid()));
39 
40         // If severity is critical, set error as system terminating
41         if (severity == Entry::Level::Critical)
42         {
43             additionalData.emplace("SEVERITY_DETAIL", "SYSTEM_TERM");
44         }
45 
46         auto method = bus.new_method_call(
47             "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
48             "xyz.openbmc_project.Logging.Create", "Create");
49         method.append(message, severity, additionalData);
50         bus.call_noreply(method);
51     }
52     catch (const std::exception& e)
53     {
54         lg2::error("Unable to log error {ERROR}: {EXCEPTION}", "ERROR", message,
55                    "EXCEPTION", e);
56     }
57 }
58 
59 bool BMCServices::isPresent(const std::string& inventoryPath)
60 {
61     // Initially assume hardware is not present
62     bool present{false};
63 
64     // Try to find cached presence value
65     auto it = presenceCache.find(inventoryPath);
66     if (it != presenceCache.end())
67     {
68         present = it->second;
69     }
70     else
71     {
72         // Get presence from D-Bus interface/property
73         try
74         {
75             util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
76                               INVENTORY_MGR_IFACE, bus, present);
77         }
78         catch (const sdbusplus::exception_t& e)
79         {
80             // If exception type is expected and indicates hardware not present
81             if (isExpectedException(e))
82             {
83                 present = false;
84             }
85             else
86             {
87                 // Re-throw unexpected exception
88                 throw;
89             }
90         }
91 
92         // Cache presence value
93         presenceCache[inventoryPath] = present;
94     }
95 
96     return present;
97 }
98 
99 std::vector<int> BMCServices::getGPIOValues(const std::string& chipLabel)
100 {
101     // Set up the chip object
102     gpiod::chip chip{chipLabel, gpiod::chip::OPEN_BY_LABEL};
103     unsigned int numLines = chip.num_lines();
104     lg2::info(
105         "Reading GPIO values from chip {NAME} with label {LABEL} and {NUM_LINES} lines",
106         "NAME", chip.name(), "LABEL", chipLabel, "NUM_LINES", numLines);
107 
108     // Read GPIO values.  Work around libgpiod bulk line maximum by getting
109     // values from individual lines.
110     std::vector<int> values;
111     for (unsigned int offset = 0; offset < numLines; ++offset)
112     {
113         gpiod::line line = chip.get_line(offset);
114         line.request({"phosphor-power-control",
115                       gpiod::line_request::DIRECTION_INPUT, 0});
116         values.push_back(line.get_value());
117         line.release();
118     }
119     return values;
120 }
121 
122 bool BMCServices::isExpectedException(const sdbusplus::exception_t& e)
123 {
124     // Initially assume exception is not one of the expected types
125     bool isExpected{false};
126 
127     // If the D-Bus error name is set within the exception
128     if (e.name() != nullptr)
129     {
130         // Check if the error name is one of the expected values when hardware
131         // is not present.
132         //
133         // Sometimes the object path does not exist.  Sometimes the object path
134         // exists, but it does not implement the D-Bus interface that contains
135         // the present property.  Both of these cases result in exceptions.
136         //
137         // In the case where the interface is not implemented, the systemd
138         // documentation seems to indicate that the error name should be
139         // SD_BUS_ERROR_UNKNOWN_INTERFACE.  However, in OpenBMC the
140         // SD_BUS_ERROR_UNKNOWN_PROPERTY error name can occur.
141         std::string name = e.name();
142         if ((name == SD_BUS_ERROR_UNKNOWN_OBJECT) ||
143             (name == SD_BUS_ERROR_UNKNOWN_INTERFACE) ||
144             (name == SD_BUS_ERROR_UNKNOWN_PROPERTY))
145         {
146             isExpected = true;
147         }
148     }
149 
150     return isExpected;
151 }
152 
153 } // namespace phosphor::power::sequencer
154