1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright (c) 2018-present Facebook.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <ipmid/api.h>
19
20 #include <boost/container/flat_map.hpp>
21 #include <commandutils.hpp>
22 #include <ipmid/utils.hpp>
23 #include <phosphor-logging/log.hpp>
24 #include <sdbusplus/message/types.hpp>
25 #include <sdbusplus/timer.hpp>
26 #include <sensorutils.hpp>
27 #include <storagecommands.hpp>
28
29 #include <iostream>
30 #include <unordered_map>
31
32 namespace ipmi
33 {
34
35 namespace storage
36 {
37 void registerStorageFunctions() __attribute__((constructor));
38
39 constexpr static const size_t maxMessageSize = 64;
40 constexpr static const size_t maxFruSdrNameSize = 16;
41 static constexpr int sensorMapUpdatePeriod = 2;
42 using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
43
44 using ManagedObjectSensor =
45 std::map<sdbusplus::message::object_path,
46 std::map<std::string, std::map<std::string, DbusVariant>>>;
47
48 static uint16_t sdrReservationID;
49
50 static boost::container::flat_map<std::string, ManagedObjectSensor> SensorCache;
51 static SensorSubTree sensorTree;
52
53 void registerSensorFunctions() __attribute__((constructor));
54 using ManagedObjectType = boost::container::flat_map<
55 sdbusplus::message::object_path,
56 boost::container::flat_map<
57 std::string, boost::container::flat_map<std::string, DbusVariant>>>;
58 using ManagedEntry = std::pair<
59 sdbusplus::message::object_path,
60 boost::container::flat_map<
61 std::string, boost::container::flat_map<std::string, DbusVariant>>>;
62
63 constexpr static const char* fruDeviceServiceName =
64 "xyz.openbmc_project.FruDevice";
65 constexpr static const size_t cacheTimeoutSeconds = 10;
66
67 static std::vector<uint8_t> fruCache;
68 static uint16_t cacheBus = 0xFFFF;
69 static uint8_t cacheAddr = 0XFF;
70
71 std::unique_ptr<sdbusplus::Timer> cacheTimer = nullptr;
72
73 // we unfortunately have to build a map of hashes in case there is a
74 // collision to verify our dev-id
75 boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
76
77 static sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
78
79 using InterfaceName = std::string;
80 using PropertyName = std::string;
81 using ThresholdStr = std::string;
82
83 enum class AlarmType
84 {
85 low,
86 high
87 };
88
89 struct Property
90 {
91 PropertyName name;
92 ThresholdStr threshold;
93 };
94
95 const std::vector<InterfaceName> thresholdCheckedOrder{
96 "xyz.openbmc_project.Sensor.Threshold.HardShutdown",
97 "xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
98 "xyz.openbmc_project.Sensor.Threshold.Critical",
99 "xyz.openbmc_project.Sensor.Threshold.Warning"};
100
101 const std::unordered_map<std::string, std::map<AlarmType, Property>>
102 alarmProperties{
103 {"xyz.openbmc_project.Sensor.Threshold.HardShutdown",
104 {{AlarmType::low, Property{"HardShutdownAlarmLow", "LNR"}},
105 {AlarmType::high, Property{"HardShutdownAlarmHigh", "UNR"}}}},
106
107 {"xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
108 {{AlarmType::low, Property{"SoftShutdownAlarmLow", "LNR"}},
109 {AlarmType::high, Property{"SoftShutdownAlarmHigh", "UNR"}}}},
110
111 {"xyz.openbmc_project.Sensor.Threshold.Critical",
112 {{AlarmType::low, Property{"CriticalAlarmLow", "LCR"}},
113 {AlarmType::high, Property{"CriticalAlarmHigh", "UCR"}}}},
114
115 {"xyz.openbmc_project.Sensor.Threshold.Warning",
116 {{AlarmType::low, Property{"WarningAlarmLow", "LNC"}},
117 {AlarmType::high, Property{"WarningAlarmHigh", "UNC"}}}},
118 };
119
getSensorMap(std::string sensorConnection,std::string sensorPath,SensorMap & sensorMap)120 static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
121 SensorMap& sensorMap)
122 {
123 static boost::container::flat_map<
124 std::string, std::chrono::time_point<std::chrono::steady_clock>>
125 updateTimeMap;
126
127 auto updateFind = updateTimeMap.find(sensorConnection);
128 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
129 if (updateFind != updateTimeMap.end())
130 {
131 lastUpdate = updateFind->second;
132 }
133
134 auto now = std::chrono::steady_clock::now();
135
136 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
137 .count() > sensorMapUpdatePeriod)
138 {
139 updateTimeMap[sensorConnection] = now;
140
141 auto managedObj = dbus.new_method_call(
142 sensorConnection.c_str(), "/xyz/openbmc_project/sensors",
143 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
144
145 ManagedObjectSensor managedObjects;
146 try
147 {
148 auto reply = dbus.call(managedObj);
149 reply.read(managedObjects);
150 }
151 catch (const sdbusplus::exception_t&)
152 {
153 phosphor::logging::log<phosphor::logging::level::ERR>(
154 "Error getting managed objects from connection",
155 phosphor::logging::entry("CONNECTION=%s",
156 sensorConnection.c_str()));
157 return false;
158 }
159
160 SensorCache[sensorConnection] = managedObjects;
161 }
162 auto connection = SensorCache.find(sensorConnection);
163 if (connection == SensorCache.end())
164 {
165 return false;
166 }
167 auto path = connection->second.find(sensorPath);
168 if (path == connection->second.end())
169 {
170 return false;
171 }
172 sensorMap = path->second;
173
174 return true;
175 }
176
writeFru()177 bool writeFru()
178 {
179 sdbusplus::message_t writeFru = dbus.new_method_call(
180 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
181 "xyz.openbmc_project.FruDeviceManager", "WriteFru");
182 writeFru.append(cacheBus, cacheAddr, fruCache);
183 try
184 {
185 sdbusplus::message_t writeFruResp = dbus.call(writeFru);
186 }
187 catch (const sdbusplus::exception_t&)
188 {
189 // todo: log sel?
190 phosphor::logging::log<phosphor::logging::level::ERR>(
191 "error writing fru");
192 return false;
193 }
194 return true;
195 }
196
createTimer()197 void createTimer()
198 {
199 if (cacheTimer == nullptr)
200 {
201 cacheTimer = std::make_unique<sdbusplus::Timer>(writeFru);
202 }
203 }
204
replaceCacheFru(uint8_t devId)205 ipmi_ret_t replaceCacheFru(uint8_t devId)
206 {
207 static uint8_t lastDevId = 0xFF;
208
209 bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
210 if (lastDevId == devId && timerRunning)
211 {
212 return ipmi::ccSuccess; // cache already up to date
213 }
214 // if timer is running, stop it and writeFru manually
215 else if (timerRunning)
216 {
217 cacheTimer->stop();
218 writeFru();
219 }
220
221 sdbusplus::message_t getObjects = dbus.new_method_call(
222 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
223 "GetManagedObjects");
224 ManagedObjectType frus;
225 try
226 {
227 sdbusplus::message_t resp = dbus.call(getObjects);
228 resp.read(frus);
229 }
230 catch (const sdbusplus::exception_t&)
231 {
232 phosphor::logging::log<phosphor::logging::level::ERR>(
233 "replaceCacheFru: error getting managed objects");
234 return ipmi::ccResponseError;
235 }
236
237 deviceHashes.clear();
238
239 uint8_t fruHash = 0;
240 uint8_t mbFruBus = 0, mbFruAddr = 0;
241
242 auto device = getMbFruDevice();
243 if (device)
244 {
245 std::tie(mbFruBus, mbFruAddr) = *device;
246 deviceHashes.emplace(0, std::make_pair(mbFruBus, mbFruAddr));
247 fruHash++;
248 }
249
250 for (const auto& fru : frus)
251 {
252 auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
253 if (fruIface == fru.second.end())
254 {
255 continue;
256 }
257
258 auto busFind = fruIface->second.find("BUS");
259 auto addrFind = fruIface->second.find("ADDRESS");
260 if (busFind == fruIface->second.end() ||
261 addrFind == fruIface->second.end())
262 {
263 phosphor::logging::log<phosphor::logging::level::INFO>(
264 "fru device missing Bus or Address",
265 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
266 continue;
267 }
268
269 uint8_t fruBus = std::get<uint32_t>(busFind->second);
270 uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
271 if (fruBus != mbFruBus || fruAddr != mbFruAddr)
272 {
273 deviceHashes.emplace(fruHash, std::make_pair(fruBus, fruAddr));
274 fruHash++;
275 }
276 }
277 auto deviceFind = deviceHashes.find(devId);
278 if (deviceFind == deviceHashes.end())
279 {
280 return ipmi::ccSensorInvalid;
281 }
282
283 fruCache.clear();
284 sdbusplus::message_t getRawFru = dbus.new_method_call(
285 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
286 "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
287 cacheBus = deviceFind->second.first;
288 cacheAddr = deviceFind->second.second;
289 getRawFru.append(cacheBus, cacheAddr);
290 try
291 {
292 sdbusplus::message_t getRawResp = dbus.call(getRawFru);
293 getRawResp.read(fruCache);
294 }
295 catch (const sdbusplus::exception_t&)
296 {
297 lastDevId = 0xFF;
298 cacheBus = 0xFFFF;
299 cacheAddr = 0xFF;
300 return ipmi::ccResponseError;
301 }
302
303 lastDevId = devId;
304 return ipmi::ccSuccess;
305 }
306
ipmiStorageReadFRUData(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)307 ipmi_ret_t ipmiStorageReadFRUData(
308 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
309 ipmi_data_len_t dataLen, ipmi_context_t)
310 {
311 if (*dataLen != 4)
312 {
313 *dataLen = 0;
314 return ipmi::ccReqDataLenInvalid;
315 }
316 *dataLen = 0; // default to 0 in case of an error
317
318 auto req = static_cast<GetFRUAreaReq*>(request);
319
320 if (req->countToRead > maxMessageSize - 1)
321 {
322 return ipmi::ccInvalidFieldRequest;
323 }
324 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
325
326 if (status != ipmi::ccSuccess)
327 {
328 return status;
329 }
330
331 size_t fromFRUByteLen = 0;
332 if (req->countToRead + req->fruInventoryOffset < fruCache.size())
333 {
334 fromFRUByteLen = req->countToRead;
335 }
336 else if (fruCache.size() > req->fruInventoryOffset)
337 {
338 fromFRUByteLen = fruCache.size() - req->fruInventoryOffset;
339 }
340 size_t padByteLen = req->countToRead - fromFRUByteLen;
341 uint8_t* respPtr = static_cast<uint8_t*>(response);
342 *respPtr = req->countToRead;
343 std::copy(fruCache.begin() + req->fruInventoryOffset,
344 fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen,
345 ++respPtr);
346 // if longer than the fru is requested, fill with 0xFF
347 if (padByteLen)
348 {
349 respPtr += fromFRUByteLen;
350 std::fill(respPtr, respPtr + padByteLen, 0xFF);
351 }
352 *dataLen = fromFRUByteLen + 1;
353
354 return ipmi::ccSuccess;
355 }
356
ipmiStorageWriteFRUData(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)357 ipmi_ret_t ipmiStorageWriteFRUData(
358 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
359 ipmi_data_len_t dataLen, ipmi_context_t)
360 {
361 if (*dataLen < 4 ||
362 *dataLen >=
363 0xFF + 3) // count written return is one byte, so limit to one
364 // byte of data after the three request data bytes
365 {
366 *dataLen = 0;
367 return ipmi::ccReqDataLenInvalid;
368 }
369
370 auto req = static_cast<WriteFRUDataReq*>(request);
371 size_t writeLen = *dataLen - 3;
372 *dataLen = 0; // default to 0 in case of an error
373
374 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
375 if (status != ipmi::ccSuccess)
376 {
377 return status;
378 }
379 size_t lastWriteAddr = req->fruInventoryOffset + writeLen;
380 if (fruCache.size() < lastWriteAddr)
381 {
382 fruCache.resize(req->fruInventoryOffset + writeLen);
383 }
384
385 std::copy(req->data, req->data + writeLen,
386 fruCache.begin() + req->fruInventoryOffset);
387
388 bool atEnd = false;
389
390 if (fruCache.size() >= sizeof(FRUHeader))
391 {
392 FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
393
394 size_t lastRecordStart = std::max(
395 header->internalOffset,
396 std::max(header->chassisOffset,
397 std::max(header->boardOffset, header->productOffset)));
398 // TODO: Handle Multi-Record FRUs?
399
400 lastRecordStart *= 8; // header starts in are multiples of 8 bytes
401
402 // get the length of the area in multiples of 8 bytes
403 if (lastWriteAddr > (lastRecordStart + 1))
404 {
405 // second byte in record area is the length
406 int areaLength(fruCache[lastRecordStart + 1]);
407 areaLength *= 8; // it is in multiples of 8 bytes
408
409 if (lastWriteAddr >= (areaLength + lastRecordStart))
410 {
411 atEnd = true;
412 }
413 }
414 }
415 uint8_t* respPtr = static_cast<uint8_t*>(response);
416 if (atEnd)
417 {
418 // cancel timer, we're at the end so might as well send it
419 cacheTimer->stop();
420 if (!writeFru())
421 {
422 return ipmi::ccInvalidFieldRequest;
423 }
424 *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF));
425 }
426 else
427 {
428 // start a timer, if no further data is sent in cacheTimeoutSeconds
429 // seconds, check to see if it is valid
430 createTimer();
431 cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
432 std::chrono::seconds(cacheTimeoutSeconds)));
433 *respPtr = 0;
434 }
435
436 *dataLen = 1;
437
438 return ipmi::ccSuccess;
439 }
440
getFruSdrCount(size_t & count)441 ipmi_ret_t getFruSdrCount(size_t& count)
442 {
443 ipmi_ret_t ret = replaceCacheFru(0);
444 if (ret != ipmi::ccSuccess)
445 {
446 return ret;
447 }
448 count = deviceHashes.size();
449 return ipmi::ccSuccess;
450 }
451
getFruSdrs(size_t index,get_sdr::SensorDataFruRecord & resp)452 ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
453 {
454 ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
455 if (ret != ipmi::ccSuccess)
456 {
457 return ret;
458 }
459 if (deviceHashes.size() < index)
460 {
461 return ipmi::ccInvalidFieldRequest;
462 }
463 auto device = deviceHashes.begin() + index;
464 uint8_t& bus = device->second.first;
465 uint8_t& address = device->second.second;
466
467 ManagedObjectType frus;
468
469 sdbusplus::message_t getObjects = dbus.new_method_call(
470 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
471 "GetManagedObjects");
472 try
473 {
474 sdbusplus::message_t resp = dbus.call(getObjects);
475 resp.read(frus);
476 }
477 catch (const sdbusplus::exception_t&)
478 {
479 return ipmi::ccResponseError;
480 }
481 boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
482 auto fru = std::find_if(
483 frus.begin(), frus.end(),
484 [bus, address, &fruData](ManagedEntry& entry) {
485 auto findFruDevice =
486 entry.second.find("xyz.openbmc_project.FruDevice");
487 if (findFruDevice == entry.second.end())
488 {
489 return false;
490 }
491 fruData = &(findFruDevice->second);
492 auto findBus = findFruDevice->second.find("BUS");
493 auto findAddress = findFruDevice->second.find("ADDRESS");
494 if (findBus == findFruDevice->second.end() ||
495 findAddress == findFruDevice->second.end())
496 {
497 return false;
498 }
499 if (std::get<uint32_t>(findBus->second) != bus)
500 {
501 return false;
502 }
503 if (std::get<uint32_t>(findAddress->second) != address)
504 {
505 return false;
506 }
507 return true;
508 });
509 if (fru == frus.end())
510 {
511 return ipmi::ccResponseError;
512 }
513 std::string name;
514 auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
515 auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
516 if (findProductName != fruData->end())
517 {
518 name = std::get<std::string>(findProductName->second);
519 }
520 else if (findBoardName != fruData->end())
521 {
522 name = std::get<std::string>(findBoardName->second);
523 }
524 else
525 {
526 name = "UNKNOWN";
527 }
528 if (name.size() > maxFruSdrNameSize)
529 {
530 name = name.substr(0, maxFruSdrNameSize);
531 }
532 size_t sizeDiff = maxFruSdrNameSize - name.size();
533
534 resp.header.record_id_lsb = 0x0; // calling code is to implement these
535 resp.header.record_id_msb = 0x0;
536 resp.header.sdr_version = ipmiSdrVersion;
537 resp.header.record_type = 0x11; // FRU Device Locator
538 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
539 resp.key.deviceAddress = 0x20;
540 resp.key.fruID = device->first;
541 resp.key.accessLun = 0x80; // logical / physical fru device
542 resp.key.channelNumber = 0x0;
543 resp.body.reserved = 0x0;
544 resp.body.deviceType = 0x10;
545 resp.body.deviceTypeModifier = 0x00;
546 resp.body.entityID = 0x0;
547 resp.body.entityInstance = 0x1;
548 resp.body.oem = 0x0;
549 resp.body.deviceIDLen = name.size();
550 name.copy(resp.body.deviceID, name.size());
551
552 return ipmi::ccSuccess;
553 }
554
ipmiStorageReserveSDR(ipmi_netfn_t netfn,ipmi_cmd_t cmd,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)555 ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
556 ipmi_request_t, ipmi_response_t response,
557 ipmi_data_len_t dataLen, ipmi_context_t)
558 {
559 printCommand(+netfn, +cmd);
560
561 if (*dataLen)
562 {
563 *dataLen = 0;
564 return ipmi::ccReqDataLenInvalid;
565 }
566 *dataLen = 0; // default to 0 in case of an error
567 sdrReservationID++;
568 if (sdrReservationID == 0)
569 {
570 sdrReservationID++;
571 }
572 *dataLen = 2;
573 auto resp = static_cast<uint8_t*>(response);
574 resp[0] = sdrReservationID & 0xFF;
575 resp[1] = sdrReservationID >> 8;
576
577 return ipmi::ccSuccess;
578 }
579
ipmiStorageGetSDR(ipmi_netfn_t netfn,ipmi_cmd_t cmd,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)580 ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
581 ipmi_request_t request, ipmi_response_t response,
582 ipmi_data_len_t dataLen, ipmi_context_t)
583 {
584 printCommand(+netfn, +cmd);
585
586 if (*dataLen != 6)
587 {
588 *dataLen = 0;
589 return ipmi::ccReqDataLenInvalid;
590 }
591 auto requestedSize = *dataLen;
592 *dataLen = 0; // default to 0 in case of an error
593
594 constexpr uint16_t lastRecordIndex = 0xFFFF;
595 auto req = static_cast<GetSDRReq*>(request);
596
597 // reservation required for partial reads with non zero offset into
598 // record
599 if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) &&
600 req->offset)
601 {
602 return ipmi::ccInvalidReservationId;
603 }
604
605 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
606 {
607 return ipmi::ccResponseError;
608 }
609
610 size_t fruCount = 0;
611 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
612 if (ret != ipmi::ccSuccess)
613 {
614 return ret;
615 }
616
617 size_t lastRecord = sensorTree.size() + fruCount - 1;
618 if (req->recordID == lastRecordIndex)
619 {
620 req->recordID = lastRecord;
621 }
622 if (req->recordID > lastRecord)
623 {
624 return ipmi::ccInvalidFieldRequest;
625 }
626
627 uint16_t nextRecord = lastRecord >= static_cast<size_t>(req->recordID + 1)
628 ? req->recordID + 1
629 : 0XFFFF;
630
631 auto responseClear = static_cast<uint8_t*>(response);
632 std::fill(responseClear, responseClear + requestedSize, 0);
633
634 auto resp = static_cast<get_sdr::GetSdrResp*>(response);
635 resp->next_record_id_lsb = nextRecord & 0xFF;
636 resp->next_record_id_msb = nextRecord >> 8;
637
638 if (req->recordID >= sensorTree.size())
639 {
640 size_t fruIndex = req->recordID - sensorTree.size();
641 if (fruIndex >= fruCount)
642 {
643 return ipmi::ccInvalidFieldRequest;
644 }
645 get_sdr::SensorDataFruRecord data;
646 if (req->offset > sizeof(data))
647 {
648 return ipmi::ccInvalidFieldRequest;
649 }
650 ret = ipmi::storage::getFruSdrs(fruIndex, data);
651 if (ret != ipmi::ccSuccess)
652 {
653 return ret;
654 }
655 data.header.record_id_msb = req->recordID << 8;
656 data.header.record_id_lsb = req->recordID & 0xFF;
657 if (sizeof(data) < (req->offset + req->bytesToRead))
658 {
659 req->bytesToRead = sizeof(data) - req->offset;
660 }
661 *dataLen = req->bytesToRead + 2; // next record
662 std::memcpy(&resp->record_data, (char*)&data + req->offset,
663 req->bytesToRead);
664 return ipmi::ccSuccess;
665 }
666
667 std::string connection;
668 std::string path;
669 uint16_t sensorIndex = req->recordID;
670 for (const auto& sensor : sensorTree)
671 {
672 if (sensorIndex-- == 0)
673 {
674 if (!sensor.second.size())
675 {
676 return ipmi::ccResponseError;
677 }
678 connection = sensor.second.begin()->first;
679 path = sensor.first;
680 break;
681 }
682 }
683
684 SensorMap sensorMap;
685 if (!getSensorMap(connection, path, sensorMap))
686 {
687 return ipmi::ccResponseError;
688 }
689 uint8_t sensornumber = (req->recordID & 0xFF);
690 get_sdr::SensorDataFullRecord record = {};
691
692 record.header.record_id_msb = req->recordID << 8;
693 record.header.record_id_lsb = req->recordID & 0xFF;
694 record.header.sdr_version = ipmiSdrVersion;
695 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
696 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
697 sizeof(get_sdr::SensorDataRecordHeader);
698 record.key.owner_id = 0x20;
699 record.key.owner_lun = 0x0;
700 record.key.sensor_number = sensornumber;
701
702 record.body.entity_id = 0x0;
703 record.body.entity_instance = 0x01;
704 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
705 record.body.sensor_type = getSensorTypeFromPath(path);
706 std::string type = getSensorTypeStringFromPath(path);
707 auto typeCstr = type.c_str();
708 auto findUnits = sensorUnits.find(typeCstr);
709 if (findUnits != sensorUnits.end())
710 {
711 record.body.sensor_units_2_base =
712 static_cast<uint8_t>(findUnits->second);
713 } // else default 0x0 unspecified
714
715 record.body.event_reading_type = getSensorEventTypeFromPath(path);
716
717 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
718 if (sensorObject == sensorMap.end())
719 {
720 return ipmi::ccResponseError;
721 }
722
723 auto maxObject = sensorObject->second.find("MaxValue");
724 auto minObject = sensorObject->second.find("MinValue");
725 double max = 128;
726 double min = -127;
727 if (maxObject != sensorObject->second.end())
728 {
729 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
730 }
731
732 if (minObject != sensorObject->second.end())
733 {
734 min = std::visit(VariantToDoubleVisitor(), minObject->second);
735 }
736
737 int16_t mValue;
738 int8_t rExp;
739 int16_t bValue;
740 int8_t bExp;
741 bool bSigned;
742
743 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
744 {
745 return ipmi::ccResponseError;
746 }
747
748 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
749 record.body.m_lsb = mValue & 0xFF;
750
751 // move the smallest bit of the MSB into place (bit 9)
752 // the MSbs are bits 7:8 in m_msb_and_tolerance
753 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
754
755 // assign the negative
756 if (mValue < 0)
757 {
758 mMsb |= (1 << 7);
759 }
760 record.body.m_msb_and_tolerance = mMsb;
761
762 record.body.b_lsb = bValue & 0xFF;
763
764 // move the smallest bit of the MSB into place
765 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
766 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
767
768 // assign the negative
769 if (bValue < 0)
770 {
771 bMsb |= (1 << 7);
772 }
773 record.body.b_msb_and_accuracy_lsb = bMsb;
774
775 record.body.r_b_exponents = bExp & 0x7;
776 if (bExp < 0)
777 {
778 record.body.r_b_exponents |= 1 << 3;
779 }
780 record.body.r_b_exponents = (rExp & 0x7) << 4;
781 if (rExp < 0)
782 {
783 record.body.r_b_exponents |= 1 << 7;
784 }
785
786 // todo fill out rest of units
787 if (bSigned)
788 {
789 record.body.sensor_units_1 = 1 << 7;
790 }
791
792 // populate sensor name from path
793 std::string name;
794 size_t nameStart = path.rfind("/");
795 if (nameStart != std::string::npos)
796 {
797 name = path.substr(nameStart + 1, std::string::npos - nameStart);
798 }
799
800 std::replace(name.begin(), name.end(), '_', ' ');
801 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
802 {
803 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
804 }
805 record.body.id_string_info = name.size();
806 std::strncpy(record.body.id_string, name.c_str(),
807 sizeof(record.body.id_string));
808
809 if (sizeof(get_sdr::SensorDataFullRecord) <
810 (req->offset + req->bytesToRead))
811 {
812 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
813 }
814
815 *dataLen = 2 +
816 req->bytesToRead; // bytesToRead + MSB and LSB of next record id
817
818 std::memcpy(&resp->record_data, (char*)&record + req->offset,
819 req->bytesToRead);
820
821 return ipmi::ccSuccess;
822 }
823
getSensorConnectionByName(std::string & name,std::string & connection,std::string & path)824 static int getSensorConnectionByName(std::string& name, std::string& connection,
825 std::string& path)
826 {
827 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
828 {
829 return -1;
830 }
831
832 for (const auto& sensor : sensorTree)
833 {
834 path = sensor.first;
835 if (path.find(name) != std::string::npos)
836 {
837 connection = sensor.second.begin()->first;
838 return 0;
839 }
840 }
841 return -1;
842 }
843
getSensorThreshold(std::string & name,std::string & thresholdStr)844 int getSensorThreshold(std::string& name, std::string& thresholdStr)
845 {
846 std::string connection;
847 std::string path;
848 int ret = -1;
849 thresholdStr = "";
850
851 ret = getSensorConnectionByName(name, connection, path);
852 if (ret < 0)
853 {
854 return ret;
855 }
856
857 SensorMap sensorMap;
858 if (!getSensorMap(connection, path, sensorMap))
859 {
860 return ret;
861 }
862
863 // Iterate threshold interfaces with priority order
864 for (auto& interface : thresholdCheckedOrder)
865 {
866 auto interfaceProperty = alarmProperties.find(interface);
867 if (interfaceProperty == alarmProperties.end())
868 {
869 continue;
870 }
871
872 auto propertyValue = interfaceProperty->second;
873
874 // Checks threshold properties value in sensorMap
875 auto thresholdInterfaceSensorMap = sensorMap.find(interface);
876
877 // Ignore if interface not set
878 if (thresholdInterfaceSensorMap == sensorMap.end())
879 {
880 continue;
881 }
882
883 auto& thresholdMap = thresholdInterfaceSensorMap->second;
884
885 auto& propertyAlarmHigh = propertyValue.at(AlarmType::high);
886 auto alarmHigh = thresholdMap.find(propertyAlarmHigh.name);
887 if (alarmHigh != thresholdMap.end())
888 {
889 if (std::get<bool>(alarmHigh->second))
890 {
891 thresholdStr = propertyAlarmHigh.threshold;
892 break;
893 }
894 }
895
896 auto& propertyAlarmLow = propertyValue.at(AlarmType::low);
897 auto alarmLow = thresholdMap.find(propertyAlarmLow.name);
898 if (alarmLow != thresholdMap.end())
899 {
900 if (std::get<bool>(alarmLow->second))
901 {
902 thresholdStr = propertyAlarmLow.threshold;
903 break;
904 }
905 }
906 }
907
908 return 0;
909 }
910
getSensorValue(std::string & name,double & val)911 int getSensorValue(std::string& name, double& val)
912 {
913 std::string connection;
914 std::string path;
915 int ret = -1;
916
917 ret = getSensorConnectionByName(name, connection, path);
918 if (ret < 0)
919 {
920 return ret;
921 }
922
923 SensorMap sensorMap;
924 if (!getSensorMap(connection, path, sensorMap))
925 {
926 return ret;
927 }
928 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
929
930 if (sensorObject == sensorMap.end() ||
931 sensorObject->second.find("Value") == sensorObject->second.end())
932 {
933 return ret;
934 }
935 auto& valueVariant = sensorObject->second["Value"];
936 val = std::visit(VariantToDoubleVisitor(), valueVariant);
937
938 return 0;
939 }
940
941 const static boost::container::flat_map<const char*, std::string, CmpStr>
942 sensorUnitStr{{{"temperature", "C"},
943 {"voltage", "V"},
944 {"current", "mA"},
945 {"fan_tach", "RPM"},
946 {"fan_pwm", "RPM"},
947 {"power", "W"}}};
948
getSensorUnit(std::string & name,std::string & unit)949 int getSensorUnit(std::string& name, std::string& unit)
950 {
951 std::string connection;
952 std::string path;
953 int ret = -1;
954
955 ret = getSensorConnectionByName(name, connection, path);
956 if (ret < 0)
957 {
958 return ret;
959 }
960
961 std::string sensorTypeStr = getSensorTypeStringFromPath(path);
962 auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str());
963 if (findSensor != sensorUnitStr.end())
964 {
965 unit = findSensor->second;
966 return 0;
967 }
968 else
969 return -1;
970 }
971
ipmiStorageGetFRUInvAreaInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)972 ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(
973 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
974 ipmi_data_len_t dataLen, ipmi_context_t)
975 {
976 if (*dataLen != 1)
977 {
978 *dataLen = 0;
979 return ipmi::ccReqDataLenInvalid;
980 }
981 *dataLen = 0; // default to 0 in case of an error
982
983 uint8_t reqDev = *(static_cast<uint8_t*>(request));
984 if (reqDev == 0xFF)
985 {
986 return ipmi::ccInvalidFieldRequest;
987 }
988 ipmi_ret_t status = replaceCacheFru(reqDev);
989
990 if (status != ipmi::ccSuccess)
991 {
992 return status;
993 }
994
995 GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response);
996 respPtr->inventorySizeLSB = fruCache.size() & 0xFF;
997 respPtr->inventorySizeMSB = fruCache.size() >> 8;
998 respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte);
999
1000 *dataLen = sizeof(GetFRUAreaResp);
1001 return ipmi::ccSuccess;
1002 }
1003
registerStorageFunctions()1004 void registerStorageFunctions()
1005 {
1006 // <Get FRU Inventory Area Info>
1007 ipmiPrintAndRegister(
1008 ipmi::netFnStorage,
1009 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo),
1010 NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR);
1011
1012 // <READ FRU Data>
1013 ipmiPrintAndRegister(
1014 ipmi::netFnStorage,
1015 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL,
1016 ipmiStorageReadFRUData, PRIVILEGE_OPERATOR);
1017
1018 // <WRITE FRU Data>
1019 ipmiPrintAndRegister(
1020 ipmi::netFnStorage,
1021 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData),
1022 NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR);
1023
1024 // <Reserve SDR Repo>
1025 ipmiPrintAndRegister(
1026 ipmi::netFnStorage,
1027 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1028 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1029
1030 // <Get Sdr>
1031 ipmiPrintAndRegister(
1032 ipmi::netFnStorage,
1033 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1034 ipmiStorageGetSDR, PRIVILEGE_USER);
1035 return;
1036 }
1037 } // namespace storage
1038 } // namespace ipmi
1039