blob: 1f072990886771275238db95b379d25b31d0f8c2 [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
2// Copyright (c) 2017 2018 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 "dbus-sdr/sensorcommands.hpp"
18
19#include "dbus-sdr/sdrutils.hpp"
20#include "dbus-sdr/sensorutils.hpp"
21#include "dbus-sdr/storagecommands.hpp"
22
23#include <algorithm>
24#include <array>
25#include <boost/algorithm/string.hpp>
26#include <boost/container/flat_map.hpp>
27#include <chrono>
28#include <cmath>
29#include <cstring>
30#include <iostream>
31#include <ipmid/api.hpp>
32#include <ipmid/types.hpp>
33#include <ipmid/utils.hpp>
34#include <map>
35#include <memory>
36#include <optional>
37#include <phosphor-logging/log.hpp>
38#include <sdbusplus/bus.hpp>
39#include <stdexcept>
40#include <string>
41#include <utility>
42#include <variant>
43
44namespace ipmi
45{
Hao Jiangd48c9212021-02-03 15:45:06 -080046
47using phosphor::logging::entry;
48using phosphor::logging::level;
49using phosphor::logging::log;
50
Willy Tude54f482021-01-26 15:59:09 -080051static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070052static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080053
Willy Tu38e7a2b2021-03-29 15:09:56 -070054// BMC I2C address is generally at 0x20
55static constexpr uint8_t bmcI2CAddr = 0x20;
56
Willy Tude54f482021-01-26 15:59:09 -080057constexpr size_t maxSDRTotalSize =
58 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
59constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
60
61static uint16_t sdrReservationID;
62static uint32_t sdrLastAdd = noTimestamp;
63static uint32_t sdrLastRemove = noTimestamp;
64static constexpr size_t lastRecordIndex = 0xFFFF;
65static constexpr int GENERAL_ERROR = -1;
66
Willy Tude54f482021-01-26 15:59:09 -080067static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
68
69// Specify the comparison required to sort and find char* map objects
70struct CmpStr
71{
72 bool operator()(const char* a, const char* b) const
73 {
74 return std::strcmp(a, b) < 0;
75 }
76};
77const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
78 sensorUnits{{{"temperature", SensorUnits::degreesC},
79 {"voltage", SensorUnits::volts},
80 {"current", SensorUnits::amps},
81 {"fan_tach", SensorUnits::rpm},
82 {"power", SensorUnits::watts}}};
83
84void registerSensorFunctions() __attribute__((constructor));
85
86static sdbusplus::bus::match::match sensorAdded(
87 *getSdBus(),
88 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
89 "sensors/'",
90 [](sdbusplus::message::message& m) {
91 getSensorTree().clear();
92 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
93 std::chrono::system_clock::now().time_since_epoch())
94 .count();
95 });
96
97static sdbusplus::bus::match::match sensorRemoved(
98 *getSdBus(),
99 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
100 "sensors/'",
101 [](sdbusplus::message::message& m) {
102 getSensorTree().clear();
103 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
104 std::chrono::system_clock::now().time_since_epoch())
105 .count();
106 });
107
108// this keeps track of deassertions for sensor event status command. A
109// deasertion can only happen if an assertion was seen first.
110static boost::container::flat_map<
111 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
112 thresholdDeassertMap;
113
114static sdbusplus::bus::match::match thresholdChanged(
115 *getSdBus(),
116 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
117 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
118 [](sdbusplus::message::message& m) {
119 boost::container::flat_map<std::string, std::variant<bool, double>>
120 values;
121 m.read(std::string(), values);
122
123 auto findAssert =
124 std::find_if(values.begin(), values.end(), [](const auto& pair) {
125 return pair.first.find("Alarm") != std::string::npos;
126 });
127 if (findAssert != values.end())
128 {
129 auto ptr = std::get_if<bool>(&(findAssert->second));
130 if (ptr == nullptr)
131 {
132 phosphor::logging::log<phosphor::logging::level::ERR>(
133 "thresholdChanged: Assert non bool");
134 return;
135 }
136 if (*ptr)
137 {
138 phosphor::logging::log<phosphor::logging::level::INFO>(
139 "thresholdChanged: Assert",
140 phosphor::logging::entry("SENSOR=%s", m.get_path()));
141 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
142 }
143 else
144 {
145 auto& value =
146 thresholdDeassertMap[m.get_path()][findAssert->first];
147 if (value)
148 {
149 phosphor::logging::log<phosphor::logging::level::INFO>(
150 "thresholdChanged: deassert",
151 phosphor::logging::entry("SENSOR=%s", m.get_path()));
152 value = *ptr;
153 }
154 }
155 }
156 });
157
Hao Jiangd2afd052020-12-10 15:09:32 -0800158namespace sensor
159{
160static constexpr const char* vrInterface =
161 "xyz.openbmc_project.Control.VoltageRegulatorMode";
162static constexpr const char* sensorInterface =
163 "xyz.openbmc_project.Sensor.Value";
164} // namespace sensor
165
Willy Tude54f482021-01-26 15:59:09 -0800166static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
167 double& min)
168{
169 max = 127;
170 min = -128;
171
Hao Jiangd2afd052020-12-10 15:09:32 -0800172 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800173 auto critical =
174 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
175 auto warning =
176 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
177
178 if (sensorObject != sensorMap.end())
179 {
180 auto maxMap = sensorObject->second.find("MaxValue");
181 auto minMap = sensorObject->second.find("MinValue");
182
183 if (maxMap != sensorObject->second.end())
184 {
185 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
186 }
187 if (minMap != sensorObject->second.end())
188 {
189 min = std::visit(VariantToDoubleVisitor(), minMap->second);
190 }
191 }
192 if (critical != sensorMap.end())
193 {
194 auto lower = critical->second.find("CriticalLow");
195 auto upper = critical->second.find("CriticalHigh");
196 if (lower != critical->second.end())
197 {
198 double value = std::visit(VariantToDoubleVisitor(), lower->second);
199 min = std::min(value, min);
200 }
201 if (upper != critical->second.end())
202 {
203 double value = std::visit(VariantToDoubleVisitor(), upper->second);
204 max = std::max(value, max);
205 }
206 }
207 if (warning != sensorMap.end())
208 {
209
210 auto lower = warning->second.find("WarningLow");
211 auto upper = warning->second.find("WarningHigh");
212 if (lower != warning->second.end())
213 {
214 double value = std::visit(VariantToDoubleVisitor(), lower->second);
215 min = std::min(value, min);
216 }
217 if (upper != warning->second.end())
218 {
219 double value = std::visit(VariantToDoubleVisitor(), upper->second);
220 max = std::max(value, max);
221 }
222 }
223}
224
225static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700226 std::string sensorPath, DbusInterfaceMap& sensorMap,
227 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800228{
229 static boost::container::flat_map<
230 std::string, std::chrono::time_point<std::chrono::steady_clock>>
231 updateTimeMap;
232
233 auto updateFind = updateTimeMap.find(sensorConnection);
234 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
235 if (updateFind != updateTimeMap.end())
236 {
237 lastUpdate = updateFind->second;
238 }
239
240 auto now = std::chrono::steady_clock::now();
241
242 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700243 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800244 {
Willy Tude54f482021-01-26 15:59:09 -0800245 ObjectValueTree managedObjects;
246 boost::system::error_code ec = getManagedObjects(
247 ctx, sensorConnection.c_str(), "/", managedObjects);
248 if (ec)
249 {
250 phosphor::logging::log<phosphor::logging::level::ERR>(
251 "GetMangagedObjects for getSensorMap failed",
252 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
253
254 return false;
255 }
256
257 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700258 // Update time after finish building the map which allow the
259 // data to be cached for updatePeriod plus the build time.
260 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800261 }
262 auto connection = SensorCache.find(sensorConnection);
263 if (connection == SensorCache.end())
264 {
265 return false;
266 }
267 auto path = connection->second.find(sensorPath);
268 if (path == connection->second.end())
269 {
270 return false;
271 }
272 sensorMap = path->second;
273
274 return true;
275}
276
Hao Jiangd2afd052020-12-10 15:09:32 -0800277namespace sensor
278{
Hao Jiangd48c9212021-02-03 15:45:06 -0800279// Read VR profiles from sensor(daemon) interface
280static std::optional<std::vector<std::string>>
281 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800282{
283 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800284 auto supportedProperty = object.find("Supported");
285 if (supportedProperty == object.end() ||
286 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800287 {
288 phosphor::logging::log<phosphor::logging::level::ERR>(
289 "Missing the required Supported and Selected properties");
290 return std::nullopt;
291 }
292
293 const auto profilesPtr =
294 std::get_if<std::vector<std::string>>(&supportedProperty->second);
295
296 if (profilesPtr == nullptr)
297 {
298 phosphor::logging::log<phosphor::logging::level::ERR>(
299 "property is not array of string");
300 return std::nullopt;
301 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800302 return *profilesPtr;
303}
304
305// Calculate VR Mode from input IPMI discrete event bytes
306static std::optional<std::string>
307 calculateVRMode(uint15_t assertOffset,
308 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
309{
310 // get VR mode profiles from Supported Interface
311 auto profiles = getSupportedVrProfiles(VRObject);
312 if (!profiles)
313 {
314 return std::nullopt;
315 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800316
317 // interpret IPMI cmd bits into profiles' index
318 long unsigned int index = 0;
319 // only one bit should be set and the highest bit should not be used.
320 if (assertOffset == 0 || assertOffset == (1u << 15) ||
321 (assertOffset & (assertOffset - 1)))
322 {
323 phosphor::logging::log<phosphor::logging::level::ERR>(
324 "IPMI cmd format incorrect",
325
326 phosphor::logging::entry("BYTES=%#02x",
327 static_cast<uint16_t>(assertOffset)));
328 return std::nullopt;
329 }
330
331 while (assertOffset != 1)
332 {
333 assertOffset >>= 1;
334 index++;
335 }
336
Hao Jiangd48c9212021-02-03 15:45:06 -0800337 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800338 {
339 phosphor::logging::log<phosphor::logging::level::ERR>(
340 "profile index out of boundary");
341 return std::nullopt;
342 }
343
Hao Jiangd48c9212021-02-03 15:45:06 -0800344 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800345}
346
347// Calculate sensor value from IPMI reading byte
348static std::optional<double>
349 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
350 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
351{
352 if (valueObject.find("Value") == valueObject.end())
353 {
354 phosphor::logging::log<phosphor::logging::level::ERR>(
355 "Missing the required Value property");
356 return std::nullopt;
357 }
358
359 double max = 0;
360 double min = 0;
361 getSensorMaxMin(sensorMap, max, min);
362
363 int16_t mValue = 0;
364 int16_t bValue = 0;
365 int8_t rExp = 0;
366 int8_t bExp = 0;
367 bool bSigned = false;
368
369 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
370 {
371 return std::nullopt;
372 }
373
374 double value = bSigned ? ((int8_t)reading) : reading;
375
376 value *= ((double)mValue);
377 value += ((double)bValue) * std::pow(10.0, bExp);
378 value *= std::pow(10.0, rExp);
379
380 return value;
381}
382
Willy Tu38e7a2b2021-03-29 15:09:56 -0700383// Extract file name from sensor path as the sensors SDR ID. Simplify the name
384// if it is too long.
385std::string parseSdrIdFromPath(const std::string& path)
386{
387 std::string name;
388 size_t nameStart = path.rfind("/");
389 if (nameStart != std::string::npos)
390 {
391 name = path.substr(nameStart + 1, std::string::npos - nameStart);
392 }
393
394 std::replace(name.begin(), name.end(), '_', ' ');
395 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
396 {
397 // try to not truncate by replacing common words
398 constexpr std::array<std::pair<const char*, const char*>, 2>
399 replaceWords = {std::make_pair("Output", "Out"),
400 std::make_pair("Input", "In")};
401 for (const auto& [find, replace] : replaceWords)
402 {
403 boost::replace_all(name, find, replace);
404 }
405
406 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
407 }
408 return name;
409}
410
Hao Jiangd48c9212021-02-03 15:45:06 -0800411bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
412 const std::string& path,
413 const ipmi::DbusInterfaceMap::mapped_type& object,
414 std::bitset<16>& assertions)
415{
416 auto profiles = sensor::getSupportedVrProfiles(object);
417 if (!profiles)
418 {
419 return false;
420 }
421 ipmi::Value modeVariant;
422
423 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
424 "Selected", modeVariant);
425 if (ec)
426 {
427 log<level::ERR>("Failed to get property",
428 entry("PROPERTY=%s", "Selected"),
429 entry("PATH=%s", path.c_str()),
430 entry("INTERFACE=%s", sensor::sensorInterface),
431 entry("WHAT=%s", ec.message().c_str()));
432 return false;
433 }
434
435 auto mode = std::get_if<std::string>(&modeVariant);
436 if (mode == nullptr)
437 {
438 log<level::ERR>("property is not a string",
439 entry("PROPERTY=%s", "Selected"),
440 entry("PATH=%s", path.c_str()),
441 entry("INTERFACE=%s", sensor::sensorInterface));
442 return false;
443 }
444
445 auto itr = std::find(profiles->begin(), profiles->end(), *mode);
446 if (itr == profiles->end())
447 {
448 using namespace phosphor::logging;
449 log<level::ERR>("VR mode doesn't match any of its profiles",
450 entry("PATH=%s", path.c_str()));
451 return false;
452 }
453 std::size_t index =
454 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
455
456 // map index to reponse event assertion bit.
457 if (index < 8)
458 {
459 assertions.set(1u << index);
460 }
461 else if (index < 15)
462 {
463 assertions.set(1u << (index - 8));
464 }
465 else
466 {
467 log<level::ERR>("VR profile index reaches max assertion bit",
468 entry("PATH=%s", path.c_str()),
469 entry("INDEX=%uz", index));
470 return false;
471 }
472 if constexpr (debug)
473 {
474 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
475 << " mode is: [" << index << "] " << *mode << std::endl;
476 }
477 return true;
478}
Hao Jiangd2afd052020-12-10 15:09:32 -0800479} // namespace sensor
480
Willy Tude54f482021-01-26 15:59:09 -0800481ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev,
482 uint8_t sensorType, uint8_t sensorNum,
483 uint8_t eventType, uint8_t eventData1,
484 std::optional<uint8_t> eventData2,
485 std::optional<uint8_t> eventData3)
486{
487 return ipmi::responseSuccess();
488}
489
Willy Tudbafbce2021-03-29 00:37:05 -0700490ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
491 uint8_t sensorNumber, uint8_t operation,
492 uint8_t reading, uint15_t assertOffset,
493 bool resvd1, uint15_t deassertOffset,
494 bool resvd2, uint8_t eventData1,
495 uint8_t eventData2, uint8_t eventData3)
496{
497 std::string connection;
498 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700499 std::vector<std::string> interfaces;
500
501 ipmi::Cc status =
502 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700503 if (status)
504 {
505 return ipmi::response(status);
506 }
507
Hao Jiangd2afd052020-12-10 15:09:32 -0800508 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700509 if (std::find(interfaces.begin(), interfaces.end(),
510 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700511 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700512 DbusInterfaceMap sensorMap;
513 if (!getSensorMap(ctx, connection, path, sensorMap))
514 {
515 return ipmi::responseResponseError();
516 }
517 auto sensorObject = sensorMap.find(sensor::sensorInterface);
518 if (sensorObject != sensorMap.end())
519 {
520 return ipmi::responseResponseError();
521 }
522
Hao Jiangd2afd052020-12-10 15:09:32 -0800523 auto value =
524 sensor::calculateValue(reading, sensorMap, sensorObject->second);
525 if (!value)
526 {
527 return ipmi::responseResponseError();
528 }
529
530 if constexpr (debug)
531 {
532 phosphor::logging::log<phosphor::logging::level::INFO>(
533 "IPMI SET_SENSOR",
534 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
535 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
536 phosphor::logging::entry("VALUE=%f", *value));
537 }
538
539 boost::system::error_code ec =
540 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
541 "Value", ipmi::Value(*value));
542
543 // setDbusProperty intended to resolve dbus exception/rc within the
544 // function but failed to achieve that. Catch SdBusError in the ipmi
545 // callback functions for now (e.g. ipmiSetSensorReading).
546 if (ec)
547 {
548 using namespace phosphor::logging;
549 log<level::ERR>("Failed to set property",
550 entry("PROPERTY=%s", "Value"),
551 entry("PATH=%s", path.c_str()),
552 entry("INTERFACE=%s", sensor::sensorInterface),
553 entry("WHAT=%s", ec.message().c_str()));
554 return ipmi::responseResponseError();
555 }
556 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700557 }
558
Hao Jiange39d4d82021-04-16 17:02:40 -0700559 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
560 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700561 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700562 DbusInterfaceMap sensorMap;
563 if (!getSensorMap(ctx, connection, path, sensorMap))
564 {
565 return ipmi::responseResponseError();
566 }
567 auto sensorObject = sensorMap.find(sensor::vrInterface);
568 if (sensorObject != sensorMap.end())
569 {
570 return ipmi::responseResponseError();
571 }
572
Hao Jiangd2afd052020-12-10 15:09:32 -0800573 // VR sensors are treated as a special case and we will not check the
574 // write permission for VR sensors, since they always deemed writable
575 // and permission table are not applied to VR sensors.
576 auto vrMode =
577 sensor::calculateVRMode(assertOffset, sensorObject->second);
578 if (!vrMode)
579 {
580 return ipmi::responseResponseError();
581 }
582 boost::system::error_code ec = setDbusProperty(
583 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
584 // setDbusProperty intended to resolve dbus exception/rc within the
585 // function but failed to achieve that. Catch SdBusError in the ipmi
586 // callback functions for now (e.g. ipmiSetSensorReading).
587 if (ec)
588 {
589 using namespace phosphor::logging;
590 log<level::ERR>("Failed to set property",
591 entry("PROPERTY=%s", "Selected"),
592 entry("PATH=%s", path.c_str()),
593 entry("INTERFACE=%s", sensor::sensorInterface),
594 entry("WHAT=%s", ec.message().c_str()));
595 return ipmi::responseResponseError();
596 }
597 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700598 }
599
Hao Jiangd2afd052020-12-10 15:09:32 -0800600 phosphor::logging::log<phosphor::logging::level::ERR>(
601 "unknown sensor type",
602 phosphor::logging::entry("PATH=%s", path.c_str()));
603 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700604}
605
Willy Tude54f482021-01-26 15:59:09 -0800606ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
607 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
608{
609 std::string connection;
610 std::string path;
611
612 auto status = getSensorConnection(ctx, sensnum, connection, path);
613 if (status)
614 {
615 return ipmi::response(status);
616 }
617
618 DbusInterfaceMap sensorMap;
619 if (!getSensorMap(ctx, connection, path, sensorMap))
620 {
621 return ipmi::responseResponseError();
622 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800623 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800624
625 if (sensorObject == sensorMap.end() ||
626 sensorObject->second.find("Value") == sensorObject->second.end())
627 {
628 return ipmi::responseResponseError();
629 }
630 auto& valueVariant = sensorObject->second["Value"];
631 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
632
633 double max = 0;
634 double min = 0;
635 getSensorMaxMin(sensorMap, max, min);
636
637 int16_t mValue = 0;
638 int16_t bValue = 0;
639 int8_t rExp = 0;
640 int8_t bExp = 0;
641 bool bSigned = false;
642
643 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
644 {
645 return ipmi::responseResponseError();
646 }
647
648 uint8_t value =
649 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
650 uint8_t operation =
651 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
652 operation |=
653 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
654 bool notReading = std::isnan(reading);
655
656 if (!notReading)
657 {
658 auto availableObject =
659 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
660 if (availableObject != sensorMap.end())
661 {
662 auto findAvailable = availableObject->second.find("Available");
663 if (findAvailable != availableObject->second.end())
664 {
665 bool* available = std::get_if<bool>(&(findAvailable->second));
666 if (available && !(*available))
667 {
668 notReading = true;
669 }
670 }
671 }
672 }
673
674 if (notReading)
675 {
676 operation |= static_cast<uint8_t>(
677 IPMISensorReadingByte2::readingStateUnavailable);
678 }
679
Josh Lehana55c9532020-10-28 21:59:06 -0700680 if constexpr (details::enableInstrumentation)
681 {
682 int byteValue;
683 if (bSigned)
684 {
685 byteValue = static_cast<int>(static_cast<int8_t>(value));
686 }
687 else
688 {
689 byteValue = static_cast<int>(static_cast<uint8_t>(value));
690 }
691
692 // Keep stats on the reading just obtained, even if it is "NaN"
693 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
694 {
695 // This is the first reading, show the coefficients
696 double step = (max - min) / 255.0;
697 std::cerr << "IPMI sensor "
698 << details::sdrStatsTable.getName(sensnum)
699 << ": Range min=" << min << " max=" << max
700 << ", step=" << step
701 << ", Coefficients mValue=" << static_cast<int>(mValue)
702 << " rExp=" << static_cast<int>(rExp)
703 << " bValue=" << static_cast<int>(bValue)
704 << " bExp=" << static_cast<int>(bExp)
705 << " bSigned=" << static_cast<int>(bSigned) << "\n";
706 }
707 }
708
Willy Tude54f482021-01-26 15:59:09 -0800709 uint8_t thresholds = 0;
710
711 auto warningObject =
712 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
713 if (warningObject != sensorMap.end())
714 {
715 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
716 auto alarmLow = warningObject->second.find("WarningAlarmLow");
717 if (alarmHigh != warningObject->second.end())
718 {
719 if (std::get<bool>(alarmHigh->second))
720 {
721 thresholds |= static_cast<uint8_t>(
722 IPMISensorReadingByte3::upperNonCritical);
723 }
724 }
725 if (alarmLow != warningObject->second.end())
726 {
727 if (std::get<bool>(alarmLow->second))
728 {
729 thresholds |= static_cast<uint8_t>(
730 IPMISensorReadingByte3::lowerNonCritical);
731 }
732 }
733 }
734
735 auto criticalObject =
736 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
737 if (criticalObject != sensorMap.end())
738 {
739 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
740 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
741 if (alarmHigh != criticalObject->second.end())
742 {
743 if (std::get<bool>(alarmHigh->second))
744 {
745 thresholds |=
746 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
747 }
748 }
749 if (alarmLow != criticalObject->second.end())
750 {
751 if (std::get<bool>(alarmLow->second))
752 {
753 thresholds |=
754 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
755 }
756 }
757 }
758
759 // no discrete as of today so optional byte is never returned
760 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
761}
762
763/** @brief implements the Set Sensor threshold command
764 * @param sensorNumber - sensor number
765 * @param lowerNonCriticalThreshMask
766 * @param lowerCriticalThreshMask
767 * @param lowerNonRecovThreshMask
768 * @param upperNonCriticalThreshMask
769 * @param upperCriticalThreshMask
770 * @param upperNonRecovThreshMask
771 * @param reserved
772 * @param lowerNonCritical - lower non-critical threshold
773 * @param lowerCritical - Lower critical threshold
774 * @param lowerNonRecoverable - Lower non recovarable threshold
775 * @param upperNonCritical - Upper non-critical threshold
776 * @param upperCritical - Upper critical
777 * @param upperNonRecoverable - Upper Non-recoverable
778 *
779 * @returns IPMI completion code
780 */
781ipmi::RspType<> ipmiSenSetSensorThresholds(
782 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
783 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
784 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
785 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
786 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
787 uint8_t upperNonCritical, uint8_t upperCritical,
788 uint8_t upperNonRecoverable)
789{
790 if (reserved)
791 {
792 return ipmi::responseInvalidFieldRequest();
793 }
794
795 // lower nc and upper nc not suppported on any sensor
796 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
797 {
798 return ipmi::responseInvalidFieldRequest();
799 }
800
801 // if none of the threshold mask are set, nothing to do
802 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
803 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
804 upperCriticalThreshMask | upperNonRecovThreshMask))
805 {
806 return ipmi::responseSuccess();
807 }
808
809 std::string connection;
810 std::string path;
811
812 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
813 if (status)
814 {
815 return ipmi::response(status);
816 }
817 DbusInterfaceMap sensorMap;
818 if (!getSensorMap(ctx, connection, path, sensorMap))
819 {
820 return ipmi::responseResponseError();
821 }
822
823 double max = 0;
824 double min = 0;
825 getSensorMaxMin(sensorMap, max, min);
826
827 int16_t mValue = 0;
828 int16_t bValue = 0;
829 int8_t rExp = 0;
830 int8_t bExp = 0;
831 bool bSigned = false;
832
833 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
834 {
835 return ipmi::responseResponseError();
836 }
837
838 // store a vector of property name, value to set, and interface
839 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
840
841 // define the indexes of the tuple
842 constexpr uint8_t propertyName = 0;
843 constexpr uint8_t thresholdValue = 1;
844 constexpr uint8_t interface = 2;
845 // verifiy all needed fields are present
846 if (lowerCriticalThreshMask || upperCriticalThreshMask)
847 {
848 auto findThreshold =
849 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
850 if (findThreshold == sensorMap.end())
851 {
852 return ipmi::responseInvalidFieldRequest();
853 }
854 if (lowerCriticalThreshMask)
855 {
856 auto findLower = findThreshold->second.find("CriticalLow");
857 if (findLower == findThreshold->second.end())
858 {
859 return ipmi::responseInvalidFieldRequest();
860 }
861 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
862 findThreshold->first);
863 }
864 if (upperCriticalThreshMask)
865 {
866 auto findUpper = findThreshold->second.find("CriticalHigh");
867 if (findUpper == findThreshold->second.end())
868 {
869 return ipmi::responseInvalidFieldRequest();
870 }
871 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
872 findThreshold->first);
873 }
874 }
875 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
876 {
877 auto findThreshold =
878 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
879 if (findThreshold == sensorMap.end())
880 {
881 return ipmi::responseInvalidFieldRequest();
882 }
883 if (lowerNonCriticalThreshMask)
884 {
885 auto findLower = findThreshold->second.find("WarningLow");
886 if (findLower == findThreshold->second.end())
887 {
888 return ipmi::responseInvalidFieldRequest();
889 }
890 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
891 findThreshold->first);
892 }
893 if (upperNonCriticalThreshMask)
894 {
895 auto findUpper = findThreshold->second.find("WarningHigh");
896 if (findUpper == findThreshold->second.end())
897 {
898 return ipmi::responseInvalidFieldRequest();
899 }
900 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
901 findThreshold->first);
902 }
903 }
904 for (const auto& property : thresholdsToSet)
905 {
906 // from section 36.3 in the IPMI Spec, assume all linear
907 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
908 (bValue * std::pow(10.0, bExp))) *
909 std::pow(10.0, rExp);
910 setDbusProperty(
911 *getSdBus(), connection, path, std::get<interface>(property),
912 std::get<propertyName>(property), ipmi::Value(valueToSet));
913 }
914 return ipmi::responseSuccess();
915}
916
917IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
918{
919 IPMIThresholds resp;
920 auto warningInterface =
921 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
922 auto criticalInterface =
923 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
924
925 if ((warningInterface != sensorMap.end()) ||
926 (criticalInterface != sensorMap.end()))
927 {
Hao Jiangd2afd052020-12-10 15:09:32 -0800928 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800929
930 if (sensorPair == sensorMap.end())
931 {
932 // should not have been able to find a sensor not implementing
933 // the sensor object
934 throw std::runtime_error("Invalid sensor map");
935 }
936
937 double max = 0;
938 double min = 0;
939 getSensorMaxMin(sensorMap, max, min);
940
941 int16_t mValue = 0;
942 int16_t bValue = 0;
943 int8_t rExp = 0;
944 int8_t bExp = 0;
945 bool bSigned = false;
946
947 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
948 {
949 throw std::runtime_error("Invalid sensor atrributes");
950 }
951 if (warningInterface != sensorMap.end())
952 {
953 auto& warningMap = warningInterface->second;
954
955 auto warningHigh = warningMap.find("WarningHigh");
956 auto warningLow = warningMap.find("WarningLow");
957
958 if (warningHigh != warningMap.end())
959 {
960
961 double value =
962 std::visit(VariantToDoubleVisitor(), warningHigh->second);
963 resp.warningHigh = scaleIPMIValueFromDouble(
964 value, mValue, rExp, bValue, bExp, bSigned);
965 }
966 if (warningLow != warningMap.end())
967 {
968 double value =
969 std::visit(VariantToDoubleVisitor(), warningLow->second);
970 resp.warningLow = scaleIPMIValueFromDouble(
971 value, mValue, rExp, bValue, bExp, bSigned);
972 }
973 }
974 if (criticalInterface != sensorMap.end())
975 {
976 auto& criticalMap = criticalInterface->second;
977
978 auto criticalHigh = criticalMap.find("CriticalHigh");
979 auto criticalLow = criticalMap.find("CriticalLow");
980
981 if (criticalHigh != criticalMap.end())
982 {
983 double value =
984 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
985 resp.criticalHigh = scaleIPMIValueFromDouble(
986 value, mValue, rExp, bValue, bExp, bSigned);
987 }
988 if (criticalLow != criticalMap.end())
989 {
990 double value =
991 std::visit(VariantToDoubleVisitor(), criticalLow->second);
992 resp.criticalLow = scaleIPMIValueFromDouble(
993 value, mValue, rExp, bValue, bExp, bSigned);
994 }
995 }
996 }
997 return resp;
998}
999
1000ipmi::RspType<uint8_t, // readable
1001 uint8_t, // lowerNCrit
1002 uint8_t, // lowerCrit
1003 uint8_t, // lowerNrecoverable
1004 uint8_t, // upperNC
1005 uint8_t, // upperCrit
1006 uint8_t> // upperNRecoverable
1007 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1008{
1009 std::string connection;
1010 std::string path;
1011
1012 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1013 if (status)
1014 {
1015 return ipmi::response(status);
1016 }
1017
1018 DbusInterfaceMap sensorMap;
1019 if (!getSensorMap(ctx, connection, path, sensorMap))
1020 {
1021 return ipmi::responseResponseError();
1022 }
1023
1024 IPMIThresholds thresholdData;
1025 try
1026 {
1027 thresholdData = getIPMIThresholds(sensorMap);
1028 }
1029 catch (std::exception&)
1030 {
1031 return ipmi::responseResponseError();
1032 }
1033
1034 uint8_t readable = 0;
1035 uint8_t lowerNC = 0;
1036 uint8_t lowerCritical = 0;
1037 uint8_t lowerNonRecoverable = 0;
1038 uint8_t upperNC = 0;
1039 uint8_t upperCritical = 0;
1040 uint8_t upperNonRecoverable = 0;
1041
1042 if (thresholdData.warningHigh)
1043 {
1044 readable |=
1045 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1046 upperNC = *thresholdData.warningHigh;
1047 }
1048 if (thresholdData.warningLow)
1049 {
1050 readable |=
1051 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1052 lowerNC = *thresholdData.warningLow;
1053 }
1054
1055 if (thresholdData.criticalHigh)
1056 {
1057 readable |=
1058 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1059 upperCritical = *thresholdData.criticalHigh;
1060 }
1061 if (thresholdData.criticalLow)
1062 {
1063 readable |=
1064 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1065 lowerCritical = *thresholdData.criticalLow;
1066 }
1067
1068 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1069 lowerNonRecoverable, upperNC, upperCritical,
1070 upperNonRecoverable);
1071}
1072
1073/** @brief implements the get Sensor event enable command
1074 * @param sensorNumber - sensor number
1075 *
1076 * @returns IPMI completion code plus response data
1077 * - enabled - Sensor Event messages
1078 * - assertionEnabledLsb - Assertion event messages
1079 * - assertionEnabledMsb - Assertion event messages
1080 * - deassertionEnabledLsb - Deassertion event messages
1081 * - deassertionEnabledMsb - Deassertion event messages
1082 */
1083
1084ipmi::RspType<uint8_t, // enabled
1085 uint8_t, // assertionEnabledLsb
1086 uint8_t, // assertionEnabledMsb
1087 uint8_t, // deassertionEnabledLsb
1088 uint8_t> // deassertionEnabledMsb
1089 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1090{
1091 std::string connection;
1092 std::string path;
1093
1094 uint8_t enabled = 0;
1095 uint8_t assertionEnabledLsb = 0;
1096 uint8_t assertionEnabledMsb = 0;
1097 uint8_t deassertionEnabledLsb = 0;
1098 uint8_t deassertionEnabledMsb = 0;
1099
1100 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1101 if (status)
1102 {
1103 return ipmi::response(status);
1104 }
1105
1106 DbusInterfaceMap sensorMap;
1107 if (!getSensorMap(ctx, connection, path, sensorMap))
1108 {
1109 return ipmi::responseResponseError();
1110 }
1111
1112 auto warningInterface =
1113 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1114 auto criticalInterface =
1115 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1116 if ((warningInterface != sensorMap.end()) ||
1117 (criticalInterface != sensorMap.end()))
1118 {
1119 enabled = static_cast<uint8_t>(
1120 IPMISensorEventEnableByte2::sensorScanningEnable);
1121 if (warningInterface != sensorMap.end())
1122 {
1123 auto& warningMap = warningInterface->second;
1124
1125 auto warningHigh = warningMap.find("WarningHigh");
1126 auto warningLow = warningMap.find("WarningLow");
1127 if (warningHigh != warningMap.end())
1128 {
1129 assertionEnabledLsb |= static_cast<uint8_t>(
1130 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1131 deassertionEnabledLsb |= static_cast<uint8_t>(
1132 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1133 }
1134 if (warningLow != warningMap.end())
1135 {
1136 assertionEnabledLsb |= static_cast<uint8_t>(
1137 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1138 deassertionEnabledLsb |= static_cast<uint8_t>(
1139 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1140 }
1141 }
1142 if (criticalInterface != sensorMap.end())
1143 {
1144 auto& criticalMap = criticalInterface->second;
1145
1146 auto criticalHigh = criticalMap.find("CriticalHigh");
1147 auto criticalLow = criticalMap.find("CriticalLow");
1148
1149 if (criticalHigh != criticalMap.end())
1150 {
1151 assertionEnabledMsb |= static_cast<uint8_t>(
1152 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1153 deassertionEnabledMsb |= static_cast<uint8_t>(
1154 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1155 }
1156 if (criticalLow != criticalMap.end())
1157 {
1158 assertionEnabledLsb |= static_cast<uint8_t>(
1159 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1160 deassertionEnabledLsb |= static_cast<uint8_t>(
1161 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1162 }
1163 }
1164 }
1165
1166 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1167 assertionEnabledMsb, deassertionEnabledLsb,
1168 deassertionEnabledMsb);
1169}
1170
1171/** @brief implements the get Sensor event status command
1172 * @param sensorNumber - sensor number, FFh = reserved
1173 *
1174 * @returns IPMI completion code plus response data
1175 * - sensorEventStatus - Sensor Event messages state
1176 * - assertions - Assertion event messages
1177 * - deassertions - Deassertion event messages
1178 */
1179ipmi::RspType<uint8_t, // sensorEventStatus
1180 std::bitset<16>, // assertions
1181 std::bitset<16> // deassertion
1182 >
1183 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1184{
1185 if (sensorNum == reservedSensorNumber)
1186 {
1187 return ipmi::responseInvalidFieldRequest();
1188 }
1189
1190 std::string connection;
1191 std::string path;
1192 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1193 if (status)
1194 {
1195 phosphor::logging::log<phosphor::logging::level::ERR>(
1196 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1197 phosphor::logging::entry("SENSOR=%d", sensorNum));
1198 return ipmi::response(status);
1199 }
1200
1201 DbusInterfaceMap sensorMap;
1202 if (!getSensorMap(ctx, connection, path, sensorMap))
1203 {
1204 phosphor::logging::log<phosphor::logging::level::ERR>(
1205 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1206 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1207 return ipmi::responseResponseError();
1208 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001209
1210 uint8_t sensorEventStatus =
1211 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1212 std::bitset<16> assertions = 0;
1213 std::bitset<16> deassertions = 0;
1214
1215 // handle VR typed sensor
1216 auto vrInterface = sensorMap.find(sensor::vrInterface);
1217 if (vrInterface != sensorMap.end())
1218 {
1219 if (!sensor::getVrEventStatus(ctx, connection, path,
1220 vrInterface->second, assertions))
1221 {
1222 return ipmi::responseResponseError();
1223 }
1224
1225 // both Event Message and Sensor Scanning are disable for VR.
1226 sensorEventStatus = 0;
1227 return ipmi::responseSuccess(sensorEventStatus, assertions,
1228 deassertions);
1229 }
1230
Willy Tude54f482021-01-26 15:59:09 -08001231 auto warningInterface =
1232 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1233 auto criticalInterface =
1234 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1235
Willy Tude54f482021-01-26 15:59:09 -08001236 std::optional<bool> criticalDeassertHigh =
1237 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1238 std::optional<bool> criticalDeassertLow =
1239 thresholdDeassertMap[path]["CriticalAlarmLow"];
1240 std::optional<bool> warningDeassertHigh =
1241 thresholdDeassertMap[path]["WarningAlarmHigh"];
1242 std::optional<bool> warningDeassertLow =
1243 thresholdDeassertMap[path]["WarningAlarmLow"];
1244
Willy Tude54f482021-01-26 15:59:09 -08001245 if (criticalDeassertHigh && !*criticalDeassertHigh)
1246 {
1247 deassertions.set(static_cast<size_t>(
1248 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1249 }
1250 if (criticalDeassertLow && !*criticalDeassertLow)
1251 {
1252 deassertions.set(static_cast<size_t>(
1253 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1254 }
1255 if (warningDeassertHigh && !*warningDeassertHigh)
1256 {
1257 deassertions.set(static_cast<size_t>(
1258 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1259 }
1260 if (warningDeassertLow && !*warningDeassertLow)
1261 {
1262 deassertions.set(static_cast<size_t>(
1263 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1264 }
1265 if ((warningInterface != sensorMap.end()) ||
1266 (criticalInterface != sensorMap.end()))
1267 {
1268 sensorEventStatus = static_cast<size_t>(
1269 IPMISensorEventEnableByte2::eventMessagesEnable);
1270 if (warningInterface != sensorMap.end())
1271 {
1272 auto& warningMap = warningInterface->second;
1273
1274 auto warningHigh = warningMap.find("WarningAlarmHigh");
1275 auto warningLow = warningMap.find("WarningAlarmLow");
1276 auto warningHighAlarm = false;
1277 auto warningLowAlarm = false;
1278
1279 if (warningHigh != warningMap.end())
1280 {
1281 warningHighAlarm = std::get<bool>(warningHigh->second);
1282 }
1283 if (warningLow != warningMap.end())
1284 {
1285 warningLowAlarm = std::get<bool>(warningLow->second);
1286 }
1287 if (warningHighAlarm)
1288 {
1289 assertions.set(
1290 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1291 upperNonCriticalGoingHigh));
1292 }
1293 if (warningLowAlarm)
1294 {
1295 assertions.set(
1296 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1297 lowerNonCriticalGoingLow));
1298 }
1299 }
1300 if (criticalInterface != sensorMap.end())
1301 {
1302 auto& criticalMap = criticalInterface->second;
1303
1304 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1305 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1306 auto criticalHighAlarm = false;
1307 auto criticalLowAlarm = false;
1308
1309 if (criticalHigh != criticalMap.end())
1310 {
1311 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1312 }
1313 if (criticalLow != criticalMap.end())
1314 {
1315 criticalLowAlarm = std::get<bool>(criticalLow->second);
1316 }
1317 if (criticalHighAlarm)
1318 {
1319 assertions.set(
1320 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1321 upperCriticalGoingHigh));
1322 }
1323 if (criticalLowAlarm)
1324 {
1325 assertions.set(static_cast<size_t>(
1326 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1327 }
1328 }
1329 }
1330
1331 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1332}
1333
Willy Tu38e7a2b2021-03-29 15:09:56 -07001334// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001335void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1336 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001337{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001338 get_sdr::header::set_record_id(
1339 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1340
Willy Tu38e7a2b2021-03-29 15:09:56 -07001341 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1342 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1343
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001344 record.header.sdr_version = ipmiSdrVersion;
1345 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1346 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1347 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001348 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001349 record.key.owner_lun = lun;
1350 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001351}
1352bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1353 uint16_t recordID, const std::string& service,
1354 const std::string& path,
1355 get_sdr::SensorDataFullRecord& record)
1356{
1357 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1358 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1359
1360 DbusInterfaceMap sensorMap;
1361 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1362 {
1363 phosphor::logging::log<phosphor::logging::level::ERR>(
1364 "Failed to update sensor map for threshold sensor",
1365 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1366 phosphor::logging::entry("PATH=%s", path.c_str()));
1367 return false;
1368 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001369
1370 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1371 record.body.sensor_type = getSensorTypeFromPath(path);
1372 std::string type = getSensorTypeStringFromPath(path);
1373 auto typeCstr = type.c_str();
1374 auto findUnits = sensorUnits.find(typeCstr);
1375 if (findUnits != sensorUnits.end())
1376 {
1377 record.body.sensor_units_2_base =
1378 static_cast<uint8_t>(findUnits->second);
1379 } // else default 0x0 unspecified
1380
1381 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1382
Hao Jiangd2afd052020-12-10 15:09:32 -08001383 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001384 if (sensorObject == sensorMap.end())
1385 {
1386 phosphor::logging::log<phosphor::logging::level::ERR>(
1387 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001388 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001389 }
1390
1391 uint8_t entityId = 0;
1392 uint8_t entityInstance = 0x01;
1393
1394 // follow the association chain to get the parent board's entityid and
1395 // entityInstance
1396 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1397
1398 record.body.entity_id = entityId;
1399 record.body.entity_instance = entityInstance;
1400
1401 auto maxObject = sensorObject->second.find("MaxValue");
1402 auto minObject = sensorObject->second.find("MinValue");
1403
1404 // If min and/or max are left unpopulated,
1405 // then default to what a signed byte would be, namely (-128,127) range.
1406 auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1407 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1408 if (maxObject != sensorObject->second.end())
1409 {
1410 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1411 }
1412
1413 if (minObject != sensorObject->second.end())
1414 {
1415 min = std::visit(VariantToDoubleVisitor(), minObject->second);
1416 }
1417
1418 int16_t mValue = 0;
1419 int8_t rExp = 0;
1420 int16_t bValue = 0;
1421 int8_t bExp = 0;
1422 bool bSigned = false;
1423
1424 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1425 {
1426 phosphor::logging::log<phosphor::logging::level::ERR>(
1427 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001428 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001429 }
1430
1431 // The record.body is a struct SensorDataFullRecordBody
1432 // from sensorhandler.hpp in phosphor-ipmi-host.
1433 // The meaning of these bits appears to come from
1434 // table 43.1 of the IPMI spec.
1435 // The above 5 sensor attributes are stuffed in as follows:
1436 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1437 // Byte 22-24 are for other purposes
1438 // Byte 25 = MMMMMMMM = LSB of M
1439 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1440 // Byte 27 = BBBBBBBB = LSB of B
1441 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1442 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1443 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1444
1445 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1446 record.body.m_lsb = mValue & 0xFF;
1447
1448 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1449 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1450
1451 // move the smallest bit of the MSB into place (bit 9)
1452 // the MSbs are bits 7:8 in m_msb_and_tolerance
1453 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1454
1455 record.body.b_lsb = bValue & 0xFF;
1456
1457 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1458 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1459
1460 // move the smallest bit of the MSB into place (bit 9)
1461 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1462 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1463
1464 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1465 uint8_t rExpBits = rExp & 0x07;
1466
1467 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1468 uint8_t bExpBits = bExp & 0x07;
1469
1470 // move rExp and bExp into place
1471 record.body.r_b_exponents =
1472 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1473
1474 // Set the analog reading byte interpretation accordingly
1475 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1476
1477 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1478 // These seem redundant, but derivable from the above 5 attributes
1479 // Original comment said "todo fill out rest of units"
1480
1481 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001482 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001483 record.body.id_string_info = name.size();
1484 std::strncpy(record.body.id_string, name.c_str(),
1485 sizeof(record.body.id_string));
1486
Josh Lehana55c9532020-10-28 21:59:06 -07001487 // Remember the sensor name, as determined for this sensor number
1488 details::sdrStatsTable.updateName(sensornumber, name);
1489
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001490 IPMIThresholds thresholdData;
1491 try
1492 {
1493 thresholdData = getIPMIThresholds(sensorMap);
1494 }
1495 catch (std::exception&)
1496 {
1497 phosphor::logging::log<phosphor::logging::level::ERR>(
1498 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001499 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001500 }
1501
1502 if (thresholdData.criticalHigh)
1503 {
1504 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1505 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1506 IPMISensorEventEnableThresholds::criticalThreshold);
1507 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1508 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1509 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1510 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1511 record.body.discrete_reading_setting_mask[0] |=
1512 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1513 }
1514 if (thresholdData.warningHigh)
1515 {
1516 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1517 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1518 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1519 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1520 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1521 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1522 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1523 record.body.discrete_reading_setting_mask[0] |=
1524 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1525 }
1526 if (thresholdData.criticalLow)
1527 {
1528 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1529 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1530 IPMISensorEventEnableThresholds::criticalThreshold);
1531 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1532 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1533 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1534 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1535 record.body.discrete_reading_setting_mask[0] |=
1536 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1537 }
1538 if (thresholdData.warningLow)
1539 {
1540 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1541 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1542 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1543 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1544 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1545 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1546 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1547 record.body.discrete_reading_setting_mask[0] |=
1548 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1549 }
1550
1551 // everything that is readable is setable
1552 record.body.discrete_reading_setting_mask[1] =
1553 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001554 return true;
1555}
1556
Hao Jiange39d4d82021-04-16 17:02:40 -07001557// Construct type 3 SDR header and key (for VR and other discrete sensors)
1558void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1559 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001560{
1561 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1562 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1563
1564 get_sdr::header::set_record_id(
1565 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1566
1567 record.header.sdr_version = ipmiSdrVersion;
1568 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1569 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1570 sizeof(get_sdr::SensorDataRecordHeader);
1571 record.key.owner_id = bmcI2CAddr;
1572 record.key.owner_lun = lun;
1573 record.key.sensor_number = sensornumber;
1574
1575 record.body.entity_id = 0x00;
1576 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001577}
Willy Tu61992ad2021-03-29 15:33:20 -07001578
Hao Jiange39d4d82021-04-16 17:02:40 -07001579// Construct a type 3 SDR for VR typed sensor(daemon).
1580bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1581 uint16_t recordID, const std::string& service,
1582 const std::string& path,
1583 get_sdr::SensorDataEventRecord& record)
1584{
1585 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1586 constructEventSdrHeaderKey(sensorNum, recordID, record);
1587
1588 DbusInterfaceMap sensorMap;
1589 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1590 {
1591 phosphor::logging::log<phosphor::logging::level::ERR>(
1592 "Failed to update sensor map for VR sensor",
1593 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1594 phosphor::logging::entry("PATH=%s", path.c_str()));
1595 return false;
1596 }
Willy Tu61992ad2021-03-29 15:33:20 -07001597 // follow the association chain to get the parent board's entityid and
1598 // entityInstance
1599 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1600 record.body.entity_instance);
1601
1602 // Sensor type is hardcoded as a module/board type instead of parsing from
1603 // sensor path. This is because VR control is allocated in an independent
1604 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1605 // types.
1606 static constexpr const uint8_t module_board_type = 0x15;
1607 record.body.sensor_type = module_board_type;
1608 record.body.event_reading_type = 0x00;
1609
1610 record.body.sensor_record_sharing_1 = 0x00;
1611 record.body.sensor_record_sharing_2 = 0x00;
1612
1613 // populate sensor name from path
1614 auto name = sensor::parseSdrIdFromPath(path);
1615 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1616 record.body.id_string_info = nameSize;
1617 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1618 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1619
1620 // Remember the sensor name, as determined for this sensor number
1621 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001622
1623 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001624}
1625
Hao Jiange39d4d82021-04-16 17:02:40 -07001626static int
1627 getSensorDataRecord(ipmi::Context::ptr ctx,
1628 std::vector<uint8_t>& recordData, uint16_t recordID,
1629 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001630{
1631 size_t fruCount = 0;
1632 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1633 if (ret != ipmi::ccSuccess)
1634 {
1635 phosphor::logging::log<phosphor::logging::level::ERR>(
1636 "getSensorDataRecord: getFruSdrCount error");
1637 return GENERAL_ERROR;
1638 }
1639
1640 auto& sensorTree = getSensorTree();
1641 size_t lastRecord =
1642 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1643 if (recordID == lastRecordIndex)
1644 {
1645 recordID = lastRecord;
1646 }
1647 if (recordID > lastRecord)
1648 {
1649 phosphor::logging::log<phosphor::logging::level::ERR>(
1650 "getSensorDataRecord: recordID > lastRecord error");
1651 return GENERAL_ERROR;
1652 }
1653
1654 if (recordID >= sensorTree.size())
1655 {
1656 size_t fruIndex = recordID - sensorTree.size();
1657
1658 if (fruIndex >= fruCount)
1659 {
1660 // handle type 12 hardcoded records
1661 size_t type12Index = fruIndex - fruCount;
1662 if (type12Index >= ipmi::storage::type12Count)
1663 {
1664 phosphor::logging::log<phosphor::logging::level::ERR>(
1665 "getSensorDataRecord: type12Index error");
1666 return GENERAL_ERROR;
1667 }
1668 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1669 }
1670 else
1671 {
1672 // handle fru records
1673 get_sdr::SensorDataFruRecord data;
1674 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1675 if (ret != IPMI_CC_OK)
1676 {
1677 return GENERAL_ERROR;
1678 }
1679 data.header.record_id_msb = recordID >> 8;
1680 data.header.record_id_lsb = recordID & 0xFF;
1681 recordData.insert(recordData.end(), (uint8_t*)&data,
1682 ((uint8_t*)&data) + sizeof(data));
1683 }
1684
1685 return 0;
1686 }
1687
1688 std::string connection;
1689 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001690 std::vector<std::string> interfaces;
1691
1692 auto status =
1693 getSensorConnection(ctx, recordID, connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001694 if (status)
1695 {
1696 phosphor::logging::log<phosphor::logging::level::ERR>(
1697 "getSensorDataRecord: getSensorConnection error");
1698 return GENERAL_ERROR;
1699 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001700 uint16_t sensorNum = getSensorNumberFromPath(path);
1701 if (sensorNum == invalidSensorNumber)
1702 {
1703 phosphor::logging::log<phosphor::logging::level::ERR>(
1704 "getSensorDataRecord: invalidSensorNumber");
1705 return GENERAL_ERROR;
1706 }
1707
Willy Tu38e7a2b2021-03-29 15:09:56 -07001708 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07001709 if (std::find(interfaces.begin(), interfaces.end(),
1710 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001711 {
1712 get_sdr::SensorDataFullRecord record = {0};
1713
Hao Jiange39d4d82021-04-16 17:02:40 -07001714 // If the request doesn't read SDR body, construct only header and key
1715 // part to avoid additional DBus transaction.
1716 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1717 {
1718 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1719 }
1720 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
1721 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07001722 {
1723 return GENERAL_ERROR;
1724 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001725
Willy Tu38e7a2b2021-03-29 15:09:56 -07001726 recordData.insert(recordData.end(), (uint8_t*)&record,
1727 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07001728
1729 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001730 }
Willy Tu61992ad2021-03-29 15:33:20 -07001731
1732 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07001733 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
1734 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07001735 {
1736 get_sdr::SensorDataEventRecord record = {0};
1737
Hao Jiange39d4d82021-04-16 17:02:40 -07001738 // If the request doesn't read SDR body, construct only header and key
1739 // part to avoid additional DBus transaction.
1740 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1741 {
1742 constructEventSdrHeaderKey(sensorNum, recordID, record);
1743 }
1744 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
1745 record))
1746 {
1747 return GENERAL_ERROR;
1748 }
Willy Tu61992ad2021-03-29 15:33:20 -07001749 recordData.insert(recordData.end(), (uint8_t*)&record,
1750 ((uint8_t*)&record) + sizeof(record));
1751 }
1752
Willy Tude54f482021-01-26 15:59:09 -08001753 return 0;
1754}
1755
1756/** @brief implements the get SDR Info command
1757 * @param count - Operation
1758 *
1759 * @returns IPMI completion code plus response data
1760 * - sdrCount - sensor/SDR count
1761 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1762 */
1763static ipmi::RspType<uint8_t, // respcount
1764 uint8_t, // dynamic population flags
1765 uint32_t // last time a sensor was added
1766 >
1767 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1768 std::optional<uint8_t> count)
1769{
1770 auto& sensorTree = getSensorTree();
1771 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001772 uint16_t recordID = 0;
1773 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08001774 // Sensors are dynamically allocated, and there is at least one LUN
1775 uint8_t lunsAndDynamicPopulation = 0x80;
1776 constexpr uint8_t getSdrCount = 0x01;
1777 constexpr uint8_t getSensorCount = 0x00;
1778
1779 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1780 {
1781 return ipmi::responseResponseError();
1782 }
Willy Tude54f482021-01-26 15:59:09 -08001783 uint16_t numSensors = sensorTree.size();
1784 if (count.value_or(0) == getSdrCount)
1785 {
1786 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001787 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08001788 {
1789 get_sdr::SensorDataRecordHeader* hdr =
1790 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001791 record.data());
Willy Tude54f482021-01-26 15:59:09 -08001792 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1793 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001794 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08001795 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001796 record.data());
1797 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08001798 {
1799 sdrCount++;
1800 }
1801 }
1802 }
1803 }
1804 else if (count.value_or(0) == getSensorCount)
1805 {
1806 // Return the number of sensors attached to the LUN
1807 if ((ctx->lun == 0) && (numSensors > 0))
1808 {
1809 sdrCount =
1810 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1811 }
1812 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1813 {
1814 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1815 ? maxSensorsPerLUN
1816 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1817 }
1818 else if (ctx->lun == 3)
1819 {
1820 if (numSensors <= maxIPMISensors)
1821 {
1822 sdrCount =
1823 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1824 }
1825 else
1826 {
1827 // error
1828 throw std::out_of_range(
1829 "Maximum number of IPMI sensors exceeded.");
1830 }
1831 }
1832 }
1833 else
1834 {
1835 return ipmi::responseInvalidFieldRequest();
1836 }
1837
1838 // Get Sensor count. This returns the number of sensors
1839 if (numSensors > 0)
1840 {
1841 lunsAndDynamicPopulation |= 1;
1842 }
1843 if (numSensors > maxSensorsPerLUN)
1844 {
1845 lunsAndDynamicPopulation |= 2;
1846 }
1847 if (numSensors >= (maxSensorsPerLUN * 2))
1848 {
1849 lunsAndDynamicPopulation |= 8;
1850 }
1851 if (numSensors > maxIPMISensors)
1852 {
1853 // error
1854 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1855 }
1856
1857 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1858 sdrLastAdd);
1859}
1860
1861/* end sensor commands */
1862
1863/* storage commands */
1864
1865ipmi::RspType<uint8_t, // sdr version
1866 uint16_t, // record count
1867 uint16_t, // free space
1868 uint32_t, // most recent addition
1869 uint32_t, // most recent erase
1870 uint8_t // operationSupport
1871 >
1872 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
1873{
1874 auto& sensorTree = getSensorTree();
1875 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001876 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001877 {
1878 return ipmi::responseResponseError();
1879 }
1880
1881 size_t fruCount = 0;
1882 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1883 if (ret != ipmi::ccSuccess)
1884 {
1885 return ipmi::response(ret);
1886 }
1887
1888 uint16_t recordCount =
1889 sensorTree.size() + fruCount + ipmi::storage::type12Count;
1890
1891 uint8_t operationSupport = static_cast<uint8_t>(
1892 SdrRepositoryInfoOps::overflow); // write not supported
1893
1894 operationSupport |=
1895 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1896 operationSupport |= static_cast<uint8_t>(
1897 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1898 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1899 unspecifiedFreeSpace, sdrLastAdd,
1900 sdrLastRemove, operationSupport);
1901}
1902
1903/** @brief implements the get SDR allocation info command
1904 *
1905 * @returns IPMI completion code plus response data
1906 * - allocUnits - Number of possible allocation units
1907 * - allocUnitSize - Allocation unit size in bytes.
1908 * - allocUnitFree - Number of free allocation units
1909 * - allocUnitLargestFree - Largest free block in allocation units
1910 * - maxRecordSize - Maximum record size in allocation units.
1911 */
1912ipmi::RspType<uint16_t, // allocUnits
1913 uint16_t, // allocUnitSize
1914 uint16_t, // allocUnitFree
1915 uint16_t, // allocUnitLargestFree
1916 uint8_t // maxRecordSize
1917 >
1918 ipmiStorageGetSDRAllocationInfo()
1919{
1920 // 0000h unspecified number of alloc units
1921 constexpr uint16_t allocUnits = 0;
1922
1923 constexpr uint16_t allocUnitFree = 0;
1924 constexpr uint16_t allocUnitLargestFree = 0;
1925 // only allow one block at a time
1926 constexpr uint8_t maxRecordSize = 1;
1927
1928 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1929 allocUnitLargestFree, maxRecordSize);
1930}
1931
1932/** @brief implements the reserve SDR command
1933 * @returns IPMI completion code plus response data
1934 * - sdrReservationID
1935 */
1936ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
1937{
1938 sdrReservationID++;
1939 if (sdrReservationID == 0)
1940 {
1941 sdrReservationID++;
1942 }
1943
1944 return ipmi::responseSuccess(sdrReservationID);
1945}
1946
1947ipmi::RspType<uint16_t, // next record ID
1948 std::vector<uint8_t> // payload
1949 >
1950 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1951 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1952{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001953 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08001954 // reservation required for partial reads with non zero offset into
1955 // record
1956 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
1957 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001958 phosphor::logging::log<phosphor::logging::level::ERR>(
1959 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08001960 return ipmi::responseInvalidReservationId();
1961 }
Willy Tude54f482021-01-26 15:59:09 -08001962 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1963 if (ret != ipmi::ccSuccess)
1964 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001965 phosphor::logging::log<phosphor::logging::level::ERR>(
1966 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08001967 return ipmi::response(ret);
1968 }
1969
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001970 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08001971 size_t lastRecord =
1972 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001973 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1974
1975 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001976 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001977 phosphor::logging::log<phosphor::logging::level::ERR>(
1978 "ipmiStorageGetSDR: getSensorSubtree error");
1979 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08001980 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001981
1982 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07001983 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08001984 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001985 phosphor::logging::log<phosphor::logging::level::ERR>(
1986 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08001987 return ipmi::responseInvalidFieldRequest();
1988 }
Willy Tude54f482021-01-26 15:59:09 -08001989 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001990 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08001991 if (!hdr)
1992 {
1993 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001994 "ipmiStorageGetSDR: record header is null");
1995 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001996 }
1997
1998 size_t sdrLength =
1999 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2000 if (sdrLength < (offset + bytesToRead))
2001 {
2002 bytesToRead = sdrLength - offset;
2003 }
2004
2005 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2006 if (!respStart)
2007 {
2008 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002009 "ipmiStorageGetSDR: record is null");
2010 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002011 }
2012
2013 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002014
Willy Tude54f482021-01-26 15:59:09 -08002015 return ipmi::responseSuccess(nextRecordId, recordData);
2016}
2017/* end storage commands */
2018
2019void registerSensorFunctions()
2020{
2021 // <Platform Event>
2022 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2023 ipmi::sensor_event::cmdPlatformEvent,
2024 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2025
Willy Tudbafbce2021-03-29 00:37:05 -07002026#ifdef FEATURE_DYNAMIC_SENSORS_WRITE
2027 // <Set Sensor Reading and Event Status>
2028 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2029 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2030 ipmi::Privilege::Operator, ipmiSetSensorReading);
2031#endif
2032
Willy Tude54f482021-01-26 15:59:09 -08002033 // <Get Sensor Reading>
2034 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2035 ipmi::sensor_event::cmdGetSensorReading,
2036 ipmi::Privilege::User, ipmiSenGetSensorReading);
2037
2038 // <Get Sensor Threshold>
2039 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2040 ipmi::sensor_event::cmdGetSensorThreshold,
2041 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2042
2043 // <Set Sensor Threshold>
2044 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2045 ipmi::sensor_event::cmdSetSensorThreshold,
2046 ipmi::Privilege::Operator,
2047 ipmiSenSetSensorThresholds);
2048
2049 // <Get Sensor Event Enable>
2050 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2051 ipmi::sensor_event::cmdGetSensorEventEnable,
2052 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2053
2054 // <Get Sensor Event Status>
2055 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2056 ipmi::sensor_event::cmdGetSensorEventStatus,
2057 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2058
2059 // register all storage commands for both Sensor and Storage command
2060 // versions
2061
2062 // <Get SDR Repository Info>
2063 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2064 ipmi::storage::cmdGetSdrRepositoryInfo,
2065 ipmi::Privilege::User,
2066 ipmiStorageGetSDRRepositoryInfo);
2067
2068 // <Get Device SDR Info>
2069 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2070 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2071 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2072
2073 // <Get SDR Allocation Info>
2074 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2075 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2076 ipmi::Privilege::User,
2077 ipmiStorageGetSDRAllocationInfo);
2078
2079 // <Reserve SDR Repo>
2080 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2081 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2082 ipmi::Privilege::User, ipmiStorageReserveSDR);
2083
2084 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2085 ipmi::storage::cmdReserveSdrRepository,
2086 ipmi::Privilege::User, ipmiStorageReserveSDR);
2087
2088 // <Get Sdr>
2089 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2090 ipmi::sensor_event::cmdGetDeviceSdr,
2091 ipmi::Privilege::User, ipmiStorageGetSDR);
2092
2093 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2094 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2095 ipmiStorageGetSDR);
2096}
2097} // namespace ipmi