blob: b3cc1043124b56a3ecc60524b738be1ba12f8e86 [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 Feist0d4f2bd2019-03-05 13:15:40 -080050std::unique_ptr<boost::asio::deadline_timer> initCmdTimer;
51
James Feist6ef20402019-01-07 16:45:08 -080052IpmbSensor::IpmbSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
53 boost::asio::io_service& io,
54 const std::string& sensorName,
55 const std::string& sensorConfiguration,
56 sdbusplus::asio::object_server& objectServer,
57 std::vector<thresholds::Threshold>&& thresholdData,
58 uint8_t deviceAddress) :
59 Sensor(boost::replace_all_copy(sensorName, " ", "_"),
James Feist930fcde2019-05-28 12:58:43 -070060 std::move(thresholdData), sensorConfiguration,
61 "xyz.openbmc_project.Configuration.ExitAirTemp", ipmbMaxReading,
62 ipmbMinReading),
James Feist6ef20402019-01-07 16:45:08 -080063 objectServer(objectServer), dbusConnection(conn), waitTimer(io),
James Feist52497fd2019-06-07 13:01:33 -070064 deviceAddress(deviceAddress), readState(PowerState::on)
James Feist6ef20402019-01-07 16:45:08 -080065{
66 sensorInterface = objectServer.add_interface(
67 "/xyz/openbmc_project/sensors/temperature/" + name,
68 "xyz.openbmc_project.Sensor.Value");
69
70 if (thresholds::hasWarningInterface(thresholds))
71 {
72 thresholdInterfaceWarning = objectServer.add_interface(
73 "/xyz/openbmc_project/sensors/temperature/" + name,
74 "xyz.openbmc_project.Sensor.Threshold.Warning");
75 }
76 if (thresholds::hasCriticalInterface(thresholds))
77 {
78 thresholdInterfaceCritical = objectServer.add_interface(
79 "/xyz/openbmc_project/sensors/temperature/" + name,
80 "xyz.openbmc_project.Sensor.Threshold.Critical");
81 }
James Feist078f2322019-03-08 11:09:05 -080082 association = objectServer.add_interface(
83 "/xyz/openbmc_project/sensors/temperature/" + name,
84 "org.openbmc.Associations");
James Feist6ef20402019-01-07 16:45:08 -080085 setupPowerMatch(conn);
86}
87
88IpmbSensor::~IpmbSensor()
89{
90 waitTimer.cancel();
91 objectServer.remove_interface(thresholdInterfaceWarning);
92 objectServer.remove_interface(thresholdInterfaceCritical);
93 objectServer.remove_interface(sensorInterface);
James Feist078f2322019-03-08 11:09:05 -080094 objectServer.remove_interface(association);
James Feist6ef20402019-01-07 16:45:08 -080095}
96
97void IpmbSensor::init(void)
98{
99 setInitialProperties(dbusConnection);
100 loadDefaults();
101 if (initCommand)
102 {
James Feistf7e2c5d2019-02-13 17:27:51 -0800103 runInitCmd();
104 }
105 read();
106}
107
108void IpmbSensor::runInitCmd()
109{
110 if (initCommand)
111 {
James Feist6ef20402019-01-07 16:45:08 -0800112 dbusConnection->async_method_call(
113 [this](boost::system::error_code ec,
114 const IpmbMethodType& response) {
115 const int& status = std::get<0>(response);
116
117 if (ec || status)
118 {
119 std::cerr
120 << "Error setting init command for device: " << name
121 << "\n";
122 }
James Feist6ef20402019-01-07 16:45:08 -0800123 },
124 "xyz.openbmc_project.Ipmi.Channel.Ipmb",
125 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
126 "sendRequest", commandAddress, netfn, lun, *initCommand, initData);
127 }
James Feist6ef20402019-01-07 16:45:08 -0800128}
129
130void IpmbSensor::loadDefaults()
131{
132 if (type == IpmbType::meSensor)
133 {
134 commandAddress = meAddress;
135 netfn = 0x4; // sensor
136 command = 0x2d; // get sensor reading
137 commandData = {deviceAddress};
138 }
139 else if (type == IpmbType::PXE1410CVR)
140 {
141 commandAddress = meAddress;
142 netfn = 0x2e; // me bridge
143 command = 0xd9; // send raw pmbus
144 initCommand = 0xd9; // send raw pmbus
145 commandData = {0x57, 0x01, 0x00, 0x16, 0x03, deviceAddress, 00,
146 0x00, 0x00, 0x00, 0x01, 0x02, 0x29};
147 initData = {0x57, 0x01, 0x00, 0x14, 0x03, deviceAddress, 0x00,
148 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x60};
149 }
150 else if (type == IpmbType::IR38363VR)
151 {
152 commandAddress = meAddress;
153 netfn = 0x2e; // me bridge
154 command = 0xd9; // send raw pmbus
155 commandData = {0x57, 0x01, 0x00, 0x16, 0x03, deviceAddress, 00,
156 0x00, 0x00, 0x00, 0x01, 0x02, 0x8D};
157 }
158 else if (type == IpmbType::mpsVR)
159 {
160 commandAddress = meAddress;
161 netfn = 0x2e; // me bridge
162 command = 0xd9; // send raw pmbus
163 initCommand = 0xd9; // send raw pmbus
164 commandData = {0x57, 0x01, 0x00, 0x16, 0x3, deviceAddress, 0x00,
165 0x00, 0x00, 0x00, 0x01, 0x02, 0x8d};
166 initData = {0x57, 0x01, 0x00, 0x14, 0x03, deviceAddress, 0x00,
167 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
168 }
169 else
170 {
171 throw std::runtime_error("Invalid sensor type");
172 }
173}
174
175void IpmbSensor::checkThresholds(void)
176{
177 if (readState == PowerState::on && !isPowerOn())
178 {
179 return;
180 }
James Feistfc94b212019-02-06 16:14:51 -0800181 else if (readState == PowerState::biosPost && !hasBiosPost())
182 {
183 return;
184 }
James Feist6ef20402019-01-07 16:45:08 -0800185 thresholds::checkThresholds(this);
186}
187
188void IpmbSensor::read(void)
189{
190 static constexpr size_t pollTime = 1; // in seconds
191
192 waitTimer.expires_from_now(boost::posix_time::seconds(pollTime));
193 waitTimer.async_wait([this](const boost::system::error_code& ec) {
194 if (ec == boost::asio::error::operation_aborted)
195 {
196 return; // we're being canceled
197 }
James Feist52497fd2019-06-07 13:01:33 -0700198 if (!isPowerOn() && readState != PowerState::always)
James Feist6ef20402019-01-07 16:45:08 -0800199 {
200 updateValue(0);
201 read();
202 return;
203 }
204 dbusConnection->async_method_call(
205 [this](boost::system::error_code ec,
206 const IpmbMethodType& response) {
207 const int& status = std::get<0>(response);
James Feist0d4f2bd2019-03-05 13:15:40 -0800208 static bool firstError = true; // don't print too much
James Feist6ef20402019-01-07 16:45:08 -0800209 if (ec || status)
210 {
James Feist0d4f2bd2019-03-05 13:15:40 -0800211 if (firstError)
212 {
213 std::cerr << "Error reading from device: " << name
214 << "\n";
215 firstError = false;
216 }
James Feist6ef20402019-01-07 16:45:08 -0800217 updateValue(0);
218 read();
219 return;
220 }
James Feist52497fd2019-06-07 13:01:33 -0700221 if (!isPowerOn() && readState != PowerState::always)
James Feist6ef20402019-01-07 16:45:08 -0800222 {
223 updateValue(0);
224 read();
225 return;
226 }
227 const std::vector<uint8_t>& data = std::get<5>(response);
228 if constexpr (debug)
229 {
230 std::cout << name << ": ";
231 for (size_t d : data)
232 {
233 std::cout << d << " ";
234 }
235 std::cout << "\n";
236 }
237 uint16_t value = 0;
238 if (type == IpmbType::meSensor)
239 {
240 if (data.empty())
241 {
James Feist0d4f2bd2019-03-05 13:15:40 -0800242 if (firstError)
243 {
244 std::cerr << "Invalid data from device: " << name
245 << "\n";
246 firstError = false;
247 }
James Feist6ef20402019-01-07 16:45:08 -0800248 read();
249 return;
250 }
251 value = data[0];
252 }
253 else if (type == IpmbType::PXE1410CVR ||
254 type == IpmbType::IR38363VR)
255 {
256 if (data.size() < 4)
257 {
James Feist0d4f2bd2019-03-05 13:15:40 -0800258 if (firstError)
259 {
260 std::cerr << "Invalid data from device: " << name
261 << "\n";
262 firstError = false;
263 }
James Feist6ef20402019-01-07 16:45:08 -0800264 read();
265 return;
266 }
267 // format based on the 11 bit linear data format
268 value = ((data[4] << 8) | data[3]) >> 3;
269 }
270 else if (type == IpmbType::mpsVR)
271 {
272 if (data.size() < 4)
273 {
James Feist0d4f2bd2019-03-05 13:15:40 -0800274 if (firstError)
275 {
276 std::cerr << "Invalid data from device: " << name
277 << "\n";
278 firstError = false;
279 }
James Feist6ef20402019-01-07 16:45:08 -0800280 read();
281 return;
282 }
283 value = data[3];
284 }
285 else
286 {
287 throw std::runtime_error("Invalid sensor type");
288 }
289 updateValue(value);
290 read();
James Feist0d4f2bd2019-03-05 13:15:40 -0800291 firstError = true; // success
James Feist6ef20402019-01-07 16:45:08 -0800292 },
293 "xyz.openbmc_project.Ipmi.Channel.Ipmb",
294 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
295 "sendRequest", commandAddress, netfn, lun, command, commandData);
296 });
297}
298void createSensors(
299 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
300 boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>&
301 sensors,
302 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
303{
304 if (!dbusConnection)
305 {
306 std::cerr << "Connection not created\n";
307 return;
308 }
309 dbusConnection->async_method_call(
310 [&](boost::system::error_code ec, const ManagedObjectType& resp) {
311 if (ec)
312 {
313 std::cerr << "Error contacting entity manager\n";
314 return;
315 }
316 for (const auto& pathPair : resp)
317 {
318 for (const auto& entry : pathPair.second)
319 {
320 if (entry.first != configInterface)
321 {
322 continue;
323 }
324 std::string name =
325 loadVariant<std::string>(entry.second, "Name");
326
327 std::vector<thresholds::Threshold> sensorThresholds;
328 if (!parseThresholdsFromConfig(pathPair.second,
329 sensorThresholds))
330 {
331 std::cerr << "error populating thresholds for " << name
332 << "\n";
333 }
334 uint8_t deviceAddress =
335 loadVariant<uint8_t>(entry.second, "Address");
336
337 std::string sensorClass =
338 loadVariant<std::string>(entry.second, "Class");
339 auto& sensor = sensors[name];
340 sensor = std::make_unique<IpmbSensor>(
341 dbusConnection, io, name, pathPair.first, objectServer,
342 std::move(sensorThresholds), deviceAddress);
343
James Feistfc94b212019-02-06 16:14:51 -0800344 auto findPowerState = entry.second.find("PowerState");
345
346 if (findPowerState != entry.second.end())
347 {
348 std::string powerState = std::visit(
349 VariantToStringVisitor(), findPowerState->second);
350
351 setReadState(powerState, sensor->readState);
352 }
353
James Feist6ef20402019-01-07 16:45:08 -0800354 if (sensorClass == "PxeBridgeTemp")
355 {
356 sensor->type = IpmbType::PXE1410CVR;
357 }
358 else if (sensorClass == "IRBridgeTemp")
359 {
360 sensor->type = IpmbType::IR38363VR;
361 }
362 else if (sensorClass == "MpsBridgeTemp")
363 {
364 sensor->type = IpmbType::mpsVR;
365 }
366 else if (sensorClass == "METemp")
367 {
368 sensor->type = IpmbType::meSensor;
369 }
370 else
371 {
372 std::cerr << "Invalid class " << sensorClass << "\n";
373 continue;
374 }
375 sensor->init();
376 }
377 }
378 },
379 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
380 "GetManagedObjects");
381}
382
James Feistf7e2c5d2019-02-13 17:27:51 -0800383void reinitSensors(sdbusplus::message::message& message)
384{
James Feist0d4f2bd2019-03-05 13:15:40 -0800385 constexpr const size_t reinitWaitSeconds = 2;
James Feistf7e2c5d2019-02-13 17:27:51 -0800386 std::string objectName;
James Feist52497fd2019-06-07 13:01:33 -0700387 boost::container::flat_map<std::string, std::variant<std::string>> values;
James Feistf7e2c5d2019-02-13 17:27:51 -0800388 message.read(objectName, values);
James Feist0d4f2bd2019-03-05 13:15:40 -0800389
James Feist52497fd2019-06-07 13:01:33 -0700390 auto findStatus = values.find(power::property);
391 if (findStatus != values.end())
James Feistf7e2c5d2019-02-13 17:27:51 -0800392 {
James Feist52497fd2019-06-07 13:01:33 -0700393 bool powerStatus = boost::ends_with(
394 std::get<std::string>(findStatus->second), "Running");
James Feistf7e2c5d2019-02-13 17:27:51 -0800395 if (powerStatus)
396 {
James Feist0d4f2bd2019-03-05 13:15:40 -0800397 if (!initCmdTimer)
James Feistf7e2c5d2019-02-13 17:27:51 -0800398 {
James Feist0d4f2bd2019-03-05 13:15:40 -0800399 // this should be impossible
400 return;
James Feistf7e2c5d2019-02-13 17:27:51 -0800401 }
James Feist0d4f2bd2019-03-05 13:15:40 -0800402 // we seem to send this command too fast sometimes, wait before
403 // sending
404 initCmdTimer->expires_from_now(
405 boost::posix_time::seconds(reinitWaitSeconds));
406
407 initCmdTimer->async_wait([](const boost::system::error_code ec) {
408 if (ec == boost::asio::error::operation_aborted)
409 {
410 return; // we're being canceled
411 }
412
413 for (const auto& sensor : sensors)
414 {
415 if (sensor.second)
416 {
417 sensor.second->runInitCmd();
418 }
419 }
420 });
James Feistf7e2c5d2019-02-13 17:27:51 -0800421 }
422 }
423}
424
James Feistb6c0b912019-07-09 12:21:44 -0700425int main()
James Feist6ef20402019-01-07 16:45:08 -0800426{
427
428 boost::asio::io_service io;
429 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
430 systemBus->request_name("xyz.openbmc_project.IpmbSensor");
431 sdbusplus::asio::object_server objectServer(systemBus);
James Feist6ef20402019-01-07 16:45:08 -0800432
James Feist0d4f2bd2019-03-05 13:15:40 -0800433 initCmdTimer = std::make_unique<boost::asio::deadline_timer>(io);
434
James Feist6ef20402019-01-07 16:45:08 -0800435 io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
436
437 boost::asio::deadline_timer configTimer(io);
438
439 std::function<void(sdbusplus::message::message&)> eventHandler =
James Feistb6c0b912019-07-09 12:21:44 -0700440 [&](sdbusplus::message::message&) {
James Feist6ef20402019-01-07 16:45:08 -0800441 configTimer.expires_from_now(boost::posix_time::seconds(1));
442 // create a timer because normally multiple properties change
443 configTimer.async_wait([&](const boost::system::error_code& ec) {
444 if (ec == boost::asio::error::operation_aborted)
445 {
446 return; // we're being canceled
447 }
448 createSensors(io, objectServer, sensors, systemBus);
449 if (sensors.empty())
450 {
451 std::cout << "Configuration not detected\n";
452 }
453 });
454 };
455
James Feistf7e2c5d2019-02-13 17:27:51 -0800456 sdbusplus::bus::match::match configMatch(
James Feist6ef20402019-01-07 16:45:08 -0800457 static_cast<sdbusplus::bus::bus&>(*systemBus),
458 "type='signal',member='PropertiesChanged',path_namespace='" +
459 std::string(inventoryPath) + "',arg0namespace='" + configInterface +
460 "'",
461 eventHandler);
462
James Feistf7e2c5d2019-02-13 17:27:51 -0800463 sdbusplus::bus::match::match powerChangeMatch(
464 static_cast<sdbusplus::bus::bus&>(*systemBus),
James Feist52497fd2019-06-07 13:01:33 -0700465 "type='signal',interface='" + std::string(properties::interface) +
466 "',path='" + std::string(power::path) + "',arg0='" +
467 std::string(power::interface) + "'",
James Feistf7e2c5d2019-02-13 17:27:51 -0800468 reinitSensors);
469
James Feist6ef20402019-01-07 16:45:08 -0800470 io.run();
471}