blob: 91978c8031227b424b1b1ec5d7dc43cc6ddf316d [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>
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +000041#include <user_channel/channel_layer.hpp>
Willy Tude54f482021-01-26 15:59:09 -080042#include <utility>
43#include <variant>
44
Scron Chang2703b022021-07-06 15:47:45 +080045#ifdef FEATURE_HYBRID_SENSORS
46
47#include "sensordatahandler.hpp"
48namespace ipmi
49{
50namespace sensor
51{
52extern const IdInfoMap sensors;
53} // namespace sensor
54} // namespace ipmi
55#endif
56
JeffLind950f412021-10-20 18:49:34 +080057constexpr std::array<const char*, 7> suffixes = {
58 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
59 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080060namespace ipmi
61{
Hao Jiangd48c9212021-02-03 15:45:06 -080062
63using phosphor::logging::entry;
64using phosphor::logging::level;
65using phosphor::logging::log;
66
Willy Tude54f482021-01-26 15:59:09 -080067static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070068static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080069
Willy Tu38e7a2b2021-03-29 15:09:56 -070070// BMC I2C address is generally at 0x20
71static constexpr uint8_t bmcI2CAddr = 0x20;
72
Willy Tude54f482021-01-26 15:59:09 -080073constexpr size_t maxSDRTotalSize =
74 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
75constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
76
77static uint16_t sdrReservationID;
78static uint32_t sdrLastAdd = noTimestamp;
79static uint32_t sdrLastRemove = noTimestamp;
80static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070081
82// The IPMI spec defines four Logical Units (LUN), each capable of supporting
83// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
84// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
85// number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
86// location.
87static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
88
89static constexpr size_t lun0MaxSensorNum = 0xfe;
90static constexpr size_t lun1MaxSensorNum = 0x1fe;
91static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -080092static constexpr int GENERAL_ERROR = -1;
93
Willy Tude54f482021-01-26 15:59:09 -080094static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
95
96// Specify the comparison required to sort and find char* map objects
97struct CmpStr
98{
99 bool operator()(const char* a, const char* b) const
100 {
101 return std::strcmp(a, b) < 0;
102 }
103};
104const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
105 sensorUnits{{{"temperature", SensorUnits::degreesC},
106 {"voltage", SensorUnits::volts},
107 {"current", SensorUnits::amps},
108 {"fan_tach", SensorUnits::rpm},
109 {"power", SensorUnits::watts}}};
110
111void registerSensorFunctions() __attribute__((constructor));
112
113static sdbusplus::bus::match::match sensorAdded(
114 *getSdBus(),
115 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
116 "sensors/'",
117 [](sdbusplus::message::message& m) {
118 getSensorTree().clear();
119 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
120 std::chrono::system_clock::now().time_since_epoch())
121 .count();
122 });
123
124static sdbusplus::bus::match::match sensorRemoved(
125 *getSdBus(),
126 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
127 "sensors/'",
128 [](sdbusplus::message::message& m) {
129 getSensorTree().clear();
130 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
131 std::chrono::system_clock::now().time_since_epoch())
132 .count();
133 });
134
135// this keeps track of deassertions for sensor event status command. A
136// deasertion can only happen if an assertion was seen first.
137static boost::container::flat_map<
138 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
139 thresholdDeassertMap;
140
141static sdbusplus::bus::match::match thresholdChanged(
142 *getSdBus(),
143 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
144 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
145 [](sdbusplus::message::message& m) {
146 boost::container::flat_map<std::string, std::variant<bool, double>>
147 values;
148 m.read(std::string(), values);
149
150 auto findAssert =
151 std::find_if(values.begin(), values.end(), [](const auto& pair) {
152 return pair.first.find("Alarm") != std::string::npos;
153 });
154 if (findAssert != values.end())
155 {
156 auto ptr = std::get_if<bool>(&(findAssert->second));
157 if (ptr == nullptr)
158 {
159 phosphor::logging::log<phosphor::logging::level::ERR>(
160 "thresholdChanged: Assert non bool");
161 return;
162 }
163 if (*ptr)
164 {
165 phosphor::logging::log<phosphor::logging::level::INFO>(
166 "thresholdChanged: Assert",
167 phosphor::logging::entry("SENSOR=%s", m.get_path()));
168 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
169 }
170 else
171 {
172 auto& value =
173 thresholdDeassertMap[m.get_path()][findAssert->first];
174 if (value)
175 {
176 phosphor::logging::log<phosphor::logging::level::INFO>(
177 "thresholdChanged: deassert",
178 phosphor::logging::entry("SENSOR=%s", m.get_path()));
179 value = *ptr;
180 }
181 }
182 }
183 });
184
Hao Jiangd2afd052020-12-10 15:09:32 -0800185namespace sensor
186{
187static constexpr const char* vrInterface =
188 "xyz.openbmc_project.Control.VoltageRegulatorMode";
189static constexpr const char* sensorInterface =
190 "xyz.openbmc_project.Sensor.Value";
191} // namespace sensor
192
Willy Tude54f482021-01-26 15:59:09 -0800193static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
194 double& min)
195{
196 max = 127;
197 min = -128;
198
Hao Jiangd2afd052020-12-10 15:09:32 -0800199 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800200 auto critical =
201 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
202 auto warning =
203 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
204
205 if (sensorObject != sensorMap.end())
206 {
207 auto maxMap = sensorObject->second.find("MaxValue");
208 auto minMap = sensorObject->second.find("MinValue");
209
210 if (maxMap != sensorObject->second.end())
211 {
212 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
213 }
214 if (minMap != sensorObject->second.end())
215 {
216 min = std::visit(VariantToDoubleVisitor(), minMap->second);
217 }
218 }
219 if (critical != sensorMap.end())
220 {
221 auto lower = critical->second.find("CriticalLow");
222 auto upper = critical->second.find("CriticalHigh");
223 if (lower != critical->second.end())
224 {
225 double value = std::visit(VariantToDoubleVisitor(), lower->second);
226 min = std::min(value, min);
227 }
228 if (upper != critical->second.end())
229 {
230 double value = std::visit(VariantToDoubleVisitor(), upper->second);
231 max = std::max(value, max);
232 }
233 }
234 if (warning != sensorMap.end())
235 {
236
237 auto lower = warning->second.find("WarningLow");
238 auto upper = warning->second.find("WarningHigh");
239 if (lower != warning->second.end())
240 {
241 double value = std::visit(VariantToDoubleVisitor(), lower->second);
242 min = std::min(value, min);
243 }
244 if (upper != warning->second.end())
245 {
246 double value = std::visit(VariantToDoubleVisitor(), upper->second);
247 max = std::max(value, max);
248 }
249 }
250}
251
252static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700253 std::string sensorPath, DbusInterfaceMap& sensorMap,
254 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800255{
Scron Chang2703b022021-07-06 15:47:45 +0800256#ifdef FEATURE_HYBRID_SENSORS
257 if (auto sensor = findStaticSensor(sensorPath);
258 sensor != ipmi::sensor::sensors.end() &&
259 getSensorEventTypeFromPath(sensorPath) !=
260 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
261 {
262 // If the incoming sensor is a discrete sensor, it might fail in
263 // getManagedObjects(), return true, and use its own getFunc to get
264 // value.
265 return true;
266 }
267#endif
268
Willy Tude54f482021-01-26 15:59:09 -0800269 static boost::container::flat_map<
270 std::string, std::chrono::time_point<std::chrono::steady_clock>>
271 updateTimeMap;
272
273 auto updateFind = updateTimeMap.find(sensorConnection);
274 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
275 if (updateFind != updateTimeMap.end())
276 {
277 lastUpdate = updateFind->second;
278 }
279
280 auto now = std::chrono::steady_clock::now();
281
282 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700283 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800284 {
Willy Tude54f482021-01-26 15:59:09 -0800285 ObjectValueTree managedObjects;
286 boost::system::error_code ec = getManagedObjects(
287 ctx, sensorConnection.c_str(), "/", managedObjects);
288 if (ec)
289 {
290 phosphor::logging::log<phosphor::logging::level::ERR>(
291 "GetMangagedObjects for getSensorMap failed",
292 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
293
294 return false;
295 }
296
297 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700298 // Update time after finish building the map which allow the
299 // data to be cached for updatePeriod plus the build time.
300 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800301 }
302 auto connection = SensorCache.find(sensorConnection);
303 if (connection == SensorCache.end())
304 {
305 return false;
306 }
307 auto path = connection->second.find(sensorPath);
308 if (path == connection->second.end())
309 {
310 return false;
311 }
312 sensorMap = path->second;
313
314 return true;
315}
316
Hao Jiangd2afd052020-12-10 15:09:32 -0800317namespace sensor
318{
Hao Jiangd48c9212021-02-03 15:45:06 -0800319// Read VR profiles from sensor(daemon) interface
320static std::optional<std::vector<std::string>>
321 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800322{
323 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800324 auto supportedProperty = object.find("Supported");
325 if (supportedProperty == object.end() ||
326 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800327 {
328 phosphor::logging::log<phosphor::logging::level::ERR>(
329 "Missing the required Supported and Selected properties");
330 return std::nullopt;
331 }
332
333 const auto profilesPtr =
334 std::get_if<std::vector<std::string>>(&supportedProperty->second);
335
336 if (profilesPtr == nullptr)
337 {
338 phosphor::logging::log<phosphor::logging::level::ERR>(
339 "property is not array of string");
340 return std::nullopt;
341 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800342 return *profilesPtr;
343}
344
345// Calculate VR Mode from input IPMI discrete event bytes
346static std::optional<std::string>
347 calculateVRMode(uint15_t assertOffset,
348 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
349{
350 // get VR mode profiles from Supported Interface
351 auto profiles = getSupportedVrProfiles(VRObject);
352 if (!profiles)
353 {
354 return std::nullopt;
355 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800356
357 // interpret IPMI cmd bits into profiles' index
358 long unsigned int index = 0;
359 // only one bit should be set and the highest bit should not be used.
360 if (assertOffset == 0 || assertOffset == (1u << 15) ||
361 (assertOffset & (assertOffset - 1)))
362 {
363 phosphor::logging::log<phosphor::logging::level::ERR>(
364 "IPMI cmd format incorrect",
365
366 phosphor::logging::entry("BYTES=%#02x",
367 static_cast<uint16_t>(assertOffset)));
368 return std::nullopt;
369 }
370
371 while (assertOffset != 1)
372 {
373 assertOffset >>= 1;
374 index++;
375 }
376
Hao Jiangd48c9212021-02-03 15:45:06 -0800377 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800378 {
379 phosphor::logging::log<phosphor::logging::level::ERR>(
380 "profile index out of boundary");
381 return std::nullopt;
382 }
383
Hao Jiangd48c9212021-02-03 15:45:06 -0800384 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800385}
386
387// Calculate sensor value from IPMI reading byte
388static std::optional<double>
389 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
390 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
391{
392 if (valueObject.find("Value") == valueObject.end())
393 {
394 phosphor::logging::log<phosphor::logging::level::ERR>(
395 "Missing the required Value property");
396 return std::nullopt;
397 }
398
399 double max = 0;
400 double min = 0;
401 getSensorMaxMin(sensorMap, max, min);
402
403 int16_t mValue = 0;
404 int16_t bValue = 0;
405 int8_t rExp = 0;
406 int8_t bExp = 0;
407 bool bSigned = false;
408
409 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
410 {
411 return std::nullopt;
412 }
413
414 double value = bSigned ? ((int8_t)reading) : reading;
415
416 value *= ((double)mValue);
417 value += ((double)bValue) * std::pow(10.0, bExp);
418 value *= std::pow(10.0, rExp);
419
420 return value;
421}
422
Willy Tu38e7a2b2021-03-29 15:09:56 -0700423// Extract file name from sensor path as the sensors SDR ID. Simplify the name
424// if it is too long.
425std::string parseSdrIdFromPath(const std::string& path)
426{
427 std::string name;
428 size_t nameStart = path.rfind("/");
429 if (nameStart != std::string::npos)
430 {
431 name = path.substr(nameStart + 1, std::string::npos - nameStart);
432 }
433
Willy Tu38e7a2b2021-03-29 15:09:56 -0700434 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
435 {
436 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800437 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700438 {
JeffLind950f412021-10-20 18:49:34 +0800439 if (boost::ends_with(name, suffix))
440 {
441 boost::replace_all(name, suffix, "");
442 break;
443 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700444 }
Duke Du97014f52021-12-16 17:21:01 +0800445 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
446 {
447 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
448 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700449 }
JeffLind950f412021-10-20 18:49:34 +0800450 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700451 return name;
452}
453
Hao Jiangd48c9212021-02-03 15:45:06 -0800454bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
455 const std::string& path,
456 const ipmi::DbusInterfaceMap::mapped_type& object,
457 std::bitset<16>& assertions)
458{
459 auto profiles = sensor::getSupportedVrProfiles(object);
460 if (!profiles)
461 {
462 return false;
463 }
464 ipmi::Value modeVariant;
465
466 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
467 "Selected", modeVariant);
468 if (ec)
469 {
470 log<level::ERR>("Failed to get property",
471 entry("PROPERTY=%s", "Selected"),
472 entry("PATH=%s", path.c_str()),
473 entry("INTERFACE=%s", sensor::sensorInterface),
474 entry("WHAT=%s", ec.message().c_str()));
475 return false;
476 }
477
478 auto mode = std::get_if<std::string>(&modeVariant);
479 if (mode == nullptr)
480 {
481 log<level::ERR>("property is not a string",
482 entry("PROPERTY=%s", "Selected"),
483 entry("PATH=%s", path.c_str()),
484 entry("INTERFACE=%s", sensor::sensorInterface));
485 return false;
486 }
487
488 auto itr = std::find(profiles->begin(), profiles->end(), *mode);
489 if (itr == profiles->end())
490 {
491 using namespace phosphor::logging;
492 log<level::ERR>("VR mode doesn't match any of its profiles",
493 entry("PATH=%s", path.c_str()));
494 return false;
495 }
496 std::size_t index =
497 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
498
499 // map index to reponse event assertion bit.
500 if (index < 8)
501 {
502 assertions.set(1u << index);
503 }
504 else if (index < 15)
505 {
506 assertions.set(1u << (index - 8));
507 }
508 else
509 {
510 log<level::ERR>("VR profile index reaches max assertion bit",
511 entry("PATH=%s", path.c_str()),
512 entry("INDEX=%uz", index));
513 return false;
514 }
515 if constexpr (debug)
516 {
517 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
518 << " mode is: [" << index << "] " << *mode << std::endl;
519 }
520 return true;
521}
Hao Jiangd2afd052020-12-10 15:09:32 -0800522} // namespace sensor
523
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000524ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
525 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800526{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000527 constexpr const uint8_t validEnvmRev = 0x04;
528 constexpr const uint8_t lastSensorType = 0x2C;
529 constexpr const uint8_t oemReserved = 0xC0;
530
531 uint8_t generatorID = 0;
532 uint8_t evmRev = 0;
533 uint8_t sensorType = 0;
534 uint8_t sensorNum = 0;
535 uint8_t eventType = 0;
536 uint8_t eventData1 = 0;
537 std::optional<uint8_t> eventData2 = 0;
538 std::optional<uint8_t> eventData3 = 0;
539 ipmi::ChannelInfo chInfo;
540
541 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
542 {
543 phosphor::logging::log<phosphor::logging::level::ERR>(
544 "Failed to get Channel Info",
545 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
546 return ipmi::responseUnspecifiedError();
547 }
548
549 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
550 ipmi::EChannelMediumType::systemInterface)
551 {
552
553 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
554 eventData1, eventData2, eventData3);
555 }
556 else
557 {
558
559 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
560 eventData2, eventData3);
561 generatorID = ctx->rqSA;
562 }
563
564 if (!p.fullyUnpacked())
565 {
566 return ipmi::responseReqDataLenInvalid();
567 }
568
569 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
570 if (evmRev != validEnvmRev)
571 {
572 return ipmi::responseInvalidFieldRequest();
573 }
574 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
575 {
576 return ipmi::responseInvalidFieldRequest();
577 }
578
Willy Tude54f482021-01-26 15:59:09 -0800579 return ipmi::responseSuccess();
580}
581
Willy Tudbafbce2021-03-29 00:37:05 -0700582ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
583 uint8_t sensorNumber, uint8_t operation,
584 uint8_t reading, uint15_t assertOffset,
585 bool resvd1, uint15_t deassertOffset,
586 bool resvd2, uint8_t eventData1,
587 uint8_t eventData2, uint8_t eventData3)
588{
589 std::string connection;
590 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700591 std::vector<std::string> interfaces;
592
593 ipmi::Cc status =
594 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700595 if (status)
596 {
597 return ipmi::response(status);
598 }
599
Hao Jiangd2afd052020-12-10 15:09:32 -0800600 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700601 if (std::find(interfaces.begin(), interfaces.end(),
602 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700603 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700604 DbusInterfaceMap sensorMap;
605 if (!getSensorMap(ctx, connection, path, sensorMap))
606 {
607 return ipmi::responseResponseError();
608 }
609 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800610 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700611 {
612 return ipmi::responseResponseError();
613 }
614
Jie Yangf0a89942021-07-29 15:30:25 -0700615 // Only allow external SetSensor if write permission granted
616 if (!details::sdrWriteTable.getWritePermission(sensorNumber))
617 {
618 return ipmi::responseResponseError();
619 }
620
Hao Jiangd2afd052020-12-10 15:09:32 -0800621 auto value =
622 sensor::calculateValue(reading, sensorMap, sensorObject->second);
623 if (!value)
624 {
625 return ipmi::responseResponseError();
626 }
627
628 if constexpr (debug)
629 {
630 phosphor::logging::log<phosphor::logging::level::INFO>(
631 "IPMI SET_SENSOR",
632 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
633 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
634 phosphor::logging::entry("VALUE=%f", *value));
635 }
636
637 boost::system::error_code ec =
638 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
639 "Value", ipmi::Value(*value));
640
641 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500642 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800643 // callback functions for now (e.g. ipmiSetSensorReading).
644 if (ec)
645 {
646 using namespace phosphor::logging;
647 log<level::ERR>("Failed to set property",
648 entry("PROPERTY=%s", "Value"),
649 entry("PATH=%s", path.c_str()),
650 entry("INTERFACE=%s", sensor::sensorInterface),
651 entry("WHAT=%s", ec.message().c_str()));
652 return ipmi::responseResponseError();
653 }
654 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700655 }
656
Hao Jiange39d4d82021-04-16 17:02:40 -0700657 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
658 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700659 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700660 DbusInterfaceMap sensorMap;
661 if (!getSensorMap(ctx, connection, path, sensorMap))
662 {
663 return ipmi::responseResponseError();
664 }
665 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800666 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700667 {
668 return ipmi::responseResponseError();
669 }
670
Hao Jiangd2afd052020-12-10 15:09:32 -0800671 // VR sensors are treated as a special case and we will not check the
672 // write permission for VR sensors, since they always deemed writable
673 // and permission table are not applied to VR sensors.
674 auto vrMode =
675 sensor::calculateVRMode(assertOffset, sensorObject->second);
676 if (!vrMode)
677 {
678 return ipmi::responseResponseError();
679 }
680 boost::system::error_code ec = setDbusProperty(
681 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
682 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500683 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800684 // callback functions for now (e.g. ipmiSetSensorReading).
685 if (ec)
686 {
687 using namespace phosphor::logging;
688 log<level::ERR>("Failed to set property",
689 entry("PROPERTY=%s", "Selected"),
690 entry("PATH=%s", path.c_str()),
691 entry("INTERFACE=%s", sensor::sensorInterface),
692 entry("WHAT=%s", ec.message().c_str()));
693 return ipmi::responseResponseError();
694 }
695 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700696 }
697
Hao Jiangd2afd052020-12-10 15:09:32 -0800698 phosphor::logging::log<phosphor::logging::level::ERR>(
699 "unknown sensor type",
700 phosphor::logging::entry("PATH=%s", path.c_str()));
701 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700702}
703
Willy Tude54f482021-01-26 15:59:09 -0800704ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
705 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
706{
707 std::string connection;
708 std::string path;
709
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000710 if (sensnum == reservedSensorNumber)
711 {
712 return ipmi::responseInvalidFieldRequest();
713 }
714
Willy Tude54f482021-01-26 15:59:09 -0800715 auto status = getSensorConnection(ctx, sensnum, connection, path);
716 if (status)
717 {
718 return ipmi::response(status);
719 }
720
Scron Chang2703b022021-07-06 15:47:45 +0800721#ifdef FEATURE_HYBRID_SENSORS
722 if (auto sensor = findStaticSensor(path);
723 sensor != ipmi::sensor::sensors.end() &&
724 getSensorEventTypeFromPath(path) !=
725 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
726 {
727 if (ipmi::sensor::Mutability::Read !=
728 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
729 {
730 return ipmi::responseIllegalCommand();
731 }
732
733 uint8_t operation;
734 try
735 {
736 ipmi::sensor::GetSensorResponse getResponse =
737 sensor->second.getFunc(sensor->second);
738
739 if (getResponse.readingOrStateUnavailable)
740 {
741 operation |= static_cast<uint8_t>(
742 IPMISensorReadingByte2::readingStateUnavailable);
743 }
744 if (getResponse.scanningEnabled)
745 {
746 operation |= static_cast<uint8_t>(
747 IPMISensorReadingByte2::sensorScanningEnable);
748 }
749 if (getResponse.allEventMessagesEnabled)
750 {
751 operation |= static_cast<uint8_t>(
752 IPMISensorReadingByte2::eventMessagesEnable);
753 }
754 return ipmi::responseSuccess(
755 getResponse.reading, operation,
756 getResponse.thresholdLevelsStates,
757 getResponse.discreteReadingSensorStates);
758 }
759 catch (const std::exception& e)
760 {
761 operation |= static_cast<uint8_t>(
762 IPMISensorReadingByte2::readingStateUnavailable);
763 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
764 }
765 }
766#endif
767
Willy Tude54f482021-01-26 15:59:09 -0800768 DbusInterfaceMap sensorMap;
769 if (!getSensorMap(ctx, connection, path, sensorMap))
770 {
771 return ipmi::responseResponseError();
772 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800773 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800774
775 if (sensorObject == sensorMap.end() ||
776 sensorObject->second.find("Value") == sensorObject->second.end())
777 {
778 return ipmi::responseResponseError();
779 }
780 auto& valueVariant = sensorObject->second["Value"];
781 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
782
783 double max = 0;
784 double min = 0;
785 getSensorMaxMin(sensorMap, max, min);
786
787 int16_t mValue = 0;
788 int16_t bValue = 0;
789 int8_t rExp = 0;
790 int8_t bExp = 0;
791 bool bSigned = false;
792
793 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
794 {
795 return ipmi::responseResponseError();
796 }
797
798 uint8_t value =
799 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
800 uint8_t operation =
801 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
802 operation |=
803 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
804 bool notReading = std::isnan(reading);
805
806 if (!notReading)
807 {
808 auto availableObject =
809 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
810 if (availableObject != sensorMap.end())
811 {
812 auto findAvailable = availableObject->second.find("Available");
813 if (findAvailable != availableObject->second.end())
814 {
815 bool* available = std::get_if<bool>(&(findAvailable->second));
816 if (available && !(*available))
817 {
818 notReading = true;
819 }
820 }
821 }
822 }
823
824 if (notReading)
825 {
826 operation |= static_cast<uint8_t>(
827 IPMISensorReadingByte2::readingStateUnavailable);
828 }
829
Josh Lehana55c9532020-10-28 21:59:06 -0700830 if constexpr (details::enableInstrumentation)
831 {
832 int byteValue;
833 if (bSigned)
834 {
835 byteValue = static_cast<int>(static_cast<int8_t>(value));
836 }
837 else
838 {
839 byteValue = static_cast<int>(static_cast<uint8_t>(value));
840 }
841
842 // Keep stats on the reading just obtained, even if it is "NaN"
843 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
844 {
845 // This is the first reading, show the coefficients
846 double step = (max - min) / 255.0;
847 std::cerr << "IPMI sensor "
848 << details::sdrStatsTable.getName(sensnum)
849 << ": Range min=" << min << " max=" << max
850 << ", step=" << step
851 << ", Coefficients mValue=" << static_cast<int>(mValue)
852 << " rExp=" << static_cast<int>(rExp)
853 << " bValue=" << static_cast<int>(bValue)
854 << " bExp=" << static_cast<int>(bExp)
855 << " bSigned=" << static_cast<int>(bSigned) << "\n";
856 }
857 }
858
Willy Tude54f482021-01-26 15:59:09 -0800859 uint8_t thresholds = 0;
860
861 auto warningObject =
862 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
863 if (warningObject != sensorMap.end())
864 {
865 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
866 auto alarmLow = warningObject->second.find("WarningAlarmLow");
867 if (alarmHigh != warningObject->second.end())
868 {
869 if (std::get<bool>(alarmHigh->second))
870 {
871 thresholds |= static_cast<uint8_t>(
872 IPMISensorReadingByte3::upperNonCritical);
873 }
874 }
875 if (alarmLow != warningObject->second.end())
876 {
877 if (std::get<bool>(alarmLow->second))
878 {
879 thresholds |= static_cast<uint8_t>(
880 IPMISensorReadingByte3::lowerNonCritical);
881 }
882 }
883 }
884
885 auto criticalObject =
886 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
887 if (criticalObject != sensorMap.end())
888 {
889 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
890 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
891 if (alarmHigh != criticalObject->second.end())
892 {
893 if (std::get<bool>(alarmHigh->second))
894 {
895 thresholds |=
896 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
897 }
898 }
899 if (alarmLow != criticalObject->second.end())
900 {
901 if (std::get<bool>(alarmLow->second))
902 {
903 thresholds |=
904 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
905 }
906 }
907 }
908
909 // no discrete as of today so optional byte is never returned
910 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
911}
912
913/** @brief implements the Set Sensor threshold command
914 * @param sensorNumber - sensor number
915 * @param lowerNonCriticalThreshMask
916 * @param lowerCriticalThreshMask
917 * @param lowerNonRecovThreshMask
918 * @param upperNonCriticalThreshMask
919 * @param upperCriticalThreshMask
920 * @param upperNonRecovThreshMask
921 * @param reserved
922 * @param lowerNonCritical - lower non-critical threshold
923 * @param lowerCritical - Lower critical threshold
924 * @param lowerNonRecoverable - Lower non recovarable threshold
925 * @param upperNonCritical - Upper non-critical threshold
926 * @param upperCritical - Upper critical
927 * @param upperNonRecoverable - Upper Non-recoverable
928 *
929 * @returns IPMI completion code
930 */
931ipmi::RspType<> ipmiSenSetSensorThresholds(
932 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
933 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
934 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
935 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
936 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
937 uint8_t upperNonCritical, uint8_t upperCritical,
938 uint8_t upperNonRecoverable)
939{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000940 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800941 {
942 return ipmi::responseInvalidFieldRequest();
943 }
944
945 // lower nc and upper nc not suppported on any sensor
946 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
947 {
948 return ipmi::responseInvalidFieldRequest();
949 }
950
951 // if none of the threshold mask are set, nothing to do
952 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
953 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
954 upperCriticalThreshMask | upperNonRecovThreshMask))
955 {
956 return ipmi::responseSuccess();
957 }
958
959 std::string connection;
960 std::string path;
961
962 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
963 if (status)
964 {
965 return ipmi::response(status);
966 }
967 DbusInterfaceMap sensorMap;
968 if (!getSensorMap(ctx, connection, path, sensorMap))
969 {
970 return ipmi::responseResponseError();
971 }
972
973 double max = 0;
974 double min = 0;
975 getSensorMaxMin(sensorMap, max, min);
976
977 int16_t mValue = 0;
978 int16_t bValue = 0;
979 int8_t rExp = 0;
980 int8_t bExp = 0;
981 bool bSigned = false;
982
983 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
984 {
985 return ipmi::responseResponseError();
986 }
987
988 // store a vector of property name, value to set, and interface
989 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
990
991 // define the indexes of the tuple
992 constexpr uint8_t propertyName = 0;
993 constexpr uint8_t thresholdValue = 1;
994 constexpr uint8_t interface = 2;
995 // verifiy all needed fields are present
996 if (lowerCriticalThreshMask || upperCriticalThreshMask)
997 {
998 auto findThreshold =
999 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1000 if (findThreshold == sensorMap.end())
1001 {
1002 return ipmi::responseInvalidFieldRequest();
1003 }
1004 if (lowerCriticalThreshMask)
1005 {
1006 auto findLower = findThreshold->second.find("CriticalLow");
1007 if (findLower == findThreshold->second.end())
1008 {
1009 return ipmi::responseInvalidFieldRequest();
1010 }
1011 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1012 findThreshold->first);
1013 }
1014 if (upperCriticalThreshMask)
1015 {
1016 auto findUpper = findThreshold->second.find("CriticalHigh");
1017 if (findUpper == findThreshold->second.end())
1018 {
1019 return ipmi::responseInvalidFieldRequest();
1020 }
1021 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1022 findThreshold->first);
1023 }
1024 }
1025 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1026 {
1027 auto findThreshold =
1028 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1029 if (findThreshold == sensorMap.end())
1030 {
1031 return ipmi::responseInvalidFieldRequest();
1032 }
1033 if (lowerNonCriticalThreshMask)
1034 {
1035 auto findLower = findThreshold->second.find("WarningLow");
1036 if (findLower == findThreshold->second.end())
1037 {
1038 return ipmi::responseInvalidFieldRequest();
1039 }
1040 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1041 findThreshold->first);
1042 }
1043 if (upperNonCriticalThreshMask)
1044 {
1045 auto findUpper = findThreshold->second.find("WarningHigh");
1046 if (findUpper == findThreshold->second.end())
1047 {
1048 return ipmi::responseInvalidFieldRequest();
1049 }
1050 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1051 findThreshold->first);
1052 }
1053 }
1054 for (const auto& property : thresholdsToSet)
1055 {
1056 // from section 36.3 in the IPMI Spec, assume all linear
1057 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1058 (bValue * std::pow(10.0, bExp))) *
1059 std::pow(10.0, rExp);
1060 setDbusProperty(
1061 *getSdBus(), connection, path, std::get<interface>(property),
1062 std::get<propertyName>(property), ipmi::Value(valueToSet));
1063 }
1064 return ipmi::responseSuccess();
1065}
1066
1067IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1068{
1069 IPMIThresholds resp;
1070 auto warningInterface =
1071 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1072 auto criticalInterface =
1073 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1074
1075 if ((warningInterface != sensorMap.end()) ||
1076 (criticalInterface != sensorMap.end()))
1077 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001078 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001079
1080 if (sensorPair == sensorMap.end())
1081 {
1082 // should not have been able to find a sensor not implementing
1083 // the sensor object
1084 throw std::runtime_error("Invalid sensor map");
1085 }
1086
1087 double max = 0;
1088 double min = 0;
1089 getSensorMaxMin(sensorMap, max, min);
1090
1091 int16_t mValue = 0;
1092 int16_t bValue = 0;
1093 int8_t rExp = 0;
1094 int8_t bExp = 0;
1095 bool bSigned = false;
1096
1097 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1098 {
1099 throw std::runtime_error("Invalid sensor atrributes");
1100 }
1101 if (warningInterface != sensorMap.end())
1102 {
1103 auto& warningMap = warningInterface->second;
1104
1105 auto warningHigh = warningMap.find("WarningHigh");
1106 auto warningLow = warningMap.find("WarningLow");
1107
1108 if (warningHigh != warningMap.end())
1109 {
1110
1111 double value =
1112 std::visit(VariantToDoubleVisitor(), warningHigh->second);
1113 resp.warningHigh = scaleIPMIValueFromDouble(
1114 value, mValue, rExp, bValue, bExp, bSigned);
1115 }
1116 if (warningLow != warningMap.end())
1117 {
1118 double value =
1119 std::visit(VariantToDoubleVisitor(), warningLow->second);
1120 resp.warningLow = scaleIPMIValueFromDouble(
1121 value, mValue, rExp, bValue, bExp, bSigned);
1122 }
1123 }
1124 if (criticalInterface != sensorMap.end())
1125 {
1126 auto& criticalMap = criticalInterface->second;
1127
1128 auto criticalHigh = criticalMap.find("CriticalHigh");
1129 auto criticalLow = criticalMap.find("CriticalLow");
1130
1131 if (criticalHigh != criticalMap.end())
1132 {
1133 double value =
1134 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1135 resp.criticalHigh = scaleIPMIValueFromDouble(
1136 value, mValue, rExp, bValue, bExp, bSigned);
1137 }
1138 if (criticalLow != criticalMap.end())
1139 {
1140 double value =
1141 std::visit(VariantToDoubleVisitor(), criticalLow->second);
1142 resp.criticalLow = scaleIPMIValueFromDouble(
1143 value, mValue, rExp, bValue, bExp, bSigned);
1144 }
1145 }
1146 }
1147 return resp;
1148}
1149
1150ipmi::RspType<uint8_t, // readable
1151 uint8_t, // lowerNCrit
1152 uint8_t, // lowerCrit
1153 uint8_t, // lowerNrecoverable
1154 uint8_t, // upperNC
1155 uint8_t, // upperCrit
1156 uint8_t> // upperNRecoverable
1157 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1158{
1159 std::string connection;
1160 std::string path;
1161
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001162 if (sensorNumber == reservedSensorNumber)
1163 {
1164 return ipmi::responseInvalidFieldRequest();
1165 }
1166
Willy Tude54f482021-01-26 15:59:09 -08001167 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1168 if (status)
1169 {
1170 return ipmi::response(status);
1171 }
1172
1173 DbusInterfaceMap sensorMap;
1174 if (!getSensorMap(ctx, connection, path, sensorMap))
1175 {
1176 return ipmi::responseResponseError();
1177 }
1178
1179 IPMIThresholds thresholdData;
1180 try
1181 {
1182 thresholdData = getIPMIThresholds(sensorMap);
1183 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001184 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001185 {
1186 return ipmi::responseResponseError();
1187 }
1188
1189 uint8_t readable = 0;
1190 uint8_t lowerNC = 0;
1191 uint8_t lowerCritical = 0;
1192 uint8_t lowerNonRecoverable = 0;
1193 uint8_t upperNC = 0;
1194 uint8_t upperCritical = 0;
1195 uint8_t upperNonRecoverable = 0;
1196
1197 if (thresholdData.warningHigh)
1198 {
1199 readable |=
1200 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1201 upperNC = *thresholdData.warningHigh;
1202 }
1203 if (thresholdData.warningLow)
1204 {
1205 readable |=
1206 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1207 lowerNC = *thresholdData.warningLow;
1208 }
1209
1210 if (thresholdData.criticalHigh)
1211 {
1212 readable |=
1213 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1214 upperCritical = *thresholdData.criticalHigh;
1215 }
1216 if (thresholdData.criticalLow)
1217 {
1218 readable |=
1219 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1220 lowerCritical = *thresholdData.criticalLow;
1221 }
1222
1223 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1224 lowerNonRecoverable, upperNC, upperCritical,
1225 upperNonRecoverable);
1226}
1227
1228/** @brief implements the get Sensor event enable command
1229 * @param sensorNumber - sensor number
1230 *
1231 * @returns IPMI completion code plus response data
1232 * - enabled - Sensor Event messages
1233 * - assertionEnabledLsb - Assertion event messages
1234 * - assertionEnabledMsb - Assertion event messages
1235 * - deassertionEnabledLsb - Deassertion event messages
1236 * - deassertionEnabledMsb - Deassertion event messages
1237 */
1238
1239ipmi::RspType<uint8_t, // enabled
1240 uint8_t, // assertionEnabledLsb
1241 uint8_t, // assertionEnabledMsb
1242 uint8_t, // deassertionEnabledLsb
1243 uint8_t> // deassertionEnabledMsb
1244 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1245{
1246 std::string connection;
1247 std::string path;
1248
1249 uint8_t enabled = 0;
1250 uint8_t assertionEnabledLsb = 0;
1251 uint8_t assertionEnabledMsb = 0;
1252 uint8_t deassertionEnabledLsb = 0;
1253 uint8_t deassertionEnabledMsb = 0;
1254
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001255 if (sensorNum == reservedSensorNumber)
1256 {
1257 return ipmi::responseInvalidFieldRequest();
1258 }
1259
Willy Tude54f482021-01-26 15:59:09 -08001260 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1261 if (status)
1262 {
1263 return ipmi::response(status);
1264 }
1265
Scron Chang2703b022021-07-06 15:47:45 +08001266#ifdef FEATURE_HYBRID_SENSORS
1267 if (auto sensor = findStaticSensor(path);
1268 sensor != ipmi::sensor::sensors.end() &&
1269 getSensorEventTypeFromPath(path) !=
1270 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1271 {
1272 enabled = static_cast<uint8_t>(
1273 IPMISensorEventEnableByte2::sensorScanningEnable);
1274 uint16_t assertionEnabled = 0;
1275 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1276 ->second.begin()
1277 ->second.second)
1278 {
1279 assertionEnabled |= (1 << offsetValMap.first);
1280 }
1281 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1282 assertionEnabledMsb =
1283 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1284
1285 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1286 assertionEnabledMsb, deassertionEnabledLsb,
1287 deassertionEnabledMsb);
1288 }
1289#endif
1290
Willy Tude54f482021-01-26 15:59:09 -08001291 DbusInterfaceMap sensorMap;
1292 if (!getSensorMap(ctx, connection, path, sensorMap))
1293 {
1294 return ipmi::responseResponseError();
1295 }
1296
1297 auto warningInterface =
1298 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1299 auto criticalInterface =
1300 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1301 if ((warningInterface != sensorMap.end()) ||
1302 (criticalInterface != sensorMap.end()))
1303 {
1304 enabled = static_cast<uint8_t>(
1305 IPMISensorEventEnableByte2::sensorScanningEnable);
1306 if (warningInterface != sensorMap.end())
1307 {
1308 auto& warningMap = warningInterface->second;
1309
1310 auto warningHigh = warningMap.find("WarningHigh");
1311 auto warningLow = warningMap.find("WarningLow");
1312 if (warningHigh != warningMap.end())
1313 {
1314 assertionEnabledLsb |= static_cast<uint8_t>(
1315 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1316 deassertionEnabledLsb |= static_cast<uint8_t>(
1317 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1318 }
1319 if (warningLow != warningMap.end())
1320 {
1321 assertionEnabledLsb |= static_cast<uint8_t>(
1322 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1323 deassertionEnabledLsb |= static_cast<uint8_t>(
1324 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1325 }
1326 }
1327 if (criticalInterface != sensorMap.end())
1328 {
1329 auto& criticalMap = criticalInterface->second;
1330
1331 auto criticalHigh = criticalMap.find("CriticalHigh");
1332 auto criticalLow = criticalMap.find("CriticalLow");
1333
1334 if (criticalHigh != criticalMap.end())
1335 {
1336 assertionEnabledMsb |= static_cast<uint8_t>(
1337 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1338 deassertionEnabledMsb |= static_cast<uint8_t>(
1339 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1340 }
1341 if (criticalLow != criticalMap.end())
1342 {
1343 assertionEnabledLsb |= static_cast<uint8_t>(
1344 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1345 deassertionEnabledLsb |= static_cast<uint8_t>(
1346 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1347 }
1348 }
1349 }
1350
1351 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1352 assertionEnabledMsb, deassertionEnabledLsb,
1353 deassertionEnabledMsb);
1354}
1355
1356/** @brief implements the get Sensor event status command
1357 * @param sensorNumber - sensor number, FFh = reserved
1358 *
1359 * @returns IPMI completion code plus response data
1360 * - sensorEventStatus - Sensor Event messages state
1361 * - assertions - Assertion event messages
1362 * - deassertions - Deassertion event messages
1363 */
1364ipmi::RspType<uint8_t, // sensorEventStatus
1365 std::bitset<16>, // assertions
1366 std::bitset<16> // deassertion
1367 >
1368 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1369{
1370 if (sensorNum == reservedSensorNumber)
1371 {
1372 return ipmi::responseInvalidFieldRequest();
1373 }
1374
1375 std::string connection;
1376 std::string path;
1377 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1378 if (status)
1379 {
1380 phosphor::logging::log<phosphor::logging::level::ERR>(
1381 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1382 phosphor::logging::entry("SENSOR=%d", sensorNum));
1383 return ipmi::response(status);
1384 }
1385
Scron Chang2703b022021-07-06 15:47:45 +08001386#ifdef FEATURE_HYBRID_SENSORS
1387 if (auto sensor = findStaticSensor(path);
1388 sensor != ipmi::sensor::sensors.end() &&
1389 getSensorEventTypeFromPath(path) !=
1390 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1391 {
1392 auto response = ipmi::sensor::get::mapDbusToAssertion(
1393 sensor->second, path, sensor->second.sensorInterface);
1394 std::bitset<16> assertions;
1395 // deassertions are not used.
1396 std::bitset<16> deassertions = 0;
1397 uint8_t sensorEventStatus;
1398 if (response.readingOrStateUnavailable)
1399 {
1400 sensorEventStatus |= static_cast<uint8_t>(
1401 IPMISensorReadingByte2::readingStateUnavailable);
1402 }
1403 if (response.scanningEnabled)
1404 {
1405 sensorEventStatus |= static_cast<uint8_t>(
1406 IPMISensorReadingByte2::sensorScanningEnable);
1407 }
1408 if (response.allEventMessagesEnabled)
1409 {
1410 sensorEventStatus |= static_cast<uint8_t>(
1411 IPMISensorReadingByte2::eventMessagesEnable);
1412 }
1413 assertions |= response.discreteReadingSensorStates << 8;
1414 assertions |= response.thresholdLevelsStates;
1415 return ipmi::responseSuccess(sensorEventStatus, assertions,
1416 deassertions);
1417 }
1418#endif
1419
Willy Tude54f482021-01-26 15:59:09 -08001420 DbusInterfaceMap sensorMap;
1421 if (!getSensorMap(ctx, connection, path, sensorMap))
1422 {
1423 phosphor::logging::log<phosphor::logging::level::ERR>(
1424 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1425 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1426 return ipmi::responseResponseError();
1427 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001428
1429 uint8_t sensorEventStatus =
1430 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1431 std::bitset<16> assertions = 0;
1432 std::bitset<16> deassertions = 0;
1433
1434 // handle VR typed sensor
1435 auto vrInterface = sensorMap.find(sensor::vrInterface);
1436 if (vrInterface != sensorMap.end())
1437 {
1438 if (!sensor::getVrEventStatus(ctx, connection, path,
1439 vrInterface->second, assertions))
1440 {
1441 return ipmi::responseResponseError();
1442 }
1443
1444 // both Event Message and Sensor Scanning are disable for VR.
1445 sensorEventStatus = 0;
1446 return ipmi::responseSuccess(sensorEventStatus, assertions,
1447 deassertions);
1448 }
1449
Willy Tude54f482021-01-26 15:59:09 -08001450 auto warningInterface =
1451 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1452 auto criticalInterface =
1453 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1454
Willy Tude54f482021-01-26 15:59:09 -08001455 std::optional<bool> criticalDeassertHigh =
1456 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1457 std::optional<bool> criticalDeassertLow =
1458 thresholdDeassertMap[path]["CriticalAlarmLow"];
1459 std::optional<bool> warningDeassertHigh =
1460 thresholdDeassertMap[path]["WarningAlarmHigh"];
1461 std::optional<bool> warningDeassertLow =
1462 thresholdDeassertMap[path]["WarningAlarmLow"];
1463
Willy Tude54f482021-01-26 15:59:09 -08001464 if (criticalDeassertHigh && !*criticalDeassertHigh)
1465 {
1466 deassertions.set(static_cast<size_t>(
1467 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1468 }
1469 if (criticalDeassertLow && !*criticalDeassertLow)
1470 {
1471 deassertions.set(static_cast<size_t>(
1472 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1473 }
1474 if (warningDeassertHigh && !*warningDeassertHigh)
1475 {
1476 deassertions.set(static_cast<size_t>(
1477 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1478 }
1479 if (warningDeassertLow && !*warningDeassertLow)
1480 {
1481 deassertions.set(static_cast<size_t>(
1482 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1483 }
1484 if ((warningInterface != sensorMap.end()) ||
1485 (criticalInterface != sensorMap.end()))
1486 {
1487 sensorEventStatus = static_cast<size_t>(
1488 IPMISensorEventEnableByte2::eventMessagesEnable);
1489 if (warningInterface != sensorMap.end())
1490 {
1491 auto& warningMap = warningInterface->second;
1492
1493 auto warningHigh = warningMap.find("WarningAlarmHigh");
1494 auto warningLow = warningMap.find("WarningAlarmLow");
1495 auto warningHighAlarm = false;
1496 auto warningLowAlarm = false;
1497
1498 if (warningHigh != warningMap.end())
1499 {
1500 warningHighAlarm = std::get<bool>(warningHigh->second);
1501 }
1502 if (warningLow != warningMap.end())
1503 {
1504 warningLowAlarm = std::get<bool>(warningLow->second);
1505 }
1506 if (warningHighAlarm)
1507 {
1508 assertions.set(
1509 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1510 upperNonCriticalGoingHigh));
1511 }
1512 if (warningLowAlarm)
1513 {
1514 assertions.set(
1515 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1516 lowerNonCriticalGoingLow));
1517 }
1518 }
1519 if (criticalInterface != sensorMap.end())
1520 {
1521 auto& criticalMap = criticalInterface->second;
1522
1523 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1524 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1525 auto criticalHighAlarm = false;
1526 auto criticalLowAlarm = false;
1527
1528 if (criticalHigh != criticalMap.end())
1529 {
1530 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1531 }
1532 if (criticalLow != criticalMap.end())
1533 {
1534 criticalLowAlarm = std::get<bool>(criticalLow->second);
1535 }
1536 if (criticalHighAlarm)
1537 {
1538 assertions.set(
1539 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1540 upperCriticalGoingHigh));
1541 }
1542 if (criticalLowAlarm)
1543 {
1544 assertions.set(static_cast<size_t>(
1545 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1546 }
1547 }
1548 }
1549
1550 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1551}
1552
Willy Tu38e7a2b2021-03-29 15:09:56 -07001553// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001554void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1555 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001556{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001557 get_sdr::header::set_record_id(
1558 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1559
Willy Tu38e7a2b2021-03-29 15:09:56 -07001560 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1561 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1562
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001563 record.header.sdr_version = ipmiSdrVersion;
1564 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1565 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1566 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001567 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001568 record.key.owner_lun = lun;
1569 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001570}
1571bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1572 uint16_t recordID, const std::string& service,
1573 const std::string& path,
1574 get_sdr::SensorDataFullRecord& record)
1575{
1576 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1577 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1578
1579 DbusInterfaceMap sensorMap;
1580 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1581 {
1582 phosphor::logging::log<phosphor::logging::level::ERR>(
1583 "Failed to update sensor map for threshold sensor",
1584 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1585 phosphor::logging::entry("PATH=%s", path.c_str()));
1586 return false;
1587 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001588
1589 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1590 record.body.sensor_type = getSensorTypeFromPath(path);
1591 std::string type = getSensorTypeStringFromPath(path);
1592 auto typeCstr = type.c_str();
1593 auto findUnits = sensorUnits.find(typeCstr);
1594 if (findUnits != sensorUnits.end())
1595 {
1596 record.body.sensor_units_2_base =
1597 static_cast<uint8_t>(findUnits->second);
1598 } // else default 0x0 unspecified
1599
1600 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1601
Hao Jiangd2afd052020-12-10 15:09:32 -08001602 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001603 if (sensorObject == sensorMap.end())
1604 {
1605 phosphor::logging::log<phosphor::logging::level::ERR>(
1606 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001607 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001608 }
1609
1610 uint8_t entityId = 0;
1611 uint8_t entityInstance = 0x01;
1612
1613 // follow the association chain to get the parent board's entityid and
1614 // entityInstance
1615 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1616
1617 record.body.entity_id = entityId;
1618 record.body.entity_instance = entityInstance;
1619
Shakeeb Pasha93889722021-10-14 10:20:13 +05301620 double max = 0;
1621 double min = 0;
1622 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001623
1624 int16_t mValue = 0;
1625 int8_t rExp = 0;
1626 int16_t bValue = 0;
1627 int8_t bExp = 0;
1628 bool bSigned = false;
1629
1630 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1631 {
1632 phosphor::logging::log<phosphor::logging::level::ERR>(
1633 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001634 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001635 }
1636
1637 // The record.body is a struct SensorDataFullRecordBody
1638 // from sensorhandler.hpp in phosphor-ipmi-host.
1639 // The meaning of these bits appears to come from
1640 // table 43.1 of the IPMI spec.
1641 // The above 5 sensor attributes are stuffed in as follows:
1642 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1643 // Byte 22-24 are for other purposes
1644 // Byte 25 = MMMMMMMM = LSB of M
1645 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1646 // Byte 27 = BBBBBBBB = LSB of B
1647 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1648 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1649 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1650
1651 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1652 record.body.m_lsb = mValue & 0xFF;
1653
1654 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1655 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1656
1657 // move the smallest bit of the MSB into place (bit 9)
1658 // the MSbs are bits 7:8 in m_msb_and_tolerance
1659 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1660
1661 record.body.b_lsb = bValue & 0xFF;
1662
1663 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1664 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1665
1666 // move the smallest bit of the MSB into place (bit 9)
1667 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1668 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1669
1670 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1671 uint8_t rExpBits = rExp & 0x07;
1672
1673 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1674 uint8_t bExpBits = bExp & 0x07;
1675
1676 // move rExp and bExp into place
1677 record.body.r_b_exponents =
1678 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1679
1680 // Set the analog reading byte interpretation accordingly
1681 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1682
1683 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1684 // These seem redundant, but derivable from the above 5 attributes
1685 // Original comment said "todo fill out rest of units"
1686
1687 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001688 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001689 record.body.id_string_info = name.size();
1690 std::strncpy(record.body.id_string, name.c_str(),
1691 sizeof(record.body.id_string));
1692
Josh Lehana55c9532020-10-28 21:59:06 -07001693 // Remember the sensor name, as determined for this sensor number
1694 details::sdrStatsTable.updateName(sensornumber, name);
1695
Jie Yangf0a89942021-07-29 15:30:25 -07001696 bool sensorSettable = false;
1697 auto mutability =
1698 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1699 if (mutability != sensorMap.end())
1700 {
1701 sensorSettable =
1702 mappedVariant<bool>(mutability->second, "Mutable", false);
1703 }
1704 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1705
1706 // Grant write permission to sensors deemed externally settable
1707 details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001708
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001709 IPMIThresholds thresholdData;
1710 try
1711 {
1712 thresholdData = getIPMIThresholds(sensorMap);
1713 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001714 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001715 {
1716 phosphor::logging::log<phosphor::logging::level::ERR>(
1717 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001718 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001719 }
1720
1721 if (thresholdData.criticalHigh)
1722 {
1723 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1724 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1725 IPMISensorEventEnableThresholds::criticalThreshold);
1726 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1727 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1728 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1729 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1730 record.body.discrete_reading_setting_mask[0] |=
1731 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1732 }
1733 if (thresholdData.warningHigh)
1734 {
1735 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1736 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1737 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1738 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1739 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1740 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1741 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1742 record.body.discrete_reading_setting_mask[0] |=
1743 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1744 }
1745 if (thresholdData.criticalLow)
1746 {
1747 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1748 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1749 IPMISensorEventEnableThresholds::criticalThreshold);
1750 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1751 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1752 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1753 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1754 record.body.discrete_reading_setting_mask[0] |=
1755 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1756 }
1757 if (thresholdData.warningLow)
1758 {
1759 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1760 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1761 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1762 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1763 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1764 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1765 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1766 record.body.discrete_reading_setting_mask[0] |=
1767 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1768 }
1769
1770 // everything that is readable is setable
1771 record.body.discrete_reading_setting_mask[1] =
1772 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001773 return true;
1774}
1775
Scron Chang2703b022021-07-06 15:47:45 +08001776#ifdef FEATURE_HYBRID_SENSORS
1777// Construct a type 1 SDR for discrete Sensor typed sensor.
1778void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1779 uint16_t recordID,
1780 ipmi::sensor::IdInfoMap::const_iterator sensor,
1781 get_sdr::SensorDataFullRecord& record)
1782{
1783 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1784
1785 record.body.entity_id = sensor->second.entityType;
1786 record.body.sensor_type = sensor->second.sensorType;
1787 record.body.event_reading_type = sensor->second.sensorReadingType;
1788 record.body.entity_instance = sensor->second.instance;
1789 if (ipmi::sensor::Mutability::Write ==
1790 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1791 {
1792 get_sdr::body::init_settable_state(true, &(record.body));
1793 }
1794
1795 auto id_string = sensor->second.sensorName;
1796
1797 if (id_string.empty())
1798 {
1799 id_string = sensor->second.sensorNameFunc(sensor->second);
1800 }
1801
1802 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1803 {
1804 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1805 &(record.body));
1806 }
1807 else
1808 {
1809 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1810 }
1811 std::strncpy(record.body.id_string, id_string.c_str(),
1812 get_sdr::body::get_id_strlen(&(record.body)));
1813}
1814#endif
1815
Hao Jiange39d4d82021-04-16 17:02:40 -07001816// Construct type 3 SDR header and key (for VR and other discrete sensors)
1817void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1818 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001819{
1820 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1821 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1822
1823 get_sdr::header::set_record_id(
1824 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1825
1826 record.header.sdr_version = ipmiSdrVersion;
1827 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1828 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1829 sizeof(get_sdr::SensorDataRecordHeader);
1830 record.key.owner_id = bmcI2CAddr;
1831 record.key.owner_lun = lun;
1832 record.key.sensor_number = sensornumber;
1833
1834 record.body.entity_id = 0x00;
1835 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001836}
Willy Tu61992ad2021-03-29 15:33:20 -07001837
Hao Jiange39d4d82021-04-16 17:02:40 -07001838// Construct a type 3 SDR for VR typed sensor(daemon).
1839bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1840 uint16_t recordID, const std::string& service,
1841 const std::string& path,
1842 get_sdr::SensorDataEventRecord& record)
1843{
1844 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1845 constructEventSdrHeaderKey(sensorNum, recordID, record);
1846
1847 DbusInterfaceMap sensorMap;
1848 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1849 {
1850 phosphor::logging::log<phosphor::logging::level::ERR>(
1851 "Failed to update sensor map for VR sensor",
1852 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1853 phosphor::logging::entry("PATH=%s", path.c_str()));
1854 return false;
1855 }
Willy Tu61992ad2021-03-29 15:33:20 -07001856 // follow the association chain to get the parent board's entityid and
1857 // entityInstance
1858 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1859 record.body.entity_instance);
1860
1861 // Sensor type is hardcoded as a module/board type instead of parsing from
1862 // sensor path. This is because VR control is allocated in an independent
1863 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1864 // types.
1865 static constexpr const uint8_t module_board_type = 0x15;
1866 record.body.sensor_type = module_board_type;
1867 record.body.event_reading_type = 0x00;
1868
1869 record.body.sensor_record_sharing_1 = 0x00;
1870 record.body.sensor_record_sharing_2 = 0x00;
1871
1872 // populate sensor name from path
1873 auto name = sensor::parseSdrIdFromPath(path);
1874 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1875 record.body.id_string_info = nameSize;
1876 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1877 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1878
1879 // Remember the sensor name, as determined for this sensor number
1880 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001881
1882 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001883}
1884
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001885static inline uint16_t getNumberOfSensors()
1886{
1887 return std::min(getSensorTree().size(), maxIPMISensors);
1888}
1889
Hao Jiange39d4d82021-04-16 17:02:40 -07001890static int
1891 getSensorDataRecord(ipmi::Context::ptr ctx,
1892 std::vector<uint8_t>& recordData, uint16_t recordID,
1893 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001894{
1895 size_t fruCount = 0;
1896 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1897 if (ret != ipmi::ccSuccess)
1898 {
1899 phosphor::logging::log<phosphor::logging::level::ERR>(
1900 "getSensorDataRecord: getFruSdrCount error");
1901 return GENERAL_ERROR;
1902 }
1903
Willy Tu38e7a2b2021-03-29 15:09:56 -07001904 size_t lastRecord =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001905 getNumberOfSensors() + fruCount + ipmi::storage::type12Count - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001906 if (recordID == lastRecordIndex)
1907 {
1908 recordID = lastRecord;
1909 }
1910 if (recordID > lastRecord)
1911 {
1912 phosphor::logging::log<phosphor::logging::level::ERR>(
1913 "getSensorDataRecord: recordID > lastRecord error");
1914 return GENERAL_ERROR;
1915 }
1916
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001917 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001918 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001919 size_t fruIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001920
1921 if (fruIndex >= fruCount)
1922 {
1923 // handle type 12 hardcoded records
1924 size_t type12Index = fruIndex - fruCount;
1925 if (type12Index >= ipmi::storage::type12Count)
1926 {
1927 phosphor::logging::log<phosphor::logging::level::ERR>(
1928 "getSensorDataRecord: type12Index error");
1929 return GENERAL_ERROR;
1930 }
1931 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1932 }
1933 else
1934 {
1935 // handle fru records
1936 get_sdr::SensorDataFruRecord data;
1937 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1938 if (ret != IPMI_CC_OK)
1939 {
1940 return GENERAL_ERROR;
1941 }
1942 data.header.record_id_msb = recordID >> 8;
1943 data.header.record_id_lsb = recordID & 0xFF;
1944 recordData.insert(recordData.end(), (uint8_t*)&data,
1945 ((uint8_t*)&data) + sizeof(data));
1946 }
1947
1948 return 0;
1949 }
1950
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001951 // Perform a incremental scan of the SDR Record ID's and translate the
1952 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1953 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1954 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
1955 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07001956 std::string connection;
1957 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001958 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07001959 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001960 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07001961 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001962 // LUN 0 has one reserved sensor number. Compensate here by adding one
1963 // to the record ID
1964 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07001965 ctx->lun = 1;
1966 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001967 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07001968 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001969 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
1970 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
1971 // rules governing its use.
1972 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07001973 ctx->lun = 3;
1974 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001975
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001976 auto status =
1977 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
1978 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001979 if (status)
1980 {
1981 phosphor::logging::log<phosphor::logging::level::ERR>(
1982 "getSensorDataRecord: getSensorConnection error");
1983 return GENERAL_ERROR;
1984 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001985 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001986 // Return an error on LUN 2 assingments, and any sensor number beyond the
1987 // range of LUN 3
1988 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
1989 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07001990 {
1991 phosphor::logging::log<phosphor::logging::level::ERR>(
1992 "getSensorDataRecord: invalidSensorNumber");
1993 return GENERAL_ERROR;
1994 }
Johnathan Manteyce982772021-07-28 15:08:30 -07001995 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1996 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1997
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001998 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
1999 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002000 {
2001 phosphor::logging::log<phosphor::logging::level::ERR>(
2002 "getSensorDataRecord: sensor record mismatch");
2003 return GENERAL_ERROR;
2004 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002005
Willy Tu38e7a2b2021-03-29 15:09:56 -07002006 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002007 if (std::find(interfaces.begin(), interfaces.end(),
2008 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002009 {
2010 get_sdr::SensorDataFullRecord record = {0};
2011
Hao Jiange39d4d82021-04-16 17:02:40 -07002012 // If the request doesn't read SDR body, construct only header and key
2013 // part to avoid additional DBus transaction.
2014 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2015 {
2016 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2017 }
2018 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2019 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002020 {
2021 return GENERAL_ERROR;
2022 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002023
Willy Tu38e7a2b2021-03-29 15:09:56 -07002024 recordData.insert(recordData.end(), (uint8_t*)&record,
2025 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002026
2027 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002028 }
Willy Tu61992ad2021-03-29 15:33:20 -07002029
Scron Chang2703b022021-07-06 15:47:45 +08002030#ifdef FEATURE_HYBRID_SENSORS
2031 if (auto sensor = findStaticSensor(path);
2032 sensor != ipmi::sensor::sensors.end() &&
2033 getSensorEventTypeFromPath(path) !=
2034 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2035 {
2036 get_sdr::SensorDataFullRecord record = {0};
2037
2038 // If the request doesn't read SDR body, construct only header and key
2039 // part to avoid additional DBus transaction.
2040 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2041 {
2042 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2043 }
2044 else
2045 {
2046 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2047 }
2048
2049 recordData.insert(recordData.end(), (uint8_t*)&record,
2050 ((uint8_t*)&record) + sizeof(record));
2051
2052 return 0;
2053 }
2054#endif
2055
Willy Tu61992ad2021-03-29 15:33:20 -07002056 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002057 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2058 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002059 {
2060 get_sdr::SensorDataEventRecord record = {0};
2061
Hao Jiange39d4d82021-04-16 17:02:40 -07002062 // If the request doesn't read SDR body, construct only header and key
2063 // part to avoid additional DBus transaction.
2064 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2065 {
2066 constructEventSdrHeaderKey(sensorNum, recordID, record);
2067 }
2068 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2069 record))
2070 {
2071 return GENERAL_ERROR;
2072 }
Willy Tu61992ad2021-03-29 15:33:20 -07002073 recordData.insert(recordData.end(), (uint8_t*)&record,
2074 ((uint8_t*)&record) + sizeof(record));
2075 }
2076
Willy Tude54f482021-01-26 15:59:09 -08002077 return 0;
2078}
2079
2080/** @brief implements the get SDR Info command
2081 * @param count - Operation
2082 *
2083 * @returns IPMI completion code plus response data
2084 * - sdrCount - sensor/SDR count
2085 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2086 */
2087static ipmi::RspType<uint8_t, // respcount
2088 uint8_t, // dynamic population flags
2089 uint32_t // last time a sensor was added
2090 >
2091 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2092 std::optional<uint8_t> count)
2093{
2094 auto& sensorTree = getSensorTree();
2095 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002096 uint16_t recordID = 0;
2097 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002098 // Sensors are dynamically allocated, and there is at least one LUN
2099 uint8_t lunsAndDynamicPopulation = 0x80;
2100 constexpr uint8_t getSdrCount = 0x01;
2101 constexpr uint8_t getSensorCount = 0x00;
2102
2103 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2104 {
2105 return ipmi::responseResponseError();
2106 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002107 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002108 if (count.value_or(0) == getSdrCount)
2109 {
2110 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002111 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002112 {
2113 get_sdr::SensorDataRecordHeader* hdr =
2114 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002115 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002116 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2117 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002118 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002119 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002120 record.data());
2121 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002122 {
2123 sdrCount++;
2124 }
2125 }
2126 }
2127 }
2128 else if (count.value_or(0) == getSensorCount)
2129 {
2130 // Return the number of sensors attached to the LUN
2131 if ((ctx->lun == 0) && (numSensors > 0))
2132 {
2133 sdrCount =
2134 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2135 }
2136 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2137 {
2138 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2139 ? maxSensorsPerLUN
2140 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2141 }
2142 else if (ctx->lun == 3)
2143 {
2144 if (numSensors <= maxIPMISensors)
2145 {
2146 sdrCount =
2147 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2148 }
2149 else
2150 {
2151 // error
2152 throw std::out_of_range(
2153 "Maximum number of IPMI sensors exceeded.");
2154 }
2155 }
2156 }
2157 else
2158 {
2159 return ipmi::responseInvalidFieldRequest();
2160 }
2161
2162 // Get Sensor count. This returns the number of sensors
2163 if (numSensors > 0)
2164 {
2165 lunsAndDynamicPopulation |= 1;
2166 }
2167 if (numSensors > maxSensorsPerLUN)
2168 {
2169 lunsAndDynamicPopulation |= 2;
2170 }
2171 if (numSensors >= (maxSensorsPerLUN * 2))
2172 {
2173 lunsAndDynamicPopulation |= 8;
2174 }
2175 if (numSensors > maxIPMISensors)
2176 {
2177 // error
2178 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2179 }
2180
2181 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2182 sdrLastAdd);
2183}
2184
2185/* end sensor commands */
2186
2187/* storage commands */
2188
2189ipmi::RspType<uint8_t, // sdr version
2190 uint16_t, // record count
2191 uint16_t, // free space
2192 uint32_t, // most recent addition
2193 uint32_t, // most recent erase
2194 uint8_t // operationSupport
2195 >
2196 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2197{
2198 auto& sensorTree = getSensorTree();
2199 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002200 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002201 {
2202 return ipmi::responseResponseError();
2203 }
2204
2205 size_t fruCount = 0;
2206 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2207 if (ret != ipmi::ccSuccess)
2208 {
2209 return ipmi::response(ret);
2210 }
2211
2212 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002213 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002214
2215 uint8_t operationSupport = static_cast<uint8_t>(
2216 SdrRepositoryInfoOps::overflow); // write not supported
2217
2218 operationSupport |=
2219 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2220 operationSupport |= static_cast<uint8_t>(
2221 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2222 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2223 unspecifiedFreeSpace, sdrLastAdd,
2224 sdrLastRemove, operationSupport);
2225}
2226
2227/** @brief implements the get SDR allocation info command
2228 *
2229 * @returns IPMI completion code plus response data
2230 * - allocUnits - Number of possible allocation units
2231 * - allocUnitSize - Allocation unit size in bytes.
2232 * - allocUnitFree - Number of free allocation units
2233 * - allocUnitLargestFree - Largest free block in allocation units
2234 * - maxRecordSize - Maximum record size in allocation units.
2235 */
2236ipmi::RspType<uint16_t, // allocUnits
2237 uint16_t, // allocUnitSize
2238 uint16_t, // allocUnitFree
2239 uint16_t, // allocUnitLargestFree
2240 uint8_t // maxRecordSize
2241 >
2242 ipmiStorageGetSDRAllocationInfo()
2243{
2244 // 0000h unspecified number of alloc units
2245 constexpr uint16_t allocUnits = 0;
2246
2247 constexpr uint16_t allocUnitFree = 0;
2248 constexpr uint16_t allocUnitLargestFree = 0;
2249 // only allow one block at a time
2250 constexpr uint8_t maxRecordSize = 1;
2251
2252 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2253 allocUnitLargestFree, maxRecordSize);
2254}
2255
2256/** @brief implements the reserve SDR command
2257 * @returns IPMI completion code plus response data
2258 * - sdrReservationID
2259 */
2260ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2261{
2262 sdrReservationID++;
2263 if (sdrReservationID == 0)
2264 {
2265 sdrReservationID++;
2266 }
2267
2268 return ipmi::responseSuccess(sdrReservationID);
2269}
2270
2271ipmi::RspType<uint16_t, // next record ID
2272 std::vector<uint8_t> // payload
2273 >
2274 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2275 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2276{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002277 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002278 // reservation required for partial reads with non zero offset into
2279 // record
2280 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2281 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002282 phosphor::logging::log<phosphor::logging::level::ERR>(
2283 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002284 return ipmi::responseInvalidReservationId();
2285 }
Willy Tude54f482021-01-26 15:59:09 -08002286 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2287 if (ret != ipmi::ccSuccess)
2288 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002289 phosphor::logging::log<phosphor::logging::level::ERR>(
2290 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002291 return ipmi::response(ret);
2292 }
2293
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002294 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08002295 size_t lastRecord =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002296 getNumberOfSensors() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002297 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2298
2299 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002300 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002301 phosphor::logging::log<phosphor::logging::level::ERR>(
2302 "ipmiStorageGetSDR: getSensorSubtree error");
2303 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002304 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002305
2306 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002307 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002308 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002309 phosphor::logging::log<phosphor::logging::level::ERR>(
2310 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002311 return ipmi::responseInvalidFieldRequest();
2312 }
Willy Tude54f482021-01-26 15:59:09 -08002313 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002314 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002315 if (!hdr)
2316 {
2317 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002318 "ipmiStorageGetSDR: record header is null");
2319 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002320 }
2321
2322 size_t sdrLength =
2323 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2324 if (sdrLength < (offset + bytesToRead))
2325 {
2326 bytesToRead = sdrLength - offset;
2327 }
2328
2329 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2330 if (!respStart)
2331 {
2332 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002333 "ipmiStorageGetSDR: record is null");
2334 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002335 }
2336
2337 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002338
Willy Tude54f482021-01-26 15:59:09 -08002339 return ipmi::responseSuccess(nextRecordId, recordData);
2340}
2341/* end storage commands */
2342
2343void registerSensorFunctions()
2344{
2345 // <Platform Event>
2346 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2347 ipmi::sensor_event::cmdPlatformEvent,
2348 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2349
Willy Tudbafbce2021-03-29 00:37:05 -07002350 // <Set Sensor Reading and Event Status>
2351 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2352 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2353 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002354
Willy Tude54f482021-01-26 15:59:09 -08002355 // <Get Sensor Reading>
2356 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2357 ipmi::sensor_event::cmdGetSensorReading,
2358 ipmi::Privilege::User, ipmiSenGetSensorReading);
2359
2360 // <Get Sensor Threshold>
2361 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2362 ipmi::sensor_event::cmdGetSensorThreshold,
2363 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2364
2365 // <Set Sensor Threshold>
2366 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2367 ipmi::sensor_event::cmdSetSensorThreshold,
2368 ipmi::Privilege::Operator,
2369 ipmiSenSetSensorThresholds);
2370
2371 // <Get Sensor Event Enable>
2372 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2373 ipmi::sensor_event::cmdGetSensorEventEnable,
2374 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2375
2376 // <Get Sensor Event Status>
2377 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2378 ipmi::sensor_event::cmdGetSensorEventStatus,
2379 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2380
2381 // register all storage commands for both Sensor and Storage command
2382 // versions
2383
2384 // <Get SDR Repository Info>
2385 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2386 ipmi::storage::cmdGetSdrRepositoryInfo,
2387 ipmi::Privilege::User,
2388 ipmiStorageGetSDRRepositoryInfo);
2389
2390 // <Get Device SDR Info>
2391 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2392 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2393 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2394
2395 // <Get SDR Allocation Info>
2396 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2397 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2398 ipmi::Privilege::User,
2399 ipmiStorageGetSDRAllocationInfo);
2400
2401 // <Reserve SDR Repo>
2402 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2403 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2404 ipmi::Privilege::User, ipmiStorageReserveSDR);
2405
2406 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2407 ipmi::storage::cmdReserveSdrRepository,
2408 ipmi::Privilege::User, ipmiStorageReserveSDR);
2409
2410 // <Get Sdr>
2411 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2412 ipmi::sensor_event::cmdGetDeviceSdr,
2413 ipmi::Privilege::User, ipmiStorageGetSDR);
2414
2415 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2416 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2417 ipmiStorageGetSDR);
2418}
2419} // namespace ipmi