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