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_t 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(), "/xyz/openbmc_project/sensors", 101 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 102 103 ManagedObjectSensor managedObjects; 104 try 105 { 106 auto reply = dbus.call(managedObj); 107 reply.read(managedObjects); 108 } 109 catch (const 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_t 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_t writeFruResp = dbus.call(writeFru); 144 } 145 catch (const 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_t getObjects = dbus.new_method_call( 180 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 181 "GetManagedObjects"); 182 ManagedObjectType frus; 183 try 184 { 185 sdbusplus::message_t resp = dbus.call(getObjects); 186 resp.read(frus); 187 } 188 catch (const 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 [[maybe_unused]] 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_t 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_t getRawResp = dbus.call(getRawFru); 270 getRawResp.read(fruCache); 271 } 272 catch (const 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, ipmi_cmd_t, 285 ipmi_request_t request, 286 ipmi_response_t response, 287 ipmi_data_len_t dataLen, ipmi_context_t) 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 335 ipmi_ret_t ipmiStorageWriteFRUData(ipmi_netfn_t, ipmi_cmd_t, 336 ipmi_request_t request, 337 ipmi_response_t response, 338 ipmi_data_len_t dataLen, ipmi_context_t) 339 { 340 if (*dataLen < 4 || 341 *dataLen >= 342 0xFF + 3) // count written return is one byte, so limit to one 343 // byte of data after the three request data bytes 344 { 345 *dataLen = 0; 346 return IPMI_CC_REQ_DATA_LEN_INVALID; 347 } 348 349 auto req = static_cast<WriteFRUDataReq*>(request); 350 size_t writeLen = *dataLen - 3; 351 *dataLen = 0; // default to 0 in case of an error 352 353 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID); 354 if (status != IPMI_CC_OK) 355 { 356 return status; 357 } 358 size_t lastWriteAddr = req->fruInventoryOffset + writeLen; 359 if (fruCache.size() < lastWriteAddr) 360 { 361 fruCache.resize(req->fruInventoryOffset + writeLen); 362 } 363 364 std::copy(req->data, req->data + writeLen, 365 fruCache.begin() + req->fruInventoryOffset); 366 367 bool atEnd = false; 368 369 if (fruCache.size() >= sizeof(FRUHeader)) 370 { 371 372 FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data()); 373 374 size_t lastRecordStart = std::max( 375 header->internalOffset, 376 std::max(header->chassisOffset, 377 std::max(header->boardOffset, header->productOffset))); 378 // TODO: Handle Multi-Record FRUs? 379 380 lastRecordStart *= 8; // header starts in are multiples of 8 bytes 381 382 // get the length of the area in multiples of 8 bytes 383 if (lastWriteAddr > (lastRecordStart + 1)) 384 { 385 // second byte in record area is the length 386 int areaLength(fruCache[lastRecordStart + 1]); 387 areaLength *= 8; // it is in multiples of 8 bytes 388 389 if (lastWriteAddr >= (areaLength + lastRecordStart)) 390 { 391 atEnd = true; 392 } 393 } 394 } 395 uint8_t* respPtr = static_cast<uint8_t*>(response); 396 if (atEnd) 397 { 398 // cancel timer, we're at the end so might as well send it 399 cacheTimer->stop(); 400 if (!writeFru()) 401 { 402 return IPMI_CC_INVALID_FIELD_REQUEST; 403 } 404 *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF)); 405 } 406 else 407 { 408 // start a timer, if no further data is sent in cacheTimeoutSeconds 409 // seconds, check to see if it is valid 410 createTimer(); 411 cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>( 412 std::chrono::seconds(cacheTimeoutSeconds))); 413 *respPtr = 0; 414 } 415 416 *dataLen = 1; 417 418 return IPMI_CC_OK; 419 } 420 421 ipmi_ret_t getFruSdrCount(size_t& count) 422 { 423 ipmi_ret_t ret = replaceCacheFru(0); 424 if (ret != IPMI_CC_OK) 425 { 426 return ret; 427 } 428 count = deviceHashes.size(); 429 return IPMI_CC_OK; 430 } 431 432 ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp) 433 { 434 ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list 435 if (ret != IPMI_CC_OK) 436 { 437 return ret; 438 } 439 if (deviceHashes.size() < index) 440 { 441 return IPMI_CC_INVALID_FIELD_REQUEST; 442 } 443 auto device = deviceHashes.begin() + index; 444 uint8_t& bus = device->second.first; 445 uint8_t& address = device->second.second; 446 447 ManagedObjectType frus; 448 449 sdbusplus::message_t getObjects = dbus.new_method_call( 450 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 451 "GetManagedObjects"); 452 try 453 { 454 sdbusplus::message_t resp = dbus.call(getObjects); 455 resp.read(frus); 456 } 457 catch (const sdbusplus::exception_t&) 458 { 459 return IPMI_CC_RESPONSE_ERROR; 460 } 461 boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr; 462 auto fru = 463 std::find_if(frus.begin(), frus.end(), 464 [bus, address, &fruData](ManagedEntry& entry) { 465 auto findFruDevice = 466 entry.second.find("xyz.openbmc_project.FruDevice"); 467 if (findFruDevice == entry.second.end()) 468 { 469 return false; 470 } 471 fruData = &(findFruDevice->second); 472 auto findBus = findFruDevice->second.find("BUS"); 473 auto findAddress = 474 findFruDevice->second.find("ADDRESS"); 475 if (findBus == findFruDevice->second.end() || 476 findAddress == findFruDevice->second.end()) 477 { 478 return false; 479 } 480 if (std::get<uint32_t>(findBus->second) != bus) 481 { 482 return false; 483 } 484 if (std::get<uint32_t>(findAddress->second) != address) 485 { 486 return false; 487 } 488 return true; 489 }); 490 if (fru == frus.end()) 491 { 492 return IPMI_CC_RESPONSE_ERROR; 493 } 494 std::string name; 495 auto findProductName = fruData->find("BOARD_PRODUCT_NAME"); 496 auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME"); 497 if (findProductName != fruData->end()) 498 { 499 name = std::get<std::string>(findProductName->second); 500 } 501 else if (findBoardName != fruData->end()) 502 { 503 name = std::get<std::string>(findBoardName->second); 504 } 505 else 506 { 507 name = "UNKNOWN"; 508 } 509 if (name.size() > maxFruSdrNameSize) 510 { 511 name = name.substr(0, maxFruSdrNameSize); 512 } 513 size_t sizeDiff = maxFruSdrNameSize - name.size(); 514 515 resp.header.record_id_lsb = 0x0; // calling code is to implement these 516 resp.header.record_id_msb = 0x0; 517 resp.header.sdr_version = ipmiSdrVersion; 518 resp.header.record_type = 0x11; // FRU Device Locator 519 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff; 520 resp.key.deviceAddress = 0x20; 521 resp.key.fruID = device->first; 522 resp.key.accessLun = 0x80; // logical / physical fru device 523 resp.key.channelNumber = 0x0; 524 resp.body.reserved = 0x0; 525 resp.body.deviceType = 0x10; 526 resp.body.entityID = 0x0; 527 resp.body.entityInstance = 0x1; 528 resp.body.oem = 0x0; 529 resp.body.deviceIDLen = name.size(); 530 name.copy(resp.body.deviceID, name.size()); 531 532 return IPMI_CC_OK; 533 } 534 535 ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 536 ipmi_request_t, ipmi_response_t response, 537 ipmi_data_len_t dataLen, ipmi_context_t) 538 { 539 printCommand(+netfn, +cmd); 540 541 if (*dataLen) 542 { 543 *dataLen = 0; 544 return IPMI_CC_REQ_DATA_LEN_INVALID; 545 } 546 *dataLen = 0; // default to 0 in case of an error 547 sdrReservationID++; 548 if (sdrReservationID == 0) 549 { 550 sdrReservationID++; 551 } 552 *dataLen = 2; 553 auto resp = static_cast<uint8_t*>(response); 554 resp[0] = sdrReservationID & 0xFF; 555 resp[1] = sdrReservationID >> 8; 556 557 return IPMI_CC_OK; 558 } 559 560 ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 561 ipmi_request_t request, ipmi_response_t response, 562 ipmi_data_len_t dataLen, ipmi_context_t) 563 { 564 printCommand(+netfn, +cmd); 565 566 if (*dataLen != 6) 567 { 568 *dataLen = 0; 569 return IPMI_CC_REQ_DATA_LEN_INVALID; 570 } 571 auto requestedSize = *dataLen; 572 *dataLen = 0; // default to 0 in case of an error 573 574 constexpr uint16_t lastRecordIndex = 0xFFFF; 575 auto req = static_cast<GetSDRReq*>(request); 576 577 // reservation required for partial reads with non zero offset into 578 // record 579 if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) && 580 req->offset) 581 { 582 return IPMI_CC_INVALID_RESERVATION_ID; 583 } 584 585 if (sensorTree.empty() && !getSensorSubtree(sensorTree)) 586 { 587 return IPMI_CC_RESPONSE_ERROR; 588 } 589 590 size_t fruCount = 0; 591 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount); 592 if (ret != IPMI_CC_OK) 593 { 594 return ret; 595 } 596 597 size_t lastRecord = sensorTree.size() + fruCount - 1; 598 if (req->recordID == lastRecordIndex) 599 { 600 req->recordID = lastRecord; 601 } 602 if (req->recordID > lastRecord) 603 { 604 return IPMI_CC_INVALID_FIELD_REQUEST; 605 } 606 607 uint16_t nextRecord = lastRecord > static_cast<size_t>(req->recordID + 1) 608 ? req->recordID + 1 609 : 0XFFFF; 610 611 auto responseClear = static_cast<uint8_t*>(response); 612 std::fill(responseClear, responseClear + requestedSize, 0); 613 614 auto resp = static_cast<get_sdr::GetSdrResp*>(response); 615 resp->next_record_id_lsb = nextRecord & 0xFF; 616 resp->next_record_id_msb = nextRecord >> 8; 617 618 if (req->recordID >= sensorTree.size()) 619 { 620 size_t fruIndex = req->recordID - sensorTree.size(); 621 if (fruIndex >= fruCount) 622 { 623 return IPMI_CC_INVALID_FIELD_REQUEST; 624 } 625 get_sdr::SensorDataFruRecord data; 626 if (req->offset > sizeof(data)) 627 { 628 return IPMI_CC_INVALID_FIELD_REQUEST; 629 } 630 ret = ipmi::storage::getFruSdrs(fruIndex, data); 631 if (ret != IPMI_CC_OK) 632 { 633 return ret; 634 } 635 data.header.record_id_msb = req->recordID << 8; 636 data.header.record_id_lsb = req->recordID & 0xFF; 637 if (sizeof(data) < (req->offset + req->bytesToRead)) 638 { 639 req->bytesToRead = sizeof(data) - req->offset; 640 } 641 *dataLen = req->bytesToRead + 2; // next record 642 std::memcpy(&resp->record_data, (char*)&data + req->offset, 643 req->bytesToRead); 644 return IPMI_CC_OK; 645 } 646 647 std::string connection; 648 std::string path; 649 uint16_t sensorIndex = req->recordID; 650 for (const auto& sensor : sensorTree) 651 { 652 if (sensorIndex-- == 0) 653 { 654 if (!sensor.second.size()) 655 { 656 return IPMI_CC_RESPONSE_ERROR; 657 } 658 connection = sensor.second.begin()->first; 659 path = sensor.first; 660 break; 661 } 662 } 663 664 SensorMap sensorMap; 665 if (!getSensorMap(connection, path, sensorMap)) 666 { 667 return IPMI_CC_RESPONSE_ERROR; 668 } 669 uint8_t sensornumber = (req->recordID & 0xFF); 670 get_sdr::SensorDataFullRecord record = {}; 671 672 record.header.record_id_msb = req->recordID << 8; 673 record.header.record_id_lsb = req->recordID & 0xFF; 674 record.header.sdr_version = ipmiSdrVersion; 675 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD; 676 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) - 677 sizeof(get_sdr::SensorDataRecordHeader); 678 record.key.owner_id = 0x20; 679 record.key.owner_lun = 0x0; 680 record.key.sensor_number = sensornumber; 681 682 record.body.entity_id = 0x0; 683 record.body.entity_instance = 0x01; 684 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis 685 record.body.sensor_type = getSensorTypeFromPath(path); 686 std::string type = getSensorTypeStringFromPath(path); 687 auto typeCstr = type.c_str(); 688 auto findUnits = sensorUnits.find(typeCstr); 689 if (findUnits != sensorUnits.end()) 690 { 691 record.body.sensor_units_2_base = 692 static_cast<uint8_t>(findUnits->second); 693 } // else default 0x0 unspecified 694 695 record.body.event_reading_type = getSensorEventTypeFromPath(path); 696 697 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 698 if (sensorObject == sensorMap.end()) 699 { 700 return IPMI_CC_RESPONSE_ERROR; 701 } 702 703 auto maxObject = sensorObject->second.find("MaxValue"); 704 auto minObject = sensorObject->second.find("MinValue"); 705 double max = 128; 706 double min = -127; 707 if (maxObject != sensorObject->second.end()) 708 { 709 max = std::visit(VariantToDoubleVisitor(), maxObject->second); 710 } 711 712 if (minObject != sensorObject->second.end()) 713 { 714 min = std::visit(VariantToDoubleVisitor(), minObject->second); 715 } 716 717 int16_t mValue; 718 int8_t rExp; 719 int16_t bValue; 720 int8_t bExp; 721 bool bSigned; 722 723 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 724 { 725 return IPMI_CC_RESPONSE_ERROR; 726 } 727 728 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4 729 record.body.m_lsb = mValue & 0xFF; 730 731 // move the smallest bit of the MSB into place (bit 9) 732 // the MSbs are bits 7:8 in m_msb_and_tolerance 733 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0; 734 735 // assign the negative 736 if (mValue < 0) 737 { 738 mMsb |= (1 << 7); 739 } 740 record.body.m_msb_and_tolerance = mMsb; 741 742 record.body.b_lsb = bValue & 0xFF; 743 744 // move the smallest bit of the MSB into place 745 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb 746 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0; 747 748 // assign the negative 749 if (bValue < 0) 750 { 751 bMsb |= (1 << 7); 752 } 753 record.body.b_msb_and_accuracy_lsb = bMsb; 754 755 record.body.r_b_exponents = bExp & 0x7; 756 if (bExp < 0) 757 { 758 record.body.r_b_exponents |= 1 << 3; 759 } 760 record.body.r_b_exponents = (rExp & 0x7) << 4; 761 if (rExp < 0) 762 { 763 record.body.r_b_exponents |= 1 << 7; 764 } 765 766 // todo fill out rest of units 767 if (bSigned) 768 { 769 record.body.sensor_units_1 = 1 << 7; 770 } 771 772 // populate sensor name from path 773 std::string name; 774 size_t nameStart = path.rfind("/"); 775 if (nameStart != std::string::npos) 776 { 777 name = path.substr(nameStart + 1, std::string::npos - nameStart); 778 } 779 780 std::replace(name.begin(), name.end(), '_', ' '); 781 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH) 782 { 783 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH); 784 } 785 record.body.id_string_info = name.size(); 786 std::strncpy(record.body.id_string, name.c_str(), 787 sizeof(record.body.id_string)); 788 789 if (sizeof(get_sdr::SensorDataFullRecord) < 790 (req->offset + req->bytesToRead)) 791 { 792 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset; 793 } 794 795 *dataLen = 796 2 + req->bytesToRead; // bytesToRead + MSB and LSB of next record id 797 798 std::memcpy(&resp->record_data, (char*)&record + req->offset, 799 req->bytesToRead); 800 801 return IPMI_CC_OK; 802 } 803 804 static int getSensorConnectionByName(std::string& name, std::string& connection, 805 std::string& path) 806 { 807 if (sensorTree.empty() && !getSensorSubtree(sensorTree)) 808 { 809 return -1; 810 } 811 812 for (const auto& sensor : sensorTree) 813 { 814 path = sensor.first; 815 if (path.find(name) != std::string::npos) 816 { 817 connection = sensor.second.begin()->first; 818 return 0; 819 } 820 } 821 return -1; 822 } 823 824 int getSensorValue(std::string& name, double& val) 825 { 826 std::string connection; 827 std::string path; 828 int ret = -1; 829 830 ret = getSensorConnectionByName(name, connection, path); 831 if (ret < 0) 832 { 833 return ret; 834 } 835 836 SensorMap sensorMap; 837 if (!getSensorMap(connection, path, sensorMap)) 838 { 839 return ret; 840 } 841 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 842 843 if (sensorObject == sensorMap.end() || 844 sensorObject->second.find("Value") == sensorObject->second.end()) 845 { 846 return ret; 847 } 848 auto& valueVariant = sensorObject->second["Value"]; 849 val = std::visit(VariantToDoubleVisitor(), valueVariant); 850 851 return 0; 852 } 853 854 const static boost::container::flat_map<const char*, std::string, CmpStr> 855 sensorUnitStr{{{"temperature", "C"}, 856 {"voltage", "V"}, 857 {"current", "mA"}, 858 {"fan_tach", "RPM"}, 859 {"fan_pwm", "RPM"}, 860 {"power", "W"}}}; 861 862 int getSensorUnit(std::string& name, std::string& unit) 863 { 864 std::string connection; 865 std::string path; 866 int ret = -1; 867 868 ret = getSensorConnectionByName(name, connection, path); 869 if (ret < 0) 870 { 871 return ret; 872 } 873 874 std::string sensorTypeStr = getSensorTypeStringFromPath(path); 875 auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str()); 876 if (findSensor != sensorUnitStr.end()) 877 { 878 unit = findSensor->second; 879 return 0; 880 } 881 else 882 return -1; 883 } 884 885 ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(ipmi_netfn_t, ipmi_cmd_t, 886 ipmi_request_t request, 887 ipmi_response_t response, 888 ipmi_data_len_t dataLen, ipmi_context_t) 889 { 890 if (*dataLen != 1) 891 { 892 *dataLen = 0; 893 return IPMI_CC_REQ_DATA_LEN_INVALID; 894 } 895 *dataLen = 0; // default to 0 in case of an error 896 897 uint8_t reqDev = *(static_cast<uint8_t*>(request)); 898 if (reqDev == 0xFF) 899 { 900 return IPMI_CC_INVALID_FIELD_REQUEST; 901 } 902 ipmi_ret_t status = replaceCacheFru(reqDev); 903 904 if (status != IPMI_CC_OK) 905 { 906 return status; 907 } 908 909 GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response); 910 respPtr->inventorySizeLSB = fruCache.size() & 0xFF; 911 respPtr->inventorySizeMSB = fruCache.size() >> 8; 912 respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte); 913 914 *dataLen = sizeof(GetFRUAreaResp); 915 return IPMI_CC_OK; 916 } 917 918 void registerStorageFunctions() 919 { 920 // <Get FRU Inventory Area Info> 921 ipmiPrintAndRegister( 922 NETFUN_STORAGE, 923 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo), 924 NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR); 925 926 // <READ FRU Data> 927 ipmiPrintAndRegister( 928 NETFUN_STORAGE, 929 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL, 930 ipmiStorageReadFRUData, PRIVILEGE_OPERATOR); 931 932 // <WRITE FRU Data> 933 ipmiPrintAndRegister( 934 NETFUN_STORAGE, 935 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData), 936 NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR); 937 938 // <Reserve SDR Repo> 939 ipmiPrintAndRegister( 940 NETFUN_STORAGE, 941 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR), 942 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER); 943 944 // <Get Sdr> 945 ipmiPrintAndRegister( 946 NETFUN_STORAGE, 947 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr, 948 ipmiStorageGetSDR, PRIVILEGE_USER); 949 return; 950 } 951 } // namespace storage 952 } // namespace ipmi 953