1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2017 Google Inc
3
4 #include "config.h"
5
6 #include "dbushelper.hpp"
7
8 #include "dbushelper_interface.hpp"
9 #include "dbusutil.hpp"
10
11 #include <phosphor-logging/log.hpp>
12 #include <sdbusplus/bus.hpp>
13 #include <sdbusplus/exception.hpp>
14 #include <xyz/openbmc_project/ObjectMapper/common.hpp>
15 #include <xyz/openbmc_project/Sensor/Value/client.hpp>
16
17 #include <cstdint>
18 #include <map>
19 #include <stdexcept>
20 #include <string>
21 #include <variant>
22 #include <vector>
23
24 using ObjectMapper = sdbusplus::common::xyz::openbmc_project::ObjectMapper;
25 using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
26
27 namespace pid_control
28 {
29
30 using Property = std::string;
31 using Value = std::variant<int64_t, double, std::string, bool>;
32 using PropertyMap = std::map<Property, Value>;
33
34 using namespace phosphor::logging;
35
36 /* TODO(venture): Basically all phosphor apps need this, maybe it should be a
37 * part of sdbusplus. There is an old version in libmapper.
38 */
getService(const std::string & intf,const std::string & path)39 std::string DbusHelper::getService(const std::string& intf,
40 const std::string& path)
41 {
42 auto mapper = _bus.new_method_call(
43 ObjectMapper::default_service, ObjectMapper::instance_path,
44 ObjectMapper::interface, ObjectMapper::method_names::get_object);
45
46 mapper.append(path);
47 mapper.append(std::vector<std::string>({intf}));
48
49 std::map<std::string, std::vector<std::string>> response;
50
51 try
52 {
53 auto responseMsg = _bus.call(mapper);
54
55 responseMsg.read(response);
56 }
57 catch (const sdbusplus::exception_t& ex)
58 {
59 log<level::ERR>("ObjectMapper call failure",
60 entry("WHAT=%s", ex.what()));
61 throw;
62 }
63
64 if (response.begin() == response.end())
65 {
66 throw std::runtime_error("Unable to find Object: " + path);
67 }
68
69 return response.begin()->first;
70 }
71
getProperties(const std::string & service,const std::string & path,SensorProperties * prop)72 void DbusHelper::getProperties(const std::string& service,
73 const std::string& path, SensorProperties* prop)
74 {
75 auto pimMsg = _bus.new_method_call(service.c_str(), path.c_str(),
76 propertiesintf, "GetAll");
77
78 pimMsg.append(SensorValue::interface);
79
80 PropertyMap propMap;
81
82 try
83 {
84 auto valueResponseMsg = _bus.call(pimMsg);
85 valueResponseMsg.read(propMap);
86 }
87 catch (const sdbusplus::exception_t& ex)
88 {
89 log<level::ERR>("GetAll Properties Failed",
90 entry("WHAT=%s", ex.what()));
91 throw;
92 }
93
94 // The PropertyMap returned will look like this because it's always
95 // reading a Sensor.Value interface.
96 // a{sv} 3:
97 // "Value" x 24875
98 // "Unit" s "xyz.openbmc_project.Sensor.Value.Unit.DegreesC"
99 // "Scale" x -3
100
101 // If no error was set, the values should all be there.
102 auto findUnit = propMap.find(SensorValue::property_names::unit);
103 if (findUnit != propMap.end())
104 {
105 prop->unit = std::get<std::string>(findUnit->second);
106 }
107 // TODO: in PDI there is no such 'Scale' property on the Sensor.Value
108 // interface
109 auto findScale = propMap.find("Scale");
110 auto findMax = propMap.find(SensorValue::property_names::max_value);
111 auto findMin = propMap.find(SensorValue::property_names::min_value);
112
113 prop->min = 0;
114 prop->max = 0;
115 prop->scale = 0;
116 if (findScale != propMap.end())
117 {
118 prop->scale = std::get<int64_t>(findScale->second);
119 }
120 if (findMax != propMap.end())
121 {
122 prop->max = std::visit(VariantToDoubleVisitor(), findMax->second);
123 }
124 if (findMin != propMap.end())
125 {
126 prop->min = std::visit(VariantToDoubleVisitor(), findMin->second);
127 }
128
129 prop->value = std::visit(VariantToDoubleVisitor(),
130 propMap[SensorValue::property_names::value]);
131
132 bool available = true;
133 try
134 {
135 getProperty(service, path, StateDecoratorAvailability::interface,
136 StateDecoratorAvailability::property_names::available,
137 available);
138 }
139 catch (const sdbusplus::exception_t& ex)
140 {
141 // unsupported Available property, leaving reading at 'True'
142 }
143 prop->available = available;
144
145 return;
146 }
147
thresholdsAsserted(const std::string & service,const std::string & path)148 bool DbusHelper::thresholdsAsserted(const std::string& service,
149 const std::string& path)
150 {
151 auto critical = _bus.new_method_call(service.c_str(), path.c_str(),
152 propertiesintf, "GetAll");
153 critical.append(SensorThresholdCritical::interface);
154 PropertyMap criticalMap;
155
156 try
157 {
158 auto msg = _bus.call(critical);
159 msg.read(criticalMap);
160 }
161 catch (const sdbusplus::exception_t&)
162 {
163 // do nothing, sensors don't have to expose critical thresholds
164 #ifndef UNC_FAILSAFE
165 return false;
166 #endif
167 }
168
169 auto findCriticalLow = criticalMap.find(
170 SensorThresholdCritical::property_names::critical_alarm_low);
171 auto findCriticalHigh = criticalMap.find(
172 SensorThresholdCritical::property_names::critical_alarm_high);
173
174 bool asserted = false;
175 if (findCriticalLow != criticalMap.end())
176 {
177 asserted = std::get<bool>(findCriticalLow->second);
178 }
179
180 // as we are catching properties changed, a sensor could theoretically jump
181 // from one threshold to the other in one event, so check both thresholds
182 if (!asserted && findCriticalHigh != criticalMap.end())
183 {
184 asserted = std::get<bool>(findCriticalHigh->second);
185 }
186 #ifdef UNC_FAILSAFE
187 if (!asserted)
188 {
189 auto warning = _bus.new_method_call(service.c_str(), path.c_str(),
190 propertiesintf, "GetAll");
191 warning.append(warningThreshInf);
192 PropertyMap warningMap;
193
194 try
195 {
196 auto msg = _bus.call(warning);
197 msg.read(warningMap);
198 }
199 catch (const sdbusplus::exception_t&)
200 {
201 // sensors don't have to expose non-critical thresholds
202 return false;
203 }
204 auto findWarningHigh = warningMap.find(
205 SensorThresholdWarning::property_names::warning_alarm_high);
206
207 if (findWarningHigh != warningMap.end())
208 {
209 asserted = std::get<bool>(findWarningHigh->second);
210 }
211 }
212 #endif
213 return asserted;
214 }
215
216 } // namespace pid_control
217