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