blob: 05f5edf72d2951ee1df178b14562f07ff16402a5 [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"
Harvey Wu05d17c02021-09-15 08:46:59 +080022#include "entity_map_json.hpp"
Willy Tude54f482021-01-26 15:59:09 -080023
24#include <algorithm>
25#include <array>
26#include <boost/algorithm/string.hpp>
27#include <boost/container/flat_map.hpp>
28#include <chrono>
29#include <cmath>
30#include <cstring>
31#include <iostream>
32#include <ipmid/api.hpp>
33#include <ipmid/types.hpp>
34#include <ipmid/utils.hpp>
35#include <map>
36#include <memory>
37#include <optional>
38#include <phosphor-logging/log.hpp>
39#include <sdbusplus/bus.hpp>
40#include <stdexcept>
41#include <string>
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +000042#include <user_channel/channel_layer.hpp>
Willy Tude54f482021-01-26 15:59:09 -080043#include <utility>
44#include <variant>
45
Scron Chang2703b022021-07-06 15:47:45 +080046#ifdef FEATURE_HYBRID_SENSORS
47
48#include "sensordatahandler.hpp"
49namespace ipmi
50{
51namespace sensor
52{
53extern const IdInfoMap sensors;
54} // namespace sensor
55} // namespace ipmi
56#endif
57
JeffLind950f412021-10-20 18:49:34 +080058constexpr std::array<const char*, 7> suffixes = {
59 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
60 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080061namespace ipmi
62{
Hao Jiangd48c9212021-02-03 15:45:06 -080063
64using phosphor::logging::entry;
65using phosphor::logging::level;
66using phosphor::logging::log;
67
Willy Tude54f482021-01-26 15:59:09 -080068static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070069static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080070
Willy Tu38e7a2b2021-03-29 15:09:56 -070071// BMC I2C address is generally at 0x20
72static constexpr uint8_t bmcI2CAddr = 0x20;
73
Willy Tude54f482021-01-26 15:59:09 -080074constexpr size_t maxSDRTotalSize =
75 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
76constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
77
78static uint16_t sdrReservationID;
79static uint32_t sdrLastAdd = noTimestamp;
80static uint32_t sdrLastRemove = noTimestamp;
81static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070082
83// The IPMI spec defines four Logical Units (LUN), each capable of supporting
84// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
85// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
86// number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
87// location.
88static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
89
90static constexpr size_t lun0MaxSensorNum = 0xfe;
91static constexpr size_t lun1MaxSensorNum = 0x1fe;
92static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -080093static constexpr int GENERAL_ERROR = -1;
94
Willy Tude54f482021-01-26 15:59:09 -080095static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
96
97// Specify the comparison required to sort and find char* map objects
98struct CmpStr
99{
100 bool operator()(const char* a, const char* b) const
101 {
102 return std::strcmp(a, b) < 0;
103 }
104};
105const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
106 sensorUnits{{{"temperature", SensorUnits::degreesC},
107 {"voltage", SensorUnits::volts},
108 {"current", SensorUnits::amps},
109 {"fan_tach", SensorUnits::rpm},
110 {"power", SensorUnits::watts}}};
111
112void registerSensorFunctions() __attribute__((constructor));
113
114static sdbusplus::bus::match::match sensorAdded(
115 *getSdBus(),
116 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
117 "sensors/'",
Willy Tu11d68892022-01-20 10:37:34 -0800118 [](sdbusplus::message::message&) {
Willy Tude54f482021-01-26 15:59:09 -0800119 getSensorTree().clear();
120 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
121 std::chrono::system_clock::now().time_since_epoch())
122 .count();
123 });
124
125static sdbusplus::bus::match::match sensorRemoved(
126 *getSdBus(),
127 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
128 "sensors/'",
Willy Tu11d68892022-01-20 10:37:34 -0800129 [](sdbusplus::message::message&) {
Willy Tude54f482021-01-26 15:59:09 -0800130 getSensorTree().clear();
131 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
132 std::chrono::system_clock::now().time_since_epoch())
133 .count();
134 });
135
136// this keeps track of deassertions for sensor event status command. A
137// deasertion can only happen if an assertion was seen first.
138static boost::container::flat_map<
139 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
140 thresholdDeassertMap;
141
142static sdbusplus::bus::match::match thresholdChanged(
143 *getSdBus(),
144 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
145 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
146 [](sdbusplus::message::message& m) {
147 boost::container::flat_map<std::string, std::variant<bool, double>>
148 values;
149 m.read(std::string(), values);
150
151 auto findAssert =
152 std::find_if(values.begin(), values.end(), [](const auto& pair) {
153 return pair.first.find("Alarm") != std::string::npos;
154 });
155 if (findAssert != values.end())
156 {
157 auto ptr = std::get_if<bool>(&(findAssert->second));
158 if (ptr == nullptr)
159 {
160 phosphor::logging::log<phosphor::logging::level::ERR>(
161 "thresholdChanged: Assert non bool");
162 return;
163 }
164 if (*ptr)
165 {
166 phosphor::logging::log<phosphor::logging::level::INFO>(
167 "thresholdChanged: Assert",
168 phosphor::logging::entry("SENSOR=%s", m.get_path()));
169 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
170 }
171 else
172 {
173 auto& value =
174 thresholdDeassertMap[m.get_path()][findAssert->first];
175 if (value)
176 {
177 phosphor::logging::log<phosphor::logging::level::INFO>(
178 "thresholdChanged: deassert",
179 phosphor::logging::entry("SENSOR=%s", m.get_path()));
180 value = *ptr;
181 }
182 }
183 }
184 });
185
Hao Jiangd2afd052020-12-10 15:09:32 -0800186namespace sensor
187{
188static constexpr const char* vrInterface =
189 "xyz.openbmc_project.Control.VoltageRegulatorMode";
190static constexpr const char* sensorInterface =
191 "xyz.openbmc_project.Sensor.Value";
192} // namespace sensor
193
Willy Tude54f482021-01-26 15:59:09 -0800194static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
195 double& min)
196{
197 max = 127;
198 min = -128;
199
Hao Jiangd2afd052020-12-10 15:09:32 -0800200 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800201 auto critical =
202 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
203 auto warning =
204 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
205
206 if (sensorObject != sensorMap.end())
207 {
208 auto maxMap = sensorObject->second.find("MaxValue");
209 auto minMap = sensorObject->second.find("MinValue");
210
211 if (maxMap != sensorObject->second.end())
212 {
213 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
214 }
215 if (minMap != sensorObject->second.end())
216 {
217 min = std::visit(VariantToDoubleVisitor(), minMap->second);
218 }
219 }
220 if (critical != sensorMap.end())
221 {
222 auto lower = critical->second.find("CriticalLow");
223 auto upper = critical->second.find("CriticalHigh");
224 if (lower != critical->second.end())
225 {
226 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300227 if (std::isfinite(value))
228 {
229 min = std::min(value, min);
230 }
Willy Tude54f482021-01-26 15:59:09 -0800231 }
232 if (upper != critical->second.end())
233 {
234 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300235 if (std::isfinite(value))
236 {
237 max = std::max(value, max);
238 }
Willy Tude54f482021-01-26 15:59:09 -0800239 }
240 }
241 if (warning != sensorMap.end())
242 {
243
244 auto lower = warning->second.find("WarningLow");
245 auto upper = warning->second.find("WarningHigh");
246 if (lower != warning->second.end())
247 {
248 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300249 if (std::isfinite(value))
250 {
251 min = std::min(value, min);
252 }
Willy Tude54f482021-01-26 15:59:09 -0800253 }
254 if (upper != warning->second.end())
255 {
256 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300257 if (std::isfinite(value))
258 {
259 max = std::max(value, max);
260 }
Willy Tude54f482021-01-26 15:59:09 -0800261 }
262 }
263}
264
265static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700266 std::string sensorPath, DbusInterfaceMap& sensorMap,
267 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800268{
Scron Chang2703b022021-07-06 15:47:45 +0800269#ifdef FEATURE_HYBRID_SENSORS
270 if (auto sensor = findStaticSensor(sensorPath);
271 sensor != ipmi::sensor::sensors.end() &&
272 getSensorEventTypeFromPath(sensorPath) !=
273 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
274 {
275 // If the incoming sensor is a discrete sensor, it might fail in
276 // getManagedObjects(), return true, and use its own getFunc to get
277 // value.
278 return true;
279 }
280#endif
281
Willy Tude54f482021-01-26 15:59:09 -0800282 static boost::container::flat_map<
283 std::string, std::chrono::time_point<std::chrono::steady_clock>>
284 updateTimeMap;
285
286 auto updateFind = updateTimeMap.find(sensorConnection);
287 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
288 if (updateFind != updateTimeMap.end())
289 {
290 lastUpdate = updateFind->second;
291 }
292
293 auto now = std::chrono::steady_clock::now();
294
295 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700296 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800297 {
Willy Tude54f482021-01-26 15:59:09 -0800298 ObjectValueTree managedObjects;
299 boost::system::error_code ec = getManagedObjects(
300 ctx, sensorConnection.c_str(), "/", managedObjects);
301 if (ec)
302 {
303 phosphor::logging::log<phosphor::logging::level::ERR>(
304 "GetMangagedObjects for getSensorMap failed",
305 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
306
307 return false;
308 }
309
310 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700311 // Update time after finish building the map which allow the
312 // data to be cached for updatePeriod plus the build time.
313 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800314 }
315 auto connection = SensorCache.find(sensorConnection);
316 if (connection == SensorCache.end())
317 {
318 return false;
319 }
320 auto path = connection->second.find(sensorPath);
321 if (path == connection->second.end())
322 {
323 return false;
324 }
325 sensorMap = path->second;
326
327 return true;
328}
329
Hao Jiangd2afd052020-12-10 15:09:32 -0800330namespace sensor
331{
Hao Jiangd48c9212021-02-03 15:45:06 -0800332// Read VR profiles from sensor(daemon) interface
333static std::optional<std::vector<std::string>>
334 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800335{
336 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800337 auto supportedProperty = object.find("Supported");
338 if (supportedProperty == object.end() ||
339 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800340 {
341 phosphor::logging::log<phosphor::logging::level::ERR>(
342 "Missing the required Supported and Selected properties");
343 return std::nullopt;
344 }
345
346 const auto profilesPtr =
347 std::get_if<std::vector<std::string>>(&supportedProperty->second);
348
349 if (profilesPtr == nullptr)
350 {
351 phosphor::logging::log<phosphor::logging::level::ERR>(
352 "property is not array of string");
353 return std::nullopt;
354 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800355 return *profilesPtr;
356}
357
358// Calculate VR Mode from input IPMI discrete event bytes
359static std::optional<std::string>
360 calculateVRMode(uint15_t assertOffset,
361 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
362{
363 // get VR mode profiles from Supported Interface
364 auto profiles = getSupportedVrProfiles(VRObject);
365 if (!profiles)
366 {
367 return std::nullopt;
368 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800369
370 // interpret IPMI cmd bits into profiles' index
371 long unsigned int index = 0;
372 // only one bit should be set and the highest bit should not be used.
373 if (assertOffset == 0 || assertOffset == (1u << 15) ||
374 (assertOffset & (assertOffset - 1)))
375 {
376 phosphor::logging::log<phosphor::logging::level::ERR>(
377 "IPMI cmd format incorrect",
378
379 phosphor::logging::entry("BYTES=%#02x",
380 static_cast<uint16_t>(assertOffset)));
381 return std::nullopt;
382 }
383
384 while (assertOffset != 1)
385 {
386 assertOffset >>= 1;
387 index++;
388 }
389
Hao Jiangd48c9212021-02-03 15:45:06 -0800390 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800391 {
392 phosphor::logging::log<phosphor::logging::level::ERR>(
393 "profile index out of boundary");
394 return std::nullopt;
395 }
396
Hao Jiangd48c9212021-02-03 15:45:06 -0800397 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800398}
399
400// Calculate sensor value from IPMI reading byte
401static std::optional<double>
402 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
403 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
404{
405 if (valueObject.find("Value") == valueObject.end())
406 {
407 phosphor::logging::log<phosphor::logging::level::ERR>(
408 "Missing the required Value property");
409 return std::nullopt;
410 }
411
412 double max = 0;
413 double min = 0;
414 getSensorMaxMin(sensorMap, max, min);
415
416 int16_t mValue = 0;
417 int16_t bValue = 0;
418 int8_t rExp = 0;
419 int8_t bExp = 0;
420 bool bSigned = false;
421
422 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
423 {
424 return std::nullopt;
425 }
426
427 double value = bSigned ? ((int8_t)reading) : reading;
428
429 value *= ((double)mValue);
430 value += ((double)bValue) * std::pow(10.0, bExp);
431 value *= std::pow(10.0, rExp);
432
433 return value;
434}
435
Willy Tu38e7a2b2021-03-29 15:09:56 -0700436// Extract file name from sensor path as the sensors SDR ID. Simplify the name
437// if it is too long.
438std::string parseSdrIdFromPath(const std::string& path)
439{
440 std::string name;
441 size_t nameStart = path.rfind("/");
442 if (nameStart != std::string::npos)
443 {
444 name = path.substr(nameStart + 1, std::string::npos - nameStart);
445 }
446
Willy Tu38e7a2b2021-03-29 15:09:56 -0700447 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
448 {
449 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800450 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700451 {
JeffLind950f412021-10-20 18:49:34 +0800452 if (boost::ends_with(name, suffix))
453 {
454 boost::replace_all(name, suffix, "");
455 break;
456 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700457 }
Duke Du97014f52021-12-16 17:21:01 +0800458 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
459 {
460 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
461 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700462 }
JeffLind950f412021-10-20 18:49:34 +0800463 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700464 return name;
465}
466
Hao Jiangd48c9212021-02-03 15:45:06 -0800467bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
468 const std::string& path,
469 const ipmi::DbusInterfaceMap::mapped_type& object,
470 std::bitset<16>& assertions)
471{
472 auto profiles = sensor::getSupportedVrProfiles(object);
473 if (!profiles)
474 {
475 return false;
476 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700477 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800478
479 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700480 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800481 if (ec)
482 {
483 log<level::ERR>("Failed to get property",
484 entry("PROPERTY=%s", "Selected"),
485 entry("PATH=%s", path.c_str()),
486 entry("INTERFACE=%s", sensor::sensorInterface),
487 entry("WHAT=%s", ec.message().c_str()));
488 return false;
489 }
490
Willy Tu8366f0b2022-04-29 05:00:17 -0700491 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800492 if (itr == profiles->end())
493 {
494 using namespace phosphor::logging;
495 log<level::ERR>("VR mode doesn't match any of its profiles",
496 entry("PATH=%s", path.c_str()));
497 return false;
498 }
499 std::size_t index =
500 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
501
502 // map index to reponse event assertion bit.
503 if (index < 8)
504 {
505 assertions.set(1u << index);
506 }
507 else if (index < 15)
508 {
509 assertions.set(1u << (index - 8));
510 }
511 else
512 {
513 log<level::ERR>("VR profile index reaches max assertion bit",
514 entry("PATH=%s", path.c_str()),
515 entry("INDEX=%uz", index));
516 return false;
517 }
518 if constexpr (debug)
519 {
520 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700521 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800522 }
523 return true;
524}
Hao Jiangd2afd052020-12-10 15:09:32 -0800525} // namespace sensor
526
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000527ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
528 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800529{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000530 constexpr const uint8_t validEnvmRev = 0x04;
531 constexpr const uint8_t lastSensorType = 0x2C;
532 constexpr const uint8_t oemReserved = 0xC0;
533
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700534 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000535 uint8_t evmRev = 0;
536 uint8_t sensorType = 0;
537 uint8_t sensorNum = 0;
538 uint8_t eventType = 0;
539 uint8_t eventData1 = 0;
540 std::optional<uint8_t> eventData2 = 0;
541 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700542 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000543 ipmi::ChannelInfo chInfo;
544
545 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
546 {
547 phosphor::logging::log<phosphor::logging::level::ERR>(
548 "Failed to get Channel Info",
549 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
550 return ipmi::responseUnspecifiedError();
551 }
552
553 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
554 ipmi::EChannelMediumType::systemInterface)
555 {
556
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700557 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000558 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700559 // Refer to IPMI Spec Table 32: SEL Event Records
560 generatorID = (ctx->channel << 12) // Channel
561 | (0x0 << 10) // Reserved
562 | (0x0 << 8) // 0x0 for sys-soft ID
563 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000564 }
565 else
566 {
567
568 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
569 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700570 // Refer to IPMI Spec Table 32: SEL Event Records
571 generatorID = (ctx->channel << 12) // Channel
572 | (0x0 << 10) // Reserved
573 | ((ctx->lun & 0x3) << 8) // Lun
574 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000575 }
576
577 if (!p.fullyUnpacked())
578 {
579 return ipmi::responseReqDataLenInvalid();
580 }
581
582 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
583 if (evmRev != validEnvmRev)
584 {
585 return ipmi::responseInvalidFieldRequest();
586 }
587 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
588 {
589 return ipmi::responseInvalidFieldRequest();
590 }
591
Willy Tude54f482021-01-26 15:59:09 -0800592 return ipmi::responseSuccess();
593}
594
Willy Tudbafbce2021-03-29 00:37:05 -0700595ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800596 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700597 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800598 bool, uint15_t, bool, uint8_t, uint8_t,
599 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700600{
601 std::string connection;
602 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700603 std::vector<std::string> interfaces;
604
605 ipmi::Cc status =
606 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700607 if (status)
608 {
609 return ipmi::response(status);
610 }
611
Hao Jiangd2afd052020-12-10 15:09:32 -0800612 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700613 if (std::find(interfaces.begin(), interfaces.end(),
614 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700615 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700616 DbusInterfaceMap sensorMap;
617 if (!getSensorMap(ctx, connection, path, sensorMap))
618 {
619 return ipmi::responseResponseError();
620 }
621 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800622 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700623 {
624 return ipmi::responseResponseError();
625 }
626
Jie Yangf0a89942021-07-29 15:30:25 -0700627 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800628 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
629 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700630 {
631 return ipmi::responseResponseError();
632 }
633
Hao Jiangd2afd052020-12-10 15:09:32 -0800634 auto value =
635 sensor::calculateValue(reading, sensorMap, sensorObject->second);
636 if (!value)
637 {
638 return ipmi::responseResponseError();
639 }
640
641 if constexpr (debug)
642 {
643 phosphor::logging::log<phosphor::logging::level::INFO>(
644 "IPMI SET_SENSOR",
645 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
646 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
647 phosphor::logging::entry("VALUE=%f", *value));
648 }
649
650 boost::system::error_code ec =
651 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
652 "Value", ipmi::Value(*value));
653
654 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500655 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800656 // callback functions for now (e.g. ipmiSetSensorReading).
657 if (ec)
658 {
659 using namespace phosphor::logging;
660 log<level::ERR>("Failed to set property",
661 entry("PROPERTY=%s", "Value"),
662 entry("PATH=%s", path.c_str()),
663 entry("INTERFACE=%s", sensor::sensorInterface),
664 entry("WHAT=%s", ec.message().c_str()));
665 return ipmi::responseResponseError();
666 }
667 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700668 }
669
Hao Jiange39d4d82021-04-16 17:02:40 -0700670 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
671 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700672 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700673 DbusInterfaceMap sensorMap;
674 if (!getSensorMap(ctx, connection, path, sensorMap))
675 {
676 return ipmi::responseResponseError();
677 }
678 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800679 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700680 {
681 return ipmi::responseResponseError();
682 }
683
Hao Jiangd2afd052020-12-10 15:09:32 -0800684 // VR sensors are treated as a special case and we will not check the
685 // write permission for VR sensors, since they always deemed writable
686 // and permission table are not applied to VR sensors.
687 auto vrMode =
688 sensor::calculateVRMode(assertOffset, sensorObject->second);
689 if (!vrMode)
690 {
691 return ipmi::responseResponseError();
692 }
693 boost::system::error_code ec = setDbusProperty(
694 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
695 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500696 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800697 // callback functions for now (e.g. ipmiSetSensorReading).
698 if (ec)
699 {
700 using namespace phosphor::logging;
701 log<level::ERR>("Failed to set property",
702 entry("PROPERTY=%s", "Selected"),
703 entry("PATH=%s", path.c_str()),
704 entry("INTERFACE=%s", sensor::sensorInterface),
705 entry("WHAT=%s", ec.message().c_str()));
706 return ipmi::responseResponseError();
707 }
708 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700709 }
710
Hao Jiangd2afd052020-12-10 15:09:32 -0800711 phosphor::logging::log<phosphor::logging::level::ERR>(
712 "unknown sensor type",
713 phosphor::logging::entry("PATH=%s", path.c_str()));
714 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700715}
716
Willy Tude54f482021-01-26 15:59:09 -0800717ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
718 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
719{
720 std::string connection;
721 std::string path;
722
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000723 if (sensnum == reservedSensorNumber)
724 {
725 return ipmi::responseInvalidFieldRequest();
726 }
727
Willy Tude54f482021-01-26 15:59:09 -0800728 auto status = getSensorConnection(ctx, sensnum, connection, path);
729 if (status)
730 {
731 return ipmi::response(status);
732 }
733
Scron Chang2703b022021-07-06 15:47:45 +0800734#ifdef FEATURE_HYBRID_SENSORS
735 if (auto sensor = findStaticSensor(path);
736 sensor != ipmi::sensor::sensors.end() &&
737 getSensorEventTypeFromPath(path) !=
738 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
739 {
740 if (ipmi::sensor::Mutability::Read !=
741 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
742 {
743 return ipmi::responseIllegalCommand();
744 }
745
746 uint8_t operation;
747 try
748 {
749 ipmi::sensor::GetSensorResponse getResponse =
750 sensor->second.getFunc(sensor->second);
751
752 if (getResponse.readingOrStateUnavailable)
753 {
754 operation |= static_cast<uint8_t>(
755 IPMISensorReadingByte2::readingStateUnavailable);
756 }
757 if (getResponse.scanningEnabled)
758 {
759 operation |= static_cast<uint8_t>(
760 IPMISensorReadingByte2::sensorScanningEnable);
761 }
762 if (getResponse.allEventMessagesEnabled)
763 {
764 operation |= static_cast<uint8_t>(
765 IPMISensorReadingByte2::eventMessagesEnable);
766 }
767 return ipmi::responseSuccess(
768 getResponse.reading, operation,
769 getResponse.thresholdLevelsStates,
770 getResponse.discreteReadingSensorStates);
771 }
772 catch (const std::exception& e)
773 {
774 operation |= static_cast<uint8_t>(
775 IPMISensorReadingByte2::readingStateUnavailable);
776 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
777 }
778 }
779#endif
780
Willy Tude54f482021-01-26 15:59:09 -0800781 DbusInterfaceMap sensorMap;
782 if (!getSensorMap(ctx, connection, path, sensorMap))
783 {
784 return ipmi::responseResponseError();
785 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800786 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800787
788 if (sensorObject == sensorMap.end() ||
789 sensorObject->second.find("Value") == sensorObject->second.end())
790 {
791 return ipmi::responseResponseError();
792 }
793 auto& valueVariant = sensorObject->second["Value"];
794 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
795
796 double max = 0;
797 double min = 0;
798 getSensorMaxMin(sensorMap, max, min);
799
800 int16_t mValue = 0;
801 int16_t bValue = 0;
802 int8_t rExp = 0;
803 int8_t bExp = 0;
804 bool bSigned = false;
805
806 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
807 {
808 return ipmi::responseResponseError();
809 }
810
811 uint8_t value =
812 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
813 uint8_t operation =
814 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
815 operation |=
816 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
817 bool notReading = std::isnan(reading);
818
819 if (!notReading)
820 {
821 auto availableObject =
822 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
823 if (availableObject != sensorMap.end())
824 {
825 auto findAvailable = availableObject->second.find("Available");
826 if (findAvailable != availableObject->second.end())
827 {
828 bool* available = std::get_if<bool>(&(findAvailable->second));
829 if (available && !(*available))
830 {
831 notReading = true;
832 }
833 }
834 }
835 }
836
837 if (notReading)
838 {
839 operation |= static_cast<uint8_t>(
840 IPMISensorReadingByte2::readingStateUnavailable);
841 }
842
Josh Lehana55c9532020-10-28 21:59:06 -0700843 if constexpr (details::enableInstrumentation)
844 {
845 int byteValue;
846 if (bSigned)
847 {
848 byteValue = static_cast<int>(static_cast<int8_t>(value));
849 }
850 else
851 {
852 byteValue = static_cast<int>(static_cast<uint8_t>(value));
853 }
854
855 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800856 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
857 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700858 {
859 // This is the first reading, show the coefficients
860 double step = (max - min) / 255.0;
861 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800862 << details::sdrStatsTable.getName((ctx->lun << 8) |
863 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700864 << ": Range min=" << min << " max=" << max
865 << ", step=" << step
866 << ", Coefficients mValue=" << static_cast<int>(mValue)
867 << " rExp=" << static_cast<int>(rExp)
868 << " bValue=" << static_cast<int>(bValue)
869 << " bExp=" << static_cast<int>(bExp)
870 << " bSigned=" << static_cast<int>(bSigned) << "\n";
871 }
872 }
873
Willy Tude54f482021-01-26 15:59:09 -0800874 uint8_t thresholds = 0;
875
876 auto warningObject =
877 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
878 if (warningObject != sensorMap.end())
879 {
880 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
881 auto alarmLow = warningObject->second.find("WarningAlarmLow");
882 if (alarmHigh != warningObject->second.end())
883 {
884 if (std::get<bool>(alarmHigh->second))
885 {
886 thresholds |= static_cast<uint8_t>(
887 IPMISensorReadingByte3::upperNonCritical);
888 }
889 }
890 if (alarmLow != warningObject->second.end())
891 {
892 if (std::get<bool>(alarmLow->second))
893 {
894 thresholds |= static_cast<uint8_t>(
895 IPMISensorReadingByte3::lowerNonCritical);
896 }
897 }
898 }
899
900 auto criticalObject =
901 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
902 if (criticalObject != sensorMap.end())
903 {
904 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
905 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
906 if (alarmHigh != criticalObject->second.end())
907 {
908 if (std::get<bool>(alarmHigh->second))
909 {
910 thresholds |=
911 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
912 }
913 }
914 if (alarmLow != criticalObject->second.end())
915 {
916 if (std::get<bool>(alarmLow->second))
917 {
918 thresholds |=
919 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
920 }
921 }
922 }
923
924 // no discrete as of today so optional byte is never returned
925 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
926}
927
928/** @brief implements the Set Sensor threshold command
929 * @param sensorNumber - sensor number
930 * @param lowerNonCriticalThreshMask
931 * @param lowerCriticalThreshMask
932 * @param lowerNonRecovThreshMask
933 * @param upperNonCriticalThreshMask
934 * @param upperCriticalThreshMask
935 * @param upperNonRecovThreshMask
936 * @param reserved
937 * @param lowerNonCritical - lower non-critical threshold
938 * @param lowerCritical - Lower critical threshold
939 * @param lowerNonRecoverable - Lower non recovarable threshold
940 * @param upperNonCritical - Upper non-critical threshold
941 * @param upperCritical - Upper critical
942 * @param upperNonRecoverable - Upper Non-recoverable
943 *
944 * @returns IPMI completion code
945 */
946ipmi::RspType<> ipmiSenSetSensorThresholds(
947 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
948 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
949 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
950 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800951 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800952 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800953 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800954{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000955 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800956 {
957 return ipmi::responseInvalidFieldRequest();
958 }
959
960 // lower nc and upper nc not suppported on any sensor
961 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
962 {
963 return ipmi::responseInvalidFieldRequest();
964 }
965
966 // if none of the threshold mask are set, nothing to do
967 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
968 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
969 upperCriticalThreshMask | upperNonRecovThreshMask))
970 {
971 return ipmi::responseSuccess();
972 }
973
974 std::string connection;
975 std::string path;
976
977 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
978 if (status)
979 {
980 return ipmi::response(status);
981 }
982 DbusInterfaceMap sensorMap;
983 if (!getSensorMap(ctx, connection, path, sensorMap))
984 {
985 return ipmi::responseResponseError();
986 }
987
988 double max = 0;
989 double min = 0;
990 getSensorMaxMin(sensorMap, max, min);
991
992 int16_t mValue = 0;
993 int16_t bValue = 0;
994 int8_t rExp = 0;
995 int8_t bExp = 0;
996 bool bSigned = false;
997
998 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
999 {
1000 return ipmi::responseResponseError();
1001 }
1002
1003 // store a vector of property name, value to set, and interface
1004 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1005
1006 // define the indexes of the tuple
1007 constexpr uint8_t propertyName = 0;
1008 constexpr uint8_t thresholdValue = 1;
1009 constexpr uint8_t interface = 2;
1010 // verifiy all needed fields are present
1011 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1012 {
1013 auto findThreshold =
1014 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1015 if (findThreshold == sensorMap.end())
1016 {
1017 return ipmi::responseInvalidFieldRequest();
1018 }
1019 if (lowerCriticalThreshMask)
1020 {
1021 auto findLower = findThreshold->second.find("CriticalLow");
1022 if (findLower == findThreshold->second.end())
1023 {
1024 return ipmi::responseInvalidFieldRequest();
1025 }
1026 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1027 findThreshold->first);
1028 }
1029 if (upperCriticalThreshMask)
1030 {
1031 auto findUpper = findThreshold->second.find("CriticalHigh");
1032 if (findUpper == findThreshold->second.end())
1033 {
1034 return ipmi::responseInvalidFieldRequest();
1035 }
1036 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1037 findThreshold->first);
1038 }
1039 }
1040 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1041 {
1042 auto findThreshold =
1043 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1044 if (findThreshold == sensorMap.end())
1045 {
1046 return ipmi::responseInvalidFieldRequest();
1047 }
1048 if (lowerNonCriticalThreshMask)
1049 {
1050 auto findLower = findThreshold->second.find("WarningLow");
1051 if (findLower == findThreshold->second.end())
1052 {
1053 return ipmi::responseInvalidFieldRequest();
1054 }
1055 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1056 findThreshold->first);
1057 }
1058 if (upperNonCriticalThreshMask)
1059 {
1060 auto findUpper = findThreshold->second.find("WarningHigh");
1061 if (findUpper == findThreshold->second.end())
1062 {
1063 return ipmi::responseInvalidFieldRequest();
1064 }
1065 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1066 findThreshold->first);
1067 }
1068 }
1069 for (const auto& property : thresholdsToSet)
1070 {
1071 // from section 36.3 in the IPMI Spec, assume all linear
1072 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1073 (bValue * std::pow(10.0, bExp))) *
1074 std::pow(10.0, rExp);
1075 setDbusProperty(
1076 *getSdBus(), connection, path, std::get<interface>(property),
1077 std::get<propertyName>(property), ipmi::Value(valueToSet));
1078 }
1079 return ipmi::responseSuccess();
1080}
1081
1082IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1083{
1084 IPMIThresholds resp;
1085 auto warningInterface =
1086 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1087 auto criticalInterface =
1088 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1089
1090 if ((warningInterface != sensorMap.end()) ||
1091 (criticalInterface != sensorMap.end()))
1092 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001093 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001094
1095 if (sensorPair == sensorMap.end())
1096 {
1097 // should not have been able to find a sensor not implementing
1098 // the sensor object
1099 throw std::runtime_error("Invalid sensor map");
1100 }
1101
1102 double max = 0;
1103 double min = 0;
1104 getSensorMaxMin(sensorMap, max, min);
1105
1106 int16_t mValue = 0;
1107 int16_t bValue = 0;
1108 int8_t rExp = 0;
1109 int8_t bExp = 0;
1110 bool bSigned = false;
1111
1112 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1113 {
1114 throw std::runtime_error("Invalid sensor atrributes");
1115 }
1116 if (warningInterface != sensorMap.end())
1117 {
1118 auto& warningMap = warningInterface->second;
1119
1120 auto warningHigh = warningMap.find("WarningHigh");
1121 auto warningLow = warningMap.find("WarningLow");
1122
1123 if (warningHigh != warningMap.end())
1124 {
1125
1126 double value =
1127 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001128 if (std::isfinite(value))
1129 {
1130 resp.warningHigh = scaleIPMIValueFromDouble(
1131 value, mValue, rExp, bValue, bExp, bSigned);
1132 }
Willy Tude54f482021-01-26 15:59:09 -08001133 }
1134 if (warningLow != warningMap.end())
1135 {
1136 double value =
1137 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001138 if (std::isfinite(value))
1139 {
1140 resp.warningLow = scaleIPMIValueFromDouble(
1141 value, mValue, rExp, bValue, bExp, bSigned);
1142 }
Willy Tude54f482021-01-26 15:59:09 -08001143 }
1144 }
1145 if (criticalInterface != sensorMap.end())
1146 {
1147 auto& criticalMap = criticalInterface->second;
1148
1149 auto criticalHigh = criticalMap.find("CriticalHigh");
1150 auto criticalLow = criticalMap.find("CriticalLow");
1151
1152 if (criticalHigh != criticalMap.end())
1153 {
1154 double value =
1155 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001156 if (std::isfinite(value))
1157 {
1158 resp.criticalHigh = scaleIPMIValueFromDouble(
1159 value, mValue, rExp, bValue, bExp, bSigned);
1160 }
Willy Tude54f482021-01-26 15:59:09 -08001161 }
1162 if (criticalLow != criticalMap.end())
1163 {
1164 double value =
1165 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001166 if (std::isfinite(value))
1167 {
1168 resp.criticalLow = scaleIPMIValueFromDouble(
1169 value, mValue, rExp, bValue, bExp, bSigned);
1170 }
Willy Tude54f482021-01-26 15:59:09 -08001171 }
1172 }
1173 }
1174 return resp;
1175}
1176
1177ipmi::RspType<uint8_t, // readable
1178 uint8_t, // lowerNCrit
1179 uint8_t, // lowerCrit
1180 uint8_t, // lowerNrecoverable
1181 uint8_t, // upperNC
1182 uint8_t, // upperCrit
1183 uint8_t> // upperNRecoverable
1184 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1185{
1186 std::string connection;
1187 std::string path;
1188
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001189 if (sensorNumber == reservedSensorNumber)
1190 {
1191 return ipmi::responseInvalidFieldRequest();
1192 }
1193
Willy Tude54f482021-01-26 15:59:09 -08001194 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1195 if (status)
1196 {
1197 return ipmi::response(status);
1198 }
1199
1200 DbusInterfaceMap sensorMap;
1201 if (!getSensorMap(ctx, connection, path, sensorMap))
1202 {
1203 return ipmi::responseResponseError();
1204 }
1205
1206 IPMIThresholds thresholdData;
1207 try
1208 {
1209 thresholdData = getIPMIThresholds(sensorMap);
1210 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001211 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001212 {
1213 return ipmi::responseResponseError();
1214 }
1215
1216 uint8_t readable = 0;
1217 uint8_t lowerNC = 0;
1218 uint8_t lowerCritical = 0;
1219 uint8_t lowerNonRecoverable = 0;
1220 uint8_t upperNC = 0;
1221 uint8_t upperCritical = 0;
1222 uint8_t upperNonRecoverable = 0;
1223
1224 if (thresholdData.warningHigh)
1225 {
1226 readable |=
1227 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1228 upperNC = *thresholdData.warningHigh;
1229 }
1230 if (thresholdData.warningLow)
1231 {
1232 readable |=
1233 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1234 lowerNC = *thresholdData.warningLow;
1235 }
1236
1237 if (thresholdData.criticalHigh)
1238 {
1239 readable |=
1240 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1241 upperCritical = *thresholdData.criticalHigh;
1242 }
1243 if (thresholdData.criticalLow)
1244 {
1245 readable |=
1246 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1247 lowerCritical = *thresholdData.criticalLow;
1248 }
1249
1250 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1251 lowerNonRecoverable, upperNC, upperCritical,
1252 upperNonRecoverable);
1253}
1254
1255/** @brief implements the get Sensor event enable command
1256 * @param sensorNumber - sensor number
1257 *
1258 * @returns IPMI completion code plus response data
1259 * - enabled - Sensor Event messages
1260 * - assertionEnabledLsb - Assertion event messages
1261 * - assertionEnabledMsb - Assertion event messages
1262 * - deassertionEnabledLsb - Deassertion event messages
1263 * - deassertionEnabledMsb - Deassertion event messages
1264 */
1265
1266ipmi::RspType<uint8_t, // enabled
1267 uint8_t, // assertionEnabledLsb
1268 uint8_t, // assertionEnabledMsb
1269 uint8_t, // deassertionEnabledLsb
1270 uint8_t> // deassertionEnabledMsb
1271 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1272{
1273 std::string connection;
1274 std::string path;
1275
1276 uint8_t enabled = 0;
1277 uint8_t assertionEnabledLsb = 0;
1278 uint8_t assertionEnabledMsb = 0;
1279 uint8_t deassertionEnabledLsb = 0;
1280 uint8_t deassertionEnabledMsb = 0;
1281
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001282 if (sensorNum == reservedSensorNumber)
1283 {
1284 return ipmi::responseInvalidFieldRequest();
1285 }
1286
Willy Tude54f482021-01-26 15:59:09 -08001287 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1288 if (status)
1289 {
1290 return ipmi::response(status);
1291 }
1292
Scron Chang2703b022021-07-06 15:47:45 +08001293#ifdef FEATURE_HYBRID_SENSORS
1294 if (auto sensor = findStaticSensor(path);
1295 sensor != ipmi::sensor::sensors.end() &&
1296 getSensorEventTypeFromPath(path) !=
1297 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1298 {
1299 enabled = static_cast<uint8_t>(
1300 IPMISensorEventEnableByte2::sensorScanningEnable);
1301 uint16_t assertionEnabled = 0;
1302 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1303 ->second.begin()
1304 ->second.second)
1305 {
1306 assertionEnabled |= (1 << offsetValMap.first);
1307 }
1308 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1309 assertionEnabledMsb =
1310 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1311
1312 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1313 assertionEnabledMsb, deassertionEnabledLsb,
1314 deassertionEnabledMsb);
1315 }
1316#endif
1317
Willy Tude54f482021-01-26 15:59:09 -08001318 DbusInterfaceMap sensorMap;
1319 if (!getSensorMap(ctx, connection, path, sensorMap))
1320 {
1321 return ipmi::responseResponseError();
1322 }
1323
1324 auto warningInterface =
1325 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1326 auto criticalInterface =
1327 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1328 if ((warningInterface != sensorMap.end()) ||
1329 (criticalInterface != sensorMap.end()))
1330 {
1331 enabled = static_cast<uint8_t>(
1332 IPMISensorEventEnableByte2::sensorScanningEnable);
1333 if (warningInterface != sensorMap.end())
1334 {
1335 auto& warningMap = warningInterface->second;
1336
1337 auto warningHigh = warningMap.find("WarningHigh");
1338 auto warningLow = warningMap.find("WarningLow");
1339 if (warningHigh != warningMap.end())
1340 {
1341 assertionEnabledLsb |= static_cast<uint8_t>(
1342 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1343 deassertionEnabledLsb |= static_cast<uint8_t>(
1344 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1345 }
1346 if (warningLow != warningMap.end())
1347 {
1348 assertionEnabledLsb |= static_cast<uint8_t>(
1349 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1350 deassertionEnabledLsb |= static_cast<uint8_t>(
1351 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1352 }
1353 }
1354 if (criticalInterface != sensorMap.end())
1355 {
1356 auto& criticalMap = criticalInterface->second;
1357
1358 auto criticalHigh = criticalMap.find("CriticalHigh");
1359 auto criticalLow = criticalMap.find("CriticalLow");
1360
1361 if (criticalHigh != criticalMap.end())
1362 {
1363 assertionEnabledMsb |= static_cast<uint8_t>(
1364 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1365 deassertionEnabledMsb |= static_cast<uint8_t>(
1366 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1367 }
1368 if (criticalLow != criticalMap.end())
1369 {
1370 assertionEnabledLsb |= static_cast<uint8_t>(
1371 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1372 deassertionEnabledLsb |= static_cast<uint8_t>(
1373 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1374 }
1375 }
1376 }
1377
1378 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1379 assertionEnabledMsb, deassertionEnabledLsb,
1380 deassertionEnabledMsb);
1381}
1382
1383/** @brief implements the get Sensor event status command
1384 * @param sensorNumber - sensor number, FFh = reserved
1385 *
1386 * @returns IPMI completion code plus response data
1387 * - sensorEventStatus - Sensor Event messages state
1388 * - assertions - Assertion event messages
1389 * - deassertions - Deassertion event messages
1390 */
1391ipmi::RspType<uint8_t, // sensorEventStatus
1392 std::bitset<16>, // assertions
1393 std::bitset<16> // deassertion
1394 >
1395 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1396{
1397 if (sensorNum == reservedSensorNumber)
1398 {
1399 return ipmi::responseInvalidFieldRequest();
1400 }
1401
1402 std::string connection;
1403 std::string path;
1404 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1405 if (status)
1406 {
1407 phosphor::logging::log<phosphor::logging::level::ERR>(
1408 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1409 phosphor::logging::entry("SENSOR=%d", sensorNum));
1410 return ipmi::response(status);
1411 }
1412
Scron Chang2703b022021-07-06 15:47:45 +08001413#ifdef FEATURE_HYBRID_SENSORS
1414 if (auto sensor = findStaticSensor(path);
1415 sensor != ipmi::sensor::sensors.end() &&
1416 getSensorEventTypeFromPath(path) !=
1417 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1418 {
1419 auto response = ipmi::sensor::get::mapDbusToAssertion(
1420 sensor->second, path, sensor->second.sensorInterface);
1421 std::bitset<16> assertions;
1422 // deassertions are not used.
1423 std::bitset<16> deassertions = 0;
1424 uint8_t sensorEventStatus;
1425 if (response.readingOrStateUnavailable)
1426 {
1427 sensorEventStatus |= static_cast<uint8_t>(
1428 IPMISensorReadingByte2::readingStateUnavailable);
1429 }
1430 if (response.scanningEnabled)
1431 {
1432 sensorEventStatus |= static_cast<uint8_t>(
1433 IPMISensorReadingByte2::sensorScanningEnable);
1434 }
1435 if (response.allEventMessagesEnabled)
1436 {
1437 sensorEventStatus |= static_cast<uint8_t>(
1438 IPMISensorReadingByte2::eventMessagesEnable);
1439 }
1440 assertions |= response.discreteReadingSensorStates << 8;
1441 assertions |= response.thresholdLevelsStates;
1442 return ipmi::responseSuccess(sensorEventStatus, assertions,
1443 deassertions);
1444 }
1445#endif
1446
Willy Tude54f482021-01-26 15:59:09 -08001447 DbusInterfaceMap sensorMap;
1448 if (!getSensorMap(ctx, connection, path, sensorMap))
1449 {
1450 phosphor::logging::log<phosphor::logging::level::ERR>(
1451 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1452 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1453 return ipmi::responseResponseError();
1454 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001455
1456 uint8_t sensorEventStatus =
1457 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1458 std::bitset<16> assertions = 0;
1459 std::bitset<16> deassertions = 0;
1460
1461 // handle VR typed sensor
1462 auto vrInterface = sensorMap.find(sensor::vrInterface);
1463 if (vrInterface != sensorMap.end())
1464 {
1465 if (!sensor::getVrEventStatus(ctx, connection, path,
1466 vrInterface->second, assertions))
1467 {
1468 return ipmi::responseResponseError();
1469 }
1470
1471 // both Event Message and Sensor Scanning are disable for VR.
1472 sensorEventStatus = 0;
1473 return ipmi::responseSuccess(sensorEventStatus, assertions,
1474 deassertions);
1475 }
1476
Willy Tude54f482021-01-26 15:59:09 -08001477 auto warningInterface =
1478 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1479 auto criticalInterface =
1480 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1481
Willy Tude54f482021-01-26 15:59:09 -08001482 std::optional<bool> criticalDeassertHigh =
1483 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1484 std::optional<bool> criticalDeassertLow =
1485 thresholdDeassertMap[path]["CriticalAlarmLow"];
1486 std::optional<bool> warningDeassertHigh =
1487 thresholdDeassertMap[path]["WarningAlarmHigh"];
1488 std::optional<bool> warningDeassertLow =
1489 thresholdDeassertMap[path]["WarningAlarmLow"];
1490
Willy Tude54f482021-01-26 15:59:09 -08001491 if (criticalDeassertHigh && !*criticalDeassertHigh)
1492 {
1493 deassertions.set(static_cast<size_t>(
1494 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1495 }
1496 if (criticalDeassertLow && !*criticalDeassertLow)
1497 {
1498 deassertions.set(static_cast<size_t>(
1499 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1500 }
1501 if (warningDeassertHigh && !*warningDeassertHigh)
1502 {
1503 deassertions.set(static_cast<size_t>(
1504 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1505 }
1506 if (warningDeassertLow && !*warningDeassertLow)
1507 {
1508 deassertions.set(static_cast<size_t>(
1509 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1510 }
1511 if ((warningInterface != sensorMap.end()) ||
1512 (criticalInterface != sensorMap.end()))
1513 {
1514 sensorEventStatus = static_cast<size_t>(
1515 IPMISensorEventEnableByte2::eventMessagesEnable);
1516 if (warningInterface != sensorMap.end())
1517 {
1518 auto& warningMap = warningInterface->second;
1519
1520 auto warningHigh = warningMap.find("WarningAlarmHigh");
1521 auto warningLow = warningMap.find("WarningAlarmLow");
1522 auto warningHighAlarm = false;
1523 auto warningLowAlarm = false;
1524
1525 if (warningHigh != warningMap.end())
1526 {
1527 warningHighAlarm = std::get<bool>(warningHigh->second);
1528 }
1529 if (warningLow != warningMap.end())
1530 {
1531 warningLowAlarm = std::get<bool>(warningLow->second);
1532 }
1533 if (warningHighAlarm)
1534 {
1535 assertions.set(
1536 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1537 upperNonCriticalGoingHigh));
1538 }
1539 if (warningLowAlarm)
1540 {
1541 assertions.set(
1542 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1543 lowerNonCriticalGoingLow));
1544 }
1545 }
1546 if (criticalInterface != sensorMap.end())
1547 {
1548 auto& criticalMap = criticalInterface->second;
1549
1550 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1551 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1552 auto criticalHighAlarm = false;
1553 auto criticalLowAlarm = false;
1554
1555 if (criticalHigh != criticalMap.end())
1556 {
1557 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1558 }
1559 if (criticalLow != criticalMap.end())
1560 {
1561 criticalLowAlarm = std::get<bool>(criticalLow->second);
1562 }
1563 if (criticalHighAlarm)
1564 {
1565 assertions.set(
1566 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1567 upperCriticalGoingHigh));
1568 }
1569 if (criticalLowAlarm)
1570 {
1571 assertions.set(static_cast<size_t>(
1572 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1573 }
1574 }
1575 }
1576
1577 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1578}
1579
Willy Tu38e7a2b2021-03-29 15:09:56 -07001580// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001581void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1582 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001583{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001584 get_sdr::header::set_record_id(
1585 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1586
Willy Tu38e7a2b2021-03-29 15:09:56 -07001587 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1588 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1589
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001590 record.header.sdr_version = ipmiSdrVersion;
1591 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1592 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1593 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001594 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001595 record.key.owner_lun = lun;
1596 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001597}
1598bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1599 uint16_t recordID, const std::string& service,
1600 const std::string& path,
1601 get_sdr::SensorDataFullRecord& record)
1602{
Hao Jiange39d4d82021-04-16 17:02:40 -07001603 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1604
1605 DbusInterfaceMap sensorMap;
1606 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1607 {
1608 phosphor::logging::log<phosphor::logging::level::ERR>(
1609 "Failed to update sensor map for threshold sensor",
1610 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1611 phosphor::logging::entry("PATH=%s", path.c_str()));
1612 return false;
1613 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001614
1615 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1616 record.body.sensor_type = getSensorTypeFromPath(path);
1617 std::string type = getSensorTypeStringFromPath(path);
1618 auto typeCstr = type.c_str();
1619 auto findUnits = sensorUnits.find(typeCstr);
1620 if (findUnits != sensorUnits.end())
1621 {
1622 record.body.sensor_units_2_base =
1623 static_cast<uint8_t>(findUnits->second);
1624 } // else default 0x0 unspecified
1625
1626 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1627
Hao Jiangd2afd052020-12-10 15:09:32 -08001628 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001629 if (sensorObject == sensorMap.end())
1630 {
1631 phosphor::logging::log<phosphor::logging::level::ERR>(
1632 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001633 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001634 }
1635
1636 uint8_t entityId = 0;
1637 uint8_t entityInstance = 0x01;
1638
1639 // follow the association chain to get the parent board's entityid and
1640 // entityInstance
1641 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1642
1643 record.body.entity_id = entityId;
1644 record.body.entity_instance = entityInstance;
1645
Shakeeb Pasha93889722021-10-14 10:20:13 +05301646 double max = 0;
1647 double min = 0;
1648 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001649
1650 int16_t mValue = 0;
1651 int8_t rExp = 0;
1652 int16_t bValue = 0;
1653 int8_t bExp = 0;
1654 bool bSigned = false;
1655
1656 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1657 {
1658 phosphor::logging::log<phosphor::logging::level::ERR>(
1659 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001660 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001661 }
1662
1663 // The record.body is a struct SensorDataFullRecordBody
1664 // from sensorhandler.hpp in phosphor-ipmi-host.
1665 // The meaning of these bits appears to come from
1666 // table 43.1 of the IPMI spec.
1667 // The above 5 sensor attributes are stuffed in as follows:
1668 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1669 // Byte 22-24 are for other purposes
1670 // Byte 25 = MMMMMMMM = LSB of M
1671 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1672 // Byte 27 = BBBBBBBB = LSB of B
1673 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1674 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1675 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1676
1677 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1678 record.body.m_lsb = mValue & 0xFF;
1679
1680 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1681 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1682
1683 // move the smallest bit of the MSB into place (bit 9)
1684 // the MSbs are bits 7:8 in m_msb_and_tolerance
1685 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1686
1687 record.body.b_lsb = bValue & 0xFF;
1688
1689 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1690 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1691
1692 // move the smallest bit of the MSB into place (bit 9)
1693 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1694 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1695
1696 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1697 uint8_t rExpBits = rExp & 0x07;
1698
1699 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1700 uint8_t bExpBits = bExp & 0x07;
1701
1702 // move rExp and bExp into place
1703 record.body.r_b_exponents =
1704 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1705
1706 // Set the analog reading byte interpretation accordingly
1707 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1708
1709 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1710 // These seem redundant, but derivable from the above 5 attributes
1711 // Original comment said "todo fill out rest of units"
1712
1713 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001714 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001715 record.body.id_string_info = name.size();
1716 std::strncpy(record.body.id_string, name.c_str(),
1717 sizeof(record.body.id_string));
1718
Josh Lehana55c9532020-10-28 21:59:06 -07001719 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001720 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001721
Jie Yangf0a89942021-07-29 15:30:25 -07001722 bool sensorSettable = false;
1723 auto mutability =
1724 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1725 if (mutability != sensorMap.end())
1726 {
1727 sensorSettable =
1728 mappedVariant<bool>(mutability->second, "Mutable", false);
1729 }
1730 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1731
1732 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001733 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001734
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001735 IPMIThresholds thresholdData;
1736 try
1737 {
1738 thresholdData = getIPMIThresholds(sensorMap);
1739 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001740 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001741 {
1742 phosphor::logging::log<phosphor::logging::level::ERR>(
1743 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001744 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001745 }
1746
1747 if (thresholdData.criticalHigh)
1748 {
1749 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1750 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1751 IPMISensorEventEnableThresholds::criticalThreshold);
1752 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1753 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1754 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1755 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1756 record.body.discrete_reading_setting_mask[0] |=
1757 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1758 }
1759 if (thresholdData.warningHigh)
1760 {
1761 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1762 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1763 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1764 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1765 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1766 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1767 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1768 record.body.discrete_reading_setting_mask[0] |=
1769 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1770 }
1771 if (thresholdData.criticalLow)
1772 {
1773 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1774 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1775 IPMISensorEventEnableThresholds::criticalThreshold);
1776 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1777 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1778 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1779 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1780 record.body.discrete_reading_setting_mask[0] |=
1781 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1782 }
1783 if (thresholdData.warningLow)
1784 {
1785 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1786 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1787 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1788 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1789 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1790 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1791 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1792 record.body.discrete_reading_setting_mask[0] |=
1793 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1794 }
1795
1796 // everything that is readable is setable
1797 record.body.discrete_reading_setting_mask[1] =
1798 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001799 return true;
1800}
1801
Scron Chang2703b022021-07-06 15:47:45 +08001802#ifdef FEATURE_HYBRID_SENSORS
1803// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001804void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001805 uint16_t recordID,
1806 ipmi::sensor::IdInfoMap::const_iterator sensor,
1807 get_sdr::SensorDataFullRecord& record)
1808{
1809 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1810
1811 record.body.entity_id = sensor->second.entityType;
1812 record.body.sensor_type = sensor->second.sensorType;
1813 record.body.event_reading_type = sensor->second.sensorReadingType;
1814 record.body.entity_instance = sensor->second.instance;
1815 if (ipmi::sensor::Mutability::Write ==
1816 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1817 {
1818 get_sdr::body::init_settable_state(true, &(record.body));
1819 }
1820
1821 auto id_string = sensor->second.sensorName;
1822
1823 if (id_string.empty())
1824 {
1825 id_string = sensor->second.sensorNameFunc(sensor->second);
1826 }
1827
1828 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1829 {
1830 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1831 &(record.body));
1832 }
1833 else
1834 {
1835 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1836 }
1837 std::strncpy(record.body.id_string, id_string.c_str(),
1838 get_sdr::body::get_id_strlen(&(record.body)));
1839}
1840#endif
1841
Hao Jiange39d4d82021-04-16 17:02:40 -07001842// Construct type 3 SDR header and key (for VR and other discrete sensors)
1843void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1844 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001845{
1846 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1847 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1848
1849 get_sdr::header::set_record_id(
1850 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1851
1852 record.header.sdr_version = ipmiSdrVersion;
1853 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1854 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1855 sizeof(get_sdr::SensorDataRecordHeader);
1856 record.key.owner_id = bmcI2CAddr;
1857 record.key.owner_lun = lun;
1858 record.key.sensor_number = sensornumber;
1859
1860 record.body.entity_id = 0x00;
1861 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001862}
Willy Tu61992ad2021-03-29 15:33:20 -07001863
Hao Jiange39d4d82021-04-16 17:02:40 -07001864// Construct a type 3 SDR for VR typed sensor(daemon).
1865bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1866 uint16_t recordID, const std::string& service,
1867 const std::string& path,
1868 get_sdr::SensorDataEventRecord& record)
1869{
Hao Jiange39d4d82021-04-16 17:02:40 -07001870 constructEventSdrHeaderKey(sensorNum, recordID, record);
1871
1872 DbusInterfaceMap sensorMap;
1873 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1874 {
1875 phosphor::logging::log<phosphor::logging::level::ERR>(
1876 "Failed to update sensor map for VR sensor",
1877 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1878 phosphor::logging::entry("PATH=%s", path.c_str()));
1879 return false;
1880 }
Willy Tu61992ad2021-03-29 15:33:20 -07001881 // follow the association chain to get the parent board's entityid and
1882 // entityInstance
1883 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1884 record.body.entity_instance);
1885
1886 // Sensor type is hardcoded as a module/board type instead of parsing from
1887 // sensor path. This is because VR control is allocated in an independent
1888 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1889 // types.
1890 static constexpr const uint8_t module_board_type = 0x15;
1891 record.body.sensor_type = module_board_type;
1892 record.body.event_reading_type = 0x00;
1893
1894 record.body.sensor_record_sharing_1 = 0x00;
1895 record.body.sensor_record_sharing_2 = 0x00;
1896
1897 // populate sensor name from path
1898 auto name = sensor::parseSdrIdFromPath(path);
1899 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1900 record.body.id_string_info = nameSize;
1901 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1902 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1903
1904 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001905 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001906
1907 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001908}
1909
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001910static inline uint16_t getNumberOfSensors()
1911{
1912 return std::min(getSensorTree().size(), maxIPMISensors);
1913}
1914
Hao Jiange39d4d82021-04-16 17:02:40 -07001915static int
1916 getSensorDataRecord(ipmi::Context::ptr ctx,
1917 std::vector<uint8_t>& recordData, uint16_t recordID,
1918 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001919{
1920 size_t fruCount = 0;
1921 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1922 if (ret != ipmi::ccSuccess)
1923 {
1924 phosphor::logging::log<phosphor::logging::level::ERR>(
1925 "getSensorDataRecord: getFruSdrCount error");
1926 return GENERAL_ERROR;
1927 }
1928
Harvey Wu05d17c02021-09-15 08:46:59 +08001929 const auto& entityRecords =
1930 ipmi::sensor::EntityInfoMapContainer::getContainer()
1931 ->getIpmiEntityRecords();
1932 size_t entityCount = entityRecords.size();
1933
1934 size_t lastRecord = getNumberOfSensors() + fruCount +
1935 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001936 if (recordID == lastRecordIndex)
1937 {
1938 recordID = lastRecord;
1939 }
1940 if (recordID > lastRecord)
1941 {
1942 phosphor::logging::log<phosphor::logging::level::ERR>(
1943 "getSensorDataRecord: recordID > lastRecord error");
1944 return GENERAL_ERROR;
1945 }
1946
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001947 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001948 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001949 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001950
Harvey Wu05d17c02021-09-15 08:46:59 +08001951 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1952 {
1953 // handle type 8 entity map records
1954 ipmi::sensor::EntityInfoMap::const_iterator entity =
1955 entityRecords.find(static_cast<uint8_t>(
1956 sdrIndex - fruCount - ipmi::storage::type12Count));
1957 if (entity == entityRecords.end())
1958 {
1959 return IPMI_CC_SENSOR_INVALID;
1960 }
1961 recordData = ipmi::storage::getType8SDRs(entity, recordID);
1962 }
1963 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07001964 {
1965 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08001966 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001967 if (type12Index >= ipmi::storage::type12Count)
1968 {
1969 phosphor::logging::log<phosphor::logging::level::ERR>(
1970 "getSensorDataRecord: type12Index error");
1971 return GENERAL_ERROR;
1972 }
1973 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1974 }
1975 else
1976 {
1977 // handle fru records
1978 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08001979 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001980 if (ret != IPMI_CC_OK)
1981 {
1982 return GENERAL_ERROR;
1983 }
1984 data.header.record_id_msb = recordID >> 8;
1985 data.header.record_id_lsb = recordID & 0xFF;
1986 recordData.insert(recordData.end(), (uint8_t*)&data,
1987 ((uint8_t*)&data) + sizeof(data));
1988 }
1989
1990 return 0;
1991 }
1992
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001993 // Perform a incremental scan of the SDR Record ID's and translate the
1994 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1995 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1996 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
1997 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07001998 std::string connection;
1999 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002000 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002001 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002002 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002003 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002004 // LUN 0 has one reserved sensor number. Compensate here by adding one
2005 // to the record ID
2006 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002007 ctx->lun = 1;
2008 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002009 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002010 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002011 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2012 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2013 // rules governing its use.
2014 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07002015 ctx->lun = 3;
2016 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002017
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002018 auto status =
2019 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2020 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002021 if (status)
2022 {
2023 phosphor::logging::log<phosphor::logging::level::ERR>(
2024 "getSensorDataRecord: getSensorConnection error");
2025 return GENERAL_ERROR;
2026 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002027 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002028 // Return an error on LUN 2 assingments, and any sensor number beyond the
2029 // range of LUN 3
2030 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2031 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002032 {
2033 phosphor::logging::log<phosphor::logging::level::ERR>(
2034 "getSensorDataRecord: invalidSensorNumber");
2035 return GENERAL_ERROR;
2036 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002037 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2038 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2039
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002040 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2041 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002042 {
2043 phosphor::logging::log<phosphor::logging::level::ERR>(
2044 "getSensorDataRecord: sensor record mismatch");
2045 return GENERAL_ERROR;
2046 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002047
Willy Tu38e7a2b2021-03-29 15:09:56 -07002048 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002049 if (std::find(interfaces.begin(), interfaces.end(),
2050 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002051 {
Willy Tu11d68892022-01-20 10:37:34 -08002052 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002053
Hao Jiange39d4d82021-04-16 17:02:40 -07002054 // If the request doesn't read SDR body, construct only header and key
2055 // part to avoid additional DBus transaction.
2056 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2057 {
2058 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2059 }
2060 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2061 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002062 {
2063 return GENERAL_ERROR;
2064 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002065
Willy Tu38e7a2b2021-03-29 15:09:56 -07002066 recordData.insert(recordData.end(), (uint8_t*)&record,
2067 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002068
2069 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002070 }
Willy Tu61992ad2021-03-29 15:33:20 -07002071
Scron Chang2703b022021-07-06 15:47:45 +08002072#ifdef FEATURE_HYBRID_SENSORS
2073 if (auto sensor = findStaticSensor(path);
2074 sensor != ipmi::sensor::sensors.end() &&
2075 getSensorEventTypeFromPath(path) !=
2076 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2077 {
Willy Tu11d68892022-01-20 10:37:34 -08002078 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002079
2080 // If the request doesn't read SDR body, construct only header and key
2081 // part to avoid additional DBus transaction.
2082 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2083 {
2084 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2085 }
2086 else
2087 {
2088 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2089 }
2090
2091 recordData.insert(recordData.end(), (uint8_t*)&record,
2092 ((uint8_t*)&record) + sizeof(record));
2093
2094 return 0;
2095 }
2096#endif
2097
Willy Tu61992ad2021-03-29 15:33:20 -07002098 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002099 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2100 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002101 {
Willy Tu11d68892022-01-20 10:37:34 -08002102 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002103
Hao Jiange39d4d82021-04-16 17:02:40 -07002104 // If the request doesn't read SDR body, construct only header and key
2105 // part to avoid additional DBus transaction.
2106 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2107 {
2108 constructEventSdrHeaderKey(sensorNum, recordID, record);
2109 }
2110 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2111 record))
2112 {
2113 return GENERAL_ERROR;
2114 }
Willy Tu61992ad2021-03-29 15:33:20 -07002115 recordData.insert(recordData.end(), (uint8_t*)&record,
2116 ((uint8_t*)&record) + sizeof(record));
2117 }
2118
Willy Tude54f482021-01-26 15:59:09 -08002119 return 0;
2120}
2121
2122/** @brief implements the get SDR Info command
2123 * @param count - Operation
2124 *
2125 * @returns IPMI completion code plus response data
2126 * - sdrCount - sensor/SDR count
2127 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2128 */
2129static ipmi::RspType<uint8_t, // respcount
2130 uint8_t, // dynamic population flags
2131 uint32_t // last time a sensor was added
2132 >
2133 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2134 std::optional<uint8_t> count)
2135{
2136 auto& sensorTree = getSensorTree();
2137 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002138 uint16_t recordID = 0;
2139 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002140 // Sensors are dynamically allocated, and there is at least one LUN
2141 uint8_t lunsAndDynamicPopulation = 0x80;
2142 constexpr uint8_t getSdrCount = 0x01;
2143 constexpr uint8_t getSensorCount = 0x00;
2144
2145 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2146 {
2147 return ipmi::responseResponseError();
2148 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002149 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002150 if (count.value_or(0) == getSdrCount)
2151 {
2152 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002153 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002154 {
2155 get_sdr::SensorDataRecordHeader* hdr =
2156 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002157 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002158 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2159 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002160 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002161 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002162 record.data());
2163 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002164 {
2165 sdrCount++;
2166 }
2167 }
2168 }
2169 }
2170 else if (count.value_or(0) == getSensorCount)
2171 {
2172 // Return the number of sensors attached to the LUN
2173 if ((ctx->lun == 0) && (numSensors > 0))
2174 {
2175 sdrCount =
2176 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2177 }
2178 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2179 {
2180 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2181 ? maxSensorsPerLUN
2182 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2183 }
2184 else if (ctx->lun == 3)
2185 {
2186 if (numSensors <= maxIPMISensors)
2187 {
2188 sdrCount =
2189 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2190 }
2191 else
2192 {
2193 // error
2194 throw std::out_of_range(
2195 "Maximum number of IPMI sensors exceeded.");
2196 }
2197 }
2198 }
2199 else
2200 {
2201 return ipmi::responseInvalidFieldRequest();
2202 }
2203
2204 // Get Sensor count. This returns the number of sensors
2205 if (numSensors > 0)
2206 {
2207 lunsAndDynamicPopulation |= 1;
2208 }
2209 if (numSensors > maxSensorsPerLUN)
2210 {
2211 lunsAndDynamicPopulation |= 2;
2212 }
2213 if (numSensors >= (maxSensorsPerLUN * 2))
2214 {
2215 lunsAndDynamicPopulation |= 8;
2216 }
2217 if (numSensors > maxIPMISensors)
2218 {
2219 // error
2220 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2221 }
2222
2223 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2224 sdrLastAdd);
2225}
2226
2227/* end sensor commands */
2228
2229/* storage commands */
2230
2231ipmi::RspType<uint8_t, // sdr version
2232 uint16_t, // record count
2233 uint16_t, // free space
2234 uint32_t, // most recent addition
2235 uint32_t, // most recent erase
2236 uint8_t // operationSupport
2237 >
2238 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2239{
2240 auto& sensorTree = getSensorTree();
2241 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002242 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002243 {
2244 return ipmi::responseResponseError();
2245 }
2246
2247 size_t fruCount = 0;
2248 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2249 if (ret != ipmi::ccSuccess)
2250 {
2251 return ipmi::response(ret);
2252 }
2253
2254 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002255 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002256
2257 uint8_t operationSupport = static_cast<uint8_t>(
2258 SdrRepositoryInfoOps::overflow); // write not supported
2259
2260 operationSupport |=
2261 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2262 operationSupport |= static_cast<uint8_t>(
2263 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2264 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2265 unspecifiedFreeSpace, sdrLastAdd,
2266 sdrLastRemove, operationSupport);
2267}
2268
2269/** @brief implements the get SDR allocation info command
2270 *
2271 * @returns IPMI completion code plus response data
2272 * - allocUnits - Number of possible allocation units
2273 * - allocUnitSize - Allocation unit size in bytes.
2274 * - allocUnitFree - Number of free allocation units
2275 * - allocUnitLargestFree - Largest free block in allocation units
2276 * - maxRecordSize - Maximum record size in allocation units.
2277 */
2278ipmi::RspType<uint16_t, // allocUnits
2279 uint16_t, // allocUnitSize
2280 uint16_t, // allocUnitFree
2281 uint16_t, // allocUnitLargestFree
2282 uint8_t // maxRecordSize
2283 >
2284 ipmiStorageGetSDRAllocationInfo()
2285{
2286 // 0000h unspecified number of alloc units
2287 constexpr uint16_t allocUnits = 0;
2288
2289 constexpr uint16_t allocUnitFree = 0;
2290 constexpr uint16_t allocUnitLargestFree = 0;
2291 // only allow one block at a time
2292 constexpr uint8_t maxRecordSize = 1;
2293
2294 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2295 allocUnitLargestFree, maxRecordSize);
2296}
2297
2298/** @brief implements the reserve SDR command
2299 * @returns IPMI completion code plus response data
2300 * - sdrReservationID
2301 */
2302ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2303{
2304 sdrReservationID++;
2305 if (sdrReservationID == 0)
2306 {
2307 sdrReservationID++;
2308 }
2309
2310 return ipmi::responseSuccess(sdrReservationID);
2311}
2312
2313ipmi::RspType<uint16_t, // next record ID
2314 std::vector<uint8_t> // payload
2315 >
2316 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2317 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2318{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002319 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002320 // reservation required for partial reads with non zero offset into
2321 // record
2322 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2323 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002324 phosphor::logging::log<phosphor::logging::level::ERR>(
2325 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002326 return ipmi::responseInvalidReservationId();
2327 }
Willy Tude54f482021-01-26 15:59:09 -08002328 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2329 if (ret != ipmi::ccSuccess)
2330 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002331 phosphor::logging::log<phosphor::logging::level::ERR>(
2332 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002333 return ipmi::response(ret);
2334 }
2335
Harvey Wu05d17c02021-09-15 08:46:59 +08002336 const auto& entityRecords =
2337 ipmi::sensor::EntityInfoMapContainer::getContainer()
2338 ->getIpmiEntityRecords();
2339 int entityCount = entityRecords.size();
2340
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002341 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002342 size_t lastRecord = getNumberOfSensors() + fruCount +
2343 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002344 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2345
2346 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002347 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002348 phosphor::logging::log<phosphor::logging::level::ERR>(
2349 "ipmiStorageGetSDR: getSensorSubtree error");
2350 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002351 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002352
2353 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002354 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002355 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002356 phosphor::logging::log<phosphor::logging::level::ERR>(
2357 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002358 return ipmi::responseInvalidFieldRequest();
2359 }
Willy Tude54f482021-01-26 15:59:09 -08002360 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002361 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002362 if (!hdr)
2363 {
2364 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002365 "ipmiStorageGetSDR: record header is null");
2366 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002367 }
2368
2369 size_t sdrLength =
2370 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2371 if (sdrLength < (offset + bytesToRead))
2372 {
2373 bytesToRead = sdrLength - offset;
2374 }
2375
2376 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2377 if (!respStart)
2378 {
2379 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002380 "ipmiStorageGetSDR: record is null");
2381 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002382 }
2383
2384 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002385
Willy Tude54f482021-01-26 15:59:09 -08002386 return ipmi::responseSuccess(nextRecordId, recordData);
2387}
2388/* end storage commands */
2389
2390void registerSensorFunctions()
2391{
2392 // <Platform Event>
2393 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2394 ipmi::sensor_event::cmdPlatformEvent,
2395 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2396
Willy Tudbafbce2021-03-29 00:37:05 -07002397 // <Set Sensor Reading and Event Status>
2398 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2399 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2400 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002401
Willy Tude54f482021-01-26 15:59:09 -08002402 // <Get Sensor Reading>
2403 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2404 ipmi::sensor_event::cmdGetSensorReading,
2405 ipmi::Privilege::User, ipmiSenGetSensorReading);
2406
2407 // <Get Sensor Threshold>
2408 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2409 ipmi::sensor_event::cmdGetSensorThreshold,
2410 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2411
2412 // <Set Sensor Threshold>
2413 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2414 ipmi::sensor_event::cmdSetSensorThreshold,
2415 ipmi::Privilege::Operator,
2416 ipmiSenSetSensorThresholds);
2417
2418 // <Get Sensor Event Enable>
2419 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2420 ipmi::sensor_event::cmdGetSensorEventEnable,
2421 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2422
2423 // <Get Sensor Event Status>
2424 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2425 ipmi::sensor_event::cmdGetSensorEventStatus,
2426 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2427
2428 // register all storage commands for both Sensor and Storage command
2429 // versions
2430
2431 // <Get SDR Repository Info>
2432 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2433 ipmi::storage::cmdGetSdrRepositoryInfo,
2434 ipmi::Privilege::User,
2435 ipmiStorageGetSDRRepositoryInfo);
2436
2437 // <Get Device SDR Info>
2438 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2439 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2440 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2441
2442 // <Get SDR Allocation Info>
2443 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2444 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2445 ipmi::Privilege::User,
2446 ipmiStorageGetSDRAllocationInfo);
2447
2448 // <Reserve SDR Repo>
2449 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2450 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2451 ipmi::Privilege::User, ipmiStorageReserveSDR);
2452
2453 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2454 ipmi::storage::cmdReserveSdrRepository,
2455 ipmi::Privilege::User, ipmiStorageReserveSDR);
2456
2457 // <Get Sdr>
2458 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2459 ipmi::sensor_event::cmdGetDeviceSdr,
2460 ipmi::Privilege::User, ipmiStorageGetSDR);
2461
2462 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2463 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2464 ipmiStorageGetSDR);
2465}
2466} // namespace ipmi