blob: 4dd6b72758ce77545583fc2ad4339e2537b229c0 [file] [log] [blame]
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001/*
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 <iostream>
Vijay Khemka1b6fae32019-03-25 17:43:01 -070023#include <ipmid/utils.hpp>
Vijay Khemkae7d23d02019-03-08 13:13:40 -080024#include <phosphor-logging/log.hpp>
25#include <sdbusplus/message/types.hpp>
26#include <sdbusplus/timer.hpp>
27#include <sensorutils.hpp>
28#include <storagecommands.hpp>
29
30namespace ipmi
31{
32
33namespace storage
34{
35void registerStorageFunctions() __attribute__((constructor));
36
37constexpr static const size_t maxMessageSize = 64;
38constexpr static const size_t maxFruSdrNameSize = 16;
39static constexpr int sensorMapUpdatePeriod = 2;
40using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
Vijay Khemkae7d23d02019-03-08 13:13:40 -080041
42using ManagedObjectSensor =
43 std::map<sdbusplus::message::object_path,
44 std::map<std::string, std::map<std::string, DbusVariant>>>;
45
46static uint16_t sdrReservationID;
47
48static boost::container::flat_map<std::string, ManagedObjectSensor> SensorCache;
49static SensorSubTree sensorTree;
50
51void registerSensorFunctions() __attribute__((constructor));
52using ManagedObjectType = boost::container::flat_map<
53 sdbusplus::message::object_path,
54 boost::container::flat_map<
55 std::string, boost::container::flat_map<std::string, DbusVariant>>>;
56using ManagedEntry = std::pair<
57 sdbusplus::message::object_path,
58 boost::container::flat_map<
59 std::string, boost::container::flat_map<std::string, DbusVariant>>>;
60
61constexpr static const char *fruDeviceServiceName =
62 "xyz.openbmc_project.FruDevice";
63constexpr static const size_t cacheTimeoutSeconds = 10;
64
65static std::vector<uint8_t> fruCache;
66static uint8_t cacheBus = 0xFF;
67static uint8_t cacheAddr = 0XFF;
68
69std::unique_ptr<phosphor::Timer> cacheTimer = nullptr;
70
71// we unfortunately have to build a map of hashes in case there is a
72// collision to verify our dev-id
73boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
74
75static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
76
77static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
78 SensorMap &sensorMap)
79{
80 static boost::container::flat_map<
81 std::string, std::chrono::time_point<std::chrono::steady_clock>>
82 updateTimeMap;
83
84 auto updateFind = updateTimeMap.find(sensorConnection);
85 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
86 if (updateFind != updateTimeMap.end())
87 {
88 lastUpdate = updateFind->second;
89 }
90
91 auto now = std::chrono::steady_clock::now();
92
93 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
94 .count() > sensorMapUpdatePeriod)
95 {
96 updateTimeMap[sensorConnection] = now;
97
98 auto managedObj = dbus.new_method_call(
99 sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
100 "GetManagedObjects");
101
102 ManagedObjectSensor managedObjects;
103 try
104 {
105 auto reply = dbus.call(managedObj);
106 reply.read(managedObjects);
107 }
108 catch (sdbusplus::exception_t &)
109 {
110 phosphor::logging::log<phosphor::logging::level::ERR>(
111 "Error getting managed objects from connection",
112 phosphor::logging::entry("CONNECTION=%s",
113 sensorConnection.c_str()));
114 return false;
115 }
116
117 SensorCache[sensorConnection] = managedObjects;
118 }
119 auto connection = SensorCache.find(sensorConnection);
120 if (connection == SensorCache.end())
121 {
122 return false;
123 }
124 auto path = connection->second.find(sensorPath);
125 if (path == connection->second.end())
126 {
127 return false;
128 }
129 sensorMap = path->second;
130
131 return true;
132}
133
134bool writeFru()
135{
136 sdbusplus::message::message writeFru = dbus.new_method_call(
137 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
138 "xyz.openbmc_project.FruDeviceManager", "WriteFru");
139 writeFru.append(cacheBus, cacheAddr, fruCache);
140 try
141 {
142 sdbusplus::message::message writeFruResp = dbus.call(writeFru);
143 }
144 catch (sdbusplus::exception_t &)
145 {
146 // todo: log sel?
147 phosphor::logging::log<phosphor::logging::level::ERR>(
148 "error writing fru");
149 return false;
150 }
151 return true;
152}
153
154void createTimer()
155{
156 if (cacheTimer == nullptr)
157 {
158 cacheTimer = std::make_unique<phosphor::Timer>(writeFru);
159 }
160}
161
162ipmi_ret_t replaceCacheFru(uint8_t devId)
163{
164 static uint8_t lastDevId = 0xFF;
165
166 bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
167 if (lastDevId == devId && timerRunning)
168 {
169 return IPMI_CC_OK; // cache already up to date
170 }
171 // if timer is running, stop it and writeFru manually
172 else if (timerRunning)
173 {
174 cacheTimer->stop();
175 writeFru();
176 }
177
178 sdbusplus::message::message getObjects = dbus.new_method_call(
179 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
180 "GetManagedObjects");
181 ManagedObjectType frus;
182 try
183 {
184 sdbusplus::message::message resp = dbus.call(getObjects);
185 resp.read(frus);
186 }
187 catch (sdbusplus::exception_t &)
188 {
189 phosphor::logging::log<phosphor::logging::level::ERR>(
190 "replaceCacheFru: error getting managed objects");
191 return IPMI_CC_RESPONSE_ERROR;
192 }
193
194 deviceHashes.clear();
195
196 // hash the object paths to create unique device id's. increment on
197 // collision
198 std::hash<std::string> hasher;
199 for (const auto &fru : frus)
200 {
201 auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
202 if (fruIface == fru.second.end())
203 {
204 continue;
205 }
206
207 auto busFind = fruIface->second.find("BUS");
208 auto addrFind = fruIface->second.find("ADDRESS");
209 if (busFind == fruIface->second.end() ||
210 addrFind == fruIface->second.end())
211 {
212 phosphor::logging::log<phosphor::logging::level::INFO>(
213 "fru device missing Bus or Address",
214 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
215 continue;
216 }
217
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500218 uint8_t fruBus = std::get<uint32_t>(busFind->second);
219 uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800220
221 uint8_t fruHash = 0;
222 // Need to revise this strategy for dev id
223 /*
224 if (fruBus != 0 || fruAddr != 0)
225 {
226 fruHash = hasher(fru.first.str);
227 // can't be 0xFF based on spec, and 0 is reserved for baseboard
228 if (fruHash == 0 || fruHash == 0xFF)
229 {
230 fruHash = 1;
231 }
232 }
233 */
234 std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr);
235
236 bool emplacePassed = false;
237 while (!emplacePassed)
238 {
239 auto resp = deviceHashes.emplace(fruHash, newDev);
240 emplacePassed = resp.second;
241 if (!emplacePassed)
242 {
243 fruHash++;
244 // can't be 0xFF based on spec, and 0 is reserved for
245 // baseboard
246 if (fruHash == 0XFF)
247 {
248 fruHash = 0x1;
249 }
250 }
251 }
252 }
253 auto deviceFind = deviceHashes.find(devId);
254 if (deviceFind == deviceHashes.end())
255 {
256 return IPMI_CC_SENSOR_INVALID;
257 }
258
259 fruCache.clear();
260 sdbusplus::message::message getRawFru = dbus.new_method_call(
261 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
262 "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
263 cacheBus = deviceFind->second.first;
264 cacheAddr = deviceFind->second.second;
265 getRawFru.append(cacheBus, cacheAddr);
266 try
267 {
268 sdbusplus::message::message getRawResp = dbus.call(getRawFru);
269 getRawResp.read(fruCache);
270 }
271 catch (sdbusplus::exception_t &)
272 {
273 lastDevId = 0xFF;
274 cacheBus = 0xFF;
275 cacheAddr = 0xFF;
276 return IPMI_CC_RESPONSE_ERROR;
277 }
278
279 lastDevId = devId;
280 return IPMI_CC_OK;
281}
282
283ipmi_ret_t ipmiStorageReadFRUData(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
284 ipmi_request_t request,
285 ipmi_response_t response,
286 ipmi_data_len_t dataLen,
287 ipmi_context_t context)
288{
289 if (*dataLen != 4)
290 {
291 *dataLen = 0;
292 return IPMI_CC_REQ_DATA_LEN_INVALID;
293 }
294 *dataLen = 0; // default to 0 in case of an error
295
296 auto req = static_cast<GetFRUAreaReq *>(request);
297
298 if (req->countToRead > maxMessageSize - 1)
299 {
300 return IPMI_CC_INVALID_FIELD_REQUEST;
301 }
302 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
303
304 if (status != IPMI_CC_OK)
305 {
306 return status;
307 }
308
309 size_t fromFRUByteLen = 0;
310 if (req->countToRead + req->fruInventoryOffset < fruCache.size())
311 {
312 fromFRUByteLen = req->countToRead;
313 }
314 else if (fruCache.size() > req->fruInventoryOffset)
315 {
316 fromFRUByteLen = fruCache.size() - req->fruInventoryOffset;
317 }
318 size_t padByteLen = req->countToRead - fromFRUByteLen;
319 uint8_t *respPtr = static_cast<uint8_t *>(response);
320 *respPtr = req->countToRead;
321 std::copy(fruCache.begin() + req->fruInventoryOffset,
322 fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen,
323 ++respPtr);
324 // if longer than the fru is requested, fill with 0xFF
325 if (padByteLen)
326 {
327 respPtr += fromFRUByteLen;
328 std::fill(respPtr, respPtr + padByteLen, 0xFF);
329 }
330 *dataLen = fromFRUByteLen + 1;
331
332 return IPMI_CC_OK;
333}
334
335ipmi_ret_t ipmiStorageWriteFRUData(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
336 ipmi_request_t request,
337 ipmi_response_t response,
338 ipmi_data_len_t dataLen,
339 ipmi_context_t context)
340{
341 if (*dataLen < 4 ||
342 *dataLen >=
343 0xFF + 3) // count written return is one byte, so limit to one
344 // byte of data after the three request data bytes
345 {
346 *dataLen = 0;
347 return IPMI_CC_REQ_DATA_LEN_INVALID;
348 }
349
350 auto req = static_cast<WriteFRUDataReq *>(request);
351 size_t writeLen = *dataLen - 3;
352 *dataLen = 0; // default to 0 in case of an error
353
354 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
355 if (status != IPMI_CC_OK)
356 {
357 return status;
358 }
359 int lastWriteAddr = req->fruInventoryOffset + writeLen;
360 if (fruCache.size() < lastWriteAddr)
361 {
362 fruCache.resize(req->fruInventoryOffset + writeLen);
363 }
364
365 std::copy(req->data, req->data + writeLen,
366 fruCache.begin() + req->fruInventoryOffset);
367
368 bool atEnd = false;
369
370 if (fruCache.size() >= sizeof(FRUHeader))
371 {
372
373 FRUHeader *header = reinterpret_cast<FRUHeader *>(fruCache.data());
374
375 int lastRecordStart = std::max(
376 header->internalOffset,
377 std::max(header->chassisOffset,
378 std::max(header->boardOffset, header->productOffset)));
379 // TODO: Handle Multi-Record FRUs?
380
381 lastRecordStart *= 8; // header starts in are multiples of 8 bytes
382
383 // get the length of the area in multiples of 8 bytes
384 if (lastWriteAddr > (lastRecordStart + 1))
385 {
386 // second byte in record area is the length
387 int areaLength(fruCache[lastRecordStart + 1]);
388 areaLength *= 8; // it is in multiples of 8 bytes
389
390 if (lastWriteAddr >= (areaLength + lastRecordStart))
391 {
392 atEnd = true;
393 }
394 }
395 }
396 uint8_t *respPtr = static_cast<uint8_t *>(response);
397 if (atEnd)
398 {
399 // cancel timer, we're at the end so might as well send it
400 cacheTimer->stop();
401 if (!writeFru())
402 {
403 return IPMI_CC_INVALID_FIELD_REQUEST;
404 }
405 *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF));
406 }
407 else
408 {
409 // start a timer, if no further data is sent in cacheTimeoutSeconds
410 // seconds, check to see if it is valid
411 createTimer();
412 cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
413 std::chrono::seconds(cacheTimeoutSeconds)));
414 *respPtr = 0;
415 }
416
417 *dataLen = 1;
418
419 return IPMI_CC_OK;
420}
421
422ipmi_ret_t getFruSdrCount(size_t &count)
423{
424 ipmi_ret_t ret = replaceCacheFru(0);
425 if (ret != IPMI_CC_OK)
426 {
427 return ret;
428 }
429 count = deviceHashes.size();
430 return IPMI_CC_OK;
431}
432
433ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord &resp)
434{
435 ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
436 if (ret != IPMI_CC_OK)
437 {
438 return ret;
439 }
440 if (deviceHashes.size() < index)
441 {
442 return IPMI_CC_INVALID_FIELD_REQUEST;
443 }
444 auto device = deviceHashes.begin() + index;
445 uint8_t &bus = device->second.first;
446 uint8_t &address = device->second.second;
447
448 ManagedObjectType frus;
449
450 sdbusplus::message::message getObjects = dbus.new_method_call(
451 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
452 "GetManagedObjects");
453 try
454 {
455 sdbusplus::message::message resp = dbus.call(getObjects);
456 resp.read(frus);
457 }
458 catch (sdbusplus::exception_t &)
459 {
460 return IPMI_CC_RESPONSE_ERROR;
461 }
462 boost::container::flat_map<std::string, DbusVariant> *fruData = nullptr;
463 auto fru =
464 std::find_if(frus.begin(), frus.end(),
465 [bus, address, &fruData](ManagedEntry &entry) {
466 auto findFruDevice =
467 entry.second.find("xyz.openbmc_project.FruDevice");
468 if (findFruDevice == entry.second.end())
469 {
470 return false;
471 }
472 fruData = &(findFruDevice->second);
473 auto findBus = findFruDevice->second.find("BUS");
474 auto findAddress =
475 findFruDevice->second.find("ADDRESS");
476 if (findBus == findFruDevice->second.end() ||
477 findAddress == findFruDevice->second.end())
478 {
479 return false;
480 }
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500481 if (std::get<uint32_t>(findBus->second) != bus)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800482 {
483 return false;
484 }
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500485 if (std::get<uint32_t>(findAddress->second) != address)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800486 {
487 return false;
488 }
489 return true;
490 });
491 if (fru == frus.end())
492 {
493 return IPMI_CC_RESPONSE_ERROR;
494 }
495 std::string name;
496 auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
497 auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
498 if (findProductName != fruData->end())
499 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500500 name = std::get<std::string>(findProductName->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800501 }
502 else if (findBoardName != fruData->end())
503 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500504 name = std::get<std::string>(findBoardName->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800505 }
506 else
507 {
508 name = "UNKNOWN";
509 }
510 if (name.size() > maxFruSdrNameSize)
511 {
512 name = name.substr(0, maxFruSdrNameSize);
513 }
514 size_t sizeDiff = maxFruSdrNameSize - name.size();
515
516 resp.header.record_id_lsb = 0x0; // calling code is to implement these
517 resp.header.record_id_msb = 0x0;
518 resp.header.sdr_version = ipmiSdrVersion;
519 resp.header.record_type = 0x11; // FRU Device Locator
520 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
521 resp.key.deviceAddress = 0x20;
522 resp.key.fruID = device->first;
523 resp.key.accessLun = 0x80; // logical / physical fru device
524 resp.key.channelNumber = 0x0;
525 resp.body.reserved = 0x0;
526 resp.body.deviceType = 0x10;
527 resp.body.entityID = 0x0;
528 resp.body.entityInstance = 0x1;
529 resp.body.oem = 0x0;
530 resp.body.deviceIDLen = name.size();
531 name.copy(resp.body.deviceID, name.size());
532
533 return IPMI_CC_OK;
534}
535
536ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
537 ipmi_request_t request,
538 ipmi_response_t response,
539 ipmi_data_len_t dataLen,
540 ipmi_context_t context)
541{
542 printCommand(+netfn, +cmd);
543
544 if (*dataLen)
545 {
546 *dataLen = 0;
547 return IPMI_CC_REQ_DATA_LEN_INVALID;
548 }
549 *dataLen = 0; // default to 0 in case of an error
550 sdrReservationID++;
551 if (sdrReservationID == 0)
552 {
553 sdrReservationID++;
554 }
555 *dataLen = 2;
556 auto resp = static_cast<uint8_t *>(response);
557 resp[0] = sdrReservationID & 0xFF;
558 resp[1] = sdrReservationID >> 8;
559
560 return IPMI_CC_OK;
561}
562
563ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
564 ipmi_request_t request, ipmi_response_t response,
565 ipmi_data_len_t dataLen, ipmi_context_t context)
566{
567 printCommand(+netfn, +cmd);
568
569 if (*dataLen != 6)
570 {
571 *dataLen = 0;
572 return IPMI_CC_REQ_DATA_LEN_INVALID;
573 }
574 auto requestedSize = *dataLen;
575 *dataLen = 0; // default to 0 in case of an error
576
577 constexpr uint16_t lastRecordIndex = 0xFFFF;
578 auto req = static_cast<GetSDRReq *>(request);
579
580 // reservation required for partial reads with non zero offset into
581 // record
582 if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) &&
583 req->offset)
584 {
585 return IPMI_CC_INVALID_RESERVATION_ID;
586 }
587
588 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
589 {
590 return IPMI_CC_RESPONSE_ERROR;
591 }
592
593 size_t fruCount = 0;
594 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
595 if (ret != IPMI_CC_OK)
596 {
597 return ret;
598 }
599
600 size_t lastRecord = sensorTree.size() + fruCount - 1;
601 if (req->recordID == lastRecordIndex)
602 {
603 req->recordID = lastRecord;
604 }
605 if (req->recordID > lastRecord)
606 {
607 return IPMI_CC_INVALID_FIELD_REQUEST;
608 }
609
610 uint16_t nextRecord =
611 lastRecord > (req->recordID + 1) ? req->recordID + 1 : 0XFFFF;
612
613 auto responseClear = static_cast<uint8_t *>(response);
614 std::fill(responseClear, responseClear + requestedSize, 0);
615
616 auto resp = static_cast<get_sdr::GetSdrResp *>(response);
617 resp->next_record_id_lsb = nextRecord & 0xFF;
618 resp->next_record_id_msb = nextRecord >> 8;
619
620 if (req->recordID >= sensorTree.size())
621 {
622 size_t fruIndex = req->recordID - sensorTree.size();
623 if (fruIndex >= fruCount)
624 {
625 return IPMI_CC_INVALID_FIELD_REQUEST;
626 }
627 get_sdr::SensorDataFruRecord data;
628 if (req->offset > sizeof(data))
629 {
630 return IPMI_CC_INVALID_FIELD_REQUEST;
631 }
632 ret = ipmi::storage::getFruSdrs(fruIndex, data);
633 if (ret != IPMI_CC_OK)
634 {
635 return ret;
636 }
637 data.header.record_id_msb = req->recordID << 8;
638 data.header.record_id_lsb = req->recordID & 0xFF;
639 if (sizeof(data) < (req->offset + req->bytesToRead))
640 {
641 req->bytesToRead = sizeof(data) - req->offset;
642 }
643 *dataLen = req->bytesToRead + 2; // next record
644 std::memcpy(&resp->record_data, (char *)&data + req->offset,
645 req->bytesToRead);
646 return IPMI_CC_OK;
647 }
648
649 std::string connection;
650 std::string path;
651 uint16_t sensorIndex = req->recordID;
652 for (const auto &sensor : sensorTree)
653 {
654 if (sensorIndex-- == 0)
655 {
656 if (!sensor.second.size())
657 {
658 return IPMI_CC_RESPONSE_ERROR;
659 }
660 connection = sensor.second.begin()->first;
661 path = sensor.first;
662 break;
663 }
664 }
665
666 SensorMap sensorMap;
667 if (!getSensorMap(connection, path, sensorMap))
668 {
669 return IPMI_CC_RESPONSE_ERROR;
670 }
671 uint8_t sensornumber = (req->recordID & 0xFF);
672 get_sdr::SensorDataFullRecord record = {0};
673
674 record.header.record_id_msb = req->recordID << 8;
675 record.header.record_id_lsb = req->recordID & 0xFF;
676 record.header.sdr_version = ipmiSdrVersion;
677 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
678 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
679 sizeof(get_sdr::SensorDataRecordHeader);
680 record.key.owner_id = 0x20;
681 record.key.owner_lun = 0x0;
682 record.key.sensor_number = sensornumber;
683
684 record.body.entity_id = 0x0;
685 record.body.entity_instance = 0x01;
686 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
687 record.body.sensor_type = getSensorTypeFromPath(path);
688 std::string type = getSensorTypeStringFromPath(path);
689 auto typeCstr = type.c_str();
690 auto findUnits = sensorUnits.find(typeCstr);
691 if (findUnits != sensorUnits.end())
692 {
693 record.body.sensor_units_2_base =
694 static_cast<uint8_t>(findUnits->second);
695 } // else default 0x0 unspecified
696
697 record.body.event_reading_type = getSensorEventTypeFromPath(path);
698
699 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
700 if (sensorObject == sensorMap.end())
701 {
702 return IPMI_CC_RESPONSE_ERROR;
703 }
704
705 auto maxObject = sensorObject->second.find("MaxValue");
706 auto minObject = sensorObject->second.find("MinValue");
707 double max = 128;
708 double min = -127;
709 if (maxObject != sensorObject->second.end())
710 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500711 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800712 }
713
714 if (minObject != sensorObject->second.end())
715 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500716 min = std::visit(VariantToDoubleVisitor(), minObject->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800717 }
718
719 int16_t mValue;
720 int8_t rExp;
721 int16_t bValue;
722 int8_t bExp;
723 bool bSigned;
724
725 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
726 {
727 return IPMI_CC_RESPONSE_ERROR;
728 }
729
730 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
731 record.body.m_lsb = mValue & 0xFF;
732
733 // move the smallest bit of the MSB into place (bit 9)
734 // the MSbs are bits 7:8 in m_msb_and_tolerance
735 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
736
737 // assign the negative
738 if (mValue < 0)
739 {
740 mMsb |= (1 << 7);
741 }
742 record.body.m_msb_and_tolerance = mMsb;
743
744 record.body.b_lsb = bValue & 0xFF;
745
746 // move the smallest bit of the MSB into place
747 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
748 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
749
750 // assign the negative
751 if (bValue < 0)
752 {
753 bMsb |= (1 << 7);
754 }
755 record.body.b_msb_and_accuracy_lsb = bMsb;
756
757 record.body.r_b_exponents = bExp & 0x7;
758 if (bExp < 0)
759 {
760 record.body.r_b_exponents |= 1 << 3;
761 }
762 record.body.r_b_exponents = (rExp & 0x7) << 4;
763 if (rExp < 0)
764 {
765 record.body.r_b_exponents |= 1 << 7;
766 }
767
768 // todo fill out rest of units
769 if (bSigned)
770 {
771 record.body.sensor_units_1 = 1 << 7;
772 }
773
774 // populate sensor name from path
775 std::string name;
776 size_t nameStart = path.rfind("/");
777 if (nameStart != std::string::npos)
778 {
779 name = path.substr(nameStart + 1, std::string::npos - nameStart);
780 }
781
782 std::replace(name.begin(), name.end(), '_', ' ');
783 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
784 {
785 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
786 }
787 record.body.id_string_info = name.size();
788 std::strncpy(record.body.id_string, name.c_str(),
789 sizeof(record.body.id_string));
790
791 if (sizeof(get_sdr::SensorDataFullRecord) <
792 (req->offset + req->bytesToRead))
793 {
794 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
795 }
796
797 *dataLen =
798 2 + req->bytesToRead; // bytesToRead + MSB and LSB of next record id
799
800 std::memcpy(&resp->record_data, (char *)&record + req->offset,
801 req->bytesToRead);
802
803 return IPMI_CC_OK;
804}
805
Vijay Khemka427b2762019-12-12 12:49:25 -0800806static int getSensorConnectionByName(std::string &name, std::string &connection,
807 std::string &path)
808{
809 if (sensorTree.empty() && !getSensorSubtree(sensorTree))
810 {
811 return -1;
812 }
813
814 for (const auto &sensor : sensorTree)
815 {
816 path = sensor.first;
817 if (path.find(name) != std::string::npos)
818 {
819 connection = sensor.second.begin()->first;
820 return 0;
821 }
822 }
823 return -1;
824}
825
826int getSensorValue(std::string &name, double &val)
827{
828 std::string connection;
829 std::string path;
830 int ret = -1;
831
832 ret = getSensorConnectionByName(name, connection, path);
833 if (ret < 0)
834 {
835 return ret;
836 }
837
838 SensorMap sensorMap;
839 if (!getSensorMap(connection, path, sensorMap))
840 {
841 return ret;
842 }
843 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
844
845 if (sensorObject == sensorMap.end() ||
846 sensorObject->second.find("Value") == sensorObject->second.end())
847 {
848 return ret;
849 }
850 auto &valueVariant = sensorObject->second["Value"];
851 val = std::visit(VariantToDoubleVisitor(), valueVariant);
852
853 return 0;
854}
855
Vijay Khemka58bd5d82019-12-13 11:05:56 -0800856const static boost::container::flat_map<const char *, std::string, CmpStr>
857 sensorUnitStr{{{"temperature", "C"},
858 {"voltage", "V"},
859 {"current", "mA"},
860 {"fan_tach", "RPM"},
861 {"fan_pwm", "RPM"},
862 {"power", "W"}}};
863
864int getSensorUnit(std::string &name, std::string &unit)
865{
866 std::string connection;
867 std::string path;
868 int ret = -1;
869
870 ret = getSensorConnectionByName(name, connection, path);
871 if (ret < 0)
872 {
873 return ret;
874 }
875
876 std::string sensorTypeStr = getSensorTypeStringFromPath(path);
877 auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str());
878 if (findSensor != sensorUnitStr.end())
879 {
880 unit = findSensor->second;
881 return 0;
882 }
883 else
884 return -1;
885}
886
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800887void registerStorageFunctions()
888{
889 // <READ FRU Data>
890 ipmiPrintAndRegister(
891 NETFUN_STORAGE,
892 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL,
893 ipmiStorageReadFRUData, PRIVILEGE_OPERATOR);
894
895 // <WRITE FRU Data>
896 ipmiPrintAndRegister(
897 NETFUN_STORAGE,
898 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData),
899 NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR);
900
901 // <Reserve SDR Repo>
902 ipmiPrintAndRegister(
903 NETFUN_STORAGE,
904 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
905 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
906
907 // <Get Sdr>
908 ipmiPrintAndRegister(
909 NETFUN_STORAGE,
910 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
911 ipmiStorageGetSDR, PRIVILEGE_USER);
912 return;
913}
914} // namespace storage
915} // namespace ipmi