blob: bebb4827494948c4952dbfdf5db24954f45eea1b [file] [log] [blame]
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001/*
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
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070017#include <boost/algorithm/string.hpp>
18#include <boost/container/flat_map.hpp>
19#include <chrono>
20#include <cmath>
21#include <commandutils.hpp>
22#include <iostream>
Jason M. Bills99b78ec2019-01-18 10:42:18 -080023#include <ipmi_to_redfish_hooks.hpp>
James Feist2a265d52019-04-08 11:16:27 -070024#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070025#include <ipmid/utils.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070026#include <phosphor-logging/log.hpp>
27#include <sdbusplus/bus.hpp>
28#include <sdrutils.hpp>
29#include <sensorcommands.hpp>
30#include <sensorutils.hpp>
31#include <storagecommands.hpp>
32#include <string>
33
34namespace ipmi
35{
36using ManagedObjectType =
37 std::map<sdbusplus::message::object_path,
38 std::map<std::string, std::map<std::string, DbusVariant>>>;
39
40using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
41
42static constexpr int sensorListUpdatePeriod = 10;
43static constexpr int sensorMapUpdatePeriod = 2;
44
45constexpr size_t maxSDRTotalSize =
46 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
47constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
48
49static uint16_t sdrReservationID;
50static uint32_t sdrLastAdd = noTimestamp;
51static uint32_t sdrLastRemove = noTimestamp;
52
Richard Marian Thomaiyar01fbcb52018-11-19 22:04:34 +053053SensorSubTree sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070054static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
55
Jason M. Bills17add592018-11-12 14:30:12 -080056// Specify the comparison required to sort and find char* map objects
57struct CmpStr
58{
59 bool operator()(const char *a, const char *b) const
60 {
61 return std::strcmp(a, b) < 0;
62 }
63};
64const static boost::container::flat_map<const char *, SensorUnits, CmpStr>
65 sensorUnits{{{"temperature", SensorUnits::degreesC},
66 {"voltage", SensorUnits::volts},
67 {"current", SensorUnits::amps},
68 {"fan_tach", SensorUnits::rpm},
69 {"power", SensorUnits::watts}}};
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070070
71void registerSensorFunctions() __attribute__((constructor));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070072
73static sdbusplus::bus::match::match sensorAdded(
Vernon Mauery15419dd2019-05-24 09:40:30 -070074 *getSdBus(),
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070075 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
76 "sensors/'",
77 [](sdbusplus::message::message &m) {
78 sensorTree.clear();
79 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
80 std::chrono::system_clock::now().time_since_epoch())
81 .count();
82 });
83
84static sdbusplus::bus::match::match sensorRemoved(
Vernon Mauery15419dd2019-05-24 09:40:30 -070085 *getSdBus(),
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070086 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
87 "sensors/'",
88 [](sdbusplus::message::message &m) {
89 sensorTree.clear();
90 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
91 std::chrono::system_clock::now().time_since_epoch())
92 .count();
93 });
94
James Feist392786a2019-03-19 13:36:10 -070095// this keeps track of deassertions for sensor event status command. A
96// deasertion can only happen if an assertion was seen first.
97static boost::container::flat_map<
98 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
99 thresholdDeassertMap;
100
101static sdbusplus::bus::match::match thresholdChanged(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700102 *getSdBus(),
James Feist392786a2019-03-19 13:36:10 -0700103 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
104 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
105 [](sdbusplus::message::message &m) {
106 boost::container::flat_map<std::string, std::variant<bool, double>>
107 values;
108 m.read(std::string(), values);
109
110 auto findAssert =
111 std::find_if(values.begin(), values.end(), [](const auto &pair) {
112 return pair.first.find("Alarm") != std::string::npos;
113 });
114 if (findAssert != values.end())
115 {
116 auto ptr = std::get_if<bool>(&(findAssert->second));
117 if (ptr == nullptr)
118 {
119 phosphor::logging::log<phosphor::logging::level::ERR>(
120 "thresholdChanged: Assert non bool");
121 return;
122 }
123 if (*ptr)
124 {
125 phosphor::logging::log<phosphor::logging::level::INFO>(
126 "thresholdChanged: Assert",
127 phosphor::logging::entry("SENSOR=%s", m.get_path()));
128 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
129 }
130 else
131 {
132 auto &value =
133 thresholdDeassertMap[m.get_path()][findAssert->first];
134 if (value)
135 {
136 phosphor::logging::log<phosphor::logging::level::INFO>(
137 "thresholdChanged: deassert",
138 phosphor::logging::entry("SENSOR=%s", m.get_path()));
139 value = *ptr;
140 }
141 }
142 }
143 });
144
James Feistaecaef72019-04-26 10:30:32 -0700145static void getSensorMaxMin(const SensorMap &sensorMap, double &max,
146 double &min)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700147{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700148 max = 127;
149 min = -128;
150
James Feistaecaef72019-04-26 10:30:32 -0700151 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
152 auto critical =
153 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
154 auto warning =
155 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
156
157 if (sensorObject != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700158 {
James Feistaecaef72019-04-26 10:30:32 -0700159 auto maxMap = sensorObject->second.find("MaxValue");
160 auto minMap = sensorObject->second.find("MinValue");
161
162 if (maxMap != sensorObject->second.end())
163 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700164 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
James Feistaecaef72019-04-26 10:30:32 -0700165 }
166 if (minMap != sensorObject->second.end())
167 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700168 min = std::visit(VariantToDoubleVisitor(), minMap->second);
James Feistaecaef72019-04-26 10:30:32 -0700169 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700170 }
James Feistaecaef72019-04-26 10:30:32 -0700171 if (critical != sensorMap.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700172 {
James Feistaecaef72019-04-26 10:30:32 -0700173 auto lower = critical->second.find("CriticalLow");
174 auto upper = critical->second.find("CriticalHigh");
175 if (lower != critical->second.end())
176 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700177 double value = std::visit(VariantToDoubleVisitor(), lower->second);
James Feistaecaef72019-04-26 10:30:32 -0700178 min = std::min(value, min);
179 }
180 if (upper != critical->second.end())
181 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700182 double value = std::visit(VariantToDoubleVisitor(), upper->second);
James Feistaecaef72019-04-26 10:30:32 -0700183 max = std::max(value, max);
184 }
185 }
186 if (warning != sensorMap.end())
187 {
188
189 auto lower = warning->second.find("WarningLow");
190 auto upper = warning->second.find("WarningHigh");
191 if (lower != warning->second.end())
192 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700193 double value = std::visit(VariantToDoubleVisitor(), lower->second);
James Feistaecaef72019-04-26 10:30:32 -0700194 min = std::min(value, min);
195 }
196 if (upper != warning->second.end())
197 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700198 double value = std::visit(VariantToDoubleVisitor(), upper->second);
James Feistaecaef72019-04-26 10:30:32 -0700199 max = std::max(value, max);
200 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700201 }
202}
203
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700204static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
205 SensorMap &sensorMap)
206{
207 static boost::container::flat_map<
208 std::string, std::chrono::time_point<std::chrono::steady_clock>>
209 updateTimeMap;
210
211 auto updateFind = updateTimeMap.find(sensorConnection);
212 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
213 if (updateFind != updateTimeMap.end())
214 {
215 lastUpdate = updateFind->second;
216 }
217
218 auto now = std::chrono::steady_clock::now();
219
220 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
221 .count() > sensorMapUpdatePeriod)
222 {
223 updateTimeMap[sensorConnection] = now;
224
Vernon Mauery15419dd2019-05-24 09:40:30 -0700225 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
226 auto managedObj = dbus->new_method_call(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700227 sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
228 "GetManagedObjects");
229
230 ManagedObjectType managedObjects;
231 try
232 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700233 auto reply = dbus->call(managedObj);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700234 reply.read(managedObjects);
235 }
236 catch (sdbusplus::exception_t &)
237 {
238 phosphor::logging::log<phosphor::logging::level::ERR>(
239 "Error getting managed objects from connection",
240 phosphor::logging::entry("CONNECTION=%s",
241 sensorConnection.c_str()));
242 return false;
243 }
244
245 SensorCache[sensorConnection] = managedObjects;
246 }
247 auto connection = SensorCache.find(sensorConnection);
248 if (connection == SensorCache.end())
249 {
250 return false;
251 }
252 auto path = connection->second.find(sensorPath);
253 if (path == connection->second.end())
254 {
255 return false;
256 }
257 sensorMap = path->second;
258
259 return true;
260}
261
262/* sensor commands */
263ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
264 ipmi_request_t request,
265 ipmi_response_t response,
266 ipmi_data_len_t dataLen,
267 ipmi_context_t context)
268{
269 *dataLen = 0;
270 printCommand(+netfn, +cmd);
271 return IPMI_CC_INVALID;
272}
273
James Feist7aaf3fe2019-06-25 11:52:11 -0700274namespace meHealth
275{
276constexpr const char *busname = "xyz.openbmc_project.NodeManagerProxy";
277constexpr const char *path = "/xyz/openbmc_project/status/me";
278constexpr const char *interface = "xyz.openbmc_project.SetHealth";
279constexpr const char *method = "SetHealth";
280constexpr const char *critical = "critical";
281constexpr const char *warning = "warning";
282constexpr const char *ok = "ok";
283} // namespace meHealth
284
285static void setMeStatus(uint8_t eventData2, uint8_t eventData3, bool disable)
286{
287 constexpr const std::array<uint8_t, 10> critical = {
288 0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xD, 0xE};
289 constexpr const std::array<uint8_t, 5> warning = {0x3, 0xA, 0x13, 0x19,
290 0x1A};
291
292 std::string state;
293 if (std::find(critical.begin(), critical.end(), eventData2) !=
294 critical.end())
295 {
296 state = meHealth::critical;
297 }
298 // special case 0x3 as we only care about a few states
299 else if (eventData2 == 0x3)
300 {
301 if (eventData3 <= 0x2)
302 {
303 state = meHealth::warning;
304 }
305 else
306 {
307 return;
308 }
309 }
310 else if (std::find(warning.begin(), warning.end(), eventData2) !=
311 warning.end())
312 {
313 state = meHealth::warning;
314 }
315 else
316 {
317 return;
318 }
319 if (disable)
320 {
321 state = meHealth::ok;
322 }
323
324 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
325 auto setHealth =
326 dbus->new_method_call(meHealth::busname, meHealth::path,
327 meHealth::interface, meHealth::method);
328 setHealth.append(std::to_string(static_cast<size_t>(eventData2)), state);
329 try
330 {
331 dbus->call(setHealth);
332 }
333 catch (sdbusplus::exception_t &)
334 {
335 phosphor::logging::log<phosphor::logging::level::ERR>(
336 "Failed to set ME Health");
337 }
338}
339
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700340ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload &p)
341{
James Feist7aaf3fe2019-06-25 11:52:11 -0700342 constexpr const uint8_t meId = 0x2C;
343 constexpr const uint8_t meSensorNum = 0x17;
344 constexpr const uint8_t disabled = 0x80;
345
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700346 uint8_t generatorID = 0;
347 uint8_t evmRev = 0;
348 uint8_t sensorType = 0;
349 uint8_t sensorNum = 0;
350 uint8_t eventType = 0;
351 uint8_t eventData1 = 0;
352 std::optional<uint8_t> eventData2 = 0;
353 std::optional<uint8_t> eventData3 = 0;
354
355 // todo: This check is supposed to be based on the incoming channel.
356 // e.g. system channel will provide upto 8 bytes including generator
357 // ID, but ipmb channel will provide only up to 7 bytes without the
358 // generator ID.
359 // Support for this check is coming in future patches, so for now just base
360 // it on if the first byte is the EvMRev (0x04).
361 if (p.size() && p.data()[0] == 0x04)
362 {
363 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
364 eventData2, eventData3);
365 // todo: the generator ID for this channel is supposed to come from the
366 // IPMB requesters slave address. Support for this is coming in future
367 // patches, so for now just assume it is coming from the ME (0x2C).
368 generatorID = 0x2C;
369 }
370 else
371 {
372 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
373 eventData1, eventData2, eventData3);
374 }
375 if (!p.fullyUnpacked())
376 {
377 return ipmi::responseReqDataLenInvalid();
378 }
379
Jason M. Bills6dd8f042019-04-11 10:39:02 -0700380 // Send this request to the Redfish hooks to log it as a Redfish message
381 // instead. There is no need to add it to the SEL, so just return success.
382 intel_oem::ipmi::sel::checkRedfishHooks(
383 generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
384 eventData2.value_or(0xFF), eventData3.value_or(0xFF));
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700385
James Feist7aaf3fe2019-06-25 11:52:11 -0700386 if (generatorID == meId && sensorNum == meSensorNum && eventData2 &&
387 eventData3)
388 {
389 setMeStatus(*eventData2, *eventData3, (eventType & disabled));
390 }
391
Jason M. Billsae6bdb12019-04-02 12:00:04 -0700392 return ipmi::responseSuccess();
393}
394
James Feist0cd014a2019-04-08 15:04:33 -0700395ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
396 ipmiSenGetSensorReading(uint8_t sensnum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700397{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700398 std::string connection;
399 std::string path;
400
401 auto status = getSensorConnection(sensnum, connection, path);
402 if (status)
403 {
James Feist0cd014a2019-04-08 15:04:33 -0700404 return ipmi::response(status);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700405 }
406
407 SensorMap sensorMap;
408 if (!getSensorMap(connection, path, sensorMap))
409 {
James Feist0cd014a2019-04-08 15:04:33 -0700410 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700411 }
412 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
413
414 if (sensorObject == sensorMap.end() ||
415 sensorObject->second.find("Value") == sensorObject->second.end())
416 {
James Feist0cd014a2019-04-08 15:04:33 -0700417 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700418 }
James Feist0cd014a2019-04-08 15:04:33 -0700419 auto &valueVariant = sensorObject->second["Value"];
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700420 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700421
Yong Li1f2eb5e2019-05-23 14:07:17 +0800422 double max = 0;
423 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700424 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700425
426 int16_t mValue = 0;
427 int16_t bValue = 0;
428 int8_t rExp = 0;
429 int8_t bExp = 0;
430 bool bSigned = false;
431
432 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
433 {
James Feist0cd014a2019-04-08 15:04:33 -0700434 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700435 }
436
James Feist0cd014a2019-04-08 15:04:33 -0700437 uint8_t value =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700438 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
James Feist0cd014a2019-04-08 15:04:33 -0700439 uint8_t operation =
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700440 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
James Feist0cd014a2019-04-08 15:04:33 -0700441 operation |=
James Feist81a95c12019-03-01 15:08:28 -0800442 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700443
James Feist0cd014a2019-04-08 15:04:33 -0700444 uint8_t thresholds = 0;
445
446 auto warningObject =
447 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
448 if (warningObject != sensorMap.end())
449 {
450 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
451 auto alarmLow = warningObject->second.find("WarningAlarmLow");
452 if (alarmHigh != warningObject->second.end())
453 {
454 if (std::get<bool>(alarmHigh->second))
455 {
456 thresholds |= static_cast<uint8_t>(
457 IPMISensorReadingByte3::upperNonCritical);
458 }
459 }
460 if (alarmLow != warningObject->second.end())
461 {
462 if (std::get<bool>(alarmLow->second))
463 {
464 thresholds |= static_cast<uint8_t>(
465 IPMISensorReadingByte3::lowerNonCritical);
466 }
467 }
468 }
469
470 auto criticalObject =
471 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
472 if (criticalObject != sensorMap.end())
473 {
474 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
475 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
476 if (alarmHigh != criticalObject->second.end())
477 {
478 if (std::get<bool>(alarmHigh->second))
479 {
480 thresholds |=
481 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
482 }
483 }
484 if (alarmLow != criticalObject->second.end())
485 {
486 if (std::get<bool>(alarmLow->second))
487 {
488 thresholds |=
489 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
490 }
491 }
492 }
493
494 // no discrete as of today so optional byte is never returned
495 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700496}
497
498ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
499 ipmi_request_t request,
500 ipmi_response_t response,
501 ipmi_data_len_t dataLen,
502 ipmi_context_t context)
503{
504 if (*dataLen != 8)
505 {
506 *dataLen = 0;
507 return IPMI_CC_REQ_DATA_LEN_INVALID;
508 }
509 *dataLen = 0;
510
511 SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
512
513 // upper two bits reserved
514 if (req->mask & 0xC0)
515 {
516 return IPMI_CC_INVALID_FIELD_REQUEST;
517 }
518
519 // lower nc and upper nc not suppported on any sensor
520 if ((req->mask & static_cast<uint8_t>(
521 SensorThresholdReqEnable::setLowerNonRecoverable)) ||
522 (req->mask & static_cast<uint8_t>(
523 SensorThresholdReqEnable::setUpperNonRecoverable)))
524 {
525 return IPMI_CC_INVALID_FIELD_REQUEST;
526 }
527
528 // if no bits are set in the mask, nothing to do
529 if (!(req->mask))
530 {
531 return IPMI_CC_OK;
532 }
533
534 std::string connection;
535 std::string path;
536
537 ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
538 if (status)
539 {
540 return status;
541 }
542 SensorMap sensorMap;
543 if (!getSensorMap(connection, path, sensorMap))
544 {
545 return IPMI_CC_RESPONSE_ERROR;
546 }
547
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700548 double max = 0;
549 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700550 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700551
552 int16_t mValue = 0;
553 int16_t bValue = 0;
554 int8_t rExp = 0;
555 int8_t bExp = 0;
556 bool bSigned = false;
557
558 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
559 {
560 return IPMI_CC_RESPONSE_ERROR;
561 }
562
563 bool setLowerCritical =
564 req->mask &
565 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
566 bool setUpperCritical =
567 req->mask &
568 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
569
570 bool setLowerWarning =
571 req->mask &
572 static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
573 bool setUpperWarning =
574 req->mask &
575 static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
576
577 // store a vector of property name, value to set, and interface
578 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
579
580 // define the indexes of the tuple
581 constexpr uint8_t propertyName = 0;
582 constexpr uint8_t thresholdValue = 1;
583 constexpr uint8_t interface = 2;
584 // verifiy all needed fields are present
585 if (setLowerCritical || setUpperCritical)
586 {
587 auto findThreshold =
588 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
589 if (findThreshold == sensorMap.end())
590 {
591 return IPMI_CC_INVALID_FIELD_REQUEST;
592 }
593 if (setLowerCritical)
594 {
595 auto findLower = findThreshold->second.find("CriticalLow");
596 if (findLower == findThreshold->second.end())
597 {
598 return IPMI_CC_INVALID_FIELD_REQUEST;
599 }
600 thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
601 findThreshold->first);
602 }
603 if (setUpperCritical)
604 {
605 auto findUpper = findThreshold->second.find("CriticalHigh");
606 if (findUpper == findThreshold->second.end())
607 {
608 return IPMI_CC_INVALID_FIELD_REQUEST;
609 }
610 thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
611 findThreshold->first);
612 }
613 }
614 if (setLowerWarning || setUpperWarning)
615 {
616 auto findThreshold =
617 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
618 if (findThreshold == sensorMap.end())
619 {
620 return IPMI_CC_INVALID_FIELD_REQUEST;
621 }
622 if (setLowerWarning)
623 {
624 auto findLower = findThreshold->second.find("WarningLow");
625 if (findLower == findThreshold->second.end())
626 {
627 return IPMI_CC_INVALID_FIELD_REQUEST;
628 }
629 thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
630 findThreshold->first);
631 }
632 if (setUpperWarning)
633 {
634 auto findUpper = findThreshold->second.find("WarningHigh");
635 if (findUpper == findThreshold->second.end())
636 {
637 return IPMI_CC_INVALID_FIELD_REQUEST;
638 }
639 thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
640 findThreshold->first);
641 }
642 }
643
644 for (const auto &property : thresholdsToSet)
645 {
646 // from section 36.3 in the IPMI Spec, assume all linear
647 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
648 (bValue * std::pow(10, bExp))) *
649 std::pow(10, rExp);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700650 setDbusProperty(
651 *getSdBus(), connection, path, std::get<interface>(property),
652 std::get<propertyName>(property), ipmi::Value(valueToSet));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700653 }
654
655 return IPMI_CC_OK;
656}
657
James Feist902c4c52019-04-16 14:51:31 -0700658IPMIThresholds getIPMIThresholds(const SensorMap &sensorMap)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700659{
James Feist902c4c52019-04-16 14:51:31 -0700660 IPMIThresholds resp;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700661 auto warningInterface =
662 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
663 auto criticalInterface =
664 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
665
666 if ((warningInterface != sensorMap.end()) ||
667 (criticalInterface != sensorMap.end()))
668 {
669 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
670
671 if (sensorPair == sensorMap.end())
672 {
673 // should not have been able to find a sensor not implementing
674 // the sensor object
James Feist902c4c52019-04-16 14:51:31 -0700675 throw std::runtime_error("Invalid sensor map");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700676 }
677
Chen,Yugang606dd9f2019-07-01 10:37:30 +0800678 double max = 0;
679 double min = 0;
James Feistaecaef72019-04-26 10:30:32 -0700680 getSensorMaxMin(sensorMap, max, min);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700681
682 int16_t mValue = 0;
683 int16_t bValue = 0;
684 int8_t rExp = 0;
685 int8_t bExp = 0;
686 bool bSigned = false;
687
688 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
689 {
James Feist902c4c52019-04-16 14:51:31 -0700690 throw std::runtime_error("Invalid sensor atrributes");
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700691 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700692 if (warningInterface != sensorMap.end())
693 {
694 auto &warningMap = warningInterface->second;
695
696 auto warningHigh = warningMap.find("WarningHigh");
697 auto warningLow = warningMap.find("WarningLow");
698
699 if (warningHigh != warningMap.end())
700 {
James Feist902c4c52019-04-16 14:51:31 -0700701
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700702 double value =
703 std::visit(VariantToDoubleVisitor(), warningHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700704 resp.warningHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700705 value, mValue, rExp, bValue, bExp, bSigned);
706 }
707 if (warningLow != warningMap.end())
708 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700709 double value =
710 std::visit(VariantToDoubleVisitor(), warningLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700711 resp.warningLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700712 value, mValue, rExp, bValue, bExp, bSigned);
713 }
714 }
715 if (criticalInterface != sensorMap.end())
716 {
717 auto &criticalMap = criticalInterface->second;
718
719 auto criticalHigh = criticalMap.find("CriticalHigh");
720 auto criticalLow = criticalMap.find("CriticalLow");
721
722 if (criticalHigh != criticalMap.end())
723 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700724 double value =
725 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
James Feist902c4c52019-04-16 14:51:31 -0700726 resp.criticalHigh = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700727 value, mValue, rExp, bValue, bExp, bSigned);
728 }
729 if (criticalLow != criticalMap.end())
730 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700731 double value =
732 std::visit(VariantToDoubleVisitor(), criticalLow->second);
James Feist902c4c52019-04-16 14:51:31 -0700733 resp.criticalLow = scaleIPMIValueFromDouble(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700734 value, mValue, rExp, bValue, bExp, bSigned);
735 }
736 }
737 }
James Feist902c4c52019-04-16 14:51:31 -0700738 return resp;
739}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700740
James Feist902c4c52019-04-16 14:51:31 -0700741ipmi::RspType<uint8_t, // readable
742 uint8_t, // lowerNCrit
743 uint8_t, // lowerCrit
744 uint8_t, // lowerNrecoverable
745 uint8_t, // upperNC
746 uint8_t, // upperCrit
747 uint8_t> // upperNRecoverable
748 ipmiSenGetSensorThresholds(uint8_t sensorNumber)
749{
750 std::string connection;
751 std::string path;
752
753 auto status = getSensorConnection(sensorNumber, connection, path);
754 if (status)
755 {
756 return ipmi::response(status);
757 }
758
759 SensorMap sensorMap;
760 if (!getSensorMap(connection, path, sensorMap))
761 {
762 return ipmi::responseResponseError();
763 }
764
765 IPMIThresholds thresholdData;
766 try
767 {
768 thresholdData = getIPMIThresholds(sensorMap);
769 }
770 catch (std::exception &)
771 {
772 return ipmi::responseResponseError();
773 }
774
775 uint8_t readable = 0;
776 uint8_t lowerNC = 0;
777 uint8_t lowerCritical = 0;
778 uint8_t lowerNonRecoverable = 0;
779 uint8_t upperNC = 0;
780 uint8_t upperCritical = 0;
781 uint8_t upperNonRecoverable = 0;
782
783 if (thresholdData.warningHigh)
784 {
785 readable |=
786 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
787 upperNC = *thresholdData.warningHigh;
788 }
789 if (thresholdData.warningLow)
790 {
791 readable |=
792 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
793 lowerNC = *thresholdData.warningLow;
794 }
795
796 if (thresholdData.criticalHigh)
797 {
798 readable |=
799 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
800 upperCritical = *thresholdData.criticalHigh;
801 }
802 if (thresholdData.criticalLow)
803 {
804 readable |=
805 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
806 lowerCritical = *thresholdData.criticalLow;
807 }
808
809 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
810 lowerNonRecoverable, upperNC, upperCritical,
811 upperNonRecoverable);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700812}
813
814ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
815 ipmi_request_t request,
816 ipmi_response_t response,
817 ipmi_data_len_t dataLen,
818 ipmi_context_t context)
819{
820 if (*dataLen != 1)
821 {
822 *dataLen = 0;
823 return IPMI_CC_REQ_DATA_LEN_INVALID;
824 }
825 *dataLen = 0; // default to 0 in case of an error
826
827 uint8_t sensnum = *(static_cast<uint8_t *>(request));
828
829 std::string connection;
830 std::string path;
831
832 auto status = getSensorConnection(sensnum, connection, path);
833 if (status)
834 {
835 return status;
836 }
837
838 SensorMap sensorMap;
839 if (!getSensorMap(connection, path, sensorMap))
840 {
841 return IPMI_CC_RESPONSE_ERROR;
842 }
843
844 auto warningInterface =
845 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
846 auto criticalInterface =
847 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
848
849 if ((warningInterface != sensorMap.end()) ||
850 (criticalInterface != sensorMap.end()))
851 {
852 // zero out response buff
853 auto responseClear = static_cast<uint8_t *>(response);
854 std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
855 0);
856
857 // assume all threshold sensors
858 auto resp = static_cast<SensorEventEnableResp *>(response);
859
860 resp->enabled = static_cast<uint8_t>(
861 IPMISensorEventEnableByte2::sensorScanningEnable);
862 if (warningInterface != sensorMap.end())
863 {
864 auto &warningMap = warningInterface->second;
865
866 auto warningHigh = warningMap.find("WarningHigh");
867 auto warningLow = warningMap.find("WarningLow");
868 if (warningHigh != warningMap.end())
869 {
870 resp->assertionEnabledLSB |= static_cast<uint8_t>(
871 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
872 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
873 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
874 }
875 if (warningLow != warningMap.end())
876 {
877 resp->assertionEnabledLSB |= static_cast<uint8_t>(
878 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
879 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
880 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
881 }
882 }
883 if (criticalInterface != sensorMap.end())
884 {
885 auto &criticalMap = criticalInterface->second;
886
887 auto criticalHigh = criticalMap.find("CriticalHigh");
888 auto criticalLow = criticalMap.find("CriticalLow");
889
890 if (criticalHigh != criticalMap.end())
891 {
892 resp->assertionEnabledMSB |= static_cast<uint8_t>(
893 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
894 resp->deassertionEnabledMSB |= static_cast<uint8_t>(
895 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
896 }
897 if (criticalLow != criticalMap.end())
898 {
899 resp->assertionEnabledLSB |= static_cast<uint8_t>(
900 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
901 resp->deassertionEnabledLSB |= static_cast<uint8_t>(
902 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
903 }
904 }
905 *dataLen =
906 sizeof(SensorEventEnableResp); // todo only return needed bytes
907 }
908 // no thresholds enabled
909 else
910 {
911 *dataLen = 1;
912 auto resp = static_cast<uint8_t *>(response);
913 *resp = static_cast<uint8_t>(
914 IPMISensorEventEnableByte2::eventMessagesEnable);
915 *resp |= static_cast<uint8_t>(
916 IPMISensorEventEnableByte2::sensorScanningEnable);
917 }
918 return IPMI_CC_OK;
919}
920
921ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
922 ipmi_request_t request,
923 ipmi_response_t response,
924 ipmi_data_len_t dataLen,
925 ipmi_context_t context)
926{
927 if (*dataLen != 1)
928 {
929 *dataLen = 0;
930 return IPMI_CC_REQ_DATA_LEN_INVALID;
931 }
932 *dataLen = 0; // default to 0 in case of an error
933
934 uint8_t sensnum = *(static_cast<uint8_t *>(request));
935
936 std::string connection;
937 std::string path;
938
939 auto status = getSensorConnection(sensnum, connection, path);
940 if (status)
941 {
942 return status;
943 }
944
945 SensorMap sensorMap;
946 if (!getSensorMap(connection, path, sensorMap))
947 {
948 return IPMI_CC_RESPONSE_ERROR;
949 }
950
951 auto warningInterface =
952 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
953 auto criticalInterface =
954 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
955
956 // zero out response buff
957 auto responseClear = static_cast<uint8_t *>(response);
958 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
959 auto resp = static_cast<SensorEventStatusResp *>(response);
960 resp->enabled =
961 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
962
James Feist392786a2019-03-19 13:36:10 -0700963 std::optional<bool> criticalDeassertHigh =
964 thresholdDeassertMap[path]["CriticalAlarmHigh"];
965 std::optional<bool> criticalDeassertLow =
966 thresholdDeassertMap[path]["CriticalAlarmLow"];
967 std::optional<bool> warningDeassertHigh =
968 thresholdDeassertMap[path]["WarningAlarmHigh"];
969 std::optional<bool> warningDeassertLow =
970 thresholdDeassertMap[path]["WarningAlarmLow"];
971
972 if (criticalDeassertHigh && !*criticalDeassertHigh)
973 {
974 resp->deassertionsMSB |= static_cast<uint8_t>(
975 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
976 }
977 if (criticalDeassertLow && !*criticalDeassertLow)
978 {
979 resp->deassertionsMSB |= static_cast<uint8_t>(
980 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
981 }
982 if (warningDeassertHigh && !*warningDeassertHigh)
983 {
984 resp->deassertionsLSB |= static_cast<uint8_t>(
985 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
986 }
987 if (warningDeassertLow && !*warningDeassertLow)
988 {
989 resp->deassertionsLSB |= static_cast<uint8_t>(
990 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
991 }
992
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700993 if ((warningInterface != sensorMap.end()) ||
994 (criticalInterface != sensorMap.end()))
995 {
996 resp->enabled = static_cast<uint8_t>(
997 IPMISensorEventEnableByte2::eventMessagesEnable);
998 if (warningInterface != sensorMap.end())
999 {
1000 auto &warningMap = warningInterface->second;
1001
1002 auto warningHigh = warningMap.find("WarningAlarmHigh");
1003 auto warningLow = warningMap.find("WarningAlarmLow");
1004 auto warningHighAlarm = false;
1005 auto warningLowAlarm = false;
1006
1007 if (warningHigh != warningMap.end())
1008 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001009 warningHighAlarm = std::get<bool>(warningHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001010 }
1011 if (warningLow != warningMap.end())
1012 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001013 warningLowAlarm = std::get<bool>(warningLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001014 }
1015 if (warningHighAlarm)
1016 {
1017 resp->assertionsLSB |= static_cast<uint8_t>(
1018 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1019 }
1020 if (warningLowAlarm)
1021 {
1022 resp->assertionsLSB |= 1; // lower nc going low
1023 }
1024 }
1025 if (criticalInterface != sensorMap.end())
1026 {
1027 auto &criticalMap = criticalInterface->second;
1028
1029 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1030 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1031 auto criticalHighAlarm = false;
1032 auto criticalLowAlarm = false;
1033
1034 if (criticalHigh != criticalMap.end())
1035 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001036 criticalHighAlarm = std::get<bool>(criticalHigh->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001037 }
1038 if (criticalLow != criticalMap.end())
1039 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001040 criticalLowAlarm = std::get<bool>(criticalLow->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001041 }
1042 if (criticalHighAlarm)
1043 {
1044 resp->assertionsMSB |= static_cast<uint8_t>(
1045 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1046 }
1047 if (criticalLowAlarm)
1048 {
1049 resp->assertionsLSB |= static_cast<uint8_t>(
1050 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1051 }
1052 }
1053 *dataLen = sizeof(SensorEventStatusResp);
1054 }
1055
1056 // no thresholds enabled, don't need assertionMSB
1057 else
1058 {
1059 *dataLen = sizeof(SensorEventStatusResp) - 1;
1060 }
1061
1062 return IPMI_CC_OK;
1063}
1064
1065/* end sensor commands */
1066
1067/* storage commands */
1068
1069ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1070 ipmi_request_t request,
1071 ipmi_response_t response,
1072 ipmi_data_len_t dataLen,
1073 ipmi_context_t context)
1074{
1075 printCommand(+netfn, +cmd);
1076
1077 if (*dataLen)
1078 {
1079 *dataLen = 0;
1080 return IPMI_CC_REQ_DATA_LEN_INVALID;
1081 }
1082 *dataLen = 0; // default to 0 in case of an error
1083
1084 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1085 {
1086 return IPMI_CC_RESPONSE_ERROR;
1087 }
1088
1089 // zero out response buff
1090 auto responseClear = static_cast<uint8_t *>(response);
1091 std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
1092
1093 auto resp = static_cast<GetSDRInfoResp *>(response);
1094 resp->sdrVersion = ipmiSdrVersion;
1095 uint16_t recordCount = sensorTree.size();
1096
1097 // todo: for now, sdr count is number of sensors
1098 resp->recordCountLS = recordCount & 0xFF;
1099 resp->recordCountMS = recordCount >> 8;
1100
1101 // free space unspcified
1102 resp->freeSpace[0] = 0xFF;
1103 resp->freeSpace[1] = 0xFF;
1104
1105 resp->mostRecentAddition = sdrLastAdd;
1106 resp->mostRecentErase = sdrLastRemove;
1107 resp->operationSupport = static_cast<uint8_t>(
1108 SdrRepositoryInfoOps::overflow); // write not supported
1109 resp->operationSupport |=
1110 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1111 resp->operationSupport |= static_cast<uint8_t>(
1112 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1113 *dataLen = sizeof(GetSDRInfoResp);
1114 return IPMI_CC_OK;
1115}
1116
1117ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1118 ipmi_request_t request,
1119 ipmi_response_t response,
1120 ipmi_data_len_t dataLen,
1121 ipmi_context_t context)
1122{
1123 if (*dataLen)
1124 {
1125 *dataLen = 0;
1126 return IPMI_CC_REQ_DATA_LEN_INVALID;
1127 }
1128 *dataLen = 0; // default to 0 in case of an error
1129 GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
1130
1131 // 0000h unspecified number of alloc units
1132 resp->allocUnitsLSB = 0;
1133 resp->allocUnitsMSB = 0;
1134
1135 // max unit size is size of max record
1136 resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
1137 resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
1138 // read only sdr, no free alloc blocks
1139 resp->allocUnitFreeLSB = 0;
1140 resp->allocUnitFreeMSB = 0;
1141 resp->allocUnitLargestFreeLSB = 0;
1142 resp->allocUnitLargestFreeMSB = 0;
1143 // only allow one block at a time
1144 resp->maxRecordSize = 1;
1145
1146 *dataLen = sizeof(GetAllocInfoResp);
1147
1148 return IPMI_CC_OK;
1149}
1150
1151ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1152 ipmi_request_t request,
1153 ipmi_response_t response,
1154 ipmi_data_len_t dataLen,
1155 ipmi_context_t context)
1156{
1157 printCommand(+netfn, +cmd);
1158
1159 if (*dataLen)
1160 {
1161 *dataLen = 0;
1162 return IPMI_CC_REQ_DATA_LEN_INVALID;
1163 }
1164 *dataLen = 0; // default to 0 in case of an error
1165 sdrReservationID++;
James Feista80cb902019-02-14 13:05:25 -08001166 if (sdrReservationID == 0)
1167 {
1168 sdrReservationID++;
1169 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001170 *dataLen = 2;
1171 auto resp = static_cast<uint8_t *>(response);
1172 resp[0] = sdrReservationID & 0xFF;
1173 resp[1] = sdrReservationID >> 8;
1174
1175 return IPMI_CC_OK;
1176}
1177
James Feistb49a98a2019-04-16 13:48:09 -07001178ipmi::RspType<uint16_t, // next record ID
1179 std::vector<uint8_t> // payload
1180 >
1181 ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset,
1182 uint8_t bytesToRead)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001183{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001184 constexpr uint16_t lastRecordIndex = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001185
1186 // reservation required for partial reads with non zero offset into
1187 // record
James Feistb49a98a2019-04-16 13:48:09 -07001188 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001189 {
James Feistb49a98a2019-04-16 13:48:09 -07001190 return ipmi::responseInvalidReservationId();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001191 }
1192
1193 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1194 {
James Feistb49a98a2019-04-16 13:48:09 -07001195 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001196 }
1197
1198 size_t fruCount = 0;
1199 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
1200 if (ret != IPMI_CC_OK)
1201 {
James Feistb49a98a2019-04-16 13:48:09 -07001202 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001203 }
1204
1205 size_t lastRecord = sensorTree.size() + fruCount - 1;
James Feistb49a98a2019-04-16 13:48:09 -07001206 if (recordID == lastRecordIndex)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001207 {
James Feistb49a98a2019-04-16 13:48:09 -07001208 recordID = lastRecord;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001209 }
James Feistb49a98a2019-04-16 13:48:09 -07001210 if (recordID > lastRecord)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001211 {
James Feistb49a98a2019-04-16 13:48:09 -07001212 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001213 }
1214
James Feistb49a98a2019-04-16 13:48:09 -07001215 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001216
James Feistb49a98a2019-04-16 13:48:09 -07001217 if (recordID >= sensorTree.size())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001218 {
James Feistb49a98a2019-04-16 13:48:09 -07001219 size_t fruIndex = recordID - sensorTree.size();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001220 if (fruIndex >= fruCount)
1221 {
James Feistb49a98a2019-04-16 13:48:09 -07001222 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001223 }
1224 get_sdr::SensorDataFruRecord data;
James Feistb49a98a2019-04-16 13:48:09 -07001225 if (offset > sizeof(data))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001226 {
James Feistb49a98a2019-04-16 13:48:09 -07001227 return ipmi::responseInvalidFieldRequest();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001228 }
1229 ret = ipmi::storage::getFruSdrs(fruIndex, data);
1230 if (ret != IPMI_CC_OK)
1231 {
James Feistb49a98a2019-04-16 13:48:09 -07001232 return ipmi::response(ret);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001233 }
James Feistb49a98a2019-04-16 13:48:09 -07001234 data.header.record_id_msb = recordID << 8;
1235 data.header.record_id_lsb = recordID & 0xFF;
1236 if (sizeof(data) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001237 {
James Feistb49a98a2019-04-16 13:48:09 -07001238 bytesToRead = sizeof(data) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001239 }
James Feistb49a98a2019-04-16 13:48:09 -07001240
1241 uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset;
1242 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1243
1244 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001245 }
1246
1247 std::string connection;
1248 std::string path;
James Feistb49a98a2019-04-16 13:48:09 -07001249 uint16_t sensorIndex = recordID;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001250 for (const auto &sensor : sensorTree)
1251 {
1252 if (sensorIndex-- == 0)
1253 {
1254 if (!sensor.second.size())
1255 {
James Feistb49a98a2019-04-16 13:48:09 -07001256 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001257 }
1258 connection = sensor.second.begin()->first;
1259 path = sensor.first;
1260 break;
1261 }
1262 }
1263
1264 SensorMap sensorMap;
1265 if (!getSensorMap(connection, path, sensorMap))
1266 {
James Feistb49a98a2019-04-16 13:48:09 -07001267 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001268 }
James Feistb49a98a2019-04-16 13:48:09 -07001269 uint8_t sensornumber = (recordID & 0xFF);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001270 get_sdr::SensorDataFullRecord record = {0};
1271
James Feistb49a98a2019-04-16 13:48:09 -07001272 record.header.record_id_msb = recordID << 8;
1273 record.header.record_id_lsb = recordID & 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001274 record.header.sdr_version = ipmiSdrVersion;
1275 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1276 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1277 sizeof(get_sdr::SensorDataRecordHeader);
1278 record.key.owner_id = 0x20;
1279 record.key.owner_lun = 0x0;
1280 record.key.sensor_number = sensornumber;
1281
1282 record.body.entity_id = 0x0;
1283 record.body.entity_instance = 0x01;
James Feist7086a882019-03-13 10:46:00 -07001284 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001285 record.body.sensor_type = getSensorTypeFromPath(path);
1286 std::string type = getSensorTypeStringFromPath(path);
1287 auto typeCstr = type.c_str();
1288 auto findUnits = sensorUnits.find(typeCstr);
1289 if (findUnits != sensorUnits.end())
1290 {
1291 record.body.sensor_units_2_base =
1292 static_cast<uint8_t>(findUnits->second);
1293 } // else default 0x0 unspecified
1294
1295 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1296
1297 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1298 if (sensorObject == sensorMap.end())
1299 {
James Feistb49a98a2019-04-16 13:48:09 -07001300 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001301 }
1302
1303 auto maxObject = sensorObject->second.find("MaxValue");
1304 auto minObject = sensorObject->second.find("MinValue");
1305 double max = 128;
1306 double min = -127;
1307 if (maxObject != sensorObject->second.end())
1308 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001309 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001310 }
1311
1312 if (minObject != sensorObject->second.end())
1313 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001314 min = std::visit(VariantToDoubleVisitor(), minObject->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001315 }
1316
Yong Li1f2eb5e2019-05-23 14:07:17 +08001317 int16_t mValue = 0;
1318 int8_t rExp = 0;
1319 int16_t bValue = 0;
1320 int8_t bExp = 0;
1321 bool bSigned = false;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001322
1323 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1324 {
James Feistb49a98a2019-04-16 13:48:09 -07001325 return ipmi::responseResponseError();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001326 }
1327
1328 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1329 record.body.m_lsb = mValue & 0xFF;
1330
1331 // move the smallest bit of the MSB into place (bit 9)
1332 // the MSbs are bits 7:8 in m_msb_and_tolerance
1333 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1334
1335 // assign the negative
1336 if (mValue < 0)
1337 {
1338 mMsb |= (1 << 7);
1339 }
1340 record.body.m_msb_and_tolerance = mMsb;
1341
1342 record.body.b_lsb = bValue & 0xFF;
1343
1344 // move the smallest bit of the MSB into place
1345 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1346 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1347
1348 // assign the negative
1349 if (bValue < 0)
1350 {
1351 bMsb |= (1 << 7);
1352 }
1353 record.body.b_msb_and_accuracy_lsb = bMsb;
1354
1355 record.body.r_b_exponents = bExp & 0x7;
1356 if (bExp < 0)
1357 {
1358 record.body.r_b_exponents |= 1 << 3;
1359 }
1360 record.body.r_b_exponents = (rExp & 0x7) << 4;
1361 if (rExp < 0)
1362 {
1363 record.body.r_b_exponents |= 1 << 7;
1364 }
1365
1366 // todo fill out rest of units
1367 if (bSigned)
1368 {
1369 record.body.sensor_units_1 = 1 << 7;
1370 }
1371
1372 // populate sensor name from path
1373 std::string name;
1374 size_t nameStart = path.rfind("/");
1375 if (nameStart != std::string::npos)
1376 {
1377 name = path.substr(nameStart + 1, std::string::npos - nameStart);
1378 }
1379
1380 std::replace(name.begin(), name.end(), '_', ' ');
1381 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1382 {
James Feist979a2322019-05-15 09:06:54 -07001383 // try to not truncate by replacing common words
1384 constexpr std::array<std::pair<const char *, const char *>, 2>
1385 replaceWords = {std::make_pair("Output", "Out"),
1386 std::make_pair("Input", "In")};
1387 for (const auto &[find, replace] : replaceWords)
1388 {
1389 boost::replace_all(name, find, replace);
1390 }
1391
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001392 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1393 }
1394 record.body.id_string_info = name.size();
1395 std::strncpy(record.body.id_string, name.c_str(),
1396 sizeof(record.body.id_string));
1397
James Feistc4b15bc2019-04-16 15:41:39 -07001398 IPMIThresholds thresholdData;
1399 try
1400 {
1401 thresholdData = getIPMIThresholds(sensorMap);
1402 }
1403 catch (std::exception &)
1404 {
1405 return ipmi::responseResponseError();
1406 }
1407
1408 if (thresholdData.criticalHigh)
1409 {
1410 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1411 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1412 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1413 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1414 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1415 record.body.discrete_reading_setting_mask[0] |=
1416 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1417 }
1418 if (thresholdData.warningHigh)
1419 {
1420 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1421 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1422 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1423 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1424 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1425 record.body.discrete_reading_setting_mask[0] |=
1426 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1427 }
1428 if (thresholdData.criticalLow)
1429 {
1430 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1431 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1432 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1433 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1434 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1435 record.body.discrete_reading_setting_mask[0] |=
1436 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1437 }
1438 if (thresholdData.warningLow)
1439 {
1440 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1441 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1442 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1443 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1444 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1445 record.body.discrete_reading_setting_mask[0] |=
1446 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1447 }
1448
1449 // everything that is readable is setable
1450 record.body.discrete_reading_setting_mask[1] =
1451 record.body.discrete_reading_setting_mask[0];
1452
James Feistb49a98a2019-04-16 13:48:09 -07001453 if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001454 {
James Feistb49a98a2019-04-16 13:48:09 -07001455 bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001456 }
1457
James Feistb49a98a2019-04-16 13:48:09 -07001458 uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset;
1459 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001460
James Feistb49a98a2019-04-16 13:48:09 -07001461 return ipmi::responseSuccess(nextRecordId, recordData);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001462}
1463/* end storage commands */
1464
1465void registerSensorFunctions()
1466{
1467 // get firmware version information
1468 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1469 ipmiSensorWildcardHandler, PRIVILEGE_USER);
1470
1471 // <Get Sensor Type>
1472 ipmiPrintAndRegister(
1473 NETFUN_SENSOR,
1474 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
1475 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1476
1477 // <Set Sensor Reading and Event Status>
1478 ipmiPrintAndRegister(
1479 NETFUN_SENSOR,
1480 static_cast<ipmi_cmd_t>(
1481 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
1482 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1483
Jason M. Billsae6bdb12019-04-02 12:00:04 -07001484 // <Platform Event>
1485 ipmi::registerHandler(
1486 ipmi::prioOemBase, ipmi::netFnSensor,
1487 static_cast<ipmi::Cmd>(ipmi::sensor_event::cmdPlatformEvent),
1488 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1489
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001490 // <Get Sensor Reading>
James Feist0cd014a2019-04-08 15:04:33 -07001491 ipmi::registerHandler(
1492 ipmi::prioOemBase, NETFUN_SENSOR,
1493 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
1494 ipmi::Privilege::User, ipmiSenGetSensorReading);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001495
1496 // <Get Sensor Threshold>
James Feist902c4c52019-04-16 14:51:31 -07001497 ipmi::registerHandler(
1498 ipmi::prioOemBase, NETFUN_SENSOR,
1499 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
1500 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001501
1502 ipmiPrintAndRegister(
1503 NETFUN_SENSOR,
1504 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
1505 nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
1506
1507 // <Get Sensor Event Enable>
1508 ipmiPrintAndRegister(NETFUN_SENSOR,
1509 static_cast<ipmi_cmd_t>(
1510 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
1511 nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
1512
1513 // <Get Sensor Event Status>
1514 ipmiPrintAndRegister(NETFUN_SENSOR,
1515 static_cast<ipmi_cmd_t>(
1516 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
1517 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1518
1519 // register all storage commands for both Sensor and Storage command
1520 // versions
1521
1522 // <Get SDR Repository Info>
1523 ipmiPrintAndRegister(
1524 NETFUN_STORAGE,
1525 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
1526 nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
1527
1528 // <Get SDR Allocation Info>
1529 ipmiPrintAndRegister(NETFUN_STORAGE,
1530 static_cast<ipmi_cmd_t>(
1531 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
1532 nullptr, ipmiStorageGetSDRAllocationInfo,
1533 PRIVILEGE_USER);
1534
1535 // <Reserve SDR Repo>
1536 ipmiPrintAndRegister(NETFUN_SENSOR,
1537 static_cast<ipmi_cmd_t>(
1538 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
1539 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1540
1541 ipmiPrintAndRegister(
1542 NETFUN_STORAGE,
1543 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1544 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1545
1546 // <Get Sdr>
James Feistb49a98a2019-04-16 13:48:09 -07001547 ipmi::registerHandler(
1548 ipmi::prioOemBase, NETFUN_SENSOR,
1549 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
1550 ipmi::Privilege::User, ipmiStorageGetSDR);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001551
James Feistb49a98a2019-04-16 13:48:09 -07001552 ipmi::registerHandler(
1553 ipmi::prioOemBase, NETFUN_STORAGE,
1554 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetSDR),
1555 ipmi::Privilege::User, ipmiStorageGetSDR);
1556
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001557 return;
1558}
1559} // namespace ipmi