blob: 0e513c5333f609995225442e0598b29e8bf44565 [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;
499 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path);
500 if (status)
501 {
502 return ipmi::response(status);
503 }
504
505 DbusInterfaceMap sensorMap;
506 if (!getSensorMap(ctx, connection, path, sensorMap))
507 {
508 return ipmi::responseResponseError();
509 }
Willy Tudbafbce2021-03-29 00:37:05 -0700510
Hao Jiangd2afd052020-12-10 15:09:32 -0800511 // we can tell the sensor type by its interface type
512 auto sensorObject = sensorMap.find(sensor::sensorInterface);
513 if (sensorObject != sensorMap.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700514 {
Hao Jiangd2afd052020-12-10 15:09:32 -0800515 auto value =
516 sensor::calculateValue(reading, sensorMap, sensorObject->second);
517 if (!value)
518 {
519 return ipmi::responseResponseError();
520 }
521
522 if constexpr (debug)
523 {
524 phosphor::logging::log<phosphor::logging::level::INFO>(
525 "IPMI SET_SENSOR",
526 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
527 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
528 phosphor::logging::entry("VALUE=%f", *value));
529 }
530
531 boost::system::error_code ec =
532 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
533 "Value", ipmi::Value(*value));
534
535 // setDbusProperty intended to resolve dbus exception/rc within the
536 // function but failed to achieve that. Catch SdBusError in the ipmi
537 // callback functions for now (e.g. ipmiSetSensorReading).
538 if (ec)
539 {
540 using namespace phosphor::logging;
541 log<level::ERR>("Failed to set property",
542 entry("PROPERTY=%s", "Value"),
543 entry("PATH=%s", path.c_str()),
544 entry("INTERFACE=%s", sensor::sensorInterface),
545 entry("WHAT=%s", ec.message().c_str()));
546 return ipmi::responseResponseError();
547 }
548 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700549 }
550
Hao Jiangd2afd052020-12-10 15:09:32 -0800551 sensorObject = sensorMap.find(sensor::vrInterface);
552 if (sensorObject != sensorMap.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700553 {
Hao Jiangd2afd052020-12-10 15:09:32 -0800554 // VR sensors are treated as a special case and we will not check the
555 // write permission for VR sensors, since they always deemed writable
556 // and permission table are not applied to VR sensors.
557 auto vrMode =
558 sensor::calculateVRMode(assertOffset, sensorObject->second);
559 if (!vrMode)
560 {
561 return ipmi::responseResponseError();
562 }
563 boost::system::error_code ec = setDbusProperty(
564 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
565 // setDbusProperty intended to resolve dbus exception/rc within the
566 // function but failed to achieve that. Catch SdBusError in the ipmi
567 // callback functions for now (e.g. ipmiSetSensorReading).
568 if (ec)
569 {
570 using namespace phosphor::logging;
571 log<level::ERR>("Failed to set property",
572 entry("PROPERTY=%s", "Selected"),
573 entry("PATH=%s", path.c_str()),
574 entry("INTERFACE=%s", sensor::sensorInterface),
575 entry("WHAT=%s", ec.message().c_str()));
576 return ipmi::responseResponseError();
577 }
578 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700579 }
580
Hao Jiangd2afd052020-12-10 15:09:32 -0800581 phosphor::logging::log<phosphor::logging::level::ERR>(
582 "unknown sensor type",
583 phosphor::logging::entry("PATH=%s", path.c_str()));
584 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700585}
586
Willy Tude54f482021-01-26 15:59:09 -0800587ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
588 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
589{
590 std::string connection;
591 std::string path;
592
593 auto status = getSensorConnection(ctx, sensnum, connection, path);
594 if (status)
595 {
596 return ipmi::response(status);
597 }
598
599 DbusInterfaceMap sensorMap;
600 if (!getSensorMap(ctx, connection, path, sensorMap))
601 {
602 return ipmi::responseResponseError();
603 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800604 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800605
606 if (sensorObject == sensorMap.end() ||
607 sensorObject->second.find("Value") == sensorObject->second.end())
608 {
609 return ipmi::responseResponseError();
610 }
611 auto& valueVariant = sensorObject->second["Value"];
612 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
613
614 double max = 0;
615 double min = 0;
616 getSensorMaxMin(sensorMap, max, min);
617
618 int16_t mValue = 0;
619 int16_t bValue = 0;
620 int8_t rExp = 0;
621 int8_t bExp = 0;
622 bool bSigned = false;
623
624 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
625 {
626 return ipmi::responseResponseError();
627 }
628
629 uint8_t value =
630 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
631 uint8_t operation =
632 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
633 operation |=
634 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
635 bool notReading = std::isnan(reading);
636
637 if (!notReading)
638 {
639 auto availableObject =
640 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
641 if (availableObject != sensorMap.end())
642 {
643 auto findAvailable = availableObject->second.find("Available");
644 if (findAvailable != availableObject->second.end())
645 {
646 bool* available = std::get_if<bool>(&(findAvailable->second));
647 if (available && !(*available))
648 {
649 notReading = true;
650 }
651 }
652 }
653 }
654
655 if (notReading)
656 {
657 operation |= static_cast<uint8_t>(
658 IPMISensorReadingByte2::readingStateUnavailable);
659 }
660
Josh Lehana55c9532020-10-28 21:59:06 -0700661 if constexpr (details::enableInstrumentation)
662 {
663 int byteValue;
664 if (bSigned)
665 {
666 byteValue = static_cast<int>(static_cast<int8_t>(value));
667 }
668 else
669 {
670 byteValue = static_cast<int>(static_cast<uint8_t>(value));
671 }
672
673 // Keep stats on the reading just obtained, even if it is "NaN"
674 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
675 {
676 // This is the first reading, show the coefficients
677 double step = (max - min) / 255.0;
678 std::cerr << "IPMI sensor "
679 << details::sdrStatsTable.getName(sensnum)
680 << ": Range min=" << min << " max=" << max
681 << ", step=" << step
682 << ", Coefficients mValue=" << static_cast<int>(mValue)
683 << " rExp=" << static_cast<int>(rExp)
684 << " bValue=" << static_cast<int>(bValue)
685 << " bExp=" << static_cast<int>(bExp)
686 << " bSigned=" << static_cast<int>(bSigned) << "\n";
687 }
688 }
689
Willy Tude54f482021-01-26 15:59:09 -0800690 uint8_t thresholds = 0;
691
692 auto warningObject =
693 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
694 if (warningObject != sensorMap.end())
695 {
696 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
697 auto alarmLow = warningObject->second.find("WarningAlarmLow");
698 if (alarmHigh != warningObject->second.end())
699 {
700 if (std::get<bool>(alarmHigh->second))
701 {
702 thresholds |= static_cast<uint8_t>(
703 IPMISensorReadingByte3::upperNonCritical);
704 }
705 }
706 if (alarmLow != warningObject->second.end())
707 {
708 if (std::get<bool>(alarmLow->second))
709 {
710 thresholds |= static_cast<uint8_t>(
711 IPMISensorReadingByte3::lowerNonCritical);
712 }
713 }
714 }
715
716 auto criticalObject =
717 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
718 if (criticalObject != sensorMap.end())
719 {
720 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
721 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
722 if (alarmHigh != criticalObject->second.end())
723 {
724 if (std::get<bool>(alarmHigh->second))
725 {
726 thresholds |=
727 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
728 }
729 }
730 if (alarmLow != criticalObject->second.end())
731 {
732 if (std::get<bool>(alarmLow->second))
733 {
734 thresholds |=
735 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
736 }
737 }
738 }
739
740 // no discrete as of today so optional byte is never returned
741 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
742}
743
744/** @brief implements the Set Sensor threshold command
745 * @param sensorNumber - sensor number
746 * @param lowerNonCriticalThreshMask
747 * @param lowerCriticalThreshMask
748 * @param lowerNonRecovThreshMask
749 * @param upperNonCriticalThreshMask
750 * @param upperCriticalThreshMask
751 * @param upperNonRecovThreshMask
752 * @param reserved
753 * @param lowerNonCritical - lower non-critical threshold
754 * @param lowerCritical - Lower critical threshold
755 * @param lowerNonRecoverable - Lower non recovarable threshold
756 * @param upperNonCritical - Upper non-critical threshold
757 * @param upperCritical - Upper critical
758 * @param upperNonRecoverable - Upper Non-recoverable
759 *
760 * @returns IPMI completion code
761 */
762ipmi::RspType<> ipmiSenSetSensorThresholds(
763 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
764 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
765 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
766 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
767 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
768 uint8_t upperNonCritical, uint8_t upperCritical,
769 uint8_t upperNonRecoverable)
770{
771 if (reserved)
772 {
773 return ipmi::responseInvalidFieldRequest();
774 }
775
776 // lower nc and upper nc not suppported on any sensor
777 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
778 {
779 return ipmi::responseInvalidFieldRequest();
780 }
781
782 // if none of the threshold mask are set, nothing to do
783 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
784 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
785 upperCriticalThreshMask | upperNonRecovThreshMask))
786 {
787 return ipmi::responseSuccess();
788 }
789
790 std::string connection;
791 std::string path;
792
793 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
794 if (status)
795 {
796 return ipmi::response(status);
797 }
798 DbusInterfaceMap sensorMap;
799 if (!getSensorMap(ctx, connection, path, sensorMap))
800 {
801 return ipmi::responseResponseError();
802 }
803
804 double max = 0;
805 double min = 0;
806 getSensorMaxMin(sensorMap, max, min);
807
808 int16_t mValue = 0;
809 int16_t bValue = 0;
810 int8_t rExp = 0;
811 int8_t bExp = 0;
812 bool bSigned = false;
813
814 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
815 {
816 return ipmi::responseResponseError();
817 }
818
819 // store a vector of property name, value to set, and interface
820 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
821
822 // define the indexes of the tuple
823 constexpr uint8_t propertyName = 0;
824 constexpr uint8_t thresholdValue = 1;
825 constexpr uint8_t interface = 2;
826 // verifiy all needed fields are present
827 if (lowerCriticalThreshMask || upperCriticalThreshMask)
828 {
829 auto findThreshold =
830 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
831 if (findThreshold == sensorMap.end())
832 {
833 return ipmi::responseInvalidFieldRequest();
834 }
835 if (lowerCriticalThreshMask)
836 {
837 auto findLower = findThreshold->second.find("CriticalLow");
838 if (findLower == findThreshold->second.end())
839 {
840 return ipmi::responseInvalidFieldRequest();
841 }
842 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
843 findThreshold->first);
844 }
845 if (upperCriticalThreshMask)
846 {
847 auto findUpper = findThreshold->second.find("CriticalHigh");
848 if (findUpper == findThreshold->second.end())
849 {
850 return ipmi::responseInvalidFieldRequest();
851 }
852 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
853 findThreshold->first);
854 }
855 }
856 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
857 {
858 auto findThreshold =
859 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
860 if (findThreshold == sensorMap.end())
861 {
862 return ipmi::responseInvalidFieldRequest();
863 }
864 if (lowerNonCriticalThreshMask)
865 {
866 auto findLower = findThreshold->second.find("WarningLow");
867 if (findLower == findThreshold->second.end())
868 {
869 return ipmi::responseInvalidFieldRequest();
870 }
871 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
872 findThreshold->first);
873 }
874 if (upperNonCriticalThreshMask)
875 {
876 auto findUpper = findThreshold->second.find("WarningHigh");
877 if (findUpper == findThreshold->second.end())
878 {
879 return ipmi::responseInvalidFieldRequest();
880 }
881 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
882 findThreshold->first);
883 }
884 }
885 for (const auto& property : thresholdsToSet)
886 {
887 // from section 36.3 in the IPMI Spec, assume all linear
888 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
889 (bValue * std::pow(10.0, bExp))) *
890 std::pow(10.0, rExp);
891 setDbusProperty(
892 *getSdBus(), connection, path, std::get<interface>(property),
893 std::get<propertyName>(property), ipmi::Value(valueToSet));
894 }
895 return ipmi::responseSuccess();
896}
897
898IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
899{
900 IPMIThresholds resp;
901 auto warningInterface =
902 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
903 auto criticalInterface =
904 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
905
906 if ((warningInterface != sensorMap.end()) ||
907 (criticalInterface != sensorMap.end()))
908 {
Hao Jiangd2afd052020-12-10 15:09:32 -0800909 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800910
911 if (sensorPair == sensorMap.end())
912 {
913 // should not have been able to find a sensor not implementing
914 // the sensor object
915 throw std::runtime_error("Invalid sensor map");
916 }
917
918 double max = 0;
919 double min = 0;
920 getSensorMaxMin(sensorMap, max, min);
921
922 int16_t mValue = 0;
923 int16_t bValue = 0;
924 int8_t rExp = 0;
925 int8_t bExp = 0;
926 bool bSigned = false;
927
928 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
929 {
930 throw std::runtime_error("Invalid sensor atrributes");
931 }
932 if (warningInterface != sensorMap.end())
933 {
934 auto& warningMap = warningInterface->second;
935
936 auto warningHigh = warningMap.find("WarningHigh");
937 auto warningLow = warningMap.find("WarningLow");
938
939 if (warningHigh != warningMap.end())
940 {
941
942 double value =
943 std::visit(VariantToDoubleVisitor(), warningHigh->second);
944 resp.warningHigh = scaleIPMIValueFromDouble(
945 value, mValue, rExp, bValue, bExp, bSigned);
946 }
947 if (warningLow != warningMap.end())
948 {
949 double value =
950 std::visit(VariantToDoubleVisitor(), warningLow->second);
951 resp.warningLow = scaleIPMIValueFromDouble(
952 value, mValue, rExp, bValue, bExp, bSigned);
953 }
954 }
955 if (criticalInterface != sensorMap.end())
956 {
957 auto& criticalMap = criticalInterface->second;
958
959 auto criticalHigh = criticalMap.find("CriticalHigh");
960 auto criticalLow = criticalMap.find("CriticalLow");
961
962 if (criticalHigh != criticalMap.end())
963 {
964 double value =
965 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
966 resp.criticalHigh = scaleIPMIValueFromDouble(
967 value, mValue, rExp, bValue, bExp, bSigned);
968 }
969 if (criticalLow != criticalMap.end())
970 {
971 double value =
972 std::visit(VariantToDoubleVisitor(), criticalLow->second);
973 resp.criticalLow = scaleIPMIValueFromDouble(
974 value, mValue, rExp, bValue, bExp, bSigned);
975 }
976 }
977 }
978 return resp;
979}
980
981ipmi::RspType<uint8_t, // readable
982 uint8_t, // lowerNCrit
983 uint8_t, // lowerCrit
984 uint8_t, // lowerNrecoverable
985 uint8_t, // upperNC
986 uint8_t, // upperCrit
987 uint8_t> // upperNRecoverable
988 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
989{
990 std::string connection;
991 std::string path;
992
993 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
994 if (status)
995 {
996 return ipmi::response(status);
997 }
998
999 DbusInterfaceMap sensorMap;
1000 if (!getSensorMap(ctx, connection, path, sensorMap))
1001 {
1002 return ipmi::responseResponseError();
1003 }
1004
1005 IPMIThresholds thresholdData;
1006 try
1007 {
1008 thresholdData = getIPMIThresholds(sensorMap);
1009 }
1010 catch (std::exception&)
1011 {
1012 return ipmi::responseResponseError();
1013 }
1014
1015 uint8_t readable = 0;
1016 uint8_t lowerNC = 0;
1017 uint8_t lowerCritical = 0;
1018 uint8_t lowerNonRecoverable = 0;
1019 uint8_t upperNC = 0;
1020 uint8_t upperCritical = 0;
1021 uint8_t upperNonRecoverable = 0;
1022
1023 if (thresholdData.warningHigh)
1024 {
1025 readable |=
1026 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1027 upperNC = *thresholdData.warningHigh;
1028 }
1029 if (thresholdData.warningLow)
1030 {
1031 readable |=
1032 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1033 lowerNC = *thresholdData.warningLow;
1034 }
1035
1036 if (thresholdData.criticalHigh)
1037 {
1038 readable |=
1039 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1040 upperCritical = *thresholdData.criticalHigh;
1041 }
1042 if (thresholdData.criticalLow)
1043 {
1044 readable |=
1045 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1046 lowerCritical = *thresholdData.criticalLow;
1047 }
1048
1049 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1050 lowerNonRecoverable, upperNC, upperCritical,
1051 upperNonRecoverable);
1052}
1053
1054/** @brief implements the get Sensor event enable command
1055 * @param sensorNumber - sensor number
1056 *
1057 * @returns IPMI completion code plus response data
1058 * - enabled - Sensor Event messages
1059 * - assertionEnabledLsb - Assertion event messages
1060 * - assertionEnabledMsb - Assertion event messages
1061 * - deassertionEnabledLsb - Deassertion event messages
1062 * - deassertionEnabledMsb - Deassertion event messages
1063 */
1064
1065ipmi::RspType<uint8_t, // enabled
1066 uint8_t, // assertionEnabledLsb
1067 uint8_t, // assertionEnabledMsb
1068 uint8_t, // deassertionEnabledLsb
1069 uint8_t> // deassertionEnabledMsb
1070 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1071{
1072 std::string connection;
1073 std::string path;
1074
1075 uint8_t enabled = 0;
1076 uint8_t assertionEnabledLsb = 0;
1077 uint8_t assertionEnabledMsb = 0;
1078 uint8_t deassertionEnabledLsb = 0;
1079 uint8_t deassertionEnabledMsb = 0;
1080
1081 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1082 if (status)
1083 {
1084 return ipmi::response(status);
1085 }
1086
1087 DbusInterfaceMap sensorMap;
1088 if (!getSensorMap(ctx, connection, path, sensorMap))
1089 {
1090 return ipmi::responseResponseError();
1091 }
1092
1093 auto warningInterface =
1094 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1095 auto criticalInterface =
1096 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1097 if ((warningInterface != sensorMap.end()) ||
1098 (criticalInterface != sensorMap.end()))
1099 {
1100 enabled = static_cast<uint8_t>(
1101 IPMISensorEventEnableByte2::sensorScanningEnable);
1102 if (warningInterface != sensorMap.end())
1103 {
1104 auto& warningMap = warningInterface->second;
1105
1106 auto warningHigh = warningMap.find("WarningHigh");
1107 auto warningLow = warningMap.find("WarningLow");
1108 if (warningHigh != warningMap.end())
1109 {
1110 assertionEnabledLsb |= static_cast<uint8_t>(
1111 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1112 deassertionEnabledLsb |= static_cast<uint8_t>(
1113 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1114 }
1115 if (warningLow != warningMap.end())
1116 {
1117 assertionEnabledLsb |= static_cast<uint8_t>(
1118 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1119 deassertionEnabledLsb |= static_cast<uint8_t>(
1120 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1121 }
1122 }
1123 if (criticalInterface != sensorMap.end())
1124 {
1125 auto& criticalMap = criticalInterface->second;
1126
1127 auto criticalHigh = criticalMap.find("CriticalHigh");
1128 auto criticalLow = criticalMap.find("CriticalLow");
1129
1130 if (criticalHigh != criticalMap.end())
1131 {
1132 assertionEnabledMsb |= static_cast<uint8_t>(
1133 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1134 deassertionEnabledMsb |= static_cast<uint8_t>(
1135 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1136 }
1137 if (criticalLow != criticalMap.end())
1138 {
1139 assertionEnabledLsb |= static_cast<uint8_t>(
1140 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1141 deassertionEnabledLsb |= static_cast<uint8_t>(
1142 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1143 }
1144 }
1145 }
1146
1147 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1148 assertionEnabledMsb, deassertionEnabledLsb,
1149 deassertionEnabledMsb);
1150}
1151
1152/** @brief implements the get Sensor event status command
1153 * @param sensorNumber - sensor number, FFh = reserved
1154 *
1155 * @returns IPMI completion code plus response data
1156 * - sensorEventStatus - Sensor Event messages state
1157 * - assertions - Assertion event messages
1158 * - deassertions - Deassertion event messages
1159 */
1160ipmi::RspType<uint8_t, // sensorEventStatus
1161 std::bitset<16>, // assertions
1162 std::bitset<16> // deassertion
1163 >
1164 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1165{
1166 if (sensorNum == reservedSensorNumber)
1167 {
1168 return ipmi::responseInvalidFieldRequest();
1169 }
1170
1171 std::string connection;
1172 std::string path;
1173 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1174 if (status)
1175 {
1176 phosphor::logging::log<phosphor::logging::level::ERR>(
1177 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1178 phosphor::logging::entry("SENSOR=%d", sensorNum));
1179 return ipmi::response(status);
1180 }
1181
1182 DbusInterfaceMap sensorMap;
1183 if (!getSensorMap(ctx, connection, path, sensorMap))
1184 {
1185 phosphor::logging::log<phosphor::logging::level::ERR>(
1186 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1187 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1188 return ipmi::responseResponseError();
1189 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001190
1191 uint8_t sensorEventStatus =
1192 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1193 std::bitset<16> assertions = 0;
1194 std::bitset<16> deassertions = 0;
1195
1196 // handle VR typed sensor
1197 auto vrInterface = sensorMap.find(sensor::vrInterface);
1198 if (vrInterface != sensorMap.end())
1199 {
1200 if (!sensor::getVrEventStatus(ctx, connection, path,
1201 vrInterface->second, assertions))
1202 {
1203 return ipmi::responseResponseError();
1204 }
1205
1206 // both Event Message and Sensor Scanning are disable for VR.
1207 sensorEventStatus = 0;
1208 return ipmi::responseSuccess(sensorEventStatus, assertions,
1209 deassertions);
1210 }
1211
Willy Tude54f482021-01-26 15:59:09 -08001212 auto warningInterface =
1213 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1214 auto criticalInterface =
1215 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1216
Willy Tude54f482021-01-26 15:59:09 -08001217 std::optional<bool> criticalDeassertHigh =
1218 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1219 std::optional<bool> criticalDeassertLow =
1220 thresholdDeassertMap[path]["CriticalAlarmLow"];
1221 std::optional<bool> warningDeassertHigh =
1222 thresholdDeassertMap[path]["WarningAlarmHigh"];
1223 std::optional<bool> warningDeassertLow =
1224 thresholdDeassertMap[path]["WarningAlarmLow"];
1225
Willy Tude54f482021-01-26 15:59:09 -08001226 if (criticalDeassertHigh && !*criticalDeassertHigh)
1227 {
1228 deassertions.set(static_cast<size_t>(
1229 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1230 }
1231 if (criticalDeassertLow && !*criticalDeassertLow)
1232 {
1233 deassertions.set(static_cast<size_t>(
1234 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1235 }
1236 if (warningDeassertHigh && !*warningDeassertHigh)
1237 {
1238 deassertions.set(static_cast<size_t>(
1239 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1240 }
1241 if (warningDeassertLow && !*warningDeassertLow)
1242 {
1243 deassertions.set(static_cast<size_t>(
1244 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1245 }
1246 if ((warningInterface != sensorMap.end()) ||
1247 (criticalInterface != sensorMap.end()))
1248 {
1249 sensorEventStatus = static_cast<size_t>(
1250 IPMISensorEventEnableByte2::eventMessagesEnable);
1251 if (warningInterface != sensorMap.end())
1252 {
1253 auto& warningMap = warningInterface->second;
1254
1255 auto warningHigh = warningMap.find("WarningAlarmHigh");
1256 auto warningLow = warningMap.find("WarningAlarmLow");
1257 auto warningHighAlarm = false;
1258 auto warningLowAlarm = false;
1259
1260 if (warningHigh != warningMap.end())
1261 {
1262 warningHighAlarm = std::get<bool>(warningHigh->second);
1263 }
1264 if (warningLow != warningMap.end())
1265 {
1266 warningLowAlarm = std::get<bool>(warningLow->second);
1267 }
1268 if (warningHighAlarm)
1269 {
1270 assertions.set(
1271 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1272 upperNonCriticalGoingHigh));
1273 }
1274 if (warningLowAlarm)
1275 {
1276 assertions.set(
1277 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1278 lowerNonCriticalGoingLow));
1279 }
1280 }
1281 if (criticalInterface != sensorMap.end())
1282 {
1283 auto& criticalMap = criticalInterface->second;
1284
1285 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1286 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1287 auto criticalHighAlarm = false;
1288 auto criticalLowAlarm = false;
1289
1290 if (criticalHigh != criticalMap.end())
1291 {
1292 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1293 }
1294 if (criticalLow != criticalMap.end())
1295 {
1296 criticalLowAlarm = std::get<bool>(criticalLow->second);
1297 }
1298 if (criticalHighAlarm)
1299 {
1300 assertions.set(
1301 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1302 upperCriticalGoingHigh));
1303 }
1304 if (criticalLowAlarm)
1305 {
1306 assertions.set(static_cast<size_t>(
1307 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1308 }
1309 }
1310 }
1311
1312 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1313}
1314
Willy Tu38e7a2b2021-03-29 15:09:56 -07001315// Construct a type 1 SDR for threshold sensor.
1316bool constructSensorSdr(uint16_t sensorNum, uint16_t recordID,
1317 const std::string& path,
1318 const DbusInterfaceMap& sensorMap,
1319 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001320{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001321 get_sdr::header::set_record_id(
1322 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1323
Willy Tu38e7a2b2021-03-29 15:09:56 -07001324 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1325 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1326
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001327 record.header.sdr_version = ipmiSdrVersion;
1328 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1329 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1330 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001331 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001332 record.key.owner_lun = lun;
1333 record.key.sensor_number = sensornumber;
1334
1335 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1336 record.body.sensor_type = getSensorTypeFromPath(path);
1337 std::string type = getSensorTypeStringFromPath(path);
1338 auto typeCstr = type.c_str();
1339 auto findUnits = sensorUnits.find(typeCstr);
1340 if (findUnits != sensorUnits.end())
1341 {
1342 record.body.sensor_units_2_base =
1343 static_cast<uint8_t>(findUnits->second);
1344 } // else default 0x0 unspecified
1345
1346 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1347
Hao Jiangd2afd052020-12-10 15:09:32 -08001348 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001349 if (sensorObject == sensorMap.end())
1350 {
1351 phosphor::logging::log<phosphor::logging::level::ERR>(
1352 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001353 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001354 }
1355
1356 uint8_t entityId = 0;
1357 uint8_t entityInstance = 0x01;
1358
1359 // follow the association chain to get the parent board's entityid and
1360 // entityInstance
1361 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1362
1363 record.body.entity_id = entityId;
1364 record.body.entity_instance = entityInstance;
1365
1366 auto maxObject = sensorObject->second.find("MaxValue");
1367 auto minObject = sensorObject->second.find("MinValue");
1368
1369 // If min and/or max are left unpopulated,
1370 // then default to what a signed byte would be, namely (-128,127) range.
1371 auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1372 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1373 if (maxObject != sensorObject->second.end())
1374 {
1375 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1376 }
1377
1378 if (minObject != sensorObject->second.end())
1379 {
1380 min = std::visit(VariantToDoubleVisitor(), minObject->second);
1381 }
1382
1383 int16_t mValue = 0;
1384 int8_t rExp = 0;
1385 int16_t bValue = 0;
1386 int8_t bExp = 0;
1387 bool bSigned = false;
1388
1389 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1390 {
1391 phosphor::logging::log<phosphor::logging::level::ERR>(
1392 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001393 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001394 }
1395
1396 // The record.body is a struct SensorDataFullRecordBody
1397 // from sensorhandler.hpp in phosphor-ipmi-host.
1398 // The meaning of these bits appears to come from
1399 // table 43.1 of the IPMI spec.
1400 // The above 5 sensor attributes are stuffed in as follows:
1401 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1402 // Byte 22-24 are for other purposes
1403 // Byte 25 = MMMMMMMM = LSB of M
1404 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1405 // Byte 27 = BBBBBBBB = LSB of B
1406 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1407 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1408 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1409
1410 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1411 record.body.m_lsb = mValue & 0xFF;
1412
1413 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1414 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1415
1416 // move the smallest bit of the MSB into place (bit 9)
1417 // the MSbs are bits 7:8 in m_msb_and_tolerance
1418 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1419
1420 record.body.b_lsb = bValue & 0xFF;
1421
1422 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1423 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1424
1425 // move the smallest bit of the MSB into place (bit 9)
1426 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1427 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1428
1429 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1430 uint8_t rExpBits = rExp & 0x07;
1431
1432 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1433 uint8_t bExpBits = bExp & 0x07;
1434
1435 // move rExp and bExp into place
1436 record.body.r_b_exponents =
1437 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1438
1439 // Set the analog reading byte interpretation accordingly
1440 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1441
1442 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1443 // These seem redundant, but derivable from the above 5 attributes
1444 // Original comment said "todo fill out rest of units"
1445
1446 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001447 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001448 record.body.id_string_info = name.size();
1449 std::strncpy(record.body.id_string, name.c_str(),
1450 sizeof(record.body.id_string));
1451
Josh Lehana55c9532020-10-28 21:59:06 -07001452 // Remember the sensor name, as determined for this sensor number
1453 details::sdrStatsTable.updateName(sensornumber, name);
1454
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001455 IPMIThresholds thresholdData;
1456 try
1457 {
1458 thresholdData = getIPMIThresholds(sensorMap);
1459 }
1460 catch (std::exception&)
1461 {
1462 phosphor::logging::log<phosphor::logging::level::ERR>(
1463 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001464 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001465 }
1466
1467 if (thresholdData.criticalHigh)
1468 {
1469 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1470 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1471 IPMISensorEventEnableThresholds::criticalThreshold);
1472 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1473 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1474 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1475 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1476 record.body.discrete_reading_setting_mask[0] |=
1477 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1478 }
1479 if (thresholdData.warningHigh)
1480 {
1481 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1482 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1483 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1484 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1485 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1486 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1487 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1488 record.body.discrete_reading_setting_mask[0] |=
1489 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1490 }
1491 if (thresholdData.criticalLow)
1492 {
1493 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1494 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1495 IPMISensorEventEnableThresholds::criticalThreshold);
1496 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1497 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1498 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1499 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1500 record.body.discrete_reading_setting_mask[0] |=
1501 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1502 }
1503 if (thresholdData.warningLow)
1504 {
1505 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1506 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1507 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1508 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1509 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1510 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1511 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1512 record.body.discrete_reading_setting_mask[0] |=
1513 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1514 }
1515
1516 // everything that is readable is setable
1517 record.body.discrete_reading_setting_mask[1] =
1518 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001519 return true;
1520}
1521
Willy Tu61992ad2021-03-29 15:33:20 -07001522// Construct a type 3 SDR for VR typed sensor(daemon).
1523void constructVrSdr(uint16_t sensorNum, uint16_t recordID,
1524 const std::string& path, const DbusInterfaceMap& sensorMap,
1525 get_sdr::SensorDataEventRecord& record)
1526{
1527 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1528 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1529
1530 get_sdr::header::set_record_id(
1531 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1532
1533 record.header.sdr_version = ipmiSdrVersion;
1534 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1535 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1536 sizeof(get_sdr::SensorDataRecordHeader);
1537 record.key.owner_id = bmcI2CAddr;
1538 record.key.owner_lun = lun;
1539 record.key.sensor_number = sensornumber;
1540
1541 record.body.entity_id = 0x00;
1542 record.body.entity_instance = 0x01;
1543
1544 // follow the association chain to get the parent board's entityid and
1545 // entityInstance
1546 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1547 record.body.entity_instance);
1548
1549 // Sensor type is hardcoded as a module/board type instead of parsing from
1550 // sensor path. This is because VR control is allocated in an independent
1551 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1552 // types.
1553 static constexpr const uint8_t module_board_type = 0x15;
1554 record.body.sensor_type = module_board_type;
1555 record.body.event_reading_type = 0x00;
1556
1557 record.body.sensor_record_sharing_1 = 0x00;
1558 record.body.sensor_record_sharing_2 = 0x00;
1559
1560 // populate sensor name from path
1561 auto name = sensor::parseSdrIdFromPath(path);
1562 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1563 record.body.id_string_info = nameSize;
1564 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1565 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1566
1567 // Remember the sensor name, as determined for this sensor number
1568 details::sdrStatsTable.updateName(sensornumber, name);
1569}
1570
Willy Tu38e7a2b2021-03-29 15:09:56 -07001571static int getSensorDataRecord(ipmi::Context::ptr ctx,
1572 std::vector<uint8_t>& recordData,
1573 uint16_t recordID)
1574{
1575 size_t fruCount = 0;
1576 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1577 if (ret != ipmi::ccSuccess)
1578 {
1579 phosphor::logging::log<phosphor::logging::level::ERR>(
1580 "getSensorDataRecord: getFruSdrCount error");
1581 return GENERAL_ERROR;
1582 }
1583
1584 auto& sensorTree = getSensorTree();
1585 size_t lastRecord =
1586 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1587 if (recordID == lastRecordIndex)
1588 {
1589 recordID = lastRecord;
1590 }
1591 if (recordID > lastRecord)
1592 {
1593 phosphor::logging::log<phosphor::logging::level::ERR>(
1594 "getSensorDataRecord: recordID > lastRecord error");
1595 return GENERAL_ERROR;
1596 }
1597
1598 if (recordID >= sensorTree.size())
1599 {
1600 size_t fruIndex = recordID - sensorTree.size();
1601
1602 if (fruIndex >= fruCount)
1603 {
1604 // handle type 12 hardcoded records
1605 size_t type12Index = fruIndex - fruCount;
1606 if (type12Index >= ipmi::storage::type12Count)
1607 {
1608 phosphor::logging::log<phosphor::logging::level::ERR>(
1609 "getSensorDataRecord: type12Index error");
1610 return GENERAL_ERROR;
1611 }
1612 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1613 }
1614 else
1615 {
1616 // handle fru records
1617 get_sdr::SensorDataFruRecord data;
1618 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1619 if (ret != IPMI_CC_OK)
1620 {
1621 return GENERAL_ERROR;
1622 }
1623 data.header.record_id_msb = recordID >> 8;
1624 data.header.record_id_lsb = recordID & 0xFF;
1625 recordData.insert(recordData.end(), (uint8_t*)&data,
1626 ((uint8_t*)&data) + sizeof(data));
1627 }
1628
1629 return 0;
1630 }
1631
1632 std::string connection;
1633 std::string path;
1634 auto status = getSensorConnection(ctx, recordID, connection, path);
1635 if (status)
1636 {
1637 phosphor::logging::log<phosphor::logging::level::ERR>(
1638 "getSensorDataRecord: getSensorConnection error");
1639 return GENERAL_ERROR;
1640 }
1641 DbusInterfaceMap sensorMap;
1642 if (!getSensorMap(ctx, connection, path, sensorMap, sensorMapUpdatePeriod))
1643 {
1644 phosphor::logging::log<phosphor::logging::level::ERR>(
1645 "getSensorDataRecord: getSensorMap error");
1646 return GENERAL_ERROR;
1647 }
1648 uint16_t sensorNum = getSensorNumberFromPath(path);
1649 if (sensorNum == invalidSensorNumber)
1650 {
1651 phosphor::logging::log<phosphor::logging::level::ERR>(
1652 "getSensorDataRecord: invalidSensorNumber");
1653 return GENERAL_ERROR;
1654 }
1655
1656 auto sensorObject = sensorMap.find(sensor::sensorInterface);
1657 // Construct full record (SDR type 1) for the threshold sensors
1658 if (sensorObject != sensorMap.end())
1659 {
1660 get_sdr::SensorDataFullRecord record = {0};
1661
1662 if (!constructSensorSdr(sensorNum, recordID, path, sensorMap, record))
1663 {
1664 return GENERAL_ERROR;
1665 }
1666 recordData.insert(recordData.end(), (uint8_t*)&record,
1667 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07001668
1669 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001670 }
Willy Tu61992ad2021-03-29 15:33:20 -07001671
1672 // Contruct SDR type 3 record for VR sensor (daemon)
1673 sensorObject = sensorMap.find(sensor::vrInterface);
1674 if (sensorObject != sensorMap.end())
1675 {
1676 get_sdr::SensorDataEventRecord record = {0};
1677
1678 constructVrSdr(sensorNum, recordID, path, sensorMap, record);
1679 recordData.insert(recordData.end(), (uint8_t*)&record,
1680 ((uint8_t*)&record) + sizeof(record));
1681 }
1682
Willy Tude54f482021-01-26 15:59:09 -08001683 return 0;
1684}
1685
1686/** @brief implements the get SDR Info command
1687 * @param count - Operation
1688 *
1689 * @returns IPMI completion code plus response data
1690 * - sdrCount - sensor/SDR count
1691 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1692 */
1693static ipmi::RspType<uint8_t, // respcount
1694 uint8_t, // dynamic population flags
1695 uint32_t // last time a sensor was added
1696 >
1697 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1698 std::optional<uint8_t> count)
1699{
1700 auto& sensorTree = getSensorTree();
1701 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001702 uint16_t recordID = 0;
1703 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08001704 // Sensors are dynamically allocated, and there is at least one LUN
1705 uint8_t lunsAndDynamicPopulation = 0x80;
1706 constexpr uint8_t getSdrCount = 0x01;
1707 constexpr uint8_t getSensorCount = 0x00;
1708
1709 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1710 {
1711 return ipmi::responseResponseError();
1712 }
Willy Tude54f482021-01-26 15:59:09 -08001713 uint16_t numSensors = sensorTree.size();
1714 if (count.value_or(0) == getSdrCount)
1715 {
1716 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001717 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08001718 {
1719 get_sdr::SensorDataRecordHeader* hdr =
1720 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001721 record.data());
Willy Tude54f482021-01-26 15:59:09 -08001722 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1723 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001724 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08001725 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001726 record.data());
1727 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08001728 {
1729 sdrCount++;
1730 }
1731 }
1732 }
1733 }
1734 else if (count.value_or(0) == getSensorCount)
1735 {
1736 // Return the number of sensors attached to the LUN
1737 if ((ctx->lun == 0) && (numSensors > 0))
1738 {
1739 sdrCount =
1740 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1741 }
1742 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1743 {
1744 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1745 ? maxSensorsPerLUN
1746 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1747 }
1748 else if (ctx->lun == 3)
1749 {
1750 if (numSensors <= maxIPMISensors)
1751 {
1752 sdrCount =
1753 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1754 }
1755 else
1756 {
1757 // error
1758 throw std::out_of_range(
1759 "Maximum number of IPMI sensors exceeded.");
1760 }
1761 }
1762 }
1763 else
1764 {
1765 return ipmi::responseInvalidFieldRequest();
1766 }
1767
1768 // Get Sensor count. This returns the number of sensors
1769 if (numSensors > 0)
1770 {
1771 lunsAndDynamicPopulation |= 1;
1772 }
1773 if (numSensors > maxSensorsPerLUN)
1774 {
1775 lunsAndDynamicPopulation |= 2;
1776 }
1777 if (numSensors >= (maxSensorsPerLUN * 2))
1778 {
1779 lunsAndDynamicPopulation |= 8;
1780 }
1781 if (numSensors > maxIPMISensors)
1782 {
1783 // error
1784 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1785 }
1786
1787 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1788 sdrLastAdd);
1789}
1790
1791/* end sensor commands */
1792
1793/* storage commands */
1794
1795ipmi::RspType<uint8_t, // sdr version
1796 uint16_t, // record count
1797 uint16_t, // free space
1798 uint32_t, // most recent addition
1799 uint32_t, // most recent erase
1800 uint8_t // operationSupport
1801 >
1802 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
1803{
1804 auto& sensorTree = getSensorTree();
1805 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001806 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001807 {
1808 return ipmi::responseResponseError();
1809 }
1810
1811 size_t fruCount = 0;
1812 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1813 if (ret != ipmi::ccSuccess)
1814 {
1815 return ipmi::response(ret);
1816 }
1817
1818 uint16_t recordCount =
1819 sensorTree.size() + fruCount + ipmi::storage::type12Count;
1820
1821 uint8_t operationSupport = static_cast<uint8_t>(
1822 SdrRepositoryInfoOps::overflow); // write not supported
1823
1824 operationSupport |=
1825 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1826 operationSupport |= static_cast<uint8_t>(
1827 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1828 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1829 unspecifiedFreeSpace, sdrLastAdd,
1830 sdrLastRemove, operationSupport);
1831}
1832
1833/** @brief implements the get SDR allocation info command
1834 *
1835 * @returns IPMI completion code plus response data
1836 * - allocUnits - Number of possible allocation units
1837 * - allocUnitSize - Allocation unit size in bytes.
1838 * - allocUnitFree - Number of free allocation units
1839 * - allocUnitLargestFree - Largest free block in allocation units
1840 * - maxRecordSize - Maximum record size in allocation units.
1841 */
1842ipmi::RspType<uint16_t, // allocUnits
1843 uint16_t, // allocUnitSize
1844 uint16_t, // allocUnitFree
1845 uint16_t, // allocUnitLargestFree
1846 uint8_t // maxRecordSize
1847 >
1848 ipmiStorageGetSDRAllocationInfo()
1849{
1850 // 0000h unspecified number of alloc units
1851 constexpr uint16_t allocUnits = 0;
1852
1853 constexpr uint16_t allocUnitFree = 0;
1854 constexpr uint16_t allocUnitLargestFree = 0;
1855 // only allow one block at a time
1856 constexpr uint8_t maxRecordSize = 1;
1857
1858 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1859 allocUnitLargestFree, maxRecordSize);
1860}
1861
1862/** @brief implements the reserve SDR command
1863 * @returns IPMI completion code plus response data
1864 * - sdrReservationID
1865 */
1866ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
1867{
1868 sdrReservationID++;
1869 if (sdrReservationID == 0)
1870 {
1871 sdrReservationID++;
1872 }
1873
1874 return ipmi::responseSuccess(sdrReservationID);
1875}
1876
1877ipmi::RspType<uint16_t, // next record ID
1878 std::vector<uint8_t> // payload
1879 >
1880 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1881 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1882{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001883 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08001884 // reservation required for partial reads with non zero offset into
1885 // record
1886 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
1887 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001888 phosphor::logging::log<phosphor::logging::level::ERR>(
1889 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08001890 return ipmi::responseInvalidReservationId();
1891 }
Willy Tude54f482021-01-26 15:59:09 -08001892 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1893 if (ret != ipmi::ccSuccess)
1894 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001895 phosphor::logging::log<phosphor::logging::level::ERR>(
1896 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08001897 return ipmi::response(ret);
1898 }
1899
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001900 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08001901 size_t lastRecord =
1902 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001903 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1904
1905 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001906 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001907 phosphor::logging::log<phosphor::logging::level::ERR>(
1908 "ipmiStorageGetSDR: getSensorSubtree error");
1909 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08001910 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001911
1912 std::vector<uint8_t> record;
1913 if (getSensorDataRecord(ctx, record, recordID))
Willy Tude54f482021-01-26 15:59:09 -08001914 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001915 phosphor::logging::log<phosphor::logging::level::ERR>(
1916 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08001917 return ipmi::responseInvalidFieldRequest();
1918 }
Willy Tude54f482021-01-26 15:59:09 -08001919 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001920 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08001921 if (!hdr)
1922 {
1923 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001924 "ipmiStorageGetSDR: record header is null");
1925 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001926 }
1927
1928 size_t sdrLength =
1929 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
1930 if (sdrLength < (offset + bytesToRead))
1931 {
1932 bytesToRead = sdrLength - offset;
1933 }
1934
1935 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
1936 if (!respStart)
1937 {
1938 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001939 "ipmiStorageGetSDR: record is null");
1940 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001941 }
1942
1943 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001944
Willy Tude54f482021-01-26 15:59:09 -08001945 return ipmi::responseSuccess(nextRecordId, recordData);
1946}
1947/* end storage commands */
1948
1949void registerSensorFunctions()
1950{
1951 // <Platform Event>
1952 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1953 ipmi::sensor_event::cmdPlatformEvent,
1954 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1955
Willy Tudbafbce2021-03-29 00:37:05 -07001956#ifdef FEATURE_DYNAMIC_SENSORS_WRITE
1957 // <Set Sensor Reading and Event Status>
1958 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1959 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
1960 ipmi::Privilege::Operator, ipmiSetSensorReading);
1961#endif
1962
Willy Tude54f482021-01-26 15:59:09 -08001963 // <Get Sensor Reading>
1964 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1965 ipmi::sensor_event::cmdGetSensorReading,
1966 ipmi::Privilege::User, ipmiSenGetSensorReading);
1967
1968 // <Get Sensor Threshold>
1969 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1970 ipmi::sensor_event::cmdGetSensorThreshold,
1971 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
1972
1973 // <Set Sensor Threshold>
1974 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1975 ipmi::sensor_event::cmdSetSensorThreshold,
1976 ipmi::Privilege::Operator,
1977 ipmiSenSetSensorThresholds);
1978
1979 // <Get Sensor Event Enable>
1980 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1981 ipmi::sensor_event::cmdGetSensorEventEnable,
1982 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
1983
1984 // <Get Sensor Event Status>
1985 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1986 ipmi::sensor_event::cmdGetSensorEventStatus,
1987 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
1988
1989 // register all storage commands for both Sensor and Storage command
1990 // versions
1991
1992 // <Get SDR Repository Info>
1993 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1994 ipmi::storage::cmdGetSdrRepositoryInfo,
1995 ipmi::Privilege::User,
1996 ipmiStorageGetSDRRepositoryInfo);
1997
1998 // <Get Device SDR Info>
1999 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2000 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2001 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2002
2003 // <Get SDR Allocation Info>
2004 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2005 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2006 ipmi::Privilege::User,
2007 ipmiStorageGetSDRAllocationInfo);
2008
2009 // <Reserve SDR Repo>
2010 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2011 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2012 ipmi::Privilege::User, ipmiStorageReserveSDR);
2013
2014 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2015 ipmi::storage::cmdReserveSdrRepository,
2016 ipmi::Privilege::User, ipmiStorageReserveSDR);
2017
2018 // <Get Sdr>
2019 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2020 ipmi::sensor_event::cmdGetDeviceSdr,
2021 ipmi::Privilege::User, ipmiStorageGetSDR);
2022
2023 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2024 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2025 ipmiStorageGetSDR);
2026}
2027} // namespace ipmi