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_CC_OK; // 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_CC_RESPONSE_ERROR;
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_CC_SENSOR_INVALID;
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_CC_RESPONSE_ERROR;
301 }
302
303 lastDevId = devId;
304 return IPMI_CC_OK;
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_CC_REQ_DATA_LEN_INVALID;
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_CC_INVALID_FIELD_REQUEST;
323 }
324 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
325
326 if (status != IPMI_CC_OK)
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_CC_OK;
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_CC_REQ_DATA_LEN_INVALID;
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_CC_OK)
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_CC_INVALID_FIELD_REQUEST;
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_CC_OK;
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_CC_OK)
445 {
446 return ret;
447 }
448 count = deviceHashes.size();
449 return IPMI_CC_OK;
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_CC_OK)
456 {
457 return ret;
458 }
459 if (deviceHashes.size() < index)
460 {
461 return IPMI_CC_INVALID_FIELD_REQUEST;
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_CC_RESPONSE_ERROR;
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_CC_RESPONSE_ERROR;
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.entityID = 0x0;
546 resp.body.entityInstance = 0x1;
547 resp.body.oem = 0x0;
548 resp.body.deviceIDLen = name.size();
549 name.copy(resp.body.deviceID, name.size());
550
551 return IPMI_CC_OK;
552 }
553
ipmiStorageReserveSDR(ipmi_netfn_t netfn,ipmi_cmd_t cmd,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)554 ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
555 ipmi_request_t, ipmi_response_t response,
556 ipmi_data_len_t dataLen, ipmi_context_t)
557 {
558 printCommand(+netfn, +cmd);
559
560 if (*dataLen)
561 {
562 *dataLen = 0;
563 return IPMI_CC_REQ_DATA_LEN_INVALID;
564 }
565 *dataLen = 0; // default to 0 in case of an error
566 sdrReservationID++;
567 if (sdrReservationID == 0)
568 {
569 sdrReservationID++;
570 }
571 *dataLen = 2;
572 auto resp = static_cast<uint8_t*>(response);
573 resp[0] = sdrReservationID & 0xFF;
574 resp[1] = sdrReservationID >> 8;
575
576 return IPMI_CC_OK;
577 }
578
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)579 ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
580 ipmi_request_t request, ipmi_response_t response,
581 ipmi_data_len_t dataLen, ipmi_context_t)
582 {
583 printCommand(+netfn, +cmd);
584
585 if (*dataLen != 6)
586 {
587 *dataLen = 0;
588 return IPMI_CC_REQ_DATA_LEN_INVALID;
589 }
590 auto requestedSize = *dataLen;
591 *dataLen = 0; // default to 0 in case of an error
592
593 constexpr uint16_t lastRecordIndex = 0xFFFF;
594 auto req = static_cast<GetSDRReq*>(request);
595
596 // reservation required for partial reads with non zero offset into
597 // record
598 if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) &&
599 req->offset)
600 {
601 return IPMI_CC_INVALID_RESERVATION_ID;
602 }
603
604 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
605 {
606 return IPMI_CC_RESPONSE_ERROR;
607 }
608
609 size_t fruCount = 0;
610 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
611 if (ret != IPMI_CC_OK)
612 {
613 return ret;
614 }
615
616 size_t lastRecord = sensorTree.size() + fruCount - 1;
617 if (req->recordID == lastRecordIndex)
618 {
619 req->recordID = lastRecord;
620 }
621 if (req->recordID > lastRecord)
622 {
623 return IPMI_CC_INVALID_FIELD_REQUEST;
624 }
625
626 uint16_t nextRecord = lastRecord >= static_cast<size_t>(req->recordID + 1)
627 ? req->recordID + 1
628 : 0XFFFF;
629
630 auto responseClear = static_cast<uint8_t*>(response);
631 std::fill(responseClear, responseClear + requestedSize, 0);
632
633 auto resp = static_cast<get_sdr::GetSdrResp*>(response);
634 resp->next_record_id_lsb = nextRecord & 0xFF;
635 resp->next_record_id_msb = nextRecord >> 8;
636
637 if (req->recordID >= sensorTree.size())
638 {
639 size_t fruIndex = req->recordID - sensorTree.size();
640 if (fruIndex >= fruCount)
641 {
642 return IPMI_CC_INVALID_FIELD_REQUEST;
643 }
644 get_sdr::SensorDataFruRecord data;
645 if (req->offset > sizeof(data))
646 {
647 return IPMI_CC_INVALID_FIELD_REQUEST;
648 }
649 ret = ipmi::storage::getFruSdrs(fruIndex, data);
650 if (ret != IPMI_CC_OK)
651 {
652 return ret;
653 }
654 data.header.record_id_msb = req->recordID << 8;
655 data.header.record_id_lsb = req->recordID & 0xFF;
656 if (sizeof(data) < (req->offset + req->bytesToRead))
657 {
658 req->bytesToRead = sizeof(data) - req->offset;
659 }
660 *dataLen = req->bytesToRead + 2; // next record
661 std::memcpy(&resp->record_data, (char*)&data + req->offset,
662 req->bytesToRead);
663 return IPMI_CC_OK;
664 }
665
666 std::string connection;
667 std::string path;
668 uint16_t sensorIndex = req->recordID;
669 for (const auto& sensor : sensorTree)
670 {
671 if (sensorIndex-- == 0)
672 {
673 if (!sensor.second.size())
674 {
675 return IPMI_CC_RESPONSE_ERROR;
676 }
677 connection = sensor.second.begin()->first;
678 path = sensor.first;
679 break;
680 }
681 }
682
683 SensorMap sensorMap;
684 if (!getSensorMap(connection, path, sensorMap))
685 {
686 return IPMI_CC_RESPONSE_ERROR;
687 }
688 uint8_t sensornumber = (req->recordID & 0xFF);
689 get_sdr::SensorDataFullRecord record = {};
690
691 record.header.record_id_msb = req->recordID << 8;
692 record.header.record_id_lsb = req->recordID & 0xFF;
693 record.header.sdr_version = ipmiSdrVersion;
694 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
695 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
696 sizeof(get_sdr::SensorDataRecordHeader);
697 record.key.owner_id = 0x20;
698 record.key.owner_lun = 0x0;
699 record.key.sensor_number = sensornumber;
700
701 record.body.entity_id = 0x0;
702 record.body.entity_instance = 0x01;
703 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
704 record.body.sensor_type = getSensorTypeFromPath(path);
705 std::string type = getSensorTypeStringFromPath(path);
706 auto typeCstr = type.c_str();
707 auto findUnits = sensorUnits.find(typeCstr);
708 if (findUnits != sensorUnits.end())
709 {
710 record.body.sensor_units_2_base =
711 static_cast<uint8_t>(findUnits->second);
712 } // else default 0x0 unspecified
713
714 record.body.event_reading_type = getSensorEventTypeFromPath(path);
715
716 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
717 if (sensorObject == sensorMap.end())
718 {
719 return IPMI_CC_RESPONSE_ERROR;
720 }
721
722 auto maxObject = sensorObject->second.find("MaxValue");
723 auto minObject = sensorObject->second.find("MinValue");
724 double max = 128;
725 double min = -127;
726 if (maxObject != sensorObject->second.end())
727 {
728 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
729 }
730
731 if (minObject != sensorObject->second.end())
732 {
733 min = std::visit(VariantToDoubleVisitor(), minObject->second);
734 }
735
736 int16_t mValue;
737 int8_t rExp;
738 int16_t bValue;
739 int8_t bExp;
740 bool bSigned;
741
742 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
743 {
744 return IPMI_CC_RESPONSE_ERROR;
745 }
746
747 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
748 record.body.m_lsb = mValue & 0xFF;
749
750 // move the smallest bit of the MSB into place (bit 9)
751 // the MSbs are bits 7:8 in m_msb_and_tolerance
752 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
753
754 // assign the negative
755 if (mValue < 0)
756 {
757 mMsb |= (1 << 7);
758 }
759 record.body.m_msb_and_tolerance = mMsb;
760
761 record.body.b_lsb = bValue & 0xFF;
762
763 // move the smallest bit of the MSB into place
764 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
765 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
766
767 // assign the negative
768 if (bValue < 0)
769 {
770 bMsb |= (1 << 7);
771 }
772 record.body.b_msb_and_accuracy_lsb = bMsb;
773
774 record.body.r_b_exponents = bExp & 0x7;
775 if (bExp < 0)
776 {
777 record.body.r_b_exponents |= 1 << 3;
778 }
779 record.body.r_b_exponents = (rExp & 0x7) << 4;
780 if (rExp < 0)
781 {
782 record.body.r_b_exponents |= 1 << 7;
783 }
784
785 // todo fill out rest of units
786 if (bSigned)
787 {
788 record.body.sensor_units_1 = 1 << 7;
789 }
790
791 // populate sensor name from path
792 std::string name;
793 size_t nameStart = path.rfind("/");
794 if (nameStart != std::string::npos)
795 {
796 name = path.substr(nameStart + 1, std::string::npos - nameStart);
797 }
798
799 std::replace(name.begin(), name.end(), '_', ' ');
800 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
801 {
802 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
803 }
804 record.body.id_string_info = name.size();
805 std::strncpy(record.body.id_string, name.c_str(),
806 sizeof(record.body.id_string));
807
808 if (sizeof(get_sdr::SensorDataFullRecord) <
809 (req->offset + req->bytesToRead))
810 {
811 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
812 }
813
814 *dataLen = 2 +
815 req->bytesToRead; // bytesToRead + MSB and LSB of next record id
816
817 std::memcpy(&resp->record_data, (char*)&record + req->offset,
818 req->bytesToRead);
819
820 return IPMI_CC_OK;
821 }
822
getSensorConnectionByName(std::string & name,std::string & connection,std::string & path)823 static int getSensorConnectionByName(std::string& name, std::string& connection,
824 std::string& path)
825 {
826 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
827 {
828 return -1;
829 }
830
831 for (const auto& sensor : sensorTree)
832 {
833 path = sensor.first;
834 if (path.find(name) != std::string::npos)
835 {
836 connection = sensor.second.begin()->first;
837 return 0;
838 }
839 }
840 return -1;
841 }
842
getSensorThreshold(std::string & name,std::string & thresholdStr)843 int getSensorThreshold(std::string& name, std::string& thresholdStr)
844 {
845 std::string connection;
846 std::string path;
847 int ret = -1;
848 thresholdStr = "";
849
850 ret = getSensorConnectionByName(name, connection, path);
851 if (ret < 0)
852 {
853 return ret;
854 }
855
856 SensorMap sensorMap;
857 if (!getSensorMap(connection, path, sensorMap))
858 {
859 return ret;
860 }
861
862 // Iterate threshold interfaces with priority order
863 for (auto& interface : thresholdCheckedOrder)
864 {
865 auto interfaceProperty = alarmProperties.find(interface);
866 if (interfaceProperty == alarmProperties.end())
867 {
868 continue;
869 }
870
871 auto propertyValue = interfaceProperty->second;
872
873 // Checks threshold properties value in sensorMap
874 auto thresholdInterfaceSensorMap = sensorMap.find(interface);
875
876 // Ignore if interface not set
877 if (thresholdInterfaceSensorMap == sensorMap.end())
878 {
879 continue;
880 }
881
882 auto& thresholdMap = thresholdInterfaceSensorMap->second;
883
884 auto& propertyAlarmHigh = propertyValue.at(AlarmType::high);
885 auto alarmHigh = thresholdMap.find(propertyAlarmHigh.name);
886 if (alarmHigh != thresholdMap.end())
887 {
888 if (std::get<bool>(alarmHigh->second))
889 {
890 thresholdStr = propertyAlarmHigh.threshold;
891 break;
892 }
893 }
894
895 auto& propertyAlarmLow = propertyValue.at(AlarmType::low);
896 auto alarmLow = thresholdMap.find(propertyAlarmLow.name);
897 if (alarmLow != thresholdMap.end())
898 {
899 if (std::get<bool>(alarmLow->second))
900 {
901 thresholdStr = propertyAlarmLow.threshold;
902 break;
903 }
904 }
905 }
906
907 return 0;
908 }
909
getSensorValue(std::string & name,double & val)910 int getSensorValue(std::string& name, double& val)
911 {
912 std::string connection;
913 std::string path;
914 int ret = -1;
915
916 ret = getSensorConnectionByName(name, connection, path);
917 if (ret < 0)
918 {
919 return ret;
920 }
921
922 SensorMap sensorMap;
923 if (!getSensorMap(connection, path, sensorMap))
924 {
925 return ret;
926 }
927 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
928
929 if (sensorObject == sensorMap.end() ||
930 sensorObject->second.find("Value") == sensorObject->second.end())
931 {
932 return ret;
933 }
934 auto& valueVariant = sensorObject->second["Value"];
935 val = std::visit(VariantToDoubleVisitor(), valueVariant);
936
937 return 0;
938 }
939
940 const static boost::container::flat_map<const char*, std::string, CmpStr>
941 sensorUnitStr{{{"temperature", "C"},
942 {"voltage", "V"},
943 {"current", "mA"},
944 {"fan_tach", "RPM"},
945 {"fan_pwm", "RPM"},
946 {"power", "W"}}};
947
getSensorUnit(std::string & name,std::string & unit)948 int getSensorUnit(std::string& name, std::string& unit)
949 {
950 std::string connection;
951 std::string path;
952 int ret = -1;
953
954 ret = getSensorConnectionByName(name, connection, path);
955 if (ret < 0)
956 {
957 return ret;
958 }
959
960 std::string sensorTypeStr = getSensorTypeStringFromPath(path);
961 auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str());
962 if (findSensor != sensorUnitStr.end())
963 {
964 unit = findSensor->second;
965 return 0;
966 }
967 else
968 return -1;
969 }
970
ipmiStorageGetFRUInvAreaInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)971 ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(
972 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
973 ipmi_data_len_t dataLen, ipmi_context_t)
974 {
975 if (*dataLen != 1)
976 {
977 *dataLen = 0;
978 return IPMI_CC_REQ_DATA_LEN_INVALID;
979 }
980 *dataLen = 0; // default to 0 in case of an error
981
982 uint8_t reqDev = *(static_cast<uint8_t*>(request));
983 if (reqDev == 0xFF)
984 {
985 return IPMI_CC_INVALID_FIELD_REQUEST;
986 }
987 ipmi_ret_t status = replaceCacheFru(reqDev);
988
989 if (status != IPMI_CC_OK)
990 {
991 return status;
992 }
993
994 GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response);
995 respPtr->inventorySizeLSB = fruCache.size() & 0xFF;
996 respPtr->inventorySizeMSB = fruCache.size() >> 8;
997 respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte);
998
999 *dataLen = sizeof(GetFRUAreaResp);
1000 return IPMI_CC_OK;
1001 }
1002
registerStorageFunctions()1003 void registerStorageFunctions()
1004 {
1005 // <Get FRU Inventory Area Info>
1006 ipmiPrintAndRegister(
1007 NETFUN_STORAGE,
1008 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo),
1009 NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR);
1010
1011 // <READ FRU Data>
1012 ipmiPrintAndRegister(
1013 NETFUN_STORAGE,
1014 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL,
1015 ipmiStorageReadFRUData, PRIVILEGE_OPERATOR);
1016
1017 // <WRITE FRU Data>
1018 ipmiPrintAndRegister(
1019 NETFUN_STORAGE,
1020 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData),
1021 NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR);
1022
1023 // <Reserve SDR Repo>
1024 ipmiPrintAndRegister(
1025 NETFUN_STORAGE,
1026 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1027 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1028
1029 // <Get Sdr>
1030 ipmiPrintAndRegister(
1031 NETFUN_STORAGE,
1032 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1033 ipmiStorageGetSDR, PRIVILEGE_USER);
1034 return;
1035 }
1036 } // namespace storage
1037 } // namespace ipmi
1038