blob: 1c19986f452da8ff450f15c1183860c335109205 [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
Willy Tubef102a2022-06-09 15:36:09 -0700502 // map index to response event assertion bit.
503 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800504 {
Willy Tubef102a2022-06-09 15:36:09 -0700505 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800506 }
507 else
508 {
509 log<level::ERR>("VR profile index reaches max assertion bit",
510 entry("PATH=%s", path.c_str()),
511 entry("INDEX=%uz", index));
512 return false;
513 }
514 if constexpr (debug)
515 {
516 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700517 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800518 }
519 return true;
520}
Hao Jiangd2afd052020-12-10 15:09:32 -0800521} // namespace sensor
522
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000523ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
524 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800525{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000526 constexpr const uint8_t validEnvmRev = 0x04;
527 constexpr const uint8_t lastSensorType = 0x2C;
528 constexpr const uint8_t oemReserved = 0xC0;
529
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700530 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000531 uint8_t evmRev = 0;
532 uint8_t sensorType = 0;
533 uint8_t sensorNum = 0;
534 uint8_t eventType = 0;
535 uint8_t eventData1 = 0;
536 std::optional<uint8_t> eventData2 = 0;
537 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700538 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000539 ipmi::ChannelInfo chInfo;
540
541 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
542 {
543 phosphor::logging::log<phosphor::logging::level::ERR>(
544 "Failed to get Channel Info",
545 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
546 return ipmi::responseUnspecifiedError();
547 }
548
549 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
550 ipmi::EChannelMediumType::systemInterface)
551 {
552
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700553 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000554 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700555 // Refer to IPMI Spec Table 32: SEL Event Records
556 generatorID = (ctx->channel << 12) // Channel
557 | (0x0 << 10) // Reserved
558 | (0x0 << 8) // 0x0 for sys-soft ID
559 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000560 }
561 else
562 {
563
564 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
565 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700566 // Refer to IPMI Spec Table 32: SEL Event Records
567 generatorID = (ctx->channel << 12) // Channel
568 | (0x0 << 10) // Reserved
569 | ((ctx->lun & 0x3) << 8) // Lun
570 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000571 }
572
573 if (!p.fullyUnpacked())
574 {
575 return ipmi::responseReqDataLenInvalid();
576 }
577
578 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
579 if (evmRev != validEnvmRev)
580 {
581 return ipmi::responseInvalidFieldRequest();
582 }
583 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
584 {
585 return ipmi::responseInvalidFieldRequest();
586 }
587
Willy Tude54f482021-01-26 15:59:09 -0800588 return ipmi::responseSuccess();
589}
590
Willy Tudbafbce2021-03-29 00:37:05 -0700591ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800592 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700593 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800594 bool, uint15_t, bool, uint8_t, uint8_t,
595 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700596{
597 std::string connection;
598 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700599 std::vector<std::string> interfaces;
600
601 ipmi::Cc status =
602 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700603 if (status)
604 {
605 return ipmi::response(status);
606 }
607
Hao Jiangd2afd052020-12-10 15:09:32 -0800608 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700609 if (std::find(interfaces.begin(), interfaces.end(),
610 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700611 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700612 DbusInterfaceMap sensorMap;
613 if (!getSensorMap(ctx, connection, path, sensorMap))
614 {
615 return ipmi::responseResponseError();
616 }
617 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800618 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700619 {
620 return ipmi::responseResponseError();
621 }
622
Jie Yangf0a89942021-07-29 15:30:25 -0700623 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800624 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
625 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700626 {
627 return ipmi::responseResponseError();
628 }
629
Hao Jiangd2afd052020-12-10 15:09:32 -0800630 auto value =
631 sensor::calculateValue(reading, sensorMap, sensorObject->second);
632 if (!value)
633 {
634 return ipmi::responseResponseError();
635 }
636
637 if constexpr (debug)
638 {
639 phosphor::logging::log<phosphor::logging::level::INFO>(
640 "IPMI SET_SENSOR",
641 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
642 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
643 phosphor::logging::entry("VALUE=%f", *value));
644 }
645
646 boost::system::error_code ec =
647 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
648 "Value", ipmi::Value(*value));
649
650 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500651 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800652 // callback functions for now (e.g. ipmiSetSensorReading).
653 if (ec)
654 {
655 using namespace phosphor::logging;
656 log<level::ERR>("Failed to set property",
657 entry("PROPERTY=%s", "Value"),
658 entry("PATH=%s", path.c_str()),
659 entry("INTERFACE=%s", sensor::sensorInterface),
660 entry("WHAT=%s", ec.message().c_str()));
661 return ipmi::responseResponseError();
662 }
663 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700664 }
665
Hao Jiange39d4d82021-04-16 17:02:40 -0700666 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
667 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700668 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700669 DbusInterfaceMap sensorMap;
670 if (!getSensorMap(ctx, connection, path, sensorMap))
671 {
672 return ipmi::responseResponseError();
673 }
674 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800675 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700676 {
677 return ipmi::responseResponseError();
678 }
679
Hao Jiangd2afd052020-12-10 15:09:32 -0800680 // VR sensors are treated as a special case and we will not check the
681 // write permission for VR sensors, since they always deemed writable
682 // and permission table are not applied to VR sensors.
683 auto vrMode =
684 sensor::calculateVRMode(assertOffset, sensorObject->second);
685 if (!vrMode)
686 {
687 return ipmi::responseResponseError();
688 }
689 boost::system::error_code ec = setDbusProperty(
690 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
691 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500692 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800693 // callback functions for now (e.g. ipmiSetSensorReading).
694 if (ec)
695 {
696 using namespace phosphor::logging;
697 log<level::ERR>("Failed to set property",
698 entry("PROPERTY=%s", "Selected"),
699 entry("PATH=%s", path.c_str()),
700 entry("INTERFACE=%s", sensor::sensorInterface),
701 entry("WHAT=%s", ec.message().c_str()));
702 return ipmi::responseResponseError();
703 }
704 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700705 }
706
Hao Jiangd2afd052020-12-10 15:09:32 -0800707 phosphor::logging::log<phosphor::logging::level::ERR>(
708 "unknown sensor type",
709 phosphor::logging::entry("PATH=%s", path.c_str()));
710 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700711}
712
Willy Tude54f482021-01-26 15:59:09 -0800713ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
714 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
715{
716 std::string connection;
717 std::string path;
718
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000719 if (sensnum == reservedSensorNumber)
720 {
721 return ipmi::responseInvalidFieldRequest();
722 }
723
Willy Tude54f482021-01-26 15:59:09 -0800724 auto status = getSensorConnection(ctx, sensnum, connection, path);
725 if (status)
726 {
727 return ipmi::response(status);
728 }
729
Scron Chang2703b022021-07-06 15:47:45 +0800730#ifdef FEATURE_HYBRID_SENSORS
731 if (auto sensor = findStaticSensor(path);
732 sensor != ipmi::sensor::sensors.end() &&
733 getSensorEventTypeFromPath(path) !=
734 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
735 {
736 if (ipmi::sensor::Mutability::Read !=
737 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
738 {
739 return ipmi::responseIllegalCommand();
740 }
741
742 uint8_t operation;
743 try
744 {
745 ipmi::sensor::GetSensorResponse getResponse =
746 sensor->second.getFunc(sensor->second);
747
748 if (getResponse.readingOrStateUnavailable)
749 {
750 operation |= static_cast<uint8_t>(
751 IPMISensorReadingByte2::readingStateUnavailable);
752 }
753 if (getResponse.scanningEnabled)
754 {
755 operation |= static_cast<uint8_t>(
756 IPMISensorReadingByte2::sensorScanningEnable);
757 }
758 if (getResponse.allEventMessagesEnabled)
759 {
760 operation |= static_cast<uint8_t>(
761 IPMISensorReadingByte2::eventMessagesEnable);
762 }
763 return ipmi::responseSuccess(
764 getResponse.reading, operation,
765 getResponse.thresholdLevelsStates,
766 getResponse.discreteReadingSensorStates);
767 }
768 catch (const std::exception& e)
769 {
770 operation |= static_cast<uint8_t>(
771 IPMISensorReadingByte2::readingStateUnavailable);
772 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
773 }
774 }
775#endif
776
Willy Tude54f482021-01-26 15:59:09 -0800777 DbusInterfaceMap sensorMap;
778 if (!getSensorMap(ctx, connection, path, sensorMap))
779 {
780 return ipmi::responseResponseError();
781 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800782 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800783
784 if (sensorObject == sensorMap.end() ||
785 sensorObject->second.find("Value") == sensorObject->second.end())
786 {
787 return ipmi::responseResponseError();
788 }
789 auto& valueVariant = sensorObject->second["Value"];
790 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
791
792 double max = 0;
793 double min = 0;
794 getSensorMaxMin(sensorMap, max, min);
795
796 int16_t mValue = 0;
797 int16_t bValue = 0;
798 int8_t rExp = 0;
799 int8_t bExp = 0;
800 bool bSigned = false;
801
802 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
803 {
804 return ipmi::responseResponseError();
805 }
806
807 uint8_t value =
808 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
809 uint8_t operation =
810 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
811 operation |=
812 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
813 bool notReading = std::isnan(reading);
814
815 if (!notReading)
816 {
817 auto availableObject =
818 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
819 if (availableObject != sensorMap.end())
820 {
821 auto findAvailable = availableObject->second.find("Available");
822 if (findAvailable != availableObject->second.end())
823 {
824 bool* available = std::get_if<bool>(&(findAvailable->second));
825 if (available && !(*available))
826 {
827 notReading = true;
828 }
829 }
830 }
831 }
832
833 if (notReading)
834 {
835 operation |= static_cast<uint8_t>(
836 IPMISensorReadingByte2::readingStateUnavailable);
837 }
838
Josh Lehana55c9532020-10-28 21:59:06 -0700839 if constexpr (details::enableInstrumentation)
840 {
841 int byteValue;
842 if (bSigned)
843 {
844 byteValue = static_cast<int>(static_cast<int8_t>(value));
845 }
846 else
847 {
848 byteValue = static_cast<int>(static_cast<uint8_t>(value));
849 }
850
851 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800852 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
853 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700854 {
855 // This is the first reading, show the coefficients
856 double step = (max - min) / 255.0;
857 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800858 << details::sdrStatsTable.getName((ctx->lun << 8) |
859 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700860 << ": Range min=" << min << " max=" << max
861 << ", step=" << step
862 << ", Coefficients mValue=" << static_cast<int>(mValue)
863 << " rExp=" << static_cast<int>(rExp)
864 << " bValue=" << static_cast<int>(bValue)
865 << " bExp=" << static_cast<int>(bExp)
866 << " bSigned=" << static_cast<int>(bSigned) << "\n";
867 }
868 }
869
Willy Tude54f482021-01-26 15:59:09 -0800870 uint8_t thresholds = 0;
871
872 auto warningObject =
873 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
874 if (warningObject != sensorMap.end())
875 {
876 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
877 auto alarmLow = warningObject->second.find("WarningAlarmLow");
878 if (alarmHigh != warningObject->second.end())
879 {
880 if (std::get<bool>(alarmHigh->second))
881 {
882 thresholds |= static_cast<uint8_t>(
883 IPMISensorReadingByte3::upperNonCritical);
884 }
885 }
886 if (alarmLow != warningObject->second.end())
887 {
888 if (std::get<bool>(alarmLow->second))
889 {
890 thresholds |= static_cast<uint8_t>(
891 IPMISensorReadingByte3::lowerNonCritical);
892 }
893 }
894 }
895
896 auto criticalObject =
897 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
898 if (criticalObject != sensorMap.end())
899 {
900 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
901 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
902 if (alarmHigh != criticalObject->second.end())
903 {
904 if (std::get<bool>(alarmHigh->second))
905 {
906 thresholds |=
907 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
908 }
909 }
910 if (alarmLow != criticalObject->second.end())
911 {
912 if (std::get<bool>(alarmLow->second))
913 {
914 thresholds |=
915 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
916 }
917 }
918 }
919
920 // no discrete as of today so optional byte is never returned
921 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
922}
923
924/** @brief implements the Set Sensor threshold command
925 * @param sensorNumber - sensor number
926 * @param lowerNonCriticalThreshMask
927 * @param lowerCriticalThreshMask
928 * @param lowerNonRecovThreshMask
929 * @param upperNonCriticalThreshMask
930 * @param upperCriticalThreshMask
931 * @param upperNonRecovThreshMask
932 * @param reserved
933 * @param lowerNonCritical - lower non-critical threshold
934 * @param lowerCritical - Lower critical threshold
935 * @param lowerNonRecoverable - Lower non recovarable threshold
936 * @param upperNonCritical - Upper non-critical threshold
937 * @param upperCritical - Upper critical
938 * @param upperNonRecoverable - Upper Non-recoverable
939 *
940 * @returns IPMI completion code
941 */
942ipmi::RspType<> ipmiSenSetSensorThresholds(
943 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
944 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
945 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
946 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800947 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800948 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800949 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800950{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000951 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800952 {
953 return ipmi::responseInvalidFieldRequest();
954 }
955
956 // lower nc and upper nc not suppported on any sensor
957 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
958 {
959 return ipmi::responseInvalidFieldRequest();
960 }
961
962 // if none of the threshold mask are set, nothing to do
963 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
964 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
965 upperCriticalThreshMask | upperNonRecovThreshMask))
966 {
967 return ipmi::responseSuccess();
968 }
969
970 std::string connection;
971 std::string path;
972
973 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
974 if (status)
975 {
976 return ipmi::response(status);
977 }
978 DbusInterfaceMap sensorMap;
979 if (!getSensorMap(ctx, connection, path, sensorMap))
980 {
981 return ipmi::responseResponseError();
982 }
983
984 double max = 0;
985 double min = 0;
986 getSensorMaxMin(sensorMap, max, min);
987
988 int16_t mValue = 0;
989 int16_t bValue = 0;
990 int8_t rExp = 0;
991 int8_t bExp = 0;
992 bool bSigned = false;
993
994 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
995 {
996 return ipmi::responseResponseError();
997 }
998
999 // store a vector of property name, value to set, and interface
1000 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1001
1002 // define the indexes of the tuple
1003 constexpr uint8_t propertyName = 0;
1004 constexpr uint8_t thresholdValue = 1;
1005 constexpr uint8_t interface = 2;
1006 // verifiy all needed fields are present
1007 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1008 {
1009 auto findThreshold =
1010 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1011 if (findThreshold == sensorMap.end())
1012 {
1013 return ipmi::responseInvalidFieldRequest();
1014 }
1015 if (lowerCriticalThreshMask)
1016 {
1017 auto findLower = findThreshold->second.find("CriticalLow");
1018 if (findLower == findThreshold->second.end())
1019 {
1020 return ipmi::responseInvalidFieldRequest();
1021 }
1022 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1023 findThreshold->first);
1024 }
1025 if (upperCriticalThreshMask)
1026 {
1027 auto findUpper = findThreshold->second.find("CriticalHigh");
1028 if (findUpper == findThreshold->second.end())
1029 {
1030 return ipmi::responseInvalidFieldRequest();
1031 }
1032 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1033 findThreshold->first);
1034 }
1035 }
1036 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1037 {
1038 auto findThreshold =
1039 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1040 if (findThreshold == sensorMap.end())
1041 {
1042 return ipmi::responseInvalidFieldRequest();
1043 }
1044 if (lowerNonCriticalThreshMask)
1045 {
1046 auto findLower = findThreshold->second.find("WarningLow");
1047 if (findLower == findThreshold->second.end())
1048 {
1049 return ipmi::responseInvalidFieldRequest();
1050 }
1051 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1052 findThreshold->first);
1053 }
1054 if (upperNonCriticalThreshMask)
1055 {
1056 auto findUpper = findThreshold->second.find("WarningHigh");
1057 if (findUpper == findThreshold->second.end())
1058 {
1059 return ipmi::responseInvalidFieldRequest();
1060 }
1061 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1062 findThreshold->first);
1063 }
1064 }
1065 for (const auto& property : thresholdsToSet)
1066 {
1067 // from section 36.3 in the IPMI Spec, assume all linear
1068 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1069 (bValue * std::pow(10.0, bExp))) *
1070 std::pow(10.0, rExp);
1071 setDbusProperty(
1072 *getSdBus(), connection, path, std::get<interface>(property),
1073 std::get<propertyName>(property), ipmi::Value(valueToSet));
1074 }
1075 return ipmi::responseSuccess();
1076}
1077
1078IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1079{
1080 IPMIThresholds resp;
1081 auto warningInterface =
1082 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1083 auto criticalInterface =
1084 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1085
1086 if ((warningInterface != sensorMap.end()) ||
1087 (criticalInterface != sensorMap.end()))
1088 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001089 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001090
1091 if (sensorPair == sensorMap.end())
1092 {
1093 // should not have been able to find a sensor not implementing
1094 // the sensor object
1095 throw std::runtime_error("Invalid sensor map");
1096 }
1097
1098 double max = 0;
1099 double min = 0;
1100 getSensorMaxMin(sensorMap, max, min);
1101
1102 int16_t mValue = 0;
1103 int16_t bValue = 0;
1104 int8_t rExp = 0;
1105 int8_t bExp = 0;
1106 bool bSigned = false;
1107
1108 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1109 {
1110 throw std::runtime_error("Invalid sensor atrributes");
1111 }
1112 if (warningInterface != sensorMap.end())
1113 {
1114 auto& warningMap = warningInterface->second;
1115
1116 auto warningHigh = warningMap.find("WarningHigh");
1117 auto warningLow = warningMap.find("WarningLow");
1118
1119 if (warningHigh != warningMap.end())
1120 {
1121
1122 double value =
1123 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001124 if (std::isfinite(value))
1125 {
1126 resp.warningHigh = scaleIPMIValueFromDouble(
1127 value, mValue, rExp, bValue, bExp, bSigned);
1128 }
Willy Tude54f482021-01-26 15:59:09 -08001129 }
1130 if (warningLow != warningMap.end())
1131 {
1132 double value =
1133 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001134 if (std::isfinite(value))
1135 {
1136 resp.warningLow = scaleIPMIValueFromDouble(
1137 value, mValue, rExp, bValue, bExp, bSigned);
1138 }
Willy Tude54f482021-01-26 15:59:09 -08001139 }
1140 }
1141 if (criticalInterface != sensorMap.end())
1142 {
1143 auto& criticalMap = criticalInterface->second;
1144
1145 auto criticalHigh = criticalMap.find("CriticalHigh");
1146 auto criticalLow = criticalMap.find("CriticalLow");
1147
1148 if (criticalHigh != criticalMap.end())
1149 {
1150 double value =
1151 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001152 if (std::isfinite(value))
1153 {
1154 resp.criticalHigh = scaleIPMIValueFromDouble(
1155 value, mValue, rExp, bValue, bExp, bSigned);
1156 }
Willy Tude54f482021-01-26 15:59:09 -08001157 }
1158 if (criticalLow != criticalMap.end())
1159 {
1160 double value =
1161 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001162 if (std::isfinite(value))
1163 {
1164 resp.criticalLow = scaleIPMIValueFromDouble(
1165 value, mValue, rExp, bValue, bExp, bSigned);
1166 }
Willy Tude54f482021-01-26 15:59:09 -08001167 }
1168 }
1169 }
1170 return resp;
1171}
1172
1173ipmi::RspType<uint8_t, // readable
1174 uint8_t, // lowerNCrit
1175 uint8_t, // lowerCrit
1176 uint8_t, // lowerNrecoverable
1177 uint8_t, // upperNC
1178 uint8_t, // upperCrit
1179 uint8_t> // upperNRecoverable
1180 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1181{
1182 std::string connection;
1183 std::string path;
1184
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001185 if (sensorNumber == reservedSensorNumber)
1186 {
1187 return ipmi::responseInvalidFieldRequest();
1188 }
1189
Willy Tude54f482021-01-26 15:59:09 -08001190 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1191 if (status)
1192 {
1193 return ipmi::response(status);
1194 }
1195
1196 DbusInterfaceMap sensorMap;
1197 if (!getSensorMap(ctx, connection, path, sensorMap))
1198 {
1199 return ipmi::responseResponseError();
1200 }
1201
1202 IPMIThresholds thresholdData;
1203 try
1204 {
1205 thresholdData = getIPMIThresholds(sensorMap);
1206 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001207 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001208 {
1209 return ipmi::responseResponseError();
1210 }
1211
1212 uint8_t readable = 0;
1213 uint8_t lowerNC = 0;
1214 uint8_t lowerCritical = 0;
1215 uint8_t lowerNonRecoverable = 0;
1216 uint8_t upperNC = 0;
1217 uint8_t upperCritical = 0;
1218 uint8_t upperNonRecoverable = 0;
1219
1220 if (thresholdData.warningHigh)
1221 {
1222 readable |=
1223 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1224 upperNC = *thresholdData.warningHigh;
1225 }
1226 if (thresholdData.warningLow)
1227 {
1228 readable |=
1229 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1230 lowerNC = *thresholdData.warningLow;
1231 }
1232
1233 if (thresholdData.criticalHigh)
1234 {
1235 readable |=
1236 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1237 upperCritical = *thresholdData.criticalHigh;
1238 }
1239 if (thresholdData.criticalLow)
1240 {
1241 readable |=
1242 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1243 lowerCritical = *thresholdData.criticalLow;
1244 }
1245
1246 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1247 lowerNonRecoverable, upperNC, upperCritical,
1248 upperNonRecoverable);
1249}
1250
1251/** @brief implements the get Sensor event enable command
1252 * @param sensorNumber - sensor number
1253 *
1254 * @returns IPMI completion code plus response data
1255 * - enabled - Sensor Event messages
1256 * - assertionEnabledLsb - Assertion event messages
1257 * - assertionEnabledMsb - Assertion event messages
1258 * - deassertionEnabledLsb - Deassertion event messages
1259 * - deassertionEnabledMsb - Deassertion event messages
1260 */
1261
1262ipmi::RspType<uint8_t, // enabled
1263 uint8_t, // assertionEnabledLsb
1264 uint8_t, // assertionEnabledMsb
1265 uint8_t, // deassertionEnabledLsb
1266 uint8_t> // deassertionEnabledMsb
1267 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1268{
1269 std::string connection;
1270 std::string path;
1271
1272 uint8_t enabled = 0;
1273 uint8_t assertionEnabledLsb = 0;
1274 uint8_t assertionEnabledMsb = 0;
1275 uint8_t deassertionEnabledLsb = 0;
1276 uint8_t deassertionEnabledMsb = 0;
1277
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001278 if (sensorNum == reservedSensorNumber)
1279 {
1280 return ipmi::responseInvalidFieldRequest();
1281 }
1282
Willy Tude54f482021-01-26 15:59:09 -08001283 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1284 if (status)
1285 {
1286 return ipmi::response(status);
1287 }
1288
Scron Chang2703b022021-07-06 15:47:45 +08001289#ifdef FEATURE_HYBRID_SENSORS
1290 if (auto sensor = findStaticSensor(path);
1291 sensor != ipmi::sensor::sensors.end() &&
1292 getSensorEventTypeFromPath(path) !=
1293 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1294 {
1295 enabled = static_cast<uint8_t>(
1296 IPMISensorEventEnableByte2::sensorScanningEnable);
1297 uint16_t assertionEnabled = 0;
1298 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1299 ->second.begin()
1300 ->second.second)
1301 {
1302 assertionEnabled |= (1 << offsetValMap.first);
1303 }
1304 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1305 assertionEnabledMsb =
1306 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1307
1308 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1309 assertionEnabledMsb, deassertionEnabledLsb,
1310 deassertionEnabledMsb);
1311 }
1312#endif
1313
Willy Tude54f482021-01-26 15:59:09 -08001314 DbusInterfaceMap sensorMap;
1315 if (!getSensorMap(ctx, connection, path, sensorMap))
1316 {
1317 return ipmi::responseResponseError();
1318 }
1319
1320 auto warningInterface =
1321 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1322 auto criticalInterface =
1323 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1324 if ((warningInterface != sensorMap.end()) ||
1325 (criticalInterface != sensorMap.end()))
1326 {
1327 enabled = static_cast<uint8_t>(
1328 IPMISensorEventEnableByte2::sensorScanningEnable);
1329 if (warningInterface != sensorMap.end())
1330 {
1331 auto& warningMap = warningInterface->second;
1332
1333 auto warningHigh = warningMap.find("WarningHigh");
1334 auto warningLow = warningMap.find("WarningLow");
1335 if (warningHigh != warningMap.end())
1336 {
1337 assertionEnabledLsb |= static_cast<uint8_t>(
1338 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1339 deassertionEnabledLsb |= static_cast<uint8_t>(
1340 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1341 }
1342 if (warningLow != warningMap.end())
1343 {
1344 assertionEnabledLsb |= static_cast<uint8_t>(
1345 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1346 deassertionEnabledLsb |= static_cast<uint8_t>(
1347 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1348 }
1349 }
1350 if (criticalInterface != sensorMap.end())
1351 {
1352 auto& criticalMap = criticalInterface->second;
1353
1354 auto criticalHigh = criticalMap.find("CriticalHigh");
1355 auto criticalLow = criticalMap.find("CriticalLow");
1356
1357 if (criticalHigh != criticalMap.end())
1358 {
1359 assertionEnabledMsb |= static_cast<uint8_t>(
1360 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1361 deassertionEnabledMsb |= static_cast<uint8_t>(
1362 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1363 }
1364 if (criticalLow != criticalMap.end())
1365 {
1366 assertionEnabledLsb |= static_cast<uint8_t>(
1367 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1368 deassertionEnabledLsb |= static_cast<uint8_t>(
1369 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1370 }
1371 }
1372 }
1373
1374 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1375 assertionEnabledMsb, deassertionEnabledLsb,
1376 deassertionEnabledMsb);
1377}
1378
1379/** @brief implements the get Sensor event status command
1380 * @param sensorNumber - sensor number, FFh = reserved
1381 *
1382 * @returns IPMI completion code plus response data
1383 * - sensorEventStatus - Sensor Event messages state
1384 * - assertions - Assertion event messages
1385 * - deassertions - Deassertion event messages
1386 */
1387ipmi::RspType<uint8_t, // sensorEventStatus
1388 std::bitset<16>, // assertions
1389 std::bitset<16> // deassertion
1390 >
1391 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1392{
1393 if (sensorNum == reservedSensorNumber)
1394 {
1395 return ipmi::responseInvalidFieldRequest();
1396 }
1397
1398 std::string connection;
1399 std::string path;
1400 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1401 if (status)
1402 {
1403 phosphor::logging::log<phosphor::logging::level::ERR>(
1404 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1405 phosphor::logging::entry("SENSOR=%d", sensorNum));
1406 return ipmi::response(status);
1407 }
1408
Scron Chang2703b022021-07-06 15:47:45 +08001409#ifdef FEATURE_HYBRID_SENSORS
1410 if (auto sensor = findStaticSensor(path);
1411 sensor != ipmi::sensor::sensors.end() &&
1412 getSensorEventTypeFromPath(path) !=
1413 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1414 {
1415 auto response = ipmi::sensor::get::mapDbusToAssertion(
1416 sensor->second, path, sensor->second.sensorInterface);
1417 std::bitset<16> assertions;
1418 // deassertions are not used.
1419 std::bitset<16> deassertions = 0;
1420 uint8_t sensorEventStatus;
1421 if (response.readingOrStateUnavailable)
1422 {
1423 sensorEventStatus |= static_cast<uint8_t>(
1424 IPMISensorReadingByte2::readingStateUnavailable);
1425 }
1426 if (response.scanningEnabled)
1427 {
1428 sensorEventStatus |= static_cast<uint8_t>(
1429 IPMISensorReadingByte2::sensorScanningEnable);
1430 }
1431 if (response.allEventMessagesEnabled)
1432 {
1433 sensorEventStatus |= static_cast<uint8_t>(
1434 IPMISensorReadingByte2::eventMessagesEnable);
1435 }
1436 assertions |= response.discreteReadingSensorStates << 8;
1437 assertions |= response.thresholdLevelsStates;
1438 return ipmi::responseSuccess(sensorEventStatus, assertions,
1439 deassertions);
1440 }
1441#endif
1442
Willy Tude54f482021-01-26 15:59:09 -08001443 DbusInterfaceMap sensorMap;
1444 if (!getSensorMap(ctx, connection, path, sensorMap))
1445 {
1446 phosphor::logging::log<phosphor::logging::level::ERR>(
1447 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1448 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1449 return ipmi::responseResponseError();
1450 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001451
1452 uint8_t sensorEventStatus =
1453 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1454 std::bitset<16> assertions = 0;
1455 std::bitset<16> deassertions = 0;
1456
1457 // handle VR typed sensor
1458 auto vrInterface = sensorMap.find(sensor::vrInterface);
1459 if (vrInterface != sensorMap.end())
1460 {
1461 if (!sensor::getVrEventStatus(ctx, connection, path,
1462 vrInterface->second, assertions))
1463 {
1464 return ipmi::responseResponseError();
1465 }
1466
1467 // both Event Message and Sensor Scanning are disable for VR.
1468 sensorEventStatus = 0;
1469 return ipmi::responseSuccess(sensorEventStatus, assertions,
1470 deassertions);
1471 }
1472
Willy Tude54f482021-01-26 15:59:09 -08001473 auto warningInterface =
1474 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1475 auto criticalInterface =
1476 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1477
Willy Tude54f482021-01-26 15:59:09 -08001478 std::optional<bool> criticalDeassertHigh =
1479 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1480 std::optional<bool> criticalDeassertLow =
1481 thresholdDeassertMap[path]["CriticalAlarmLow"];
1482 std::optional<bool> warningDeassertHigh =
1483 thresholdDeassertMap[path]["WarningAlarmHigh"];
1484 std::optional<bool> warningDeassertLow =
1485 thresholdDeassertMap[path]["WarningAlarmLow"];
1486
Willy Tude54f482021-01-26 15:59:09 -08001487 if (criticalDeassertHigh && !*criticalDeassertHigh)
1488 {
1489 deassertions.set(static_cast<size_t>(
1490 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1491 }
1492 if (criticalDeassertLow && !*criticalDeassertLow)
1493 {
1494 deassertions.set(static_cast<size_t>(
1495 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1496 }
1497 if (warningDeassertHigh && !*warningDeassertHigh)
1498 {
1499 deassertions.set(static_cast<size_t>(
1500 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1501 }
1502 if (warningDeassertLow && !*warningDeassertLow)
1503 {
1504 deassertions.set(static_cast<size_t>(
1505 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1506 }
1507 if ((warningInterface != sensorMap.end()) ||
1508 (criticalInterface != sensorMap.end()))
1509 {
1510 sensorEventStatus = static_cast<size_t>(
1511 IPMISensorEventEnableByte2::eventMessagesEnable);
1512 if (warningInterface != sensorMap.end())
1513 {
1514 auto& warningMap = warningInterface->second;
1515
1516 auto warningHigh = warningMap.find("WarningAlarmHigh");
1517 auto warningLow = warningMap.find("WarningAlarmLow");
1518 auto warningHighAlarm = false;
1519 auto warningLowAlarm = false;
1520
1521 if (warningHigh != warningMap.end())
1522 {
1523 warningHighAlarm = std::get<bool>(warningHigh->second);
1524 }
1525 if (warningLow != warningMap.end())
1526 {
1527 warningLowAlarm = std::get<bool>(warningLow->second);
1528 }
1529 if (warningHighAlarm)
1530 {
1531 assertions.set(
1532 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1533 upperNonCriticalGoingHigh));
1534 }
1535 if (warningLowAlarm)
1536 {
1537 assertions.set(
1538 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1539 lowerNonCriticalGoingLow));
1540 }
1541 }
1542 if (criticalInterface != sensorMap.end())
1543 {
1544 auto& criticalMap = criticalInterface->second;
1545
1546 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1547 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1548 auto criticalHighAlarm = false;
1549 auto criticalLowAlarm = false;
1550
1551 if (criticalHigh != criticalMap.end())
1552 {
1553 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1554 }
1555 if (criticalLow != criticalMap.end())
1556 {
1557 criticalLowAlarm = std::get<bool>(criticalLow->second);
1558 }
1559 if (criticalHighAlarm)
1560 {
1561 assertions.set(
1562 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1563 upperCriticalGoingHigh));
1564 }
1565 if (criticalLowAlarm)
1566 {
1567 assertions.set(static_cast<size_t>(
1568 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1569 }
1570 }
1571 }
1572
1573 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1574}
1575
Willy Tu38e7a2b2021-03-29 15:09:56 -07001576// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001577void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1578 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001579{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001580 get_sdr::header::set_record_id(
1581 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1582
Willy Tu38e7a2b2021-03-29 15:09:56 -07001583 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1584 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1585
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001586 record.header.sdr_version = ipmiSdrVersion;
1587 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1588 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1589 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001590 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001591 record.key.owner_lun = lun;
1592 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001593}
1594bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1595 uint16_t recordID, const std::string& service,
1596 const std::string& path,
1597 get_sdr::SensorDataFullRecord& record)
1598{
Hao Jiange39d4d82021-04-16 17:02:40 -07001599 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1600
1601 DbusInterfaceMap sensorMap;
1602 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1603 {
1604 phosphor::logging::log<phosphor::logging::level::ERR>(
1605 "Failed to update sensor map for threshold sensor",
1606 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1607 phosphor::logging::entry("PATH=%s", path.c_str()));
1608 return false;
1609 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001610
1611 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1612 record.body.sensor_type = getSensorTypeFromPath(path);
1613 std::string type = getSensorTypeStringFromPath(path);
1614 auto typeCstr = type.c_str();
1615 auto findUnits = sensorUnits.find(typeCstr);
1616 if (findUnits != sensorUnits.end())
1617 {
1618 record.body.sensor_units_2_base =
1619 static_cast<uint8_t>(findUnits->second);
1620 } // else default 0x0 unspecified
1621
1622 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1623
Hao Jiangd2afd052020-12-10 15:09:32 -08001624 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001625 if (sensorObject == sensorMap.end())
1626 {
1627 phosphor::logging::log<phosphor::logging::level::ERR>(
1628 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001629 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001630 }
1631
1632 uint8_t entityId = 0;
1633 uint8_t entityInstance = 0x01;
1634
1635 // follow the association chain to get the parent board's entityid and
1636 // entityInstance
1637 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1638
1639 record.body.entity_id = entityId;
1640 record.body.entity_instance = entityInstance;
1641
Shakeeb Pasha93889722021-10-14 10:20:13 +05301642 double max = 0;
1643 double min = 0;
1644 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001645
1646 int16_t mValue = 0;
1647 int8_t rExp = 0;
1648 int16_t bValue = 0;
1649 int8_t bExp = 0;
1650 bool bSigned = false;
1651
1652 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1653 {
1654 phosphor::logging::log<phosphor::logging::level::ERR>(
1655 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001656 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001657 }
1658
1659 // The record.body is a struct SensorDataFullRecordBody
1660 // from sensorhandler.hpp in phosphor-ipmi-host.
1661 // The meaning of these bits appears to come from
1662 // table 43.1 of the IPMI spec.
1663 // The above 5 sensor attributes are stuffed in as follows:
1664 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1665 // Byte 22-24 are for other purposes
1666 // Byte 25 = MMMMMMMM = LSB of M
1667 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1668 // Byte 27 = BBBBBBBB = LSB of B
1669 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1670 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1671 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1672
1673 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1674 record.body.m_lsb = mValue & 0xFF;
1675
1676 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1677 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1678
1679 // move the smallest bit of the MSB into place (bit 9)
1680 // the MSbs are bits 7:8 in m_msb_and_tolerance
1681 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1682
1683 record.body.b_lsb = bValue & 0xFF;
1684
1685 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1686 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1687
1688 // move the smallest bit of the MSB into place (bit 9)
1689 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1690 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1691
1692 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1693 uint8_t rExpBits = rExp & 0x07;
1694
1695 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1696 uint8_t bExpBits = bExp & 0x07;
1697
1698 // move rExp and bExp into place
1699 record.body.r_b_exponents =
1700 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1701
1702 // Set the analog reading byte interpretation accordingly
1703 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1704
1705 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1706 // These seem redundant, but derivable from the above 5 attributes
1707 // Original comment said "todo fill out rest of units"
1708
1709 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001710 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001711 record.body.id_string_info = name.size();
1712 std::strncpy(record.body.id_string, name.c_str(),
1713 sizeof(record.body.id_string));
1714
Josh Lehana55c9532020-10-28 21:59:06 -07001715 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001716 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001717
Jie Yangf0a89942021-07-29 15:30:25 -07001718 bool sensorSettable = false;
1719 auto mutability =
1720 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1721 if (mutability != sensorMap.end())
1722 {
1723 sensorSettable =
1724 mappedVariant<bool>(mutability->second, "Mutable", false);
1725 }
1726 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1727
1728 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001729 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001730
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001731 IPMIThresholds thresholdData;
1732 try
1733 {
1734 thresholdData = getIPMIThresholds(sensorMap);
1735 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001736 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001737 {
1738 phosphor::logging::log<phosphor::logging::level::ERR>(
1739 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001740 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001741 }
1742
1743 if (thresholdData.criticalHigh)
1744 {
1745 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1746 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1747 IPMISensorEventEnableThresholds::criticalThreshold);
1748 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1749 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1750 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1751 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1752 record.body.discrete_reading_setting_mask[0] |=
1753 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1754 }
1755 if (thresholdData.warningHigh)
1756 {
1757 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1758 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1759 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1760 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1761 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1762 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1763 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1764 record.body.discrete_reading_setting_mask[0] |=
1765 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1766 }
1767 if (thresholdData.criticalLow)
1768 {
1769 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1770 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1771 IPMISensorEventEnableThresholds::criticalThreshold);
1772 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1773 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1774 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1775 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1776 record.body.discrete_reading_setting_mask[0] |=
1777 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1778 }
1779 if (thresholdData.warningLow)
1780 {
1781 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1782 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1783 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1784 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1785 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1786 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1787 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1788 record.body.discrete_reading_setting_mask[0] |=
1789 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1790 }
1791
1792 // everything that is readable is setable
1793 record.body.discrete_reading_setting_mask[1] =
1794 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001795 return true;
1796}
1797
Scron Chang2703b022021-07-06 15:47:45 +08001798#ifdef FEATURE_HYBRID_SENSORS
1799// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001800void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001801 uint16_t recordID,
1802 ipmi::sensor::IdInfoMap::const_iterator sensor,
1803 get_sdr::SensorDataFullRecord& record)
1804{
1805 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1806
1807 record.body.entity_id = sensor->second.entityType;
1808 record.body.sensor_type = sensor->second.sensorType;
1809 record.body.event_reading_type = sensor->second.sensorReadingType;
1810 record.body.entity_instance = sensor->second.instance;
1811 if (ipmi::sensor::Mutability::Write ==
1812 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1813 {
1814 get_sdr::body::init_settable_state(true, &(record.body));
1815 }
1816
1817 auto id_string = sensor->second.sensorName;
1818
1819 if (id_string.empty())
1820 {
1821 id_string = sensor->second.sensorNameFunc(sensor->second);
1822 }
1823
1824 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1825 {
1826 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1827 &(record.body));
1828 }
1829 else
1830 {
1831 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1832 }
1833 std::strncpy(record.body.id_string, id_string.c_str(),
1834 get_sdr::body::get_id_strlen(&(record.body)));
1835}
1836#endif
1837
Hao Jiange39d4d82021-04-16 17:02:40 -07001838// Construct type 3 SDR header and key (for VR and other discrete sensors)
1839void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1840 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001841{
1842 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1843 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1844
1845 get_sdr::header::set_record_id(
1846 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1847
1848 record.header.sdr_version = ipmiSdrVersion;
1849 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1850 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1851 sizeof(get_sdr::SensorDataRecordHeader);
1852 record.key.owner_id = bmcI2CAddr;
1853 record.key.owner_lun = lun;
1854 record.key.sensor_number = sensornumber;
1855
1856 record.body.entity_id = 0x00;
1857 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001858}
Willy Tu61992ad2021-03-29 15:33:20 -07001859
Hao Jiange39d4d82021-04-16 17:02:40 -07001860// Construct a type 3 SDR for VR typed sensor(daemon).
1861bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1862 uint16_t recordID, const std::string& service,
1863 const std::string& path,
1864 get_sdr::SensorDataEventRecord& record)
1865{
Hao Jiange39d4d82021-04-16 17:02:40 -07001866 constructEventSdrHeaderKey(sensorNum, recordID, record);
1867
1868 DbusInterfaceMap sensorMap;
1869 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1870 {
1871 phosphor::logging::log<phosphor::logging::level::ERR>(
1872 "Failed to update sensor map for VR sensor",
1873 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1874 phosphor::logging::entry("PATH=%s", path.c_str()));
1875 return false;
1876 }
Willy Tu61992ad2021-03-29 15:33:20 -07001877 // follow the association chain to get the parent board's entityid and
1878 // entityInstance
1879 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1880 record.body.entity_instance);
1881
1882 // Sensor type is hardcoded as a module/board type instead of parsing from
1883 // sensor path. This is because VR control is allocated in an independent
1884 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1885 // types.
1886 static constexpr const uint8_t module_board_type = 0x15;
1887 record.body.sensor_type = module_board_type;
1888 record.body.event_reading_type = 0x00;
1889
1890 record.body.sensor_record_sharing_1 = 0x00;
1891 record.body.sensor_record_sharing_2 = 0x00;
1892
1893 // populate sensor name from path
1894 auto name = sensor::parseSdrIdFromPath(path);
1895 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1896 record.body.id_string_info = nameSize;
1897 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1898 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1899
1900 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001901 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001902
1903 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001904}
1905
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001906static inline uint16_t getNumberOfSensors()
1907{
1908 return std::min(getSensorTree().size(), maxIPMISensors);
1909}
1910
Hao Jiange39d4d82021-04-16 17:02:40 -07001911static int
1912 getSensorDataRecord(ipmi::Context::ptr ctx,
1913 std::vector<uint8_t>& recordData, uint16_t recordID,
1914 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001915{
1916 size_t fruCount = 0;
1917 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1918 if (ret != ipmi::ccSuccess)
1919 {
1920 phosphor::logging::log<phosphor::logging::level::ERR>(
1921 "getSensorDataRecord: getFruSdrCount error");
1922 return GENERAL_ERROR;
1923 }
1924
Harvey Wu05d17c02021-09-15 08:46:59 +08001925 const auto& entityRecords =
1926 ipmi::sensor::EntityInfoMapContainer::getContainer()
1927 ->getIpmiEntityRecords();
1928 size_t entityCount = entityRecords.size();
1929
1930 size_t lastRecord = getNumberOfSensors() + fruCount +
1931 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001932 if (recordID == lastRecordIndex)
1933 {
1934 recordID = lastRecord;
1935 }
1936 if (recordID > lastRecord)
1937 {
1938 phosphor::logging::log<phosphor::logging::level::ERR>(
1939 "getSensorDataRecord: recordID > lastRecord error");
1940 return GENERAL_ERROR;
1941 }
1942
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001943 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001944 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001945 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001946
Harvey Wu05d17c02021-09-15 08:46:59 +08001947 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1948 {
1949 // handle type 8 entity map records
1950 ipmi::sensor::EntityInfoMap::const_iterator entity =
1951 entityRecords.find(static_cast<uint8_t>(
1952 sdrIndex - fruCount - ipmi::storage::type12Count));
1953 if (entity == entityRecords.end())
1954 {
1955 return IPMI_CC_SENSOR_INVALID;
1956 }
1957 recordData = ipmi::storage::getType8SDRs(entity, recordID);
1958 }
1959 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07001960 {
1961 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08001962 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001963 if (type12Index >= ipmi::storage::type12Count)
1964 {
1965 phosphor::logging::log<phosphor::logging::level::ERR>(
1966 "getSensorDataRecord: type12Index error");
1967 return GENERAL_ERROR;
1968 }
1969 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1970 }
1971 else
1972 {
1973 // handle fru records
1974 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08001975 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001976 if (ret != IPMI_CC_OK)
1977 {
1978 return GENERAL_ERROR;
1979 }
1980 data.header.record_id_msb = recordID >> 8;
1981 data.header.record_id_lsb = recordID & 0xFF;
1982 recordData.insert(recordData.end(), (uint8_t*)&data,
1983 ((uint8_t*)&data) + sizeof(data));
1984 }
1985
1986 return 0;
1987 }
1988
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001989 // Perform a incremental scan of the SDR Record ID's and translate the
1990 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1991 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1992 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
1993 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07001994 std::string connection;
1995 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001996 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07001997 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001998 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07001999 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002000 // LUN 0 has one reserved sensor number. Compensate here by adding one
2001 // to the record ID
2002 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002003 ctx->lun = 1;
2004 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002005 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002006 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002007 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2008 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2009 // rules governing its use.
2010 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07002011 ctx->lun = 3;
2012 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002013
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002014 auto status =
2015 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2016 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002017 if (status)
2018 {
2019 phosphor::logging::log<phosphor::logging::level::ERR>(
2020 "getSensorDataRecord: getSensorConnection error");
2021 return GENERAL_ERROR;
2022 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002023 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002024 // Return an error on LUN 2 assingments, and any sensor number beyond the
2025 // range of LUN 3
2026 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2027 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002028 {
2029 phosphor::logging::log<phosphor::logging::level::ERR>(
2030 "getSensorDataRecord: invalidSensorNumber");
2031 return GENERAL_ERROR;
2032 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002033 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2034 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2035
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002036 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2037 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002038 {
2039 phosphor::logging::log<phosphor::logging::level::ERR>(
2040 "getSensorDataRecord: sensor record mismatch");
2041 return GENERAL_ERROR;
2042 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002043
Willy Tu38e7a2b2021-03-29 15:09:56 -07002044 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002045 if (std::find(interfaces.begin(), interfaces.end(),
2046 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002047 {
Willy Tu11d68892022-01-20 10:37:34 -08002048 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002049
Hao Jiange39d4d82021-04-16 17:02:40 -07002050 // If the request doesn't read SDR body, construct only header and key
2051 // part to avoid additional DBus transaction.
2052 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2053 {
2054 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2055 }
2056 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2057 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002058 {
2059 return GENERAL_ERROR;
2060 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002061
Willy Tu38e7a2b2021-03-29 15:09:56 -07002062 recordData.insert(recordData.end(), (uint8_t*)&record,
2063 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002064
2065 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002066 }
Willy Tu61992ad2021-03-29 15:33:20 -07002067
Scron Chang2703b022021-07-06 15:47:45 +08002068#ifdef FEATURE_HYBRID_SENSORS
2069 if (auto sensor = findStaticSensor(path);
2070 sensor != ipmi::sensor::sensors.end() &&
2071 getSensorEventTypeFromPath(path) !=
2072 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2073 {
Willy Tu11d68892022-01-20 10:37:34 -08002074 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002075
2076 // If the request doesn't read SDR body, construct only header and key
2077 // part to avoid additional DBus transaction.
2078 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2079 {
2080 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2081 }
2082 else
2083 {
2084 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2085 }
2086
2087 recordData.insert(recordData.end(), (uint8_t*)&record,
2088 ((uint8_t*)&record) + sizeof(record));
2089
2090 return 0;
2091 }
2092#endif
2093
Willy Tu61992ad2021-03-29 15:33:20 -07002094 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002095 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2096 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002097 {
Willy Tu11d68892022-01-20 10:37:34 -08002098 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002099
Hao Jiange39d4d82021-04-16 17:02:40 -07002100 // If the request doesn't read SDR body, construct only header and key
2101 // part to avoid additional DBus transaction.
2102 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2103 {
2104 constructEventSdrHeaderKey(sensorNum, recordID, record);
2105 }
2106 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2107 record))
2108 {
2109 return GENERAL_ERROR;
2110 }
Willy Tu61992ad2021-03-29 15:33:20 -07002111 recordData.insert(recordData.end(), (uint8_t*)&record,
2112 ((uint8_t*)&record) + sizeof(record));
2113 }
2114
Willy Tude54f482021-01-26 15:59:09 -08002115 return 0;
2116}
2117
2118/** @brief implements the get SDR Info command
2119 * @param count - Operation
2120 *
2121 * @returns IPMI completion code plus response data
2122 * - sdrCount - sensor/SDR count
2123 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2124 */
2125static ipmi::RspType<uint8_t, // respcount
2126 uint8_t, // dynamic population flags
2127 uint32_t // last time a sensor was added
2128 >
2129 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2130 std::optional<uint8_t> count)
2131{
2132 auto& sensorTree = getSensorTree();
2133 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002134 uint16_t recordID = 0;
2135 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002136 // Sensors are dynamically allocated, and there is at least one LUN
2137 uint8_t lunsAndDynamicPopulation = 0x80;
2138 constexpr uint8_t getSdrCount = 0x01;
2139 constexpr uint8_t getSensorCount = 0x00;
2140
2141 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2142 {
2143 return ipmi::responseResponseError();
2144 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002145 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002146 if (count.value_or(0) == getSdrCount)
2147 {
2148 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002149 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002150 {
2151 get_sdr::SensorDataRecordHeader* hdr =
2152 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002153 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002154 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2155 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002156 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002157 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002158 record.data());
2159 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002160 {
2161 sdrCount++;
2162 }
2163 }
2164 }
2165 }
2166 else if (count.value_or(0) == getSensorCount)
2167 {
2168 // Return the number of sensors attached to the LUN
2169 if ((ctx->lun == 0) && (numSensors > 0))
2170 {
2171 sdrCount =
2172 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2173 }
2174 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2175 {
2176 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2177 ? maxSensorsPerLUN
2178 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2179 }
2180 else if (ctx->lun == 3)
2181 {
2182 if (numSensors <= maxIPMISensors)
2183 {
2184 sdrCount =
2185 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2186 }
2187 else
2188 {
2189 // error
2190 throw std::out_of_range(
2191 "Maximum number of IPMI sensors exceeded.");
2192 }
2193 }
2194 }
2195 else
2196 {
2197 return ipmi::responseInvalidFieldRequest();
2198 }
2199
2200 // Get Sensor count. This returns the number of sensors
2201 if (numSensors > 0)
2202 {
2203 lunsAndDynamicPopulation |= 1;
2204 }
2205 if (numSensors > maxSensorsPerLUN)
2206 {
2207 lunsAndDynamicPopulation |= 2;
2208 }
2209 if (numSensors >= (maxSensorsPerLUN * 2))
2210 {
2211 lunsAndDynamicPopulation |= 8;
2212 }
2213 if (numSensors > maxIPMISensors)
2214 {
2215 // error
2216 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2217 }
2218
2219 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2220 sdrLastAdd);
2221}
2222
2223/* end sensor commands */
2224
2225/* storage commands */
2226
2227ipmi::RspType<uint8_t, // sdr version
2228 uint16_t, // record count
2229 uint16_t, // free space
2230 uint32_t, // most recent addition
2231 uint32_t, // most recent erase
2232 uint8_t // operationSupport
2233 >
2234 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2235{
2236 auto& sensorTree = getSensorTree();
2237 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002238 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002239 {
2240 return ipmi::responseResponseError();
2241 }
2242
2243 size_t fruCount = 0;
2244 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2245 if (ret != ipmi::ccSuccess)
2246 {
2247 return ipmi::response(ret);
2248 }
2249
2250 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002251 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002252
2253 uint8_t operationSupport = static_cast<uint8_t>(
2254 SdrRepositoryInfoOps::overflow); // write not supported
2255
2256 operationSupport |=
2257 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2258 operationSupport |= static_cast<uint8_t>(
2259 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2260 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2261 unspecifiedFreeSpace, sdrLastAdd,
2262 sdrLastRemove, operationSupport);
2263}
2264
2265/** @brief implements the get SDR allocation info command
2266 *
2267 * @returns IPMI completion code plus response data
2268 * - allocUnits - Number of possible allocation units
2269 * - allocUnitSize - Allocation unit size in bytes.
2270 * - allocUnitFree - Number of free allocation units
2271 * - allocUnitLargestFree - Largest free block in allocation units
2272 * - maxRecordSize - Maximum record size in allocation units.
2273 */
2274ipmi::RspType<uint16_t, // allocUnits
2275 uint16_t, // allocUnitSize
2276 uint16_t, // allocUnitFree
2277 uint16_t, // allocUnitLargestFree
2278 uint8_t // maxRecordSize
2279 >
2280 ipmiStorageGetSDRAllocationInfo()
2281{
2282 // 0000h unspecified number of alloc units
2283 constexpr uint16_t allocUnits = 0;
2284
2285 constexpr uint16_t allocUnitFree = 0;
2286 constexpr uint16_t allocUnitLargestFree = 0;
2287 // only allow one block at a time
2288 constexpr uint8_t maxRecordSize = 1;
2289
2290 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2291 allocUnitLargestFree, maxRecordSize);
2292}
2293
2294/** @brief implements the reserve SDR command
2295 * @returns IPMI completion code plus response data
2296 * - sdrReservationID
2297 */
2298ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2299{
2300 sdrReservationID++;
2301 if (sdrReservationID == 0)
2302 {
2303 sdrReservationID++;
2304 }
2305
2306 return ipmi::responseSuccess(sdrReservationID);
2307}
2308
2309ipmi::RspType<uint16_t, // next record ID
2310 std::vector<uint8_t> // payload
2311 >
2312 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2313 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2314{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002315 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002316 // reservation required for partial reads with non zero offset into
2317 // record
2318 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2319 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002320 phosphor::logging::log<phosphor::logging::level::ERR>(
2321 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002322 return ipmi::responseInvalidReservationId();
2323 }
Willy Tude54f482021-01-26 15:59:09 -08002324 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2325 if (ret != ipmi::ccSuccess)
2326 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002327 phosphor::logging::log<phosphor::logging::level::ERR>(
2328 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002329 return ipmi::response(ret);
2330 }
2331
Harvey Wu05d17c02021-09-15 08:46:59 +08002332 const auto& entityRecords =
2333 ipmi::sensor::EntityInfoMapContainer::getContainer()
2334 ->getIpmiEntityRecords();
2335 int entityCount = entityRecords.size();
2336
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002337 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002338 size_t lastRecord = getNumberOfSensors() + fruCount +
2339 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002340 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2341
2342 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002343 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002344 phosphor::logging::log<phosphor::logging::level::ERR>(
2345 "ipmiStorageGetSDR: getSensorSubtree error");
2346 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002347 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002348
2349 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002350 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002351 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002352 phosphor::logging::log<phosphor::logging::level::ERR>(
2353 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002354 return ipmi::responseInvalidFieldRequest();
2355 }
Willy Tude54f482021-01-26 15:59:09 -08002356 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002357 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002358 if (!hdr)
2359 {
2360 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002361 "ipmiStorageGetSDR: record header is null");
2362 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002363 }
2364
2365 size_t sdrLength =
2366 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2367 if (sdrLength < (offset + bytesToRead))
2368 {
2369 bytesToRead = sdrLength - offset;
2370 }
2371
2372 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2373 if (!respStart)
2374 {
2375 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002376 "ipmiStorageGetSDR: record is null");
2377 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002378 }
2379
2380 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002381
Willy Tude54f482021-01-26 15:59:09 -08002382 return ipmi::responseSuccess(nextRecordId, recordData);
2383}
2384/* end storage commands */
2385
2386void registerSensorFunctions()
2387{
2388 // <Platform Event>
2389 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2390 ipmi::sensor_event::cmdPlatformEvent,
2391 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2392
Willy Tudbafbce2021-03-29 00:37:05 -07002393 // <Set Sensor Reading and Event Status>
2394 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2395 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2396 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002397
Willy Tude54f482021-01-26 15:59:09 -08002398 // <Get Sensor Reading>
2399 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2400 ipmi::sensor_event::cmdGetSensorReading,
2401 ipmi::Privilege::User, ipmiSenGetSensorReading);
2402
2403 // <Get Sensor Threshold>
2404 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2405 ipmi::sensor_event::cmdGetSensorThreshold,
2406 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2407
2408 // <Set Sensor Threshold>
2409 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2410 ipmi::sensor_event::cmdSetSensorThreshold,
2411 ipmi::Privilege::Operator,
2412 ipmiSenSetSensorThresholds);
2413
2414 // <Get Sensor Event Enable>
2415 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2416 ipmi::sensor_event::cmdGetSensorEventEnable,
2417 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2418
2419 // <Get Sensor Event Status>
2420 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2421 ipmi::sensor_event::cmdGetSensorEventStatus,
2422 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2423
2424 // register all storage commands for both Sensor and Storage command
2425 // versions
2426
2427 // <Get SDR Repository Info>
2428 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2429 ipmi::storage::cmdGetSdrRepositoryInfo,
2430 ipmi::Privilege::User,
2431 ipmiStorageGetSDRRepositoryInfo);
2432
2433 // <Get Device SDR Info>
2434 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2435 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2436 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2437
2438 // <Get SDR Allocation Info>
2439 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2440 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2441 ipmi::Privilege::User,
2442 ipmiStorageGetSDRAllocationInfo);
2443
2444 // <Reserve SDR Repo>
2445 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2446 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2447 ipmi::Privilege::User, ipmiStorageReserveSDR);
2448
2449 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2450 ipmi::storage::cmdReserveSdrRepository,
2451 ipmi::Privilege::User, ipmiStorageReserveSDR);
2452
2453 // <Get Sdr>
2454 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2455 ipmi::sensor_event::cmdGetDeviceSdr,
2456 ipmi::Privilege::User, ipmiStorageGetSDR);
2457
2458 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2459 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2460 ipmiStorageGetSDR);
2461}
2462} // namespace ipmi