blob: f15b80b45c6a1acf9b5457c467c0fa52ba918028 [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{
Willy Tude54f482021-01-26 15:59:09 -080046static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070047static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080048
Willy Tu38e7a2b2021-03-29 15:09:56 -070049// BMC I2C address is generally at 0x20
50static constexpr uint8_t bmcI2CAddr = 0x20;
51
Willy Tude54f482021-01-26 15:59:09 -080052constexpr size_t maxSDRTotalSize =
53 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
54constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
55
56static uint16_t sdrReservationID;
57static uint32_t sdrLastAdd = noTimestamp;
58static uint32_t sdrLastRemove = noTimestamp;
59static constexpr size_t lastRecordIndex = 0xFFFF;
60static constexpr int GENERAL_ERROR = -1;
61
Willy Tude54f482021-01-26 15:59:09 -080062static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
63
64// Specify the comparison required to sort and find char* map objects
65struct CmpStr
66{
67 bool operator()(const char* a, const char* b) const
68 {
69 return std::strcmp(a, b) < 0;
70 }
71};
72const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
73 sensorUnits{{{"temperature", SensorUnits::degreesC},
74 {"voltage", SensorUnits::volts},
75 {"current", SensorUnits::amps},
76 {"fan_tach", SensorUnits::rpm},
77 {"power", SensorUnits::watts}}};
78
79void registerSensorFunctions() __attribute__((constructor));
80
81static sdbusplus::bus::match::match sensorAdded(
82 *getSdBus(),
83 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
84 "sensors/'",
85 [](sdbusplus::message::message& m) {
86 getSensorTree().clear();
87 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
88 std::chrono::system_clock::now().time_since_epoch())
89 .count();
90 });
91
92static sdbusplus::bus::match::match sensorRemoved(
93 *getSdBus(),
94 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
95 "sensors/'",
96 [](sdbusplus::message::message& m) {
97 getSensorTree().clear();
98 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
99 std::chrono::system_clock::now().time_since_epoch())
100 .count();
101 });
102
103// this keeps track of deassertions for sensor event status command. A
104// deasertion can only happen if an assertion was seen first.
105static boost::container::flat_map<
106 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
107 thresholdDeassertMap;
108
109static sdbusplus::bus::match::match thresholdChanged(
110 *getSdBus(),
111 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
112 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
113 [](sdbusplus::message::message& m) {
114 boost::container::flat_map<std::string, std::variant<bool, double>>
115 values;
116 m.read(std::string(), values);
117
118 auto findAssert =
119 std::find_if(values.begin(), values.end(), [](const auto& pair) {
120 return pair.first.find("Alarm") != std::string::npos;
121 });
122 if (findAssert != values.end())
123 {
124 auto ptr = std::get_if<bool>(&(findAssert->second));
125 if (ptr == nullptr)
126 {
127 phosphor::logging::log<phosphor::logging::level::ERR>(
128 "thresholdChanged: Assert non bool");
129 return;
130 }
131 if (*ptr)
132 {
133 phosphor::logging::log<phosphor::logging::level::INFO>(
134 "thresholdChanged: Assert",
135 phosphor::logging::entry("SENSOR=%s", m.get_path()));
136 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
137 }
138 else
139 {
140 auto& value =
141 thresholdDeassertMap[m.get_path()][findAssert->first];
142 if (value)
143 {
144 phosphor::logging::log<phosphor::logging::level::INFO>(
145 "thresholdChanged: deassert",
146 phosphor::logging::entry("SENSOR=%s", m.get_path()));
147 value = *ptr;
148 }
149 }
150 }
151 });
152
Hao Jiangd2afd052020-12-10 15:09:32 -0800153namespace sensor
154{
155static constexpr const char* vrInterface =
156 "xyz.openbmc_project.Control.VoltageRegulatorMode";
157static constexpr const char* sensorInterface =
158 "xyz.openbmc_project.Sensor.Value";
159} // namespace sensor
160
Willy Tude54f482021-01-26 15:59:09 -0800161static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
162 double& min)
163{
164 max = 127;
165 min = -128;
166
Hao Jiangd2afd052020-12-10 15:09:32 -0800167 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800168 auto critical =
169 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
170 auto warning =
171 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
172
173 if (sensorObject != sensorMap.end())
174 {
175 auto maxMap = sensorObject->second.find("MaxValue");
176 auto minMap = sensorObject->second.find("MinValue");
177
178 if (maxMap != sensorObject->second.end())
179 {
180 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
181 }
182 if (minMap != sensorObject->second.end())
183 {
184 min = std::visit(VariantToDoubleVisitor(), minMap->second);
185 }
186 }
187 if (critical != sensorMap.end())
188 {
189 auto lower = critical->second.find("CriticalLow");
190 auto upper = critical->second.find("CriticalHigh");
191 if (lower != critical->second.end())
192 {
193 double value = std::visit(VariantToDoubleVisitor(), lower->second);
194 min = std::min(value, min);
195 }
196 if (upper != critical->second.end())
197 {
198 double value = std::visit(VariantToDoubleVisitor(), upper->second);
199 max = std::max(value, max);
200 }
201 }
202 if (warning != sensorMap.end())
203 {
204
205 auto lower = warning->second.find("WarningLow");
206 auto upper = warning->second.find("WarningHigh");
207 if (lower != warning->second.end())
208 {
209 double value = std::visit(VariantToDoubleVisitor(), lower->second);
210 min = std::min(value, min);
211 }
212 if (upper != warning->second.end())
213 {
214 double value = std::visit(VariantToDoubleVisitor(), upper->second);
215 max = std::max(value, max);
216 }
217 }
218}
219
220static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700221 std::string sensorPath, DbusInterfaceMap& sensorMap,
222 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800223{
224 static boost::container::flat_map<
225 std::string, std::chrono::time_point<std::chrono::steady_clock>>
226 updateTimeMap;
227
228 auto updateFind = updateTimeMap.find(sensorConnection);
229 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
230 if (updateFind != updateTimeMap.end())
231 {
232 lastUpdate = updateFind->second;
233 }
234
235 auto now = std::chrono::steady_clock::now();
236
237 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700238 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800239 {
Willy Tude54f482021-01-26 15:59:09 -0800240 ObjectValueTree managedObjects;
241 boost::system::error_code ec = getManagedObjects(
242 ctx, sensorConnection.c_str(), "/", managedObjects);
243 if (ec)
244 {
245 phosphor::logging::log<phosphor::logging::level::ERR>(
246 "GetMangagedObjects for getSensorMap failed",
247 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
248
249 return false;
250 }
251
252 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700253 // Update time after finish building the map which allow the
254 // data to be cached for updatePeriod plus the build time.
255 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800256 }
257 auto connection = SensorCache.find(sensorConnection);
258 if (connection == SensorCache.end())
259 {
260 return false;
261 }
262 auto path = connection->second.find(sensorPath);
263 if (path == connection->second.end())
264 {
265 return false;
266 }
267 sensorMap = path->second;
268
269 return true;
270}
271
Hao Jiangd2afd052020-12-10 15:09:32 -0800272namespace sensor
273{
274// Calculate VR Mode from input IPMI discrete event bytes
275static std::optional<std::string>
276 calculateVRMode(uint15_t assertOffset,
277 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
278{
279 // get VR mode profiles from Supported Interface
280 auto supportedProperty = VRObject.find("Supported");
281 if (supportedProperty == VRObject.end() ||
282 VRObject.find("Selected") == VRObject.end())
283 {
284 phosphor::logging::log<phosphor::logging::level::ERR>(
285 "Missing the required Supported and Selected properties");
286 return std::nullopt;
287 }
288
289 const auto profilesPtr =
290 std::get_if<std::vector<std::string>>(&supportedProperty->second);
291
292 if (profilesPtr == nullptr)
293 {
294 phosphor::logging::log<phosphor::logging::level::ERR>(
295 "property is not array of string");
296 return std::nullopt;
297 }
298
299 // interpret IPMI cmd bits into profiles' index
300 long unsigned int index = 0;
301 // only one bit should be set and the highest bit should not be used.
302 if (assertOffset == 0 || assertOffset == (1u << 15) ||
303 (assertOffset & (assertOffset - 1)))
304 {
305 phosphor::logging::log<phosphor::logging::level::ERR>(
306 "IPMI cmd format incorrect",
307
308 phosphor::logging::entry("BYTES=%#02x",
309 static_cast<uint16_t>(assertOffset)));
310 return std::nullopt;
311 }
312
313 while (assertOffset != 1)
314 {
315 assertOffset >>= 1;
316 index++;
317 }
318
319 if (index >= profilesPtr->size())
320 {
321 phosphor::logging::log<phosphor::logging::level::ERR>(
322 "profile index out of boundary");
323 return std::nullopt;
324 }
325
326 return profilesPtr->at(index);
327}
328
329// Calculate sensor value from IPMI reading byte
330static std::optional<double>
331 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
332 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
333{
334 if (valueObject.find("Value") == valueObject.end())
335 {
336 phosphor::logging::log<phosphor::logging::level::ERR>(
337 "Missing the required Value property");
338 return std::nullopt;
339 }
340
341 double max = 0;
342 double min = 0;
343 getSensorMaxMin(sensorMap, max, min);
344
345 int16_t mValue = 0;
346 int16_t bValue = 0;
347 int8_t rExp = 0;
348 int8_t bExp = 0;
349 bool bSigned = false;
350
351 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
352 {
353 return std::nullopt;
354 }
355
356 double value = bSigned ? ((int8_t)reading) : reading;
357
358 value *= ((double)mValue);
359 value += ((double)bValue) * std::pow(10.0, bExp);
360 value *= std::pow(10.0, rExp);
361
362 return value;
363}
364
Willy Tu38e7a2b2021-03-29 15:09:56 -0700365// Extract file name from sensor path as the sensors SDR ID. Simplify the name
366// if it is too long.
367std::string parseSdrIdFromPath(const std::string& path)
368{
369 std::string name;
370 size_t nameStart = path.rfind("/");
371 if (nameStart != std::string::npos)
372 {
373 name = path.substr(nameStart + 1, std::string::npos - nameStart);
374 }
375
376 std::replace(name.begin(), name.end(), '_', ' ');
377 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
378 {
379 // try to not truncate by replacing common words
380 constexpr std::array<std::pair<const char*, const char*>, 2>
381 replaceWords = {std::make_pair("Output", "Out"),
382 std::make_pair("Input", "In")};
383 for (const auto& [find, replace] : replaceWords)
384 {
385 boost::replace_all(name, find, replace);
386 }
387
388 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
389 }
390 return name;
391}
392
Hao Jiangd2afd052020-12-10 15:09:32 -0800393} // namespace sensor
394
Willy Tude54f482021-01-26 15:59:09 -0800395ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev,
396 uint8_t sensorType, uint8_t sensorNum,
397 uint8_t eventType, uint8_t eventData1,
398 std::optional<uint8_t> eventData2,
399 std::optional<uint8_t> eventData3)
400{
401 return ipmi::responseSuccess();
402}
403
Willy Tudbafbce2021-03-29 00:37:05 -0700404ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
405 uint8_t sensorNumber, uint8_t operation,
406 uint8_t reading, uint15_t assertOffset,
407 bool resvd1, uint15_t deassertOffset,
408 bool resvd2, uint8_t eventData1,
409 uint8_t eventData2, uint8_t eventData3)
410{
411 std::string connection;
412 std::string path;
413 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path);
414 if (status)
415 {
416 return ipmi::response(status);
417 }
418
419 DbusInterfaceMap sensorMap;
420 if (!getSensorMap(ctx, connection, path, sensorMap))
421 {
422 return ipmi::responseResponseError();
423 }
Willy Tudbafbce2021-03-29 00:37:05 -0700424
Hao Jiangd2afd052020-12-10 15:09:32 -0800425 // we can tell the sensor type by its interface type
426 auto sensorObject = sensorMap.find(sensor::sensorInterface);
427 if (sensorObject != sensorMap.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700428 {
Hao Jiangd2afd052020-12-10 15:09:32 -0800429 auto value =
430 sensor::calculateValue(reading, sensorMap, sensorObject->second);
431 if (!value)
432 {
433 return ipmi::responseResponseError();
434 }
435
436 if constexpr (debug)
437 {
438 phosphor::logging::log<phosphor::logging::level::INFO>(
439 "IPMI SET_SENSOR",
440 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
441 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
442 phosphor::logging::entry("VALUE=%f", *value));
443 }
444
445 boost::system::error_code ec =
446 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
447 "Value", ipmi::Value(*value));
448
449 // setDbusProperty intended to resolve dbus exception/rc within the
450 // function but failed to achieve that. Catch SdBusError in the ipmi
451 // callback functions for now (e.g. ipmiSetSensorReading).
452 if (ec)
453 {
454 using namespace phosphor::logging;
455 log<level::ERR>("Failed to set property",
456 entry("PROPERTY=%s", "Value"),
457 entry("PATH=%s", path.c_str()),
458 entry("INTERFACE=%s", sensor::sensorInterface),
459 entry("WHAT=%s", ec.message().c_str()));
460 return ipmi::responseResponseError();
461 }
462 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700463 }
464
Hao Jiangd2afd052020-12-10 15:09:32 -0800465 sensorObject = sensorMap.find(sensor::vrInterface);
466 if (sensorObject != sensorMap.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700467 {
Hao Jiangd2afd052020-12-10 15:09:32 -0800468 // VR sensors are treated as a special case and we will not check the
469 // write permission for VR sensors, since they always deemed writable
470 // and permission table are not applied to VR sensors.
471 auto vrMode =
472 sensor::calculateVRMode(assertOffset, sensorObject->second);
473 if (!vrMode)
474 {
475 return ipmi::responseResponseError();
476 }
477 boost::system::error_code ec = setDbusProperty(
478 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
479 // setDbusProperty intended to resolve dbus exception/rc within the
480 // function but failed to achieve that. Catch SdBusError in the ipmi
481 // callback functions for now (e.g. ipmiSetSensorReading).
482 if (ec)
483 {
484 using namespace phosphor::logging;
485 log<level::ERR>("Failed to set property",
486 entry("PROPERTY=%s", "Selected"),
487 entry("PATH=%s", path.c_str()),
488 entry("INTERFACE=%s", sensor::sensorInterface),
489 entry("WHAT=%s", ec.message().c_str()));
490 return ipmi::responseResponseError();
491 }
492 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700493 }
494
Hao Jiangd2afd052020-12-10 15:09:32 -0800495 phosphor::logging::log<phosphor::logging::level::ERR>(
496 "unknown sensor type",
497 phosphor::logging::entry("PATH=%s", path.c_str()));
498 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700499}
500
Willy Tude54f482021-01-26 15:59:09 -0800501ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
502 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
503{
504 std::string connection;
505 std::string path;
506
507 auto status = getSensorConnection(ctx, sensnum, connection, path);
508 if (status)
509 {
510 return ipmi::response(status);
511 }
512
513 DbusInterfaceMap sensorMap;
514 if (!getSensorMap(ctx, connection, path, sensorMap))
515 {
516 return ipmi::responseResponseError();
517 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800518 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800519
520 if (sensorObject == sensorMap.end() ||
521 sensorObject->second.find("Value") == sensorObject->second.end())
522 {
523 return ipmi::responseResponseError();
524 }
525 auto& valueVariant = sensorObject->second["Value"];
526 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
527
528 double max = 0;
529 double min = 0;
530 getSensorMaxMin(sensorMap, max, min);
531
532 int16_t mValue = 0;
533 int16_t bValue = 0;
534 int8_t rExp = 0;
535 int8_t bExp = 0;
536 bool bSigned = false;
537
538 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
539 {
540 return ipmi::responseResponseError();
541 }
542
543 uint8_t value =
544 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
545 uint8_t operation =
546 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
547 operation |=
548 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
549 bool notReading = std::isnan(reading);
550
551 if (!notReading)
552 {
553 auto availableObject =
554 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
555 if (availableObject != sensorMap.end())
556 {
557 auto findAvailable = availableObject->second.find("Available");
558 if (findAvailable != availableObject->second.end())
559 {
560 bool* available = std::get_if<bool>(&(findAvailable->second));
561 if (available && !(*available))
562 {
563 notReading = true;
564 }
565 }
566 }
567 }
568
569 if (notReading)
570 {
571 operation |= static_cast<uint8_t>(
572 IPMISensorReadingByte2::readingStateUnavailable);
573 }
574
Josh Lehana55c9532020-10-28 21:59:06 -0700575 if constexpr (details::enableInstrumentation)
576 {
577 int byteValue;
578 if (bSigned)
579 {
580 byteValue = static_cast<int>(static_cast<int8_t>(value));
581 }
582 else
583 {
584 byteValue = static_cast<int>(static_cast<uint8_t>(value));
585 }
586
587 // Keep stats on the reading just obtained, even if it is "NaN"
588 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
589 {
590 // This is the first reading, show the coefficients
591 double step = (max - min) / 255.0;
592 std::cerr << "IPMI sensor "
593 << details::sdrStatsTable.getName(sensnum)
594 << ": Range min=" << min << " max=" << max
595 << ", step=" << step
596 << ", Coefficients mValue=" << static_cast<int>(mValue)
597 << " rExp=" << static_cast<int>(rExp)
598 << " bValue=" << static_cast<int>(bValue)
599 << " bExp=" << static_cast<int>(bExp)
600 << " bSigned=" << static_cast<int>(bSigned) << "\n";
601 }
602 }
603
Willy Tude54f482021-01-26 15:59:09 -0800604 uint8_t thresholds = 0;
605
606 auto warningObject =
607 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
608 if (warningObject != sensorMap.end())
609 {
610 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
611 auto alarmLow = warningObject->second.find("WarningAlarmLow");
612 if (alarmHigh != warningObject->second.end())
613 {
614 if (std::get<bool>(alarmHigh->second))
615 {
616 thresholds |= static_cast<uint8_t>(
617 IPMISensorReadingByte3::upperNonCritical);
618 }
619 }
620 if (alarmLow != warningObject->second.end())
621 {
622 if (std::get<bool>(alarmLow->second))
623 {
624 thresholds |= static_cast<uint8_t>(
625 IPMISensorReadingByte3::lowerNonCritical);
626 }
627 }
628 }
629
630 auto criticalObject =
631 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
632 if (criticalObject != sensorMap.end())
633 {
634 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
635 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
636 if (alarmHigh != criticalObject->second.end())
637 {
638 if (std::get<bool>(alarmHigh->second))
639 {
640 thresholds |=
641 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
642 }
643 }
644 if (alarmLow != criticalObject->second.end())
645 {
646 if (std::get<bool>(alarmLow->second))
647 {
648 thresholds |=
649 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
650 }
651 }
652 }
653
654 // no discrete as of today so optional byte is never returned
655 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
656}
657
658/** @brief implements the Set Sensor threshold command
659 * @param sensorNumber - sensor number
660 * @param lowerNonCriticalThreshMask
661 * @param lowerCriticalThreshMask
662 * @param lowerNonRecovThreshMask
663 * @param upperNonCriticalThreshMask
664 * @param upperCriticalThreshMask
665 * @param upperNonRecovThreshMask
666 * @param reserved
667 * @param lowerNonCritical - lower non-critical threshold
668 * @param lowerCritical - Lower critical threshold
669 * @param lowerNonRecoverable - Lower non recovarable threshold
670 * @param upperNonCritical - Upper non-critical threshold
671 * @param upperCritical - Upper critical
672 * @param upperNonRecoverable - Upper Non-recoverable
673 *
674 * @returns IPMI completion code
675 */
676ipmi::RspType<> ipmiSenSetSensorThresholds(
677 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
678 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
679 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
680 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
681 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
682 uint8_t upperNonCritical, uint8_t upperCritical,
683 uint8_t upperNonRecoverable)
684{
685 if (reserved)
686 {
687 return ipmi::responseInvalidFieldRequest();
688 }
689
690 // lower nc and upper nc not suppported on any sensor
691 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
692 {
693 return ipmi::responseInvalidFieldRequest();
694 }
695
696 // if none of the threshold mask are set, nothing to do
697 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
698 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
699 upperCriticalThreshMask | upperNonRecovThreshMask))
700 {
701 return ipmi::responseSuccess();
702 }
703
704 std::string connection;
705 std::string path;
706
707 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
708 if (status)
709 {
710 return ipmi::response(status);
711 }
712 DbusInterfaceMap sensorMap;
713 if (!getSensorMap(ctx, connection, path, sensorMap))
714 {
715 return ipmi::responseResponseError();
716 }
717
718 double max = 0;
719 double min = 0;
720 getSensorMaxMin(sensorMap, max, min);
721
722 int16_t mValue = 0;
723 int16_t bValue = 0;
724 int8_t rExp = 0;
725 int8_t bExp = 0;
726 bool bSigned = false;
727
728 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
729 {
730 return ipmi::responseResponseError();
731 }
732
733 // store a vector of property name, value to set, and interface
734 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
735
736 // define the indexes of the tuple
737 constexpr uint8_t propertyName = 0;
738 constexpr uint8_t thresholdValue = 1;
739 constexpr uint8_t interface = 2;
740 // verifiy all needed fields are present
741 if (lowerCriticalThreshMask || upperCriticalThreshMask)
742 {
743 auto findThreshold =
744 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
745 if (findThreshold == sensorMap.end())
746 {
747 return ipmi::responseInvalidFieldRequest();
748 }
749 if (lowerCriticalThreshMask)
750 {
751 auto findLower = findThreshold->second.find("CriticalLow");
752 if (findLower == findThreshold->second.end())
753 {
754 return ipmi::responseInvalidFieldRequest();
755 }
756 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
757 findThreshold->first);
758 }
759 if (upperCriticalThreshMask)
760 {
761 auto findUpper = findThreshold->second.find("CriticalHigh");
762 if (findUpper == findThreshold->second.end())
763 {
764 return ipmi::responseInvalidFieldRequest();
765 }
766 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
767 findThreshold->first);
768 }
769 }
770 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
771 {
772 auto findThreshold =
773 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
774 if (findThreshold == sensorMap.end())
775 {
776 return ipmi::responseInvalidFieldRequest();
777 }
778 if (lowerNonCriticalThreshMask)
779 {
780 auto findLower = findThreshold->second.find("WarningLow");
781 if (findLower == findThreshold->second.end())
782 {
783 return ipmi::responseInvalidFieldRequest();
784 }
785 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
786 findThreshold->first);
787 }
788 if (upperNonCriticalThreshMask)
789 {
790 auto findUpper = findThreshold->second.find("WarningHigh");
791 if (findUpper == findThreshold->second.end())
792 {
793 return ipmi::responseInvalidFieldRequest();
794 }
795 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
796 findThreshold->first);
797 }
798 }
799 for (const auto& property : thresholdsToSet)
800 {
801 // from section 36.3 in the IPMI Spec, assume all linear
802 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
803 (bValue * std::pow(10.0, bExp))) *
804 std::pow(10.0, rExp);
805 setDbusProperty(
806 *getSdBus(), connection, path, std::get<interface>(property),
807 std::get<propertyName>(property), ipmi::Value(valueToSet));
808 }
809 return ipmi::responseSuccess();
810}
811
812IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
813{
814 IPMIThresholds resp;
815 auto warningInterface =
816 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
817 auto criticalInterface =
818 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
819
820 if ((warningInterface != sensorMap.end()) ||
821 (criticalInterface != sensorMap.end()))
822 {
Hao Jiangd2afd052020-12-10 15:09:32 -0800823 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800824
825 if (sensorPair == sensorMap.end())
826 {
827 // should not have been able to find a sensor not implementing
828 // the sensor object
829 throw std::runtime_error("Invalid sensor map");
830 }
831
832 double max = 0;
833 double min = 0;
834 getSensorMaxMin(sensorMap, max, min);
835
836 int16_t mValue = 0;
837 int16_t bValue = 0;
838 int8_t rExp = 0;
839 int8_t bExp = 0;
840 bool bSigned = false;
841
842 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
843 {
844 throw std::runtime_error("Invalid sensor atrributes");
845 }
846 if (warningInterface != sensorMap.end())
847 {
848 auto& warningMap = warningInterface->second;
849
850 auto warningHigh = warningMap.find("WarningHigh");
851 auto warningLow = warningMap.find("WarningLow");
852
853 if (warningHigh != warningMap.end())
854 {
855
856 double value =
857 std::visit(VariantToDoubleVisitor(), warningHigh->second);
858 resp.warningHigh = scaleIPMIValueFromDouble(
859 value, mValue, rExp, bValue, bExp, bSigned);
860 }
861 if (warningLow != warningMap.end())
862 {
863 double value =
864 std::visit(VariantToDoubleVisitor(), warningLow->second);
865 resp.warningLow = scaleIPMIValueFromDouble(
866 value, mValue, rExp, bValue, bExp, bSigned);
867 }
868 }
869 if (criticalInterface != sensorMap.end())
870 {
871 auto& criticalMap = criticalInterface->second;
872
873 auto criticalHigh = criticalMap.find("CriticalHigh");
874 auto criticalLow = criticalMap.find("CriticalLow");
875
876 if (criticalHigh != criticalMap.end())
877 {
878 double value =
879 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
880 resp.criticalHigh = scaleIPMIValueFromDouble(
881 value, mValue, rExp, bValue, bExp, bSigned);
882 }
883 if (criticalLow != criticalMap.end())
884 {
885 double value =
886 std::visit(VariantToDoubleVisitor(), criticalLow->second);
887 resp.criticalLow = scaleIPMIValueFromDouble(
888 value, mValue, rExp, bValue, bExp, bSigned);
889 }
890 }
891 }
892 return resp;
893}
894
895ipmi::RspType<uint8_t, // readable
896 uint8_t, // lowerNCrit
897 uint8_t, // lowerCrit
898 uint8_t, // lowerNrecoverable
899 uint8_t, // upperNC
900 uint8_t, // upperCrit
901 uint8_t> // upperNRecoverable
902 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
903{
904 std::string connection;
905 std::string path;
906
907 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
908 if (status)
909 {
910 return ipmi::response(status);
911 }
912
913 DbusInterfaceMap sensorMap;
914 if (!getSensorMap(ctx, connection, path, sensorMap))
915 {
916 return ipmi::responseResponseError();
917 }
918
919 IPMIThresholds thresholdData;
920 try
921 {
922 thresholdData = getIPMIThresholds(sensorMap);
923 }
924 catch (std::exception&)
925 {
926 return ipmi::responseResponseError();
927 }
928
929 uint8_t readable = 0;
930 uint8_t lowerNC = 0;
931 uint8_t lowerCritical = 0;
932 uint8_t lowerNonRecoverable = 0;
933 uint8_t upperNC = 0;
934 uint8_t upperCritical = 0;
935 uint8_t upperNonRecoverable = 0;
936
937 if (thresholdData.warningHigh)
938 {
939 readable |=
940 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
941 upperNC = *thresholdData.warningHigh;
942 }
943 if (thresholdData.warningLow)
944 {
945 readable |=
946 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
947 lowerNC = *thresholdData.warningLow;
948 }
949
950 if (thresholdData.criticalHigh)
951 {
952 readable |=
953 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
954 upperCritical = *thresholdData.criticalHigh;
955 }
956 if (thresholdData.criticalLow)
957 {
958 readable |=
959 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
960 lowerCritical = *thresholdData.criticalLow;
961 }
962
963 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
964 lowerNonRecoverable, upperNC, upperCritical,
965 upperNonRecoverable);
966}
967
968/** @brief implements the get Sensor event enable command
969 * @param sensorNumber - sensor number
970 *
971 * @returns IPMI completion code plus response data
972 * - enabled - Sensor Event messages
973 * - assertionEnabledLsb - Assertion event messages
974 * - assertionEnabledMsb - Assertion event messages
975 * - deassertionEnabledLsb - Deassertion event messages
976 * - deassertionEnabledMsb - Deassertion event messages
977 */
978
979ipmi::RspType<uint8_t, // enabled
980 uint8_t, // assertionEnabledLsb
981 uint8_t, // assertionEnabledMsb
982 uint8_t, // deassertionEnabledLsb
983 uint8_t> // deassertionEnabledMsb
984 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
985{
986 std::string connection;
987 std::string path;
988
989 uint8_t enabled = 0;
990 uint8_t assertionEnabledLsb = 0;
991 uint8_t assertionEnabledMsb = 0;
992 uint8_t deassertionEnabledLsb = 0;
993 uint8_t deassertionEnabledMsb = 0;
994
995 auto status = getSensorConnection(ctx, sensorNum, connection, path);
996 if (status)
997 {
998 return ipmi::response(status);
999 }
1000
1001 DbusInterfaceMap sensorMap;
1002 if (!getSensorMap(ctx, connection, path, sensorMap))
1003 {
1004 return ipmi::responseResponseError();
1005 }
1006
1007 auto warningInterface =
1008 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1009 auto criticalInterface =
1010 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1011 if ((warningInterface != sensorMap.end()) ||
1012 (criticalInterface != sensorMap.end()))
1013 {
1014 enabled = static_cast<uint8_t>(
1015 IPMISensorEventEnableByte2::sensorScanningEnable);
1016 if (warningInterface != sensorMap.end())
1017 {
1018 auto& warningMap = warningInterface->second;
1019
1020 auto warningHigh = warningMap.find("WarningHigh");
1021 auto warningLow = warningMap.find("WarningLow");
1022 if (warningHigh != warningMap.end())
1023 {
1024 assertionEnabledLsb |= static_cast<uint8_t>(
1025 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1026 deassertionEnabledLsb |= static_cast<uint8_t>(
1027 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1028 }
1029 if (warningLow != warningMap.end())
1030 {
1031 assertionEnabledLsb |= static_cast<uint8_t>(
1032 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1033 deassertionEnabledLsb |= static_cast<uint8_t>(
1034 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1035 }
1036 }
1037 if (criticalInterface != sensorMap.end())
1038 {
1039 auto& criticalMap = criticalInterface->second;
1040
1041 auto criticalHigh = criticalMap.find("CriticalHigh");
1042 auto criticalLow = criticalMap.find("CriticalLow");
1043
1044 if (criticalHigh != criticalMap.end())
1045 {
1046 assertionEnabledMsb |= static_cast<uint8_t>(
1047 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1048 deassertionEnabledMsb |= static_cast<uint8_t>(
1049 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1050 }
1051 if (criticalLow != criticalMap.end())
1052 {
1053 assertionEnabledLsb |= static_cast<uint8_t>(
1054 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1055 deassertionEnabledLsb |= static_cast<uint8_t>(
1056 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1057 }
1058 }
1059 }
1060
1061 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1062 assertionEnabledMsb, deassertionEnabledLsb,
1063 deassertionEnabledMsb);
1064}
1065
1066/** @brief implements the get Sensor event status command
1067 * @param sensorNumber - sensor number, FFh = reserved
1068 *
1069 * @returns IPMI completion code plus response data
1070 * - sensorEventStatus - Sensor Event messages state
1071 * - assertions - Assertion event messages
1072 * - deassertions - Deassertion event messages
1073 */
1074ipmi::RspType<uint8_t, // sensorEventStatus
1075 std::bitset<16>, // assertions
1076 std::bitset<16> // deassertion
1077 >
1078 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1079{
1080 if (sensorNum == reservedSensorNumber)
1081 {
1082 return ipmi::responseInvalidFieldRequest();
1083 }
1084
1085 std::string connection;
1086 std::string path;
1087 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1088 if (status)
1089 {
1090 phosphor::logging::log<phosphor::logging::level::ERR>(
1091 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1092 phosphor::logging::entry("SENSOR=%d", sensorNum));
1093 return ipmi::response(status);
1094 }
1095
1096 DbusInterfaceMap sensorMap;
1097 if (!getSensorMap(ctx, connection, path, sensorMap))
1098 {
1099 phosphor::logging::log<phosphor::logging::level::ERR>(
1100 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1101 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1102 return ipmi::responseResponseError();
1103 }
1104 auto warningInterface =
1105 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1106 auto criticalInterface =
1107 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1108
1109 uint8_t sensorEventStatus =
1110 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1111
1112 std::optional<bool> criticalDeassertHigh =
1113 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1114 std::optional<bool> criticalDeassertLow =
1115 thresholdDeassertMap[path]["CriticalAlarmLow"];
1116 std::optional<bool> warningDeassertHigh =
1117 thresholdDeassertMap[path]["WarningAlarmHigh"];
1118 std::optional<bool> warningDeassertLow =
1119 thresholdDeassertMap[path]["WarningAlarmLow"];
1120
1121 std::bitset<16> assertions = 0;
1122 std::bitset<16> deassertions = 0;
1123
1124 if (criticalDeassertHigh && !*criticalDeassertHigh)
1125 {
1126 deassertions.set(static_cast<size_t>(
1127 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1128 }
1129 if (criticalDeassertLow && !*criticalDeassertLow)
1130 {
1131 deassertions.set(static_cast<size_t>(
1132 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1133 }
1134 if (warningDeassertHigh && !*warningDeassertHigh)
1135 {
1136 deassertions.set(static_cast<size_t>(
1137 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1138 }
1139 if (warningDeassertLow && !*warningDeassertLow)
1140 {
1141 deassertions.set(static_cast<size_t>(
1142 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1143 }
1144 if ((warningInterface != sensorMap.end()) ||
1145 (criticalInterface != sensorMap.end()))
1146 {
1147 sensorEventStatus = static_cast<size_t>(
1148 IPMISensorEventEnableByte2::eventMessagesEnable);
1149 if (warningInterface != sensorMap.end())
1150 {
1151 auto& warningMap = warningInterface->second;
1152
1153 auto warningHigh = warningMap.find("WarningAlarmHigh");
1154 auto warningLow = warningMap.find("WarningAlarmLow");
1155 auto warningHighAlarm = false;
1156 auto warningLowAlarm = false;
1157
1158 if (warningHigh != warningMap.end())
1159 {
1160 warningHighAlarm = std::get<bool>(warningHigh->second);
1161 }
1162 if (warningLow != warningMap.end())
1163 {
1164 warningLowAlarm = std::get<bool>(warningLow->second);
1165 }
1166 if (warningHighAlarm)
1167 {
1168 assertions.set(
1169 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1170 upperNonCriticalGoingHigh));
1171 }
1172 if (warningLowAlarm)
1173 {
1174 assertions.set(
1175 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1176 lowerNonCriticalGoingLow));
1177 }
1178 }
1179 if (criticalInterface != sensorMap.end())
1180 {
1181 auto& criticalMap = criticalInterface->second;
1182
1183 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1184 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1185 auto criticalHighAlarm = false;
1186 auto criticalLowAlarm = false;
1187
1188 if (criticalHigh != criticalMap.end())
1189 {
1190 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1191 }
1192 if (criticalLow != criticalMap.end())
1193 {
1194 criticalLowAlarm = std::get<bool>(criticalLow->second);
1195 }
1196 if (criticalHighAlarm)
1197 {
1198 assertions.set(
1199 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1200 upperCriticalGoingHigh));
1201 }
1202 if (criticalLowAlarm)
1203 {
1204 assertions.set(static_cast<size_t>(
1205 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1206 }
1207 }
1208 }
1209
1210 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1211}
1212
Willy Tu38e7a2b2021-03-29 15:09:56 -07001213// Construct a type 1 SDR for threshold sensor.
1214bool constructSensorSdr(uint16_t sensorNum, uint16_t recordID,
1215 const std::string& path,
1216 const DbusInterfaceMap& sensorMap,
1217 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001218{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001219 get_sdr::header::set_record_id(
1220 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1221
Willy Tu38e7a2b2021-03-29 15:09:56 -07001222 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1223 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1224
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001225 record.header.sdr_version = ipmiSdrVersion;
1226 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1227 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1228 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001229 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001230 record.key.owner_lun = lun;
1231 record.key.sensor_number = sensornumber;
1232
1233 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1234 record.body.sensor_type = getSensorTypeFromPath(path);
1235 std::string type = getSensorTypeStringFromPath(path);
1236 auto typeCstr = type.c_str();
1237 auto findUnits = sensorUnits.find(typeCstr);
1238 if (findUnits != sensorUnits.end())
1239 {
1240 record.body.sensor_units_2_base =
1241 static_cast<uint8_t>(findUnits->second);
1242 } // else default 0x0 unspecified
1243
1244 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1245
Hao Jiangd2afd052020-12-10 15:09:32 -08001246 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001247 if (sensorObject == sensorMap.end())
1248 {
1249 phosphor::logging::log<phosphor::logging::level::ERR>(
1250 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001251 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001252 }
1253
1254 uint8_t entityId = 0;
1255 uint8_t entityInstance = 0x01;
1256
1257 // follow the association chain to get the parent board's entityid and
1258 // entityInstance
1259 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1260
1261 record.body.entity_id = entityId;
1262 record.body.entity_instance = entityInstance;
1263
1264 auto maxObject = sensorObject->second.find("MaxValue");
1265 auto minObject = sensorObject->second.find("MinValue");
1266
1267 // If min and/or max are left unpopulated,
1268 // then default to what a signed byte would be, namely (-128,127) range.
1269 auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1270 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1271 if (maxObject != sensorObject->second.end())
1272 {
1273 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1274 }
1275
1276 if (minObject != sensorObject->second.end())
1277 {
1278 min = std::visit(VariantToDoubleVisitor(), minObject->second);
1279 }
1280
1281 int16_t mValue = 0;
1282 int8_t rExp = 0;
1283 int16_t bValue = 0;
1284 int8_t bExp = 0;
1285 bool bSigned = false;
1286
1287 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1288 {
1289 phosphor::logging::log<phosphor::logging::level::ERR>(
1290 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001291 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001292 }
1293
1294 // The record.body is a struct SensorDataFullRecordBody
1295 // from sensorhandler.hpp in phosphor-ipmi-host.
1296 // The meaning of these bits appears to come from
1297 // table 43.1 of the IPMI spec.
1298 // The above 5 sensor attributes are stuffed in as follows:
1299 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1300 // Byte 22-24 are for other purposes
1301 // Byte 25 = MMMMMMMM = LSB of M
1302 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1303 // Byte 27 = BBBBBBBB = LSB of B
1304 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1305 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1306 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1307
1308 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1309 record.body.m_lsb = mValue & 0xFF;
1310
1311 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1312 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1313
1314 // move the smallest bit of the MSB into place (bit 9)
1315 // the MSbs are bits 7:8 in m_msb_and_tolerance
1316 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1317
1318 record.body.b_lsb = bValue & 0xFF;
1319
1320 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1321 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1322
1323 // move the smallest bit of the MSB into place (bit 9)
1324 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1325 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1326
1327 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1328 uint8_t rExpBits = rExp & 0x07;
1329
1330 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1331 uint8_t bExpBits = bExp & 0x07;
1332
1333 // move rExp and bExp into place
1334 record.body.r_b_exponents =
1335 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1336
1337 // Set the analog reading byte interpretation accordingly
1338 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1339
1340 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1341 // These seem redundant, but derivable from the above 5 attributes
1342 // Original comment said "todo fill out rest of units"
1343
1344 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001345 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001346 record.body.id_string_info = name.size();
1347 std::strncpy(record.body.id_string, name.c_str(),
1348 sizeof(record.body.id_string));
1349
Josh Lehana55c9532020-10-28 21:59:06 -07001350 // Remember the sensor name, as determined for this sensor number
1351 details::sdrStatsTable.updateName(sensornumber, name);
1352
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001353 IPMIThresholds thresholdData;
1354 try
1355 {
1356 thresholdData = getIPMIThresholds(sensorMap);
1357 }
1358 catch (std::exception&)
1359 {
1360 phosphor::logging::log<phosphor::logging::level::ERR>(
1361 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001362 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001363 }
1364
1365 if (thresholdData.criticalHigh)
1366 {
1367 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1368 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1369 IPMISensorEventEnableThresholds::criticalThreshold);
1370 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1371 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1372 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1373 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1374 record.body.discrete_reading_setting_mask[0] |=
1375 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1376 }
1377 if (thresholdData.warningHigh)
1378 {
1379 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1380 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1381 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1382 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1383 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1384 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1385 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1386 record.body.discrete_reading_setting_mask[0] |=
1387 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1388 }
1389 if (thresholdData.criticalLow)
1390 {
1391 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1392 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1393 IPMISensorEventEnableThresholds::criticalThreshold);
1394 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1395 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1396 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1397 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1398 record.body.discrete_reading_setting_mask[0] |=
1399 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1400 }
1401 if (thresholdData.warningLow)
1402 {
1403 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1404 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1405 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1406 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1407 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1408 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1409 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1410 record.body.discrete_reading_setting_mask[0] |=
1411 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1412 }
1413
1414 // everything that is readable is setable
1415 record.body.discrete_reading_setting_mask[1] =
1416 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001417 return true;
1418}
1419
Willy Tu61992ad2021-03-29 15:33:20 -07001420// Construct a type 3 SDR for VR typed sensor(daemon).
1421void constructVrSdr(uint16_t sensorNum, uint16_t recordID,
1422 const std::string& path, const DbusInterfaceMap& sensorMap,
1423 get_sdr::SensorDataEventRecord& record)
1424{
1425 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1426 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1427
1428 get_sdr::header::set_record_id(
1429 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1430
1431 record.header.sdr_version = ipmiSdrVersion;
1432 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1433 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1434 sizeof(get_sdr::SensorDataRecordHeader);
1435 record.key.owner_id = bmcI2CAddr;
1436 record.key.owner_lun = lun;
1437 record.key.sensor_number = sensornumber;
1438
1439 record.body.entity_id = 0x00;
1440 record.body.entity_instance = 0x01;
1441
1442 // follow the association chain to get the parent board's entityid and
1443 // entityInstance
1444 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1445 record.body.entity_instance);
1446
1447 // Sensor type is hardcoded as a module/board type instead of parsing from
1448 // sensor path. This is because VR control is allocated in an independent
1449 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1450 // types.
1451 static constexpr const uint8_t module_board_type = 0x15;
1452 record.body.sensor_type = module_board_type;
1453 record.body.event_reading_type = 0x00;
1454
1455 record.body.sensor_record_sharing_1 = 0x00;
1456 record.body.sensor_record_sharing_2 = 0x00;
1457
1458 // populate sensor name from path
1459 auto name = sensor::parseSdrIdFromPath(path);
1460 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1461 record.body.id_string_info = nameSize;
1462 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1463 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1464
1465 // Remember the sensor name, as determined for this sensor number
1466 details::sdrStatsTable.updateName(sensornumber, name);
1467}
1468
Willy Tu38e7a2b2021-03-29 15:09:56 -07001469static int getSensorDataRecord(ipmi::Context::ptr ctx,
1470 std::vector<uint8_t>& recordData,
1471 uint16_t recordID)
1472{
1473 size_t fruCount = 0;
1474 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1475 if (ret != ipmi::ccSuccess)
1476 {
1477 phosphor::logging::log<phosphor::logging::level::ERR>(
1478 "getSensorDataRecord: getFruSdrCount error");
1479 return GENERAL_ERROR;
1480 }
1481
1482 auto& sensorTree = getSensorTree();
1483 size_t lastRecord =
1484 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1485 if (recordID == lastRecordIndex)
1486 {
1487 recordID = lastRecord;
1488 }
1489 if (recordID > lastRecord)
1490 {
1491 phosphor::logging::log<phosphor::logging::level::ERR>(
1492 "getSensorDataRecord: recordID > lastRecord error");
1493 return GENERAL_ERROR;
1494 }
1495
1496 if (recordID >= sensorTree.size())
1497 {
1498 size_t fruIndex = recordID - sensorTree.size();
1499
1500 if (fruIndex >= fruCount)
1501 {
1502 // handle type 12 hardcoded records
1503 size_t type12Index = fruIndex - fruCount;
1504 if (type12Index >= ipmi::storage::type12Count)
1505 {
1506 phosphor::logging::log<phosphor::logging::level::ERR>(
1507 "getSensorDataRecord: type12Index error");
1508 return GENERAL_ERROR;
1509 }
1510 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1511 }
1512 else
1513 {
1514 // handle fru records
1515 get_sdr::SensorDataFruRecord data;
1516 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1517 if (ret != IPMI_CC_OK)
1518 {
1519 return GENERAL_ERROR;
1520 }
1521 data.header.record_id_msb = recordID >> 8;
1522 data.header.record_id_lsb = recordID & 0xFF;
1523 recordData.insert(recordData.end(), (uint8_t*)&data,
1524 ((uint8_t*)&data) + sizeof(data));
1525 }
1526
1527 return 0;
1528 }
1529
1530 std::string connection;
1531 std::string path;
1532 auto status = getSensorConnection(ctx, recordID, connection, path);
1533 if (status)
1534 {
1535 phosphor::logging::log<phosphor::logging::level::ERR>(
1536 "getSensorDataRecord: getSensorConnection error");
1537 return GENERAL_ERROR;
1538 }
1539 DbusInterfaceMap sensorMap;
1540 if (!getSensorMap(ctx, connection, path, sensorMap, sensorMapUpdatePeriod))
1541 {
1542 phosphor::logging::log<phosphor::logging::level::ERR>(
1543 "getSensorDataRecord: getSensorMap error");
1544 return GENERAL_ERROR;
1545 }
1546 uint16_t sensorNum = getSensorNumberFromPath(path);
1547 if (sensorNum == invalidSensorNumber)
1548 {
1549 phosphor::logging::log<phosphor::logging::level::ERR>(
1550 "getSensorDataRecord: invalidSensorNumber");
1551 return GENERAL_ERROR;
1552 }
1553
1554 auto sensorObject = sensorMap.find(sensor::sensorInterface);
1555 // Construct full record (SDR type 1) for the threshold sensors
1556 if (sensorObject != sensorMap.end())
1557 {
1558 get_sdr::SensorDataFullRecord record = {0};
1559
1560 if (!constructSensorSdr(sensorNum, recordID, path, sensorMap, record))
1561 {
1562 return GENERAL_ERROR;
1563 }
1564 recordData.insert(recordData.end(), (uint8_t*)&record,
1565 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07001566
1567 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001568 }
Willy Tu61992ad2021-03-29 15:33:20 -07001569
1570 // Contruct SDR type 3 record for VR sensor (daemon)
1571 sensorObject = sensorMap.find(sensor::vrInterface);
1572 if (sensorObject != sensorMap.end())
1573 {
1574 get_sdr::SensorDataEventRecord record = {0};
1575
1576 constructVrSdr(sensorNum, recordID, path, sensorMap, record);
1577 recordData.insert(recordData.end(), (uint8_t*)&record,
1578 ((uint8_t*)&record) + sizeof(record));
1579 }
1580
Willy Tude54f482021-01-26 15:59:09 -08001581 return 0;
1582}
1583
1584/** @brief implements the get SDR Info command
1585 * @param count - Operation
1586 *
1587 * @returns IPMI completion code plus response data
1588 * - sdrCount - sensor/SDR count
1589 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1590 */
1591static ipmi::RspType<uint8_t, // respcount
1592 uint8_t, // dynamic population flags
1593 uint32_t // last time a sensor was added
1594 >
1595 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1596 std::optional<uint8_t> count)
1597{
1598 auto& sensorTree = getSensorTree();
1599 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001600 uint16_t recordID = 0;
1601 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08001602 // Sensors are dynamically allocated, and there is at least one LUN
1603 uint8_t lunsAndDynamicPopulation = 0x80;
1604 constexpr uint8_t getSdrCount = 0x01;
1605 constexpr uint8_t getSensorCount = 0x00;
1606
1607 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1608 {
1609 return ipmi::responseResponseError();
1610 }
Willy Tude54f482021-01-26 15:59:09 -08001611 uint16_t numSensors = sensorTree.size();
1612 if (count.value_or(0) == getSdrCount)
1613 {
1614 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001615 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08001616 {
1617 get_sdr::SensorDataRecordHeader* hdr =
1618 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001619 record.data());
Willy Tude54f482021-01-26 15:59:09 -08001620 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1621 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001622 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08001623 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001624 record.data());
1625 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08001626 {
1627 sdrCount++;
1628 }
1629 }
1630 }
1631 }
1632 else if (count.value_or(0) == getSensorCount)
1633 {
1634 // Return the number of sensors attached to the LUN
1635 if ((ctx->lun == 0) && (numSensors > 0))
1636 {
1637 sdrCount =
1638 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1639 }
1640 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1641 {
1642 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1643 ? maxSensorsPerLUN
1644 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1645 }
1646 else if (ctx->lun == 3)
1647 {
1648 if (numSensors <= maxIPMISensors)
1649 {
1650 sdrCount =
1651 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1652 }
1653 else
1654 {
1655 // error
1656 throw std::out_of_range(
1657 "Maximum number of IPMI sensors exceeded.");
1658 }
1659 }
1660 }
1661 else
1662 {
1663 return ipmi::responseInvalidFieldRequest();
1664 }
1665
1666 // Get Sensor count. This returns the number of sensors
1667 if (numSensors > 0)
1668 {
1669 lunsAndDynamicPopulation |= 1;
1670 }
1671 if (numSensors > maxSensorsPerLUN)
1672 {
1673 lunsAndDynamicPopulation |= 2;
1674 }
1675 if (numSensors >= (maxSensorsPerLUN * 2))
1676 {
1677 lunsAndDynamicPopulation |= 8;
1678 }
1679 if (numSensors > maxIPMISensors)
1680 {
1681 // error
1682 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1683 }
1684
1685 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1686 sdrLastAdd);
1687}
1688
1689/* end sensor commands */
1690
1691/* storage commands */
1692
1693ipmi::RspType<uint8_t, // sdr version
1694 uint16_t, // record count
1695 uint16_t, // free space
1696 uint32_t, // most recent addition
1697 uint32_t, // most recent erase
1698 uint8_t // operationSupport
1699 >
1700 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
1701{
1702 auto& sensorTree = getSensorTree();
1703 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001704 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001705 {
1706 return ipmi::responseResponseError();
1707 }
1708
1709 size_t fruCount = 0;
1710 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1711 if (ret != ipmi::ccSuccess)
1712 {
1713 return ipmi::response(ret);
1714 }
1715
1716 uint16_t recordCount =
1717 sensorTree.size() + fruCount + ipmi::storage::type12Count;
1718
1719 uint8_t operationSupport = static_cast<uint8_t>(
1720 SdrRepositoryInfoOps::overflow); // write not supported
1721
1722 operationSupport |=
1723 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1724 operationSupport |= static_cast<uint8_t>(
1725 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1726 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1727 unspecifiedFreeSpace, sdrLastAdd,
1728 sdrLastRemove, operationSupport);
1729}
1730
1731/** @brief implements the get SDR allocation info command
1732 *
1733 * @returns IPMI completion code plus response data
1734 * - allocUnits - Number of possible allocation units
1735 * - allocUnitSize - Allocation unit size in bytes.
1736 * - allocUnitFree - Number of free allocation units
1737 * - allocUnitLargestFree - Largest free block in allocation units
1738 * - maxRecordSize - Maximum record size in allocation units.
1739 */
1740ipmi::RspType<uint16_t, // allocUnits
1741 uint16_t, // allocUnitSize
1742 uint16_t, // allocUnitFree
1743 uint16_t, // allocUnitLargestFree
1744 uint8_t // maxRecordSize
1745 >
1746 ipmiStorageGetSDRAllocationInfo()
1747{
1748 // 0000h unspecified number of alloc units
1749 constexpr uint16_t allocUnits = 0;
1750
1751 constexpr uint16_t allocUnitFree = 0;
1752 constexpr uint16_t allocUnitLargestFree = 0;
1753 // only allow one block at a time
1754 constexpr uint8_t maxRecordSize = 1;
1755
1756 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1757 allocUnitLargestFree, maxRecordSize);
1758}
1759
1760/** @brief implements the reserve SDR command
1761 * @returns IPMI completion code plus response data
1762 * - sdrReservationID
1763 */
1764ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
1765{
1766 sdrReservationID++;
1767 if (sdrReservationID == 0)
1768 {
1769 sdrReservationID++;
1770 }
1771
1772 return ipmi::responseSuccess(sdrReservationID);
1773}
1774
1775ipmi::RspType<uint16_t, // next record ID
1776 std::vector<uint8_t> // payload
1777 >
1778 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1779 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1780{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001781 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08001782 // reservation required for partial reads with non zero offset into
1783 // record
1784 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
1785 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001786 phosphor::logging::log<phosphor::logging::level::ERR>(
1787 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08001788 return ipmi::responseInvalidReservationId();
1789 }
Willy Tude54f482021-01-26 15:59:09 -08001790 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1791 if (ret != ipmi::ccSuccess)
1792 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001793 phosphor::logging::log<phosphor::logging::level::ERR>(
1794 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08001795 return ipmi::response(ret);
1796 }
1797
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001798 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08001799 size_t lastRecord =
1800 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001801 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1802
1803 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08001804 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001805 phosphor::logging::log<phosphor::logging::level::ERR>(
1806 "ipmiStorageGetSDR: getSensorSubtree error");
1807 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08001808 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001809
1810 std::vector<uint8_t> record;
1811 if (getSensorDataRecord(ctx, record, recordID))
Willy Tude54f482021-01-26 15:59:09 -08001812 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001813 phosphor::logging::log<phosphor::logging::level::ERR>(
1814 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08001815 return ipmi::responseInvalidFieldRequest();
1816 }
Willy Tude54f482021-01-26 15:59:09 -08001817 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001818 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08001819 if (!hdr)
1820 {
1821 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001822 "ipmiStorageGetSDR: record header is null");
1823 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001824 }
1825
1826 size_t sdrLength =
1827 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
1828 if (sdrLength < (offset + bytesToRead))
1829 {
1830 bytesToRead = sdrLength - offset;
1831 }
1832
1833 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
1834 if (!respStart)
1835 {
1836 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001837 "ipmiStorageGetSDR: record is null");
1838 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08001839 }
1840
1841 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001842
Willy Tude54f482021-01-26 15:59:09 -08001843 return ipmi::responseSuccess(nextRecordId, recordData);
1844}
1845/* end storage commands */
1846
1847void registerSensorFunctions()
1848{
1849 // <Platform Event>
1850 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1851 ipmi::sensor_event::cmdPlatformEvent,
1852 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1853
Willy Tudbafbce2021-03-29 00:37:05 -07001854#ifdef FEATURE_DYNAMIC_SENSORS_WRITE
1855 // <Set Sensor Reading and Event Status>
1856 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1857 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
1858 ipmi::Privilege::Operator, ipmiSetSensorReading);
1859#endif
1860
Willy Tude54f482021-01-26 15:59:09 -08001861 // <Get Sensor Reading>
1862 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1863 ipmi::sensor_event::cmdGetSensorReading,
1864 ipmi::Privilege::User, ipmiSenGetSensorReading);
1865
1866 // <Get Sensor Threshold>
1867 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1868 ipmi::sensor_event::cmdGetSensorThreshold,
1869 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
1870
1871 // <Set Sensor Threshold>
1872 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1873 ipmi::sensor_event::cmdSetSensorThreshold,
1874 ipmi::Privilege::Operator,
1875 ipmiSenSetSensorThresholds);
1876
1877 // <Get Sensor Event Enable>
1878 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1879 ipmi::sensor_event::cmdGetSensorEventEnable,
1880 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
1881
1882 // <Get Sensor Event Status>
1883 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1884 ipmi::sensor_event::cmdGetSensorEventStatus,
1885 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
1886
1887 // register all storage commands for both Sensor and Storage command
1888 // versions
1889
1890 // <Get SDR Repository Info>
1891 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1892 ipmi::storage::cmdGetSdrRepositoryInfo,
1893 ipmi::Privilege::User,
1894 ipmiStorageGetSDRRepositoryInfo);
1895
1896 // <Get Device SDR Info>
1897 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1898 ipmi::sensor_event::cmdGetDeviceSdrInfo,
1899 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1900
1901 // <Get SDR Allocation Info>
1902 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1903 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
1904 ipmi::Privilege::User,
1905 ipmiStorageGetSDRAllocationInfo);
1906
1907 // <Reserve SDR Repo>
1908 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1909 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
1910 ipmi::Privilege::User, ipmiStorageReserveSDR);
1911
1912 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1913 ipmi::storage::cmdReserveSdrRepository,
1914 ipmi::Privilege::User, ipmiStorageReserveSDR);
1915
1916 // <Get Sdr>
1917 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1918 ipmi::sensor_event::cmdGetDeviceSdr,
1919 ipmi::Privilege::User, ipmiStorageGetSDR);
1920
1921 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1922 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
1923 ipmiStorageGetSDR);
1924}
1925} // namespace ipmi