1 /*
2 // Copyright (c) 2017 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "dbus-sdr/sensorcommands.hpp"
18 
19 #include "dbus-sdr/sdrutils.hpp"
20 #include "dbus-sdr/sensorutils.hpp"
21 #include "dbus-sdr/storagecommands.hpp"
22 #include "entity_map_json.hpp"
23 
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>
42 #include <user_channel/channel_layer.hpp>
43 #include <utility>
44 #include <variant>
45 
46 #ifdef FEATURE_HYBRID_SENSORS
47 
48 #include "sensordatahandler.hpp"
49 namespace ipmi
50 {
51 namespace sensor
52 {
53 extern const IdInfoMap sensors;
54 } // namespace sensor
55 } // namespace ipmi
56 #endif
57 
58 constexpr std::array<const char*, 7> suffixes = {
59     "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
60     "_Output_Power",   "_Input_Power",   "_Temperature"};
61 namespace ipmi
62 {
63 
64 using phosphor::logging::entry;
65 using phosphor::logging::level;
66 using phosphor::logging::log;
67 
68 static constexpr int sensorMapUpdatePeriod = 10;
69 static constexpr int sensorMapSdrUpdatePeriod = 60;
70 
71 // BMC I2C address is generally at 0x20
72 static constexpr uint8_t bmcI2CAddr = 0x20;
73 
74 constexpr size_t maxSDRTotalSize =
75     76; // Largest SDR Record Size (type 01) + SDR Overheader Size
76 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
77 
78 static uint16_t sdrReservationID;
79 static uint32_t sdrLastAdd = noTimestamp;
80 static uint32_t sdrLastRemove = noTimestamp;
81 static constexpr size_t lastRecordIndex = 0xFFFF;
82 
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.
88 static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
89 
90 static constexpr size_t lun0MaxSensorNum = 0xfe;
91 static constexpr size_t lun1MaxSensorNum = 0x1fe;
92 static constexpr size_t lun3MaxSensorNum = 0x3fe;
93 static constexpr int GENERAL_ERROR = -1;
94 
95 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
96 
97 // Specify the comparison required to sort and find char* map objects
98 struct CmpStr
99 {
100     bool operator()(const char* a, const char* b) const
101     {
102         return std::strcmp(a, b) < 0;
103     }
104 };
105 const 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 
112 void registerSensorFunctions() __attribute__((constructor));
113 
114 static sdbusplus::bus::match::match sensorAdded(
115     *getSdBus(),
116     "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
117     "sensors/'",
118     [](sdbusplus::message::message& m) {
119         getSensorTree().clear();
120         sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
121                          std::chrono::system_clock::now().time_since_epoch())
122                          .count();
123     });
124 
125 static sdbusplus::bus::match::match sensorRemoved(
126     *getSdBus(),
127     "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
128     "sensors/'",
129     [](sdbusplus::message::message& m) {
130         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.
138 static boost::container::flat_map<
139     std::string, boost::container::flat_map<std::string, std::optional<bool>>>
140     thresholdDeassertMap;
141 
142 static 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 
186 namespace sensor
187 {
188 static constexpr const char* vrInterface =
189     "xyz.openbmc_project.Control.VoltageRegulatorMode";
190 static constexpr const char* sensorInterface =
191     "xyz.openbmc_project.Sensor.Value";
192 } // namespace sensor
193 
194 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
195                             double& min)
196 {
197     max = 127;
198     min = -128;
199 
200     auto sensorObject = sensorMap.find(sensor::sensorInterface);
201     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);
227             if (std::isfinite(value))
228             {
229                 min = std::min(value, min);
230             }
231         }
232         if (upper != critical->second.end())
233         {
234             double value = std::visit(VariantToDoubleVisitor(), upper->second);
235             if (std::isfinite(value))
236             {
237                 max = std::max(value, max);
238             }
239         }
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);
249             if (std::isfinite(value))
250             {
251                 min = std::min(value, min);
252             }
253         }
254         if (upper != warning->second.end())
255         {
256             double value = std::visit(VariantToDoubleVisitor(), upper->second);
257             if (std::isfinite(value))
258             {
259                 max = std::max(value, max);
260             }
261         }
262     }
263 }
264 
265 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
266                          std::string sensorPath, DbusInterfaceMap& sensorMap,
267                          int updatePeriod = sensorMapUpdatePeriod)
268 {
269 #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 
282     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)
296             .count() > updatePeriod)
297     {
298         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;
311         // 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();
314     }
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 
330 namespace sensor
331 {
332 // Read VR profiles from sensor(daemon) interface
333 static std::optional<std::vector<std::string>>
334     getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
335 {
336     // get VR mode profiles from Supported Interface
337     auto supportedProperty = object.find("Supported");
338     if (supportedProperty == object.end() ||
339         object.find("Selected") == object.end())
340     {
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     }
355     return *profilesPtr;
356 }
357 
358 // Calculate VR Mode from input IPMI discrete event bytes
359 static 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     }
369 
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 
390     if (index >= profiles->size())
391     {
392         phosphor::logging::log<phosphor::logging::level::ERR>(
393             "profile index out of boundary");
394         return std::nullopt;
395     }
396 
397     return profiles->at(index);
398 }
399 
400 // Calculate sensor value from IPMI reading byte
401 static 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 
436 // Extract file name from sensor path as the sensors SDR ID. Simplify the name
437 // if it is too long.
438 std::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 
447     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
448     {
449         // try to not truncate by replacing common words
450         for (const auto& suffix : suffixes)
451         {
452             if (boost::ends_with(name, suffix))
453             {
454                 boost::replace_all(name, suffix, "");
455                 break;
456             }
457         }
458         if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
459         {
460             name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
461         }
462     }
463     std::replace(name.begin(), name.end(), '_', ' ');
464     return name;
465 }
466 
467 bool 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     }
477     std::string mode;
478 
479     auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
480                               "Selected", mode);
481     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 
491     auto itr = std::find(profiles->begin(), profiles->end(), mode);
492     if (itr == profiles->end())
493     {
494         using namespace phosphor::logging;
495         log<level::ERR>("VR mode doesn't match any of its profiles",
496                         entry("PATH=%s", path.c_str()));
497         return false;
498     }
499     std::size_t index =
500         static_cast<std::size_t>(std::distance(profiles->begin(), itr));
501 
502     // map index to reponse event assertion bit.
503     if (index < 8)
504     {
505         assertions.set(1u << index);
506     }
507     else if (index < 15)
508     {
509         assertions.set(1u << (index - 8));
510     }
511     else
512     {
513         log<level::ERR>("VR profile index reaches max assertion bit",
514                         entry("PATH=%s", path.c_str()),
515                         entry("INDEX=%uz", index));
516         return false;
517     }
518     if constexpr (debug)
519     {
520         std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
521                   << " mode is: [" << index << "] " << mode << std::endl;
522     }
523     return true;
524 }
525 } // namespace sensor
526 
527 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
528                                      ipmi::message::Payload& p)
529 {
530     constexpr const uint8_t validEnvmRev = 0x04;
531     constexpr const uint8_t lastSensorType = 0x2C;
532     constexpr const uint8_t oemReserved = 0xC0;
533 
534     uint8_t sysgeneratorID = 0;
535     uint8_t evmRev = 0;
536     uint8_t sensorType = 0;
537     uint8_t sensorNum = 0;
538     uint8_t eventType = 0;
539     uint8_t eventData1 = 0;
540     std::optional<uint8_t> eventData2 = 0;
541     std::optional<uint8_t> eventData3 = 0;
542     [[maybe_unused]] uint16_t generatorID = 0;
543     ipmi::ChannelInfo chInfo;
544 
545     if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
546     {
547         phosphor::logging::log<phosphor::logging::level::ERR>(
548             "Failed to get Channel Info",
549             phosphor::logging::entry("CHANNEL=%d", ctx->channel));
550         return ipmi::responseUnspecifiedError();
551     }
552 
553     if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
554         ipmi::EChannelMediumType::systemInterface)
555     {
556 
557         p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
558                  eventData1, eventData2, eventData3);
559         // Refer to IPMI Spec Table 32: SEL Event Records
560         generatorID = (ctx->channel << 12) // Channel
561                       | (0x0 << 10)        // Reserved
562                       | (0x0 << 8)         // 0x0 for sys-soft ID
563                       | ((sysgeneratorID << 1) | 0x1);
564     }
565     else
566     {
567 
568         p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
569                  eventData2, eventData3);
570         // Refer to IPMI Spec Table 32: SEL Event Records
571         generatorID = (ctx->channel << 12)      // Channel
572                       | (0x0 << 10)             // Reserved
573                       | ((ctx->lun & 0x3) << 8) // Lun
574                       | (ctx->rqSA << 1);
575     }
576 
577     if (!p.fullyUnpacked())
578     {
579         return ipmi::responseReqDataLenInvalid();
580     }
581 
582     // Check for valid evmRev and Sensor Type(per Table 42 of spec)
583     if (evmRev != validEnvmRev)
584     {
585         return ipmi::responseInvalidFieldRequest();
586     }
587     if ((sensorType > lastSensorType) && (sensorType < oemReserved))
588     {
589         return ipmi::responseInvalidFieldRequest();
590     }
591 
592     return ipmi::responseSuccess();
593 }
594 
595 ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
596                                      uint8_t sensorNumber, uint8_t operation,
597                                      uint8_t reading, uint15_t assertOffset,
598                                      bool resvd1, uint15_t deassertOffset,
599                                      bool resvd2, uint8_t eventData1,
600                                      uint8_t eventData2, uint8_t eventData3)
601 {
602     std::string connection;
603     std::string path;
604     std::vector<std::string> interfaces;
605 
606     ipmi::Cc status =
607         getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
608     if (status)
609     {
610         return ipmi::response(status);
611     }
612 
613     // we can tell the sensor type by its interface type
614     if (std::find(interfaces.begin(), interfaces.end(),
615                   sensor::sensorInterface) != interfaces.end())
616     {
617         DbusInterfaceMap sensorMap;
618         if (!getSensorMap(ctx, connection, path, sensorMap))
619         {
620             return ipmi::responseResponseError();
621         }
622         auto sensorObject = sensorMap.find(sensor::sensorInterface);
623         if (sensorObject == sensorMap.end())
624         {
625             return ipmi::responseResponseError();
626         }
627 
628         // Only allow external SetSensor if write permission granted
629         if (!details::sdrWriteTable.getWritePermission(sensorNumber))
630         {
631             return ipmi::responseResponseError();
632         }
633 
634         auto value =
635             sensor::calculateValue(reading, sensorMap, sensorObject->second);
636         if (!value)
637         {
638             return ipmi::responseResponseError();
639         }
640 
641         if constexpr (debug)
642         {
643             phosphor::logging::log<phosphor::logging::level::INFO>(
644                 "IPMI SET_SENSOR",
645                 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
646                 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
647                 phosphor::logging::entry("VALUE=%f", *value));
648         }
649 
650         boost::system::error_code ec =
651             setDbusProperty(ctx, connection, path, sensor::sensorInterface,
652                             "Value", ipmi::Value(*value));
653 
654         // setDbusProperty intended to resolve dbus exception/rc within the
655         // function but failed to achieve that. Catch exception in the ipmi
656         // callback functions for now (e.g. ipmiSetSensorReading).
657         if (ec)
658         {
659             using namespace phosphor::logging;
660             log<level::ERR>("Failed to set property",
661                             entry("PROPERTY=%s", "Value"),
662                             entry("PATH=%s", path.c_str()),
663                             entry("INTERFACE=%s", sensor::sensorInterface),
664                             entry("WHAT=%s", ec.message().c_str()));
665             return ipmi::responseResponseError();
666         }
667         return ipmi::responseSuccess();
668     }
669 
670     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
671         interfaces.end())
672     {
673         DbusInterfaceMap sensorMap;
674         if (!getSensorMap(ctx, connection, path, sensorMap))
675         {
676             return ipmi::responseResponseError();
677         }
678         auto sensorObject = sensorMap.find(sensor::vrInterface);
679         if (sensorObject == sensorMap.end())
680         {
681             return ipmi::responseResponseError();
682         }
683 
684         // VR sensors are treated as a special case and we will not check the
685         // write permission for VR sensors, since they always deemed writable
686         // and permission table are not applied to VR sensors.
687         auto vrMode =
688             sensor::calculateVRMode(assertOffset, sensorObject->second);
689         if (!vrMode)
690         {
691             return ipmi::responseResponseError();
692         }
693         boost::system::error_code ec = setDbusProperty(
694             ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
695         // setDbusProperty intended to resolve dbus exception/rc within the
696         // function but failed to achieve that. Catch exception in the ipmi
697         // callback functions for now (e.g. ipmiSetSensorReading).
698         if (ec)
699         {
700             using namespace phosphor::logging;
701             log<level::ERR>("Failed to set property",
702                             entry("PROPERTY=%s", "Selected"),
703                             entry("PATH=%s", path.c_str()),
704                             entry("INTERFACE=%s", sensor::sensorInterface),
705                             entry("WHAT=%s", ec.message().c_str()));
706             return ipmi::responseResponseError();
707         }
708         return ipmi::responseSuccess();
709     }
710 
711     phosphor::logging::log<phosphor::logging::level::ERR>(
712         "unknown sensor type",
713         phosphor::logging::entry("PATH=%s", path.c_str()));
714     return ipmi::responseResponseError();
715 }
716 
717 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
718     ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
719 {
720     std::string connection;
721     std::string path;
722 
723     if (sensnum == reservedSensorNumber)
724     {
725         return ipmi::responseInvalidFieldRequest();
726     }
727 
728     auto status = getSensorConnection(ctx, sensnum, connection, path);
729     if (status)
730     {
731         return ipmi::response(status);
732     }
733 
734 #ifdef FEATURE_HYBRID_SENSORS
735     if (auto sensor = findStaticSensor(path);
736         sensor != ipmi::sensor::sensors.end() &&
737         getSensorEventTypeFromPath(path) !=
738             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
739     {
740         if (ipmi::sensor::Mutability::Read !=
741             (sensor->second.mutability & ipmi::sensor::Mutability::Read))
742         {
743             return ipmi::responseIllegalCommand();
744         }
745 
746         uint8_t operation;
747         try
748         {
749             ipmi::sensor::GetSensorResponse getResponse =
750                 sensor->second.getFunc(sensor->second);
751 
752             if (getResponse.readingOrStateUnavailable)
753             {
754                 operation |= static_cast<uint8_t>(
755                     IPMISensorReadingByte2::readingStateUnavailable);
756             }
757             if (getResponse.scanningEnabled)
758             {
759                 operation |= static_cast<uint8_t>(
760                     IPMISensorReadingByte2::sensorScanningEnable);
761             }
762             if (getResponse.allEventMessagesEnabled)
763             {
764                 operation |= static_cast<uint8_t>(
765                     IPMISensorReadingByte2::eventMessagesEnable);
766             }
767             return ipmi::responseSuccess(
768                 getResponse.reading, operation,
769                 getResponse.thresholdLevelsStates,
770                 getResponse.discreteReadingSensorStates);
771         }
772         catch (const std::exception& e)
773         {
774             operation |= static_cast<uint8_t>(
775                 IPMISensorReadingByte2::readingStateUnavailable);
776             return ipmi::responseSuccess(0, operation, 0, std::nullopt);
777         }
778     }
779 #endif
780 
781     DbusInterfaceMap sensorMap;
782     if (!getSensorMap(ctx, connection, path, sensorMap))
783     {
784         return ipmi::responseResponseError();
785     }
786     auto sensorObject = sensorMap.find(sensor::sensorInterface);
787 
788     if (sensorObject == sensorMap.end() ||
789         sensorObject->second.find("Value") == sensorObject->second.end())
790     {
791         return ipmi::responseResponseError();
792     }
793     auto& valueVariant = sensorObject->second["Value"];
794     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
795 
796     double max = 0;
797     double min = 0;
798     getSensorMaxMin(sensorMap, max, min);
799 
800     int16_t mValue = 0;
801     int16_t bValue = 0;
802     int8_t rExp = 0;
803     int8_t bExp = 0;
804     bool bSigned = false;
805 
806     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
807     {
808         return ipmi::responseResponseError();
809     }
810 
811     uint8_t value =
812         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
813     uint8_t operation =
814         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
815     operation |=
816         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
817     bool notReading = std::isnan(reading);
818 
819     if (!notReading)
820     {
821         auto availableObject =
822             sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
823         if (availableObject != sensorMap.end())
824         {
825             auto findAvailable = availableObject->second.find("Available");
826             if (findAvailable != availableObject->second.end())
827             {
828                 bool* available = std::get_if<bool>(&(findAvailable->second));
829                 if (available && !(*available))
830                 {
831                     notReading = true;
832                 }
833             }
834         }
835     }
836 
837     if (notReading)
838     {
839         operation |= static_cast<uint8_t>(
840             IPMISensorReadingByte2::readingStateUnavailable);
841     }
842 
843     if constexpr (details::enableInstrumentation)
844     {
845         int byteValue;
846         if (bSigned)
847         {
848             byteValue = static_cast<int>(static_cast<int8_t>(value));
849         }
850         else
851         {
852             byteValue = static_cast<int>(static_cast<uint8_t>(value));
853         }
854 
855         // Keep stats on the reading just obtained, even if it is "NaN"
856         if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
857         {
858             // This is the first reading, show the coefficients
859             double step = (max - min) / 255.0;
860             std::cerr << "IPMI sensor "
861                       << details::sdrStatsTable.getName(sensnum)
862                       << ": Range min=" << min << " max=" << max
863                       << ", step=" << step
864                       << ", Coefficients mValue=" << static_cast<int>(mValue)
865                       << " rExp=" << static_cast<int>(rExp)
866                       << " bValue=" << static_cast<int>(bValue)
867                       << " bExp=" << static_cast<int>(bExp)
868                       << " bSigned=" << static_cast<int>(bSigned) << "\n";
869         }
870     }
871 
872     uint8_t thresholds = 0;
873 
874     auto warningObject =
875         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
876     if (warningObject != sensorMap.end())
877     {
878         auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
879         auto alarmLow = warningObject->second.find("WarningAlarmLow");
880         if (alarmHigh != warningObject->second.end())
881         {
882             if (std::get<bool>(alarmHigh->second))
883             {
884                 thresholds |= static_cast<uint8_t>(
885                     IPMISensorReadingByte3::upperNonCritical);
886             }
887         }
888         if (alarmLow != warningObject->second.end())
889         {
890             if (std::get<bool>(alarmLow->second))
891             {
892                 thresholds |= static_cast<uint8_t>(
893                     IPMISensorReadingByte3::lowerNonCritical);
894             }
895         }
896     }
897 
898     auto criticalObject =
899         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
900     if (criticalObject != sensorMap.end())
901     {
902         auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
903         auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
904         if (alarmHigh != criticalObject->second.end())
905         {
906             if (std::get<bool>(alarmHigh->second))
907             {
908                 thresholds |=
909                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
910             }
911         }
912         if (alarmLow != criticalObject->second.end())
913         {
914             if (std::get<bool>(alarmLow->second))
915             {
916                 thresholds |=
917                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
918             }
919         }
920     }
921 
922     // no discrete as of today so optional byte is never returned
923     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
924 }
925 
926 /** @brief implements the Set Sensor threshold command
927  *  @param sensorNumber        - sensor number
928  *  @param lowerNonCriticalThreshMask
929  *  @param lowerCriticalThreshMask
930  *  @param lowerNonRecovThreshMask
931  *  @param upperNonCriticalThreshMask
932  *  @param upperCriticalThreshMask
933  *  @param upperNonRecovThreshMask
934  *  @param reserved
935  *  @param lowerNonCritical    - lower non-critical threshold
936  *  @param lowerCritical       - Lower critical threshold
937  *  @param lowerNonRecoverable - Lower non recovarable threshold
938  *  @param upperNonCritical    - Upper non-critical threshold
939  *  @param upperCritical       - Upper critical
940  *  @param upperNonRecoverable - Upper Non-recoverable
941  *
942  *  @returns IPMI completion code
943  */
944 ipmi::RspType<> ipmiSenSetSensorThresholds(
945     ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
946     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
947     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
948     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
949     uint8_t lowerCritical, uint8_t lowerNonRecoverable,
950     uint8_t upperNonCritical, uint8_t upperCritical,
951     uint8_t upperNonRecoverable)
952 {
953     if (sensorNum == reservedSensorNumber || reserved)
954     {
955         return ipmi::responseInvalidFieldRequest();
956     }
957 
958     // lower nc and upper nc not suppported on any sensor
959     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
960     {
961         return ipmi::responseInvalidFieldRequest();
962     }
963 
964     // if none of the threshold mask are set, nothing to do
965     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
966           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
967           upperCriticalThreshMask | upperNonRecovThreshMask))
968     {
969         return ipmi::responseSuccess();
970     }
971 
972     std::string connection;
973     std::string path;
974 
975     ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
976     if (status)
977     {
978         return ipmi::response(status);
979     }
980     DbusInterfaceMap sensorMap;
981     if (!getSensorMap(ctx, connection, path, sensorMap))
982     {
983         return ipmi::responseResponseError();
984     }
985 
986     double max = 0;
987     double min = 0;
988     getSensorMaxMin(sensorMap, max, min);
989 
990     int16_t mValue = 0;
991     int16_t bValue = 0;
992     int8_t rExp = 0;
993     int8_t bExp = 0;
994     bool bSigned = false;
995 
996     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
997     {
998         return ipmi::responseResponseError();
999     }
1000 
1001     // store a vector of property name, value to set, and interface
1002     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1003 
1004     // define the indexes of the tuple
1005     constexpr uint8_t propertyName = 0;
1006     constexpr uint8_t thresholdValue = 1;
1007     constexpr uint8_t interface = 2;
1008     // verifiy all needed fields are present
1009     if (lowerCriticalThreshMask || upperCriticalThreshMask)
1010     {
1011         auto findThreshold =
1012             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1013         if (findThreshold == sensorMap.end())
1014         {
1015             return ipmi::responseInvalidFieldRequest();
1016         }
1017         if (lowerCriticalThreshMask)
1018         {
1019             auto findLower = findThreshold->second.find("CriticalLow");
1020             if (findLower == findThreshold->second.end())
1021             {
1022                 return ipmi::responseInvalidFieldRequest();
1023             }
1024             thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1025                                          findThreshold->first);
1026         }
1027         if (upperCriticalThreshMask)
1028         {
1029             auto findUpper = findThreshold->second.find("CriticalHigh");
1030             if (findUpper == findThreshold->second.end())
1031             {
1032                 return ipmi::responseInvalidFieldRequest();
1033             }
1034             thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1035                                          findThreshold->first);
1036         }
1037     }
1038     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1039     {
1040         auto findThreshold =
1041             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1042         if (findThreshold == sensorMap.end())
1043         {
1044             return ipmi::responseInvalidFieldRequest();
1045         }
1046         if (lowerNonCriticalThreshMask)
1047         {
1048             auto findLower = findThreshold->second.find("WarningLow");
1049             if (findLower == findThreshold->second.end())
1050             {
1051                 return ipmi::responseInvalidFieldRequest();
1052             }
1053             thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1054                                          findThreshold->first);
1055         }
1056         if (upperNonCriticalThreshMask)
1057         {
1058             auto findUpper = findThreshold->second.find("WarningHigh");
1059             if (findUpper == findThreshold->second.end())
1060             {
1061                 return ipmi::responseInvalidFieldRequest();
1062             }
1063             thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1064                                          findThreshold->first);
1065         }
1066     }
1067     for (const auto& property : thresholdsToSet)
1068     {
1069         // from section 36.3 in the IPMI Spec, assume all linear
1070         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1071                              (bValue * std::pow(10.0, bExp))) *
1072                             std::pow(10.0, rExp);
1073         setDbusProperty(
1074             *getSdBus(), connection, path, std::get<interface>(property),
1075             std::get<propertyName>(property), ipmi::Value(valueToSet));
1076     }
1077     return ipmi::responseSuccess();
1078 }
1079 
1080 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1081 {
1082     IPMIThresholds resp;
1083     auto warningInterface =
1084         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1085     auto criticalInterface =
1086         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1087 
1088     if ((warningInterface != sensorMap.end()) ||
1089         (criticalInterface != sensorMap.end()))
1090     {
1091         auto sensorPair = sensorMap.find(sensor::sensorInterface);
1092 
1093         if (sensorPair == sensorMap.end())
1094         {
1095             // should not have been able to find a sensor not implementing
1096             // the sensor object
1097             throw std::runtime_error("Invalid sensor map");
1098         }
1099 
1100         double max = 0;
1101         double min = 0;
1102         getSensorMaxMin(sensorMap, max, min);
1103 
1104         int16_t mValue = 0;
1105         int16_t bValue = 0;
1106         int8_t rExp = 0;
1107         int8_t bExp = 0;
1108         bool bSigned = false;
1109 
1110         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1111         {
1112             throw std::runtime_error("Invalid sensor atrributes");
1113         }
1114         if (warningInterface != sensorMap.end())
1115         {
1116             auto& warningMap = warningInterface->second;
1117 
1118             auto warningHigh = warningMap.find("WarningHigh");
1119             auto warningLow = warningMap.find("WarningLow");
1120 
1121             if (warningHigh != warningMap.end())
1122             {
1123 
1124                 double value =
1125                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
1126                 if (std::isfinite(value))
1127                 {
1128                     resp.warningHigh = scaleIPMIValueFromDouble(
1129                         value, mValue, rExp, bValue, bExp, bSigned);
1130                 }
1131             }
1132             if (warningLow != warningMap.end())
1133             {
1134                 double value =
1135                     std::visit(VariantToDoubleVisitor(), warningLow->second);
1136                 if (std::isfinite(value))
1137                 {
1138                     resp.warningLow = scaleIPMIValueFromDouble(
1139                         value, mValue, rExp, bValue, bExp, bSigned);
1140                 }
1141             }
1142         }
1143         if (criticalInterface != sensorMap.end())
1144         {
1145             auto& criticalMap = criticalInterface->second;
1146 
1147             auto criticalHigh = criticalMap.find("CriticalHigh");
1148             auto criticalLow = criticalMap.find("CriticalLow");
1149 
1150             if (criticalHigh != criticalMap.end())
1151             {
1152                 double value =
1153                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1154                 if (std::isfinite(value))
1155                 {
1156                     resp.criticalHigh = scaleIPMIValueFromDouble(
1157                         value, mValue, rExp, bValue, bExp, bSigned);
1158                 }
1159             }
1160             if (criticalLow != criticalMap.end())
1161             {
1162                 double value =
1163                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
1164                 if (std::isfinite(value))
1165                 {
1166                     resp.criticalLow = scaleIPMIValueFromDouble(
1167                         value, mValue, rExp, bValue, bExp, bSigned);
1168                 }
1169             }
1170         }
1171     }
1172     return resp;
1173 }
1174 
1175 ipmi::RspType<uint8_t, // readable
1176               uint8_t, // lowerNCrit
1177               uint8_t, // lowerCrit
1178               uint8_t, // lowerNrecoverable
1179               uint8_t, // upperNC
1180               uint8_t, // upperCrit
1181               uint8_t> // upperNRecoverable
1182     ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1183 {
1184     std::string connection;
1185     std::string path;
1186 
1187     if (sensorNumber == reservedSensorNumber)
1188     {
1189         return ipmi::responseInvalidFieldRequest();
1190     }
1191 
1192     auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1193     if (status)
1194     {
1195         return ipmi::response(status);
1196     }
1197 
1198     DbusInterfaceMap sensorMap;
1199     if (!getSensorMap(ctx, connection, path, sensorMap))
1200     {
1201         return ipmi::responseResponseError();
1202     }
1203 
1204     IPMIThresholds thresholdData;
1205     try
1206     {
1207         thresholdData = getIPMIThresholds(sensorMap);
1208     }
1209     catch (const std::exception&)
1210     {
1211         return ipmi::responseResponseError();
1212     }
1213 
1214     uint8_t readable = 0;
1215     uint8_t lowerNC = 0;
1216     uint8_t lowerCritical = 0;
1217     uint8_t lowerNonRecoverable = 0;
1218     uint8_t upperNC = 0;
1219     uint8_t upperCritical = 0;
1220     uint8_t upperNonRecoverable = 0;
1221 
1222     if (thresholdData.warningHigh)
1223     {
1224         readable |=
1225             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1226         upperNC = *thresholdData.warningHigh;
1227     }
1228     if (thresholdData.warningLow)
1229     {
1230         readable |=
1231             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1232         lowerNC = *thresholdData.warningLow;
1233     }
1234 
1235     if (thresholdData.criticalHigh)
1236     {
1237         readable |=
1238             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1239         upperCritical = *thresholdData.criticalHigh;
1240     }
1241     if (thresholdData.criticalLow)
1242     {
1243         readable |=
1244             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1245         lowerCritical = *thresholdData.criticalLow;
1246     }
1247 
1248     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1249                                  lowerNonRecoverable, upperNC, upperCritical,
1250                                  upperNonRecoverable);
1251 }
1252 
1253 /** @brief implements the get Sensor event enable command
1254  *  @param sensorNumber - sensor number
1255  *
1256  *  @returns IPMI completion code plus response data
1257  *   - enabled               - Sensor Event messages
1258  *   - assertionEnabledLsb   - Assertion event messages
1259  *   - assertionEnabledMsb   - Assertion event messages
1260  *   - deassertionEnabledLsb - Deassertion event messages
1261  *   - deassertionEnabledMsb - Deassertion event messages
1262  */
1263 
1264 ipmi::RspType<uint8_t, // enabled
1265               uint8_t, // assertionEnabledLsb
1266               uint8_t, // assertionEnabledMsb
1267               uint8_t, // deassertionEnabledLsb
1268               uint8_t> // deassertionEnabledMsb
1269     ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1270 {
1271     std::string connection;
1272     std::string path;
1273 
1274     uint8_t enabled = 0;
1275     uint8_t assertionEnabledLsb = 0;
1276     uint8_t assertionEnabledMsb = 0;
1277     uint8_t deassertionEnabledLsb = 0;
1278     uint8_t deassertionEnabledMsb = 0;
1279 
1280     if (sensorNum == reservedSensorNumber)
1281     {
1282         return ipmi::responseInvalidFieldRequest();
1283     }
1284 
1285     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1286     if (status)
1287     {
1288         return ipmi::response(status);
1289     }
1290 
1291 #ifdef FEATURE_HYBRID_SENSORS
1292     if (auto sensor = findStaticSensor(path);
1293         sensor != ipmi::sensor::sensors.end() &&
1294         getSensorEventTypeFromPath(path) !=
1295             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1296     {
1297         enabled = static_cast<uint8_t>(
1298             IPMISensorEventEnableByte2::sensorScanningEnable);
1299         uint16_t assertionEnabled = 0;
1300         for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1301                                       ->second.begin()
1302                                       ->second.second)
1303         {
1304             assertionEnabled |= (1 << offsetValMap.first);
1305         }
1306         assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1307         assertionEnabledMsb =
1308             static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1309 
1310         return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1311                                      assertionEnabledMsb, deassertionEnabledLsb,
1312                                      deassertionEnabledMsb);
1313     }
1314 #endif
1315 
1316     DbusInterfaceMap sensorMap;
1317     if (!getSensorMap(ctx, connection, path, sensorMap))
1318     {
1319         return ipmi::responseResponseError();
1320     }
1321 
1322     auto warningInterface =
1323         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1324     auto criticalInterface =
1325         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1326     if ((warningInterface != sensorMap.end()) ||
1327         (criticalInterface != sensorMap.end()))
1328     {
1329         enabled = static_cast<uint8_t>(
1330             IPMISensorEventEnableByte2::sensorScanningEnable);
1331         if (warningInterface != sensorMap.end())
1332         {
1333             auto& warningMap = warningInterface->second;
1334 
1335             auto warningHigh = warningMap.find("WarningHigh");
1336             auto warningLow = warningMap.find("WarningLow");
1337             if (warningHigh != warningMap.end())
1338             {
1339                 assertionEnabledLsb |= static_cast<uint8_t>(
1340                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1341                 deassertionEnabledLsb |= static_cast<uint8_t>(
1342                     IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1343             }
1344             if (warningLow != warningMap.end())
1345             {
1346                 assertionEnabledLsb |= static_cast<uint8_t>(
1347                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1348                 deassertionEnabledLsb |= static_cast<uint8_t>(
1349                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1350             }
1351         }
1352         if (criticalInterface != sensorMap.end())
1353         {
1354             auto& criticalMap = criticalInterface->second;
1355 
1356             auto criticalHigh = criticalMap.find("CriticalHigh");
1357             auto criticalLow = criticalMap.find("CriticalLow");
1358 
1359             if (criticalHigh != criticalMap.end())
1360             {
1361                 assertionEnabledMsb |= static_cast<uint8_t>(
1362                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1363                 deassertionEnabledMsb |= static_cast<uint8_t>(
1364                     IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1365             }
1366             if (criticalLow != criticalMap.end())
1367             {
1368                 assertionEnabledLsb |= static_cast<uint8_t>(
1369                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1370                 deassertionEnabledLsb |= static_cast<uint8_t>(
1371                     IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1372             }
1373         }
1374     }
1375 
1376     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1377                                  assertionEnabledMsb, deassertionEnabledLsb,
1378                                  deassertionEnabledMsb);
1379 }
1380 
1381 /** @brief implements the get Sensor event status command
1382  *  @param sensorNumber - sensor number, FFh = reserved
1383  *
1384  *  @returns IPMI completion code plus response data
1385  *   - sensorEventStatus - Sensor Event messages state
1386  *   - assertions        - Assertion event messages
1387  *   - deassertions      - Deassertion event messages
1388  */
1389 ipmi::RspType<uint8_t,         // sensorEventStatus
1390               std::bitset<16>, // assertions
1391               std::bitset<16>  // deassertion
1392               >
1393     ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1394 {
1395     if (sensorNum == reservedSensorNumber)
1396     {
1397         return ipmi::responseInvalidFieldRequest();
1398     }
1399 
1400     std::string connection;
1401     std::string path;
1402     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1403     if (status)
1404     {
1405         phosphor::logging::log<phosphor::logging::level::ERR>(
1406             "ipmiSenGetSensorEventStatus: Sensor connection Error",
1407             phosphor::logging::entry("SENSOR=%d", sensorNum));
1408         return ipmi::response(status);
1409     }
1410 
1411 #ifdef FEATURE_HYBRID_SENSORS
1412     if (auto sensor = findStaticSensor(path);
1413         sensor != ipmi::sensor::sensors.end() &&
1414         getSensorEventTypeFromPath(path) !=
1415             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1416     {
1417         auto response = ipmi::sensor::get::mapDbusToAssertion(
1418             sensor->second, path, sensor->second.sensorInterface);
1419         std::bitset<16> assertions;
1420         // deassertions are not used.
1421         std::bitset<16> deassertions = 0;
1422         uint8_t sensorEventStatus;
1423         if (response.readingOrStateUnavailable)
1424         {
1425             sensorEventStatus |= static_cast<uint8_t>(
1426                 IPMISensorReadingByte2::readingStateUnavailable);
1427         }
1428         if (response.scanningEnabled)
1429         {
1430             sensorEventStatus |= static_cast<uint8_t>(
1431                 IPMISensorReadingByte2::sensorScanningEnable);
1432         }
1433         if (response.allEventMessagesEnabled)
1434         {
1435             sensorEventStatus |= static_cast<uint8_t>(
1436                 IPMISensorReadingByte2::eventMessagesEnable);
1437         }
1438         assertions |= response.discreteReadingSensorStates << 8;
1439         assertions |= response.thresholdLevelsStates;
1440         return ipmi::responseSuccess(sensorEventStatus, assertions,
1441                                      deassertions);
1442     }
1443 #endif
1444 
1445     DbusInterfaceMap sensorMap;
1446     if (!getSensorMap(ctx, connection, path, sensorMap))
1447     {
1448         phosphor::logging::log<phosphor::logging::level::ERR>(
1449             "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1450             phosphor::logging::entry("SENSOR=%s", path.c_str()));
1451         return ipmi::responseResponseError();
1452     }
1453 
1454     uint8_t sensorEventStatus =
1455         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1456     std::bitset<16> assertions = 0;
1457     std::bitset<16> deassertions = 0;
1458 
1459     // handle VR typed sensor
1460     auto vrInterface = sensorMap.find(sensor::vrInterface);
1461     if (vrInterface != sensorMap.end())
1462     {
1463         if (!sensor::getVrEventStatus(ctx, connection, path,
1464                                       vrInterface->second, assertions))
1465         {
1466             return ipmi::responseResponseError();
1467         }
1468 
1469         // both Event Message and Sensor Scanning are disable for VR.
1470         sensorEventStatus = 0;
1471         return ipmi::responseSuccess(sensorEventStatus, assertions,
1472                                      deassertions);
1473     }
1474 
1475     auto warningInterface =
1476         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1477     auto criticalInterface =
1478         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1479 
1480     std::optional<bool> criticalDeassertHigh =
1481         thresholdDeassertMap[path]["CriticalAlarmHigh"];
1482     std::optional<bool> criticalDeassertLow =
1483         thresholdDeassertMap[path]["CriticalAlarmLow"];
1484     std::optional<bool> warningDeassertHigh =
1485         thresholdDeassertMap[path]["WarningAlarmHigh"];
1486     std::optional<bool> warningDeassertLow =
1487         thresholdDeassertMap[path]["WarningAlarmLow"];
1488 
1489     if (criticalDeassertHigh && !*criticalDeassertHigh)
1490     {
1491         deassertions.set(static_cast<size_t>(
1492             IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1493     }
1494     if (criticalDeassertLow && !*criticalDeassertLow)
1495     {
1496         deassertions.set(static_cast<size_t>(
1497             IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1498     }
1499     if (warningDeassertHigh && !*warningDeassertHigh)
1500     {
1501         deassertions.set(static_cast<size_t>(
1502             IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1503     }
1504     if (warningDeassertLow && !*warningDeassertLow)
1505     {
1506         deassertions.set(static_cast<size_t>(
1507             IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1508     }
1509     if ((warningInterface != sensorMap.end()) ||
1510         (criticalInterface != sensorMap.end()))
1511     {
1512         sensorEventStatus = static_cast<size_t>(
1513             IPMISensorEventEnableByte2::eventMessagesEnable);
1514         if (warningInterface != sensorMap.end())
1515         {
1516             auto& warningMap = warningInterface->second;
1517 
1518             auto warningHigh = warningMap.find("WarningAlarmHigh");
1519             auto warningLow = warningMap.find("WarningAlarmLow");
1520             auto warningHighAlarm = false;
1521             auto warningLowAlarm = false;
1522 
1523             if (warningHigh != warningMap.end())
1524             {
1525                 warningHighAlarm = std::get<bool>(warningHigh->second);
1526             }
1527             if (warningLow != warningMap.end())
1528             {
1529                 warningLowAlarm = std::get<bool>(warningLow->second);
1530             }
1531             if (warningHighAlarm)
1532             {
1533                 assertions.set(
1534                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1535                                             upperNonCriticalGoingHigh));
1536             }
1537             if (warningLowAlarm)
1538             {
1539                 assertions.set(
1540                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1541                                             lowerNonCriticalGoingLow));
1542             }
1543         }
1544         if (criticalInterface != sensorMap.end())
1545         {
1546             auto& criticalMap = criticalInterface->second;
1547 
1548             auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1549             auto criticalLow = criticalMap.find("CriticalAlarmLow");
1550             auto criticalHighAlarm = false;
1551             auto criticalLowAlarm = false;
1552 
1553             if (criticalHigh != criticalMap.end())
1554             {
1555                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1556             }
1557             if (criticalLow != criticalMap.end())
1558             {
1559                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1560             }
1561             if (criticalHighAlarm)
1562             {
1563                 assertions.set(
1564                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1565                                             upperCriticalGoingHigh));
1566             }
1567             if (criticalLowAlarm)
1568             {
1569                 assertions.set(static_cast<size_t>(
1570                     IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1571             }
1572         }
1573     }
1574 
1575     return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1576 }
1577 
1578 // Construct a type 1 SDR for threshold sensor.
1579 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1580                                  get_sdr::SensorDataFullRecord& record)
1581 {
1582     get_sdr::header::set_record_id(
1583         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1584 
1585     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1586     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1587 
1588     record.header.sdr_version = ipmiSdrVersion;
1589     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1590     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1591                                   sizeof(get_sdr::SensorDataRecordHeader);
1592     record.key.owner_id = bmcI2CAddr;
1593     record.key.owner_lun = lun;
1594     record.key.sensor_number = sensornumber;
1595 }
1596 bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1597                         uint16_t recordID, const std::string& service,
1598                         const std::string& path,
1599                         get_sdr::SensorDataFullRecord& record)
1600 {
1601     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1602     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1603 
1604     DbusInterfaceMap sensorMap;
1605     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1606     {
1607         phosphor::logging::log<phosphor::logging::level::ERR>(
1608             "Failed to update sensor map for threshold sensor",
1609             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1610             phosphor::logging::entry("PATH=%s", path.c_str()));
1611         return false;
1612     }
1613 
1614     record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1615     record.body.sensor_type = getSensorTypeFromPath(path);
1616     std::string type = getSensorTypeStringFromPath(path);
1617     auto typeCstr = type.c_str();
1618     auto findUnits = sensorUnits.find(typeCstr);
1619     if (findUnits != sensorUnits.end())
1620     {
1621         record.body.sensor_units_2_base =
1622             static_cast<uint8_t>(findUnits->second);
1623     } // else default 0x0 unspecified
1624 
1625     record.body.event_reading_type = getSensorEventTypeFromPath(path);
1626 
1627     auto sensorObject = sensorMap.find(sensor::sensorInterface);
1628     if (sensorObject == sensorMap.end())
1629     {
1630         phosphor::logging::log<phosphor::logging::level::ERR>(
1631             "getSensorDataRecord: sensorObject error");
1632         return false;
1633     }
1634 
1635     uint8_t entityId = 0;
1636     uint8_t entityInstance = 0x01;
1637 
1638     // follow the association chain to get the parent board's entityid and
1639     // entityInstance
1640     updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1641 
1642     record.body.entity_id = entityId;
1643     record.body.entity_instance = entityInstance;
1644 
1645     double max = 0;
1646     double min = 0;
1647     getSensorMaxMin(sensorMap, max, min);
1648 
1649     int16_t mValue = 0;
1650     int8_t rExp = 0;
1651     int16_t bValue = 0;
1652     int8_t bExp = 0;
1653     bool bSigned = false;
1654 
1655     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1656     {
1657         phosphor::logging::log<phosphor::logging::level::ERR>(
1658             "getSensorDataRecord: getSensorAttributes error");
1659         return false;
1660     }
1661 
1662     // The record.body is a struct SensorDataFullRecordBody
1663     // from sensorhandler.hpp in phosphor-ipmi-host.
1664     // The meaning of these bits appears to come from
1665     // table 43.1 of the IPMI spec.
1666     // The above 5 sensor attributes are stuffed in as follows:
1667     // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1668     // Byte 22-24 are for other purposes
1669     // Byte 25 = MMMMMMMM = LSB of M
1670     // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1671     // Byte 27 = BBBBBBBB = LSB of B
1672     // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1673     // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1674     // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1675 
1676     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1677     record.body.m_lsb = mValue & 0xFF;
1678 
1679     uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1680     uint8_t mBitNine = (mValue & 0x0100) >> 8;
1681 
1682     // move the smallest bit of the MSB into place (bit 9)
1683     // the MSbs are bits 7:8 in m_msb_and_tolerance
1684     record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1685 
1686     record.body.b_lsb = bValue & 0xFF;
1687 
1688     uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1689     uint8_t bBitNine = (bValue & 0x0100) >> 8;
1690 
1691     // move the smallest bit of the MSB into place (bit 9)
1692     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1693     record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1694 
1695     uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1696     uint8_t rExpBits = rExp & 0x07;
1697 
1698     uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1699     uint8_t bExpBits = bExp & 0x07;
1700 
1701     // move rExp and bExp into place
1702     record.body.r_b_exponents =
1703         (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1704 
1705     // Set the analog reading byte interpretation accordingly
1706     record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1707 
1708     // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1709     // These seem redundant, but derivable from the above 5 attributes
1710     // Original comment said "todo fill out rest of units"
1711 
1712     // populate sensor name from path
1713     auto name = sensor::parseSdrIdFromPath(path);
1714     record.body.id_string_info = name.size();
1715     std::strncpy(record.body.id_string, name.c_str(),
1716                  sizeof(record.body.id_string));
1717 
1718     // Remember the sensor name, as determined for this sensor number
1719     details::sdrStatsTable.updateName(sensornumber, name);
1720 
1721     bool sensorSettable = false;
1722     auto mutability =
1723         sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1724     if (mutability != sensorMap.end())
1725     {
1726         sensorSettable =
1727             mappedVariant<bool>(mutability->second, "Mutable", false);
1728     }
1729     get_sdr::body::init_settable_state(sensorSettable, &record.body);
1730 
1731     // Grant write permission to sensors deemed externally settable
1732     details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
1733 
1734     IPMIThresholds thresholdData;
1735     try
1736     {
1737         thresholdData = getIPMIThresholds(sensorMap);
1738     }
1739     catch (const std::exception&)
1740     {
1741         phosphor::logging::log<phosphor::logging::level::ERR>(
1742             "getSensorDataRecord: getIPMIThresholds error");
1743         return false;
1744     }
1745 
1746     if (thresholdData.criticalHigh)
1747     {
1748         record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1749         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1750             IPMISensorEventEnableThresholds::criticalThreshold);
1751         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1752             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1753         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1754             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1755         record.body.discrete_reading_setting_mask[0] |=
1756             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1757     }
1758     if (thresholdData.warningHigh)
1759     {
1760         record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1761         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1762             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1763         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1764             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1765         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1766             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1767         record.body.discrete_reading_setting_mask[0] |=
1768             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1769     }
1770     if (thresholdData.criticalLow)
1771     {
1772         record.body.lower_critical_threshold = *thresholdData.criticalLow;
1773         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1774             IPMISensorEventEnableThresholds::criticalThreshold);
1775         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1776             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1777         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1778             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1779         record.body.discrete_reading_setting_mask[0] |=
1780             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1781     }
1782     if (thresholdData.warningLow)
1783     {
1784         record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1785         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1786             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1787         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1788             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1789         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1790             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1791         record.body.discrete_reading_setting_mask[0] |=
1792             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1793     }
1794 
1795     // everything that is readable is setable
1796     record.body.discrete_reading_setting_mask[1] =
1797         record.body.discrete_reading_setting_mask[0];
1798     return true;
1799 }
1800 
1801 #ifdef FEATURE_HYBRID_SENSORS
1802 // Construct a type 1 SDR for discrete Sensor typed sensor.
1803 void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1804                               uint16_t recordID,
1805                               ipmi::sensor::IdInfoMap::const_iterator sensor,
1806                               get_sdr::SensorDataFullRecord& record)
1807 {
1808     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1809 
1810     record.body.entity_id = sensor->second.entityType;
1811     record.body.sensor_type = sensor->second.sensorType;
1812     record.body.event_reading_type = sensor->second.sensorReadingType;
1813     record.body.entity_instance = sensor->second.instance;
1814     if (ipmi::sensor::Mutability::Write ==
1815         (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1816     {
1817         get_sdr::body::init_settable_state(true, &(record.body));
1818     }
1819 
1820     auto id_string = sensor->second.sensorName;
1821 
1822     if (id_string.empty())
1823     {
1824         id_string = sensor->second.sensorNameFunc(sensor->second);
1825     }
1826 
1827     if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1828     {
1829         get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1830                                      &(record.body));
1831     }
1832     else
1833     {
1834         get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1835     }
1836     std::strncpy(record.body.id_string, id_string.c_str(),
1837                  get_sdr::body::get_id_strlen(&(record.body)));
1838 }
1839 #endif
1840 
1841 // Construct type 3 SDR header and key (for VR and other discrete sensors)
1842 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1843                                 get_sdr::SensorDataEventRecord& record)
1844 {
1845     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1846     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1847 
1848     get_sdr::header::set_record_id(
1849         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1850 
1851     record.header.sdr_version = ipmiSdrVersion;
1852     record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1853     record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1854                                   sizeof(get_sdr::SensorDataRecordHeader);
1855     record.key.owner_id = bmcI2CAddr;
1856     record.key.owner_lun = lun;
1857     record.key.sensor_number = sensornumber;
1858 
1859     record.body.entity_id = 0x00;
1860     record.body.entity_instance = 0x01;
1861 }
1862 
1863 // Construct a type 3 SDR for VR typed sensor(daemon).
1864 bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1865                     uint16_t recordID, const std::string& service,
1866                     const std::string& path,
1867                     get_sdr::SensorDataEventRecord& record)
1868 {
1869     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1870     constructEventSdrHeaderKey(sensorNum, recordID, record);
1871 
1872     DbusInterfaceMap sensorMap;
1873     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1874     {
1875         phosphor::logging::log<phosphor::logging::level::ERR>(
1876             "Failed to update sensor map for VR sensor",
1877             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1878             phosphor::logging::entry("PATH=%s", path.c_str()));
1879         return false;
1880     }
1881     // follow the association chain to get the parent board's entityid and
1882     // entityInstance
1883     updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1884                               record.body.entity_instance);
1885 
1886     // Sensor type is hardcoded as a module/board type instead of parsing from
1887     // sensor path. This is because VR control is allocated in an independent
1888     // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1889     // types.
1890     static constexpr const uint8_t module_board_type = 0x15;
1891     record.body.sensor_type = module_board_type;
1892     record.body.event_reading_type = 0x00;
1893 
1894     record.body.sensor_record_sharing_1 = 0x00;
1895     record.body.sensor_record_sharing_2 = 0x00;
1896 
1897     // populate sensor name from path
1898     auto name = sensor::parseSdrIdFromPath(path);
1899     int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1900     record.body.id_string_info = nameSize;
1901     std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1902     std::memcpy(record.body.id_string, name.c_str(), nameSize);
1903 
1904     // Remember the sensor name, as determined for this sensor number
1905     details::sdrStatsTable.updateName(sensornumber, name);
1906 
1907     return true;
1908 }
1909 
1910 static inline uint16_t getNumberOfSensors()
1911 {
1912     return std::min(getSensorTree().size(), maxIPMISensors);
1913 }
1914 
1915 static int
1916     getSensorDataRecord(ipmi::Context::ptr ctx,
1917                         std::vector<uint8_t>& recordData, uint16_t recordID,
1918                         uint8_t readBytes = std::numeric_limits<uint8_t>::max())
1919 {
1920     size_t fruCount = 0;
1921     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1922     if (ret != ipmi::ccSuccess)
1923     {
1924         phosphor::logging::log<phosphor::logging::level::ERR>(
1925             "getSensorDataRecord: getFruSdrCount error");
1926         return GENERAL_ERROR;
1927     }
1928 
1929     const auto& entityRecords =
1930         ipmi::sensor::EntityInfoMapContainer::getContainer()
1931             ->getIpmiEntityRecords();
1932     size_t entityCount = entityRecords.size();
1933 
1934     size_t lastRecord = getNumberOfSensors() + fruCount +
1935                         ipmi::storage::type12Count + entityCount - 1;
1936     if (recordID == lastRecordIndex)
1937     {
1938         recordID = lastRecord;
1939     }
1940     if (recordID > lastRecord)
1941     {
1942         phosphor::logging::log<phosphor::logging::level::ERR>(
1943             "getSensorDataRecord: recordID > lastRecord error");
1944         return GENERAL_ERROR;
1945     }
1946 
1947     if (recordID >= getNumberOfSensors())
1948     {
1949         size_t sdrIndex = recordID - getNumberOfSensors();
1950 
1951         if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1952         {
1953             // handle type 8 entity map records
1954             ipmi::sensor::EntityInfoMap::const_iterator entity =
1955                 entityRecords.find(static_cast<uint8_t>(
1956                     sdrIndex - fruCount - ipmi::storage::type12Count));
1957             if (entity == entityRecords.end())
1958             {
1959                 return IPMI_CC_SENSOR_INVALID;
1960             }
1961             recordData = ipmi::storage::getType8SDRs(entity, recordID);
1962         }
1963         else if (sdrIndex >= fruCount)
1964         {
1965             // handle type 12 hardcoded records
1966             size_t type12Index = sdrIndex - fruCount;
1967             if (type12Index >= ipmi::storage::type12Count)
1968             {
1969                 phosphor::logging::log<phosphor::logging::level::ERR>(
1970                     "getSensorDataRecord: type12Index error");
1971                 return GENERAL_ERROR;
1972             }
1973             recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1974         }
1975         else
1976         {
1977             // handle fru records
1978             get_sdr::SensorDataFruRecord data;
1979             ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
1980             if (ret != IPMI_CC_OK)
1981             {
1982                 return GENERAL_ERROR;
1983             }
1984             data.header.record_id_msb = recordID >> 8;
1985             data.header.record_id_lsb = recordID & 0xFF;
1986             recordData.insert(recordData.end(), (uint8_t*)&data,
1987                               ((uint8_t*)&data) + sizeof(data));
1988         }
1989 
1990         return 0;
1991     }
1992 
1993     // Perform a incremental scan of the SDR Record ID's and translate the
1994     // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1995     // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1996     // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
1997     // which has special meaning.
1998     std::string connection;
1999     std::string path;
2000     std::vector<std::string> interfaces;
2001     uint16_t sensNumFromRecID{recordID};
2002     if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
2003     {
2004         // LUN 0 has one reserved sensor number. Compensate here by adding one
2005         // to the record ID
2006         sensNumFromRecID = recordID + 1;
2007         ctx->lun = 1;
2008     }
2009     else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
2010     {
2011         // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2012         // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2013         // rules governing its use.
2014         sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
2015         ctx->lun = 3;
2016     }
2017 
2018     auto status =
2019         getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2020                             connection, path, &interfaces);
2021     if (status)
2022     {
2023         phosphor::logging::log<phosphor::logging::level::ERR>(
2024             "getSensorDataRecord: getSensorConnection error");
2025         return GENERAL_ERROR;
2026     }
2027     uint16_t sensorNum = getSensorNumberFromPath(path);
2028     // Return an error on LUN 2 assingments, and any sensor number beyond the
2029     // range of LUN 3
2030     if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2031         (sensorNum > lun3MaxSensorNum))
2032     {
2033         phosphor::logging::log<phosphor::logging::level::ERR>(
2034             "getSensorDataRecord: invalidSensorNumber");
2035         return GENERAL_ERROR;
2036     }
2037     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2038     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2039 
2040     if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2041         (lun != ctx->lun))
2042     {
2043         phosphor::logging::log<phosphor::logging::level::ERR>(
2044             "getSensorDataRecord: sensor record mismatch");
2045         return GENERAL_ERROR;
2046     }
2047 
2048     // Construct full record (SDR type 1) for the threshold sensors
2049     if (std::find(interfaces.begin(), interfaces.end(),
2050                   sensor::sensorInterface) != interfaces.end())
2051     {
2052         get_sdr::SensorDataFullRecord record = {0};
2053 
2054         // If the request doesn't read SDR body, construct only header and key
2055         // part to avoid additional DBus transaction.
2056         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2057         {
2058             constructSensorSdrHeaderKey(sensorNum, recordID, record);
2059         }
2060         else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2061                                      record))
2062         {
2063             return GENERAL_ERROR;
2064         }
2065 
2066         recordData.insert(recordData.end(), (uint8_t*)&record,
2067                           ((uint8_t*)&record) + sizeof(record));
2068 
2069         return 0;
2070     }
2071 
2072 #ifdef FEATURE_HYBRID_SENSORS
2073     if (auto sensor = findStaticSensor(path);
2074         sensor != ipmi::sensor::sensors.end() &&
2075         getSensorEventTypeFromPath(path) !=
2076             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2077     {
2078         get_sdr::SensorDataFullRecord record = {0};
2079 
2080         // If the request doesn't read SDR body, construct only header and key
2081         // part to avoid additional DBus transaction.
2082         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2083         {
2084             constructSensorSdrHeaderKey(sensorNum, recordID, record);
2085         }
2086         else
2087         {
2088             constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2089         }
2090 
2091         recordData.insert(recordData.end(), (uint8_t*)&record,
2092                           ((uint8_t*)&record) + sizeof(record));
2093 
2094         return 0;
2095     }
2096 #endif
2097 
2098     // Contruct SDR type 3 record for VR sensor (daemon)
2099     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2100         interfaces.end())
2101     {
2102         get_sdr::SensorDataEventRecord record = {0};
2103 
2104         // If the request doesn't read SDR body, construct only header and key
2105         // part to avoid additional DBus transaction.
2106         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2107         {
2108             constructEventSdrHeaderKey(sensorNum, recordID, record);
2109         }
2110         else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2111                                  record))
2112         {
2113             return GENERAL_ERROR;
2114         }
2115         recordData.insert(recordData.end(), (uint8_t*)&record,
2116                           ((uint8_t*)&record) + sizeof(record));
2117     }
2118 
2119     return 0;
2120 }
2121 
2122 /** @brief implements the get SDR Info command
2123  *  @param count - Operation
2124  *
2125  *  @returns IPMI completion code plus response data
2126  *   - sdrCount - sensor/SDR count
2127  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2128  */
2129 static ipmi::RspType<uint8_t, // respcount
2130                      uint8_t, // dynamic population flags
2131                      uint32_t // last time a sensor was added
2132                      >
2133     ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2134                                std::optional<uint8_t> count)
2135 {
2136     auto& sensorTree = getSensorTree();
2137     uint8_t sdrCount = 0;
2138     uint16_t recordID = 0;
2139     std::vector<uint8_t> record;
2140     // Sensors are dynamically allocated, and there is at least one LUN
2141     uint8_t lunsAndDynamicPopulation = 0x80;
2142     constexpr uint8_t getSdrCount = 0x01;
2143     constexpr uint8_t getSensorCount = 0x00;
2144 
2145     if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2146     {
2147         return ipmi::responseResponseError();
2148     }
2149     uint16_t numSensors = getNumberOfSensors();
2150     if (count.value_or(0) == getSdrCount)
2151     {
2152         // Count the number of Type 1 SDR entries assigned to the LUN
2153         while (!getSensorDataRecord(ctx, record, recordID++))
2154         {
2155             get_sdr::SensorDataRecordHeader* hdr =
2156                 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
2157                     record.data());
2158             if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2159             {
2160                 get_sdr::SensorDataFullRecord* recordData =
2161                     reinterpret_cast<get_sdr::SensorDataFullRecord*>(
2162                         record.data());
2163                 if (ctx->lun == recordData->key.owner_lun)
2164                 {
2165                     sdrCount++;
2166                 }
2167             }
2168         }
2169     }
2170     else if (count.value_or(0) == getSensorCount)
2171     {
2172         // Return the number of sensors attached to the LUN
2173         if ((ctx->lun == 0) && (numSensors > 0))
2174         {
2175             sdrCount =
2176                 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2177         }
2178         else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2179         {
2180             sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2181                            ? maxSensorsPerLUN
2182                            : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2183         }
2184         else if (ctx->lun == 3)
2185         {
2186             if (numSensors <= maxIPMISensors)
2187             {
2188                 sdrCount =
2189                     (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2190             }
2191             else
2192             {
2193                 // error
2194                 throw std::out_of_range(
2195                     "Maximum number of IPMI sensors exceeded.");
2196             }
2197         }
2198     }
2199     else
2200     {
2201         return ipmi::responseInvalidFieldRequest();
2202     }
2203 
2204     // Get Sensor count. This returns the number of sensors
2205     if (numSensors > 0)
2206     {
2207         lunsAndDynamicPopulation |= 1;
2208     }
2209     if (numSensors > maxSensorsPerLUN)
2210     {
2211         lunsAndDynamicPopulation |= 2;
2212     }
2213     if (numSensors >= (maxSensorsPerLUN * 2))
2214     {
2215         lunsAndDynamicPopulation |= 8;
2216     }
2217     if (numSensors > maxIPMISensors)
2218     {
2219         // error
2220         throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2221     }
2222 
2223     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2224                                  sdrLastAdd);
2225 }
2226 
2227 /* end sensor commands */
2228 
2229 /* storage commands */
2230 
2231 ipmi::RspType<uint8_t,  // sdr version
2232               uint16_t, // record count
2233               uint16_t, // free space
2234               uint32_t, // most recent addition
2235               uint32_t, // most recent erase
2236               uint8_t   // operationSupport
2237               >
2238     ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2239 {
2240     auto& sensorTree = getSensorTree();
2241     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
2242     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2243     {
2244         return ipmi::responseResponseError();
2245     }
2246 
2247     size_t fruCount = 0;
2248     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2249     if (ret != ipmi::ccSuccess)
2250     {
2251         return ipmi::response(ret);
2252     }
2253 
2254     uint16_t recordCount =
2255         getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
2256 
2257     uint8_t operationSupport = static_cast<uint8_t>(
2258         SdrRepositoryInfoOps::overflow); // write not supported
2259 
2260     operationSupport |=
2261         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2262     operationSupport |= static_cast<uint8_t>(
2263         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2264     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2265                                  unspecifiedFreeSpace, sdrLastAdd,
2266                                  sdrLastRemove, operationSupport);
2267 }
2268 
2269 /** @brief implements the get SDR allocation info command
2270  *
2271  *  @returns IPMI completion code plus response data
2272  *   - allocUnits    - Number of possible allocation units
2273  *   - allocUnitSize - Allocation unit size in bytes.
2274  *   - allocUnitFree - Number of free allocation units
2275  *   - allocUnitLargestFree - Largest free block in allocation units
2276  *   - maxRecordSize    - Maximum record size in allocation units.
2277  */
2278 ipmi::RspType<uint16_t, // allocUnits
2279               uint16_t, // allocUnitSize
2280               uint16_t, // allocUnitFree
2281               uint16_t, // allocUnitLargestFree
2282               uint8_t   // maxRecordSize
2283               >
2284     ipmiStorageGetSDRAllocationInfo()
2285 {
2286     // 0000h unspecified number of alloc units
2287     constexpr uint16_t allocUnits = 0;
2288 
2289     constexpr uint16_t allocUnitFree = 0;
2290     constexpr uint16_t allocUnitLargestFree = 0;
2291     // only allow one block at a time
2292     constexpr uint8_t maxRecordSize = 1;
2293 
2294     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2295                                  allocUnitLargestFree, maxRecordSize);
2296 }
2297 
2298 /** @brief implements the reserve SDR command
2299  *  @returns IPMI completion code plus response data
2300  *   - sdrReservationID
2301  */
2302 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2303 {
2304     sdrReservationID++;
2305     if (sdrReservationID == 0)
2306     {
2307         sdrReservationID++;
2308     }
2309 
2310     return ipmi::responseSuccess(sdrReservationID);
2311 }
2312 
2313 ipmi::RspType<uint16_t,            // next record ID
2314               std::vector<uint8_t> // payload
2315               >
2316     ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2317                       uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2318 {
2319     size_t fruCount = 0;
2320     // reservation required for partial reads with non zero offset into
2321     // record
2322     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2323     {
2324         phosphor::logging::log<phosphor::logging::level::ERR>(
2325             "ipmiStorageGetSDR: responseInvalidReservationId");
2326         return ipmi::responseInvalidReservationId();
2327     }
2328     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2329     if (ret != ipmi::ccSuccess)
2330     {
2331         phosphor::logging::log<phosphor::logging::level::ERR>(
2332             "ipmiStorageGetSDR: getFruSdrCount error");
2333         return ipmi::response(ret);
2334     }
2335 
2336     const auto& entityRecords =
2337         ipmi::sensor::EntityInfoMapContainer::getContainer()
2338             ->getIpmiEntityRecords();
2339     int entityCount = entityRecords.size();
2340 
2341     auto& sensorTree = getSensorTree();
2342     size_t lastRecord = getNumberOfSensors() + fruCount +
2343                         ipmi::storage::type12Count + entityCount - 1;
2344     uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2345 
2346     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2347     {
2348         phosphor::logging::log<phosphor::logging::level::ERR>(
2349             "ipmiStorageGetSDR: getSensorSubtree error");
2350         return ipmi::responseResponseError();
2351     }
2352 
2353     std::vector<uint8_t> record;
2354     if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
2355     {
2356         phosphor::logging::log<phosphor::logging::level::ERR>(
2357             "ipmiStorageGetSDR: fail to get SDR");
2358         return ipmi::responseInvalidFieldRequest();
2359     }
2360     get_sdr::SensorDataRecordHeader* hdr =
2361         reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
2362     if (!hdr)
2363     {
2364         phosphor::logging::log<phosphor::logging::level::ERR>(
2365             "ipmiStorageGetSDR: record header is null");
2366         return ipmi::responseSuccess(nextRecordId, record);
2367     }
2368 
2369     size_t sdrLength =
2370         sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2371     if (sdrLength < (offset + bytesToRead))
2372     {
2373         bytesToRead = sdrLength - offset;
2374     }
2375 
2376     uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2377     if (!respStart)
2378     {
2379         phosphor::logging::log<phosphor::logging::level::ERR>(
2380             "ipmiStorageGetSDR: record is null");
2381         return ipmi::responseSuccess(nextRecordId, record);
2382     }
2383 
2384     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
2385 
2386     return ipmi::responseSuccess(nextRecordId, recordData);
2387 }
2388 /* end storage commands */
2389 
2390 void registerSensorFunctions()
2391 {
2392     // <Platform Event>
2393     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2394                           ipmi::sensor_event::cmdPlatformEvent,
2395                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2396 
2397     // <Set Sensor Reading and Event Status>
2398     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2399                           ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2400                           ipmi::Privilege::Operator, ipmiSetSensorReading);
2401 
2402     // <Get Sensor Reading>
2403     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2404                           ipmi::sensor_event::cmdGetSensorReading,
2405                           ipmi::Privilege::User, ipmiSenGetSensorReading);
2406 
2407     // <Get Sensor Threshold>
2408     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2409                           ipmi::sensor_event::cmdGetSensorThreshold,
2410                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2411 
2412     // <Set Sensor Threshold>
2413     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2414                           ipmi::sensor_event::cmdSetSensorThreshold,
2415                           ipmi::Privilege::Operator,
2416                           ipmiSenSetSensorThresholds);
2417 
2418     // <Get Sensor Event Enable>
2419     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2420                           ipmi::sensor_event::cmdGetSensorEventEnable,
2421                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2422 
2423     // <Get Sensor Event Status>
2424     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2425                           ipmi::sensor_event::cmdGetSensorEventStatus,
2426                           ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2427 
2428     // register all storage commands for both Sensor and Storage command
2429     // versions
2430 
2431     // <Get SDR Repository Info>
2432     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2433                           ipmi::storage::cmdGetSdrRepositoryInfo,
2434                           ipmi::Privilege::User,
2435                           ipmiStorageGetSDRRepositoryInfo);
2436 
2437     // <Get Device SDR Info>
2438     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2439                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
2440                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2441 
2442     // <Get SDR Allocation Info>
2443     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2444                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2445                           ipmi::Privilege::User,
2446                           ipmiStorageGetSDRAllocationInfo);
2447 
2448     // <Reserve SDR Repo>
2449     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2450                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2451                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2452 
2453     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2454                           ipmi::storage::cmdReserveSdrRepository,
2455                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2456 
2457     // <Get Sdr>
2458     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2459                           ipmi::sensor_event::cmdGetDeviceSdr,
2460                           ipmi::Privilege::User, ipmiStorageGetSDR);
2461 
2462     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2463                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2464                           ipmiStorageGetSDR);
2465 }
2466 } // namespace ipmi
2467