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