blob: e72f1af612e3e531c0c52f7699f1e265c90cbd43 [file] [log] [blame]
James Feist6ef20402019-01-07 16:45:08 -08001/*
2// Copyright (c) 2019 Intel 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 "IpmbSensor.hpp"
18
19#include "Utils.hpp"
20#include "VariantVisitors.hpp"
21
22#include <math.h>
23
24#include <boost/algorithm/string.hpp>
25#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/replace.hpp>
27#include <chrono>
28#include <iostream>
29#include <limits>
30#include <numeric>
31#include <sdbusplus/asio/connection.hpp>
32#include <sdbusplus/asio/object_server.hpp>
33#include <vector>
34
35constexpr const bool debug = false;
36
37constexpr const char* configInterface =
38 "xyz.openbmc_project.Configuration.IpmbSensor";
39static constexpr double ipmbMaxReading = 0xFF;
40static constexpr double ipmbMinReading = 0;
41
42static constexpr uint8_t meAddress = 1;
43static constexpr uint8_t lun = 0;
44
45using IpmbMethodType =
46 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
47
James Feistf7e2c5d2019-02-13 17:27:51 -080048boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>> sensors;
49
James Feist6ef20402019-01-07 16:45:08 -080050IpmbSensor::IpmbSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
51 boost::asio::io_service& io,
52 const std::string& sensorName,
53 const std::string& sensorConfiguration,
54 sdbusplus::asio::object_server& objectServer,
55 std::vector<thresholds::Threshold>&& thresholdData,
56 uint8_t deviceAddress) :
57 Sensor(boost::replace_all_copy(sensorName, " ", "_"),
58 "" /* todo: remove arg from base*/, std::move(thresholdData),
59 sensorConfiguration, "xyz.openbmc_project.Configuration.ExitAirTemp",
60 ipmbMaxReading, ipmbMinReading),
61 objectServer(objectServer), dbusConnection(conn), waitTimer(io),
62 deviceAddress(deviceAddress)
63{
64 sensorInterface = objectServer.add_interface(
65 "/xyz/openbmc_project/sensors/temperature/" + name,
66 "xyz.openbmc_project.Sensor.Value");
67
68 if (thresholds::hasWarningInterface(thresholds))
69 {
70 thresholdInterfaceWarning = objectServer.add_interface(
71 "/xyz/openbmc_project/sensors/temperature/" + name,
72 "xyz.openbmc_project.Sensor.Threshold.Warning");
73 }
74 if (thresholds::hasCriticalInterface(thresholds))
75 {
76 thresholdInterfaceCritical = objectServer.add_interface(
77 "/xyz/openbmc_project/sensors/temperature/" + name,
78 "xyz.openbmc_project.Sensor.Threshold.Critical");
79 }
80 setupPowerMatch(conn);
81}
82
83IpmbSensor::~IpmbSensor()
84{
85 waitTimer.cancel();
86 objectServer.remove_interface(thresholdInterfaceWarning);
87 objectServer.remove_interface(thresholdInterfaceCritical);
88 objectServer.remove_interface(sensorInterface);
89}
90
91void IpmbSensor::init(void)
92{
93 setInitialProperties(dbusConnection);
94 loadDefaults();
95 if (initCommand)
96 {
James Feistf7e2c5d2019-02-13 17:27:51 -080097 runInitCmd();
98 }
99 read();
100}
101
102void IpmbSensor::runInitCmd()
103{
104 if (initCommand)
105 {
James Feist6ef20402019-01-07 16:45:08 -0800106 dbusConnection->async_method_call(
107 [this](boost::system::error_code ec,
108 const IpmbMethodType& response) {
109 const int& status = std::get<0>(response);
110
111 if (ec || status)
112 {
113 std::cerr
114 << "Error setting init command for device: " << name
115 << "\n";
116 }
117 read();
118 },
119 "xyz.openbmc_project.Ipmi.Channel.Ipmb",
120 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
121 "sendRequest", commandAddress, netfn, lun, *initCommand, initData);
122 }
James Feist6ef20402019-01-07 16:45:08 -0800123}
124
125void IpmbSensor::loadDefaults()
126{
127 if (type == IpmbType::meSensor)
128 {
129 commandAddress = meAddress;
130 netfn = 0x4; // sensor
131 command = 0x2d; // get sensor reading
132 commandData = {deviceAddress};
133 }
134 else if (type == IpmbType::PXE1410CVR)
135 {
136 commandAddress = meAddress;
137 netfn = 0x2e; // me bridge
138 command = 0xd9; // send raw pmbus
139 initCommand = 0xd9; // send raw pmbus
140 commandData = {0x57, 0x01, 0x00, 0x16, 0x03, deviceAddress, 00,
141 0x00, 0x00, 0x00, 0x01, 0x02, 0x29};
142 initData = {0x57, 0x01, 0x00, 0x14, 0x03, deviceAddress, 0x00,
143 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x60};
144 }
145 else if (type == IpmbType::IR38363VR)
146 {
147 commandAddress = meAddress;
148 netfn = 0x2e; // me bridge
149 command = 0xd9; // send raw pmbus
150 commandData = {0x57, 0x01, 0x00, 0x16, 0x03, deviceAddress, 00,
151 0x00, 0x00, 0x00, 0x01, 0x02, 0x8D};
152 }
153 else if (type == IpmbType::mpsVR)
154 {
155 commandAddress = meAddress;
156 netfn = 0x2e; // me bridge
157 command = 0xd9; // send raw pmbus
158 initCommand = 0xd9; // send raw pmbus
159 commandData = {0x57, 0x01, 0x00, 0x16, 0x3, deviceAddress, 0x00,
160 0x00, 0x00, 0x00, 0x01, 0x02, 0x8d};
161 initData = {0x57, 0x01, 0x00, 0x14, 0x03, deviceAddress, 0x00,
162 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
163 }
164 else
165 {
166 throw std::runtime_error("Invalid sensor type");
167 }
168}
169
170void IpmbSensor::checkThresholds(void)
171{
172 if (readState == PowerState::on && !isPowerOn())
173 {
174 return;
175 }
James Feistfc94b212019-02-06 16:14:51 -0800176 else if (readState == PowerState::biosPost && !hasBiosPost())
177 {
178 return;
179 }
James Feist6ef20402019-01-07 16:45:08 -0800180 thresholds::checkThresholds(this);
181}
182
183void IpmbSensor::read(void)
184{
185 static constexpr size_t pollTime = 1; // in seconds
186
187 waitTimer.expires_from_now(boost::posix_time::seconds(pollTime));
188 waitTimer.async_wait([this](const boost::system::error_code& ec) {
189 if (ec == boost::asio::error::operation_aborted)
190 {
191 return; // we're being canceled
192 }
193 if (!isPowerOn() && readState == PowerState::on)
194 {
195 updateValue(0);
196 read();
197 return;
198 }
199 dbusConnection->async_method_call(
200 [this](boost::system::error_code ec,
201 const IpmbMethodType& response) {
202 const int& status = std::get<0>(response);
203 if (ec || status)
204 {
205 std::cerr << "Error reading from device: " << name << "\n";
206 updateValue(0);
207 read();
208 return;
209 }
210 if (!isPowerOn() && readState == PowerState::on)
211 {
212 updateValue(0);
213 read();
214 return;
215 }
216 const std::vector<uint8_t>& data = std::get<5>(response);
217 if constexpr (debug)
218 {
219 std::cout << name << ": ";
220 for (size_t d : data)
221 {
222 std::cout << d << " ";
223 }
224 std::cout << "\n";
225 }
226 uint16_t value = 0;
227 if (type == IpmbType::meSensor)
228 {
229 if (data.empty())
230 {
231 std::cerr << "Invalid data from device: " << name
232 << "\n";
233 read();
234 return;
235 }
236 value = data[0];
237 }
238 else if (type == IpmbType::PXE1410CVR ||
239 type == IpmbType::IR38363VR)
240 {
241 if (data.size() < 4)
242 {
243 std::cerr << "Invalid data from device: " << name
244 << "\n";
245 read();
246 return;
247 }
248 // format based on the 11 bit linear data format
249 value = ((data[4] << 8) | data[3]) >> 3;
250 }
251 else if (type == IpmbType::mpsVR)
252 {
253 if (data.size() < 4)
254 {
255 std::cerr << "Invalid data from device: " << name
256 << "\n";
257 read();
258 return;
259 }
260 value = data[3];
261 }
262 else
263 {
264 throw std::runtime_error("Invalid sensor type");
265 }
266 updateValue(value);
267 read();
268 },
269 "xyz.openbmc_project.Ipmi.Channel.Ipmb",
270 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
271 "sendRequest", commandAddress, netfn, lun, command, commandData);
272 });
273}
274void createSensors(
275 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
276 boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>&
277 sensors,
278 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
279{
280 if (!dbusConnection)
281 {
282 std::cerr << "Connection not created\n";
283 return;
284 }
285 dbusConnection->async_method_call(
286 [&](boost::system::error_code ec, const ManagedObjectType& resp) {
287 if (ec)
288 {
289 std::cerr << "Error contacting entity manager\n";
290 return;
291 }
292 for (const auto& pathPair : resp)
293 {
294 for (const auto& entry : pathPair.second)
295 {
296 if (entry.first != configInterface)
297 {
298 continue;
299 }
300 std::string name =
301 loadVariant<std::string>(entry.second, "Name");
302
303 std::vector<thresholds::Threshold> sensorThresholds;
304 if (!parseThresholdsFromConfig(pathPair.second,
305 sensorThresholds))
306 {
307 std::cerr << "error populating thresholds for " << name
308 << "\n";
309 }
310 uint8_t deviceAddress =
311 loadVariant<uint8_t>(entry.second, "Address");
312
313 std::string sensorClass =
314 loadVariant<std::string>(entry.second, "Class");
315 auto& sensor = sensors[name];
316 sensor = std::make_unique<IpmbSensor>(
317 dbusConnection, io, name, pathPair.first, objectServer,
318 std::move(sensorThresholds), deviceAddress);
319
James Feistfc94b212019-02-06 16:14:51 -0800320 auto findPowerState = entry.second.find("PowerState");
321
322 if (findPowerState != entry.second.end())
323 {
324 std::string powerState = std::visit(
325 VariantToStringVisitor(), findPowerState->second);
326
327 setReadState(powerState, sensor->readState);
328 }
329
James Feist6ef20402019-01-07 16:45:08 -0800330 if (sensorClass == "PxeBridgeTemp")
331 {
332 sensor->type = IpmbType::PXE1410CVR;
333 }
334 else if (sensorClass == "IRBridgeTemp")
335 {
336 sensor->type = IpmbType::IR38363VR;
337 }
338 else if (sensorClass == "MpsBridgeTemp")
339 {
340 sensor->type = IpmbType::mpsVR;
341 }
342 else if (sensorClass == "METemp")
343 {
344 sensor->type = IpmbType::meSensor;
345 }
346 else
347 {
348 std::cerr << "Invalid class " << sensorClass << "\n";
349 continue;
350 }
351 sensor->init();
352 }
353 }
354 },
355 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
356 "GetManagedObjects");
357}
358
James Feistf7e2c5d2019-02-13 17:27:51 -0800359void reinitSensors(sdbusplus::message::message& message)
360{
361
362 std::string objectName;
363 boost::container::flat_map<std::string, std::variant<int32_t>> values;
364 message.read(objectName, values);
365 auto findPgood = values.find("pgood");
366 if (findPgood != values.end())
367 {
368 int32_t powerStatus = std::get<int32_t>(findPgood->second);
369 if (powerStatus)
370 {
371 for (auto& sensor : sensors)
372 {
373 if (sensor.second)
374 {
375 sensor.second->runInitCmd();
376 }
377 }
378 }
379 }
380}
381
James Feist6ef20402019-01-07 16:45:08 -0800382int main(int argc, char** argv)
383{
384
385 boost::asio::io_service io;
386 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
387 systemBus->request_name("xyz.openbmc_project.IpmbSensor");
388 sdbusplus::asio::object_server objectServer(systemBus);
James Feist6ef20402019-01-07 16:45:08 -0800389
390 io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
391
392 boost::asio::deadline_timer configTimer(io);
393
394 std::function<void(sdbusplus::message::message&)> eventHandler =
395 [&](sdbusplus::message::message& message) {
396 configTimer.expires_from_now(boost::posix_time::seconds(1));
397 // create a timer because normally multiple properties change
398 configTimer.async_wait([&](const boost::system::error_code& ec) {
399 if (ec == boost::asio::error::operation_aborted)
400 {
401 return; // we're being canceled
402 }
403 createSensors(io, objectServer, sensors, systemBus);
404 if (sensors.empty())
405 {
406 std::cout << "Configuration not detected\n";
407 }
408 });
409 };
410
James Feistf7e2c5d2019-02-13 17:27:51 -0800411 sdbusplus::bus::match::match configMatch(
James Feist6ef20402019-01-07 16:45:08 -0800412 static_cast<sdbusplus::bus::bus&>(*systemBus),
413 "type='signal',member='PropertiesChanged',path_namespace='" +
414 std::string(inventoryPath) + "',arg0namespace='" + configInterface +
415 "'",
416 eventHandler);
417
James Feistf7e2c5d2019-02-13 17:27:51 -0800418 sdbusplus::bus::match::match powerChangeMatch(
419 static_cast<sdbusplus::bus::bus&>(*systemBus),
420 "type='signal',interface='org.freedesktop.DBus.Properties',path_"
421 "namespace='/xyz/openbmc_project/Chassis/Control/"
422 "Power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
423 reinitSensors);
424
James Feist6ef20402019-01-07 16:45:08 -0800425 io.run();
426}