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     // Get presence from D-Bus interface/property
65     try
66     {
67         util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
68                           INVENTORY_MGR_IFACE, bus, present);
69     }
70     catch (const sdbusplus::exception_t& e)
71     {
72         // If exception type is expected and indicates hardware not present
73         if (isExpectedException(e))
74         {
75             present = false;
76         }
77         else
78         {
79             // Re-throw unexpected exception
80             throw;
81         }
82     }
83 
84     return present;
85 }
86 
87 std::vector<int> BMCServices::getGPIOValues(const std::string& chipLabel)
88 {
89     // Set up the chip object
90     gpiod::chip chip{chipLabel, gpiod::chip::OPEN_BY_LABEL};
91     unsigned int numLines = chip.num_lines();
92     lg2::info(
93         "Reading GPIO values from chip {NAME} with label {LABEL} and {NUM_LINES} lines",
94         "NAME", chip.name(), "LABEL", chipLabel, "NUM_LINES", numLines);
95 
96     // Read GPIO values.  Work around libgpiod bulk line maximum by getting
97     // values from individual lines.
98     std::vector<int> values;
99     for (unsigned int offset = 0; offset < numLines; ++offset)
100     {
101         gpiod::line line = chip.get_line(offset);
102         line.request({"phosphor-power-control",
103                       gpiod::line_request::DIRECTION_INPUT, 0});
104         values.push_back(line.get_value());
105         line.release();
106     }
107     return values;
108 }
109 
110 bool BMCServices::isExpectedException(const sdbusplus::exception_t& e)
111 {
112     // Initially assume exception is not one of the expected types
113     bool isExpected{false};
114 
115     // If the D-Bus error name is set within the exception
116     if (e.name() != nullptr)
117     {
118         // Check if the error name is one of the expected values when hardware
119         // is not present.
120         //
121         // Sometimes the object path does not exist.  Sometimes the object path
122         // exists, but it does not implement the D-Bus interface that contains
123         // the present property.  Both of these cases result in exceptions.
124         //
125         // In the case where the interface is not implemented, the systemd
126         // documentation seems to indicate that the error name should be
127         // SD_BUS_ERROR_UNKNOWN_INTERFACE.  However, in OpenBMC the
128         // SD_BUS_ERROR_UNKNOWN_PROPERTY error name can occur.
129         std::string name = e.name();
130         if ((name == SD_BUS_ERROR_UNKNOWN_OBJECT) ||
131             (name == SD_BUS_ERROR_UNKNOWN_INTERFACE) ||
132             (name == SD_BUS_ERROR_UNKNOWN_PROPERTY))
133         {
134             isExpected = true;
135         }
136     }
137 
138     return isExpected;
139 }
140 
141 } // namespace phosphor::power::sequencer
142