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 #include <unordered_map> 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 FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data()); 414 415 size_t lastRecordStart = std::max( 416 header->internalOffset, 417 std::max(header->chassisOffset, 418 std::max(header->boardOffset, header->productOffset))); 419 // TODO: Handle Multi-Record FRUs? 420 421 lastRecordStart *= 8; // header starts in are multiples of 8 bytes 422 423 // get the length of the area in multiples of 8 bytes 424 if (lastWriteAddr > (lastRecordStart + 1)) 425 { 426 // second byte in record area is the length 427 int areaLength(fruCache[lastRecordStart + 1]); 428 areaLength *= 8; // it is in multiples of 8 bytes 429 430 if (lastWriteAddr >= (areaLength + lastRecordStart)) 431 { 432 atEnd = true; 433 } 434 } 435 } 436 uint8_t* respPtr = static_cast<uint8_t*>(response); 437 if (atEnd) 438 { 439 // cancel timer, we're at the end so might as well send it 440 cacheTimer->stop(); 441 if (!writeFru()) 442 { 443 return IPMI_CC_INVALID_FIELD_REQUEST; 444 } 445 *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF)); 446 } 447 else 448 { 449 // start a timer, if no further data is sent in cacheTimeoutSeconds 450 // seconds, check to see if it is valid 451 createTimer(); 452 cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>( 453 std::chrono::seconds(cacheTimeoutSeconds))); 454 *respPtr = 0; 455 } 456 457 *dataLen = 1; 458 459 return IPMI_CC_OK; 460 } 461 462 ipmi_ret_t getFruSdrCount(size_t& count) 463 { 464 ipmi_ret_t ret = replaceCacheFru(0); 465 if (ret != IPMI_CC_OK) 466 { 467 return ret; 468 } 469 count = deviceHashes.size(); 470 return IPMI_CC_OK; 471 } 472 473 ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp) 474 { 475 ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list 476 if (ret != IPMI_CC_OK) 477 { 478 return ret; 479 } 480 if (deviceHashes.size() < index) 481 { 482 return IPMI_CC_INVALID_FIELD_REQUEST; 483 } 484 auto device = deviceHashes.begin() + index; 485 uint8_t& bus = device->second.first; 486 uint8_t& address = device->second.second; 487 488 ManagedObjectType frus; 489 490 sdbusplus::message_t getObjects = dbus.new_method_call( 491 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 492 "GetManagedObjects"); 493 try 494 { 495 sdbusplus::message_t resp = dbus.call(getObjects); 496 resp.read(frus); 497 } 498 catch (const sdbusplus::exception_t&) 499 { 500 return IPMI_CC_RESPONSE_ERROR; 501 } 502 boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr; 503 auto fru = std::find_if(frus.begin(), frus.end(), 504 [bus, address, &fruData](ManagedEntry& entry) { 505 auto findFruDevice = entry.second.find("xyz.openbmc_project.FruDevice"); 506 if (findFruDevice == entry.second.end()) 507 { 508 return false; 509 } 510 fruData = &(findFruDevice->second); 511 auto findBus = findFruDevice->second.find("BUS"); 512 auto findAddress = findFruDevice->second.find("ADDRESS"); 513 if (findBus == findFruDevice->second.end() || 514 findAddress == findFruDevice->second.end()) 515 { 516 return false; 517 } 518 if (std::get<uint32_t>(findBus->second) != bus) 519 { 520 return false; 521 } 522 if (std::get<uint32_t>(findAddress->second) != address) 523 { 524 return false; 525 } 526 return true; 527 }); 528 if (fru == frus.end()) 529 { 530 return IPMI_CC_RESPONSE_ERROR; 531 } 532 std::string name; 533 auto findProductName = fruData->find("BOARD_PRODUCT_NAME"); 534 auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME"); 535 if (findProductName != fruData->end()) 536 { 537 name = std::get<std::string>(findProductName->second); 538 } 539 else if (findBoardName != fruData->end()) 540 { 541 name = std::get<std::string>(findBoardName->second); 542 } 543 else 544 { 545 name = "UNKNOWN"; 546 } 547 if (name.size() > maxFruSdrNameSize) 548 { 549 name = name.substr(0, maxFruSdrNameSize); 550 } 551 size_t sizeDiff = maxFruSdrNameSize - name.size(); 552 553 resp.header.record_id_lsb = 0x0; // calling code is to implement these 554 resp.header.record_id_msb = 0x0; 555 resp.header.sdr_version = ipmiSdrVersion; 556 resp.header.record_type = 0x11; // FRU Device Locator 557 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff; 558 resp.key.deviceAddress = 0x20; 559 resp.key.fruID = device->first; 560 resp.key.accessLun = 0x80; // logical / physical fru device 561 resp.key.channelNumber = 0x0; 562 resp.body.reserved = 0x0; 563 resp.body.deviceType = 0x10; 564 resp.body.entityID = 0x0; 565 resp.body.entityInstance = 0x1; 566 resp.body.oem = 0x0; 567 resp.body.deviceIDLen = name.size(); 568 name.copy(resp.body.deviceID, name.size()); 569 570 return IPMI_CC_OK; 571 } 572 573 ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 574 ipmi_request_t, ipmi_response_t response, 575 ipmi_data_len_t dataLen, ipmi_context_t) 576 { 577 printCommand(+netfn, +cmd); 578 579 if (*dataLen) 580 { 581 *dataLen = 0; 582 return IPMI_CC_REQ_DATA_LEN_INVALID; 583 } 584 *dataLen = 0; // default to 0 in case of an error 585 sdrReservationID++; 586 if (sdrReservationID == 0) 587 { 588 sdrReservationID++; 589 } 590 *dataLen = 2; 591 auto resp = static_cast<uint8_t*>(response); 592 resp[0] = sdrReservationID & 0xFF; 593 resp[1] = sdrReservationID >> 8; 594 595 return IPMI_CC_OK; 596 } 597 598 ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 599 ipmi_request_t request, ipmi_response_t response, 600 ipmi_data_len_t dataLen, ipmi_context_t) 601 { 602 printCommand(+netfn, +cmd); 603 604 if (*dataLen != 6) 605 { 606 *dataLen = 0; 607 return IPMI_CC_REQ_DATA_LEN_INVALID; 608 } 609 auto requestedSize = *dataLen; 610 *dataLen = 0; // default to 0 in case of an error 611 612 constexpr uint16_t lastRecordIndex = 0xFFFF; 613 auto req = static_cast<GetSDRReq*>(request); 614 615 // reservation required for partial reads with non zero offset into 616 // record 617 if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) && 618 req->offset) 619 { 620 return IPMI_CC_INVALID_RESERVATION_ID; 621 } 622 623 if (sensorTree.empty() && !getSensorSubtree(sensorTree)) 624 { 625 return IPMI_CC_RESPONSE_ERROR; 626 } 627 628 size_t fruCount = 0; 629 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount); 630 if (ret != IPMI_CC_OK) 631 { 632 return ret; 633 } 634 635 size_t lastRecord = sensorTree.size() + fruCount - 1; 636 if (req->recordID == lastRecordIndex) 637 { 638 req->recordID = lastRecord; 639 } 640 if (req->recordID > lastRecord) 641 { 642 return IPMI_CC_INVALID_FIELD_REQUEST; 643 } 644 645 uint16_t nextRecord = lastRecord > static_cast<size_t>(req->recordID + 1) 646 ? req->recordID + 1 647 : 0XFFFF; 648 649 auto responseClear = static_cast<uint8_t*>(response); 650 std::fill(responseClear, responseClear + requestedSize, 0); 651 652 auto resp = static_cast<get_sdr::GetSdrResp*>(response); 653 resp->next_record_id_lsb = nextRecord & 0xFF; 654 resp->next_record_id_msb = nextRecord >> 8; 655 656 if (req->recordID >= sensorTree.size()) 657 { 658 size_t fruIndex = req->recordID - sensorTree.size(); 659 if (fruIndex >= fruCount) 660 { 661 return IPMI_CC_INVALID_FIELD_REQUEST; 662 } 663 get_sdr::SensorDataFruRecord data; 664 if (req->offset > sizeof(data)) 665 { 666 return IPMI_CC_INVALID_FIELD_REQUEST; 667 } 668 ret = ipmi::storage::getFruSdrs(fruIndex, data); 669 if (ret != IPMI_CC_OK) 670 { 671 return ret; 672 } 673 data.header.record_id_msb = req->recordID << 8; 674 data.header.record_id_lsb = req->recordID & 0xFF; 675 if (sizeof(data) < (req->offset + req->bytesToRead)) 676 { 677 req->bytesToRead = sizeof(data) - req->offset; 678 } 679 *dataLen = req->bytesToRead + 2; // next record 680 std::memcpy(&resp->record_data, (char*)&data + req->offset, 681 req->bytesToRead); 682 return IPMI_CC_OK; 683 } 684 685 std::string connection; 686 std::string path; 687 uint16_t sensorIndex = req->recordID; 688 for (const auto& sensor : sensorTree) 689 { 690 if (sensorIndex-- == 0) 691 { 692 if (!sensor.second.size()) 693 { 694 return IPMI_CC_RESPONSE_ERROR; 695 } 696 connection = sensor.second.begin()->first; 697 path = sensor.first; 698 break; 699 } 700 } 701 702 SensorMap sensorMap; 703 if (!getSensorMap(connection, path, sensorMap)) 704 { 705 return IPMI_CC_RESPONSE_ERROR; 706 } 707 uint8_t sensornumber = (req->recordID & 0xFF); 708 get_sdr::SensorDataFullRecord record = {}; 709 710 record.header.record_id_msb = req->recordID << 8; 711 record.header.record_id_lsb = req->recordID & 0xFF; 712 record.header.sdr_version = ipmiSdrVersion; 713 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD; 714 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) - 715 sizeof(get_sdr::SensorDataRecordHeader); 716 record.key.owner_id = 0x20; 717 record.key.owner_lun = 0x0; 718 record.key.sensor_number = sensornumber; 719 720 record.body.entity_id = 0x0; 721 record.body.entity_instance = 0x01; 722 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis 723 record.body.sensor_type = getSensorTypeFromPath(path); 724 std::string type = getSensorTypeStringFromPath(path); 725 auto typeCstr = type.c_str(); 726 auto findUnits = sensorUnits.find(typeCstr); 727 if (findUnits != sensorUnits.end()) 728 { 729 record.body.sensor_units_2_base = 730 static_cast<uint8_t>(findUnits->second); 731 } // else default 0x0 unspecified 732 733 record.body.event_reading_type = getSensorEventTypeFromPath(path); 734 735 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 736 if (sensorObject == sensorMap.end()) 737 { 738 return IPMI_CC_RESPONSE_ERROR; 739 } 740 741 auto maxObject = sensorObject->second.find("MaxValue"); 742 auto minObject = sensorObject->second.find("MinValue"); 743 double max = 128; 744 double min = -127; 745 if (maxObject != sensorObject->second.end()) 746 { 747 max = std::visit(VariantToDoubleVisitor(), maxObject->second); 748 } 749 750 if (minObject != sensorObject->second.end()) 751 { 752 min = std::visit(VariantToDoubleVisitor(), minObject->second); 753 } 754 755 int16_t mValue; 756 int8_t rExp; 757 int16_t bValue; 758 int8_t bExp; 759 bool bSigned; 760 761 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 762 { 763 return IPMI_CC_RESPONSE_ERROR; 764 } 765 766 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4 767 record.body.m_lsb = mValue & 0xFF; 768 769 // move the smallest bit of the MSB into place (bit 9) 770 // the MSbs are bits 7:8 in m_msb_and_tolerance 771 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0; 772 773 // assign the negative 774 if (mValue < 0) 775 { 776 mMsb |= (1 << 7); 777 } 778 record.body.m_msb_and_tolerance = mMsb; 779 780 record.body.b_lsb = bValue & 0xFF; 781 782 // move the smallest bit of the MSB into place 783 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb 784 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0; 785 786 // assign the negative 787 if (bValue < 0) 788 { 789 bMsb |= (1 << 7); 790 } 791 record.body.b_msb_and_accuracy_lsb = bMsb; 792 793 record.body.r_b_exponents = bExp & 0x7; 794 if (bExp < 0) 795 { 796 record.body.r_b_exponents |= 1 << 3; 797 } 798 record.body.r_b_exponents = (rExp & 0x7) << 4; 799 if (rExp < 0) 800 { 801 record.body.r_b_exponents |= 1 << 7; 802 } 803 804 // todo fill out rest of units 805 if (bSigned) 806 { 807 record.body.sensor_units_1 = 1 << 7; 808 } 809 810 // populate sensor name from path 811 std::string name; 812 size_t nameStart = path.rfind("/"); 813 if (nameStart != std::string::npos) 814 { 815 name = path.substr(nameStart + 1, std::string::npos - nameStart); 816 } 817 818 std::replace(name.begin(), name.end(), '_', ' '); 819 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH) 820 { 821 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH); 822 } 823 record.body.id_string_info = name.size(); 824 std::strncpy(record.body.id_string, name.c_str(), 825 sizeof(record.body.id_string)); 826 827 if (sizeof(get_sdr::SensorDataFullRecord) < 828 (req->offset + req->bytesToRead)) 829 { 830 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset; 831 } 832 833 *dataLen = 2 + 834 req->bytesToRead; // bytesToRead + MSB and LSB of next record id 835 836 std::memcpy(&resp->record_data, (char*)&record + req->offset, 837 req->bytesToRead); 838 839 return IPMI_CC_OK; 840 } 841 842 static int getSensorConnectionByName(std::string& name, std::string& connection, 843 std::string& path) 844 { 845 if (sensorTree.empty() && !getSensorSubtree(sensorTree)) 846 { 847 return -1; 848 } 849 850 for (const auto& sensor : sensorTree) 851 { 852 path = sensor.first; 853 if (path.find(name) != std::string::npos) 854 { 855 connection = sensor.second.begin()->first; 856 return 0; 857 } 858 } 859 return -1; 860 } 861 862 int getSensorThreshold(std::string& name, std::string& thresholdStr) 863 { 864 std::string connection; 865 std::string path; 866 int ret = -1; 867 thresholdStr = ""; 868 869 ret = getSensorConnectionByName(name, connection, path); 870 if (ret < 0) 871 { 872 return ret; 873 } 874 875 SensorMap sensorMap; 876 if (!getSensorMap(connection, path, sensorMap)) 877 { 878 return ret; 879 } 880 881 // Iterate threshold interfaces with priority order 882 for (auto& interface : thresholdCheckedOrder) 883 { 884 auto interfaceProperty = alarmProperties.find(interface); 885 if (interfaceProperty == alarmProperties.end()) 886 { 887 continue; 888 } 889 890 auto propertyValue = interfaceProperty->second; 891 892 // Checks threshold properties value in sensorMap 893 auto thresholdInterfaceSensorMap = sensorMap.find(interface); 894 895 // Ignore if interface not set 896 if (thresholdInterfaceSensorMap == sensorMap.end()) 897 { 898 continue; 899 } 900 901 auto& thresholdMap = thresholdInterfaceSensorMap->second; 902 903 auto& propertyAlarmHigh = propertyValue.at(AlarmType::high); 904 auto alarmHigh = thresholdMap.find(propertyAlarmHigh.name); 905 if (alarmHigh != thresholdMap.end()) 906 { 907 if (std::get<bool>(alarmHigh->second)) 908 { 909 thresholdStr = propertyAlarmHigh.threshold; 910 break; 911 } 912 } 913 914 auto& propertyAlarmLow = propertyValue.at(AlarmType::low); 915 auto alarmLow = thresholdMap.find(propertyAlarmLow.name); 916 if (alarmLow != thresholdMap.end()) 917 { 918 if (std::get<bool>(alarmLow->second)) 919 { 920 thresholdStr = propertyAlarmLow.threshold; 921 break; 922 } 923 } 924 } 925 926 return 0; 927 } 928 929 int getSensorValue(std::string& name, double& val) 930 { 931 std::string connection; 932 std::string path; 933 int ret = -1; 934 935 ret = getSensorConnectionByName(name, connection, path); 936 if (ret < 0) 937 { 938 return ret; 939 } 940 941 SensorMap sensorMap; 942 if (!getSensorMap(connection, path, sensorMap)) 943 { 944 return ret; 945 } 946 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 947 948 if (sensorObject == sensorMap.end() || 949 sensorObject->second.find("Value") == sensorObject->second.end()) 950 { 951 return ret; 952 } 953 auto& valueVariant = sensorObject->second["Value"]; 954 val = std::visit(VariantToDoubleVisitor(), valueVariant); 955 956 return 0; 957 } 958 959 const static boost::container::flat_map<const char*, std::string, CmpStr> 960 sensorUnitStr{{{"temperature", "C"}, 961 {"voltage", "V"}, 962 {"current", "mA"}, 963 {"fan_tach", "RPM"}, 964 {"fan_pwm", "RPM"}, 965 {"power", "W"}}}; 966 967 int getSensorUnit(std::string& name, std::string& unit) 968 { 969 std::string connection; 970 std::string path; 971 int ret = -1; 972 973 ret = getSensorConnectionByName(name, connection, path); 974 if (ret < 0) 975 { 976 return ret; 977 } 978 979 std::string sensorTypeStr = getSensorTypeStringFromPath(path); 980 auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str()); 981 if (findSensor != sensorUnitStr.end()) 982 { 983 unit = findSensor->second; 984 return 0; 985 } 986 else 987 return -1; 988 } 989 990 ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(ipmi_netfn_t, ipmi_cmd_t, 991 ipmi_request_t request, 992 ipmi_response_t response, 993 ipmi_data_len_t dataLen, ipmi_context_t) 994 { 995 if (*dataLen != 1) 996 { 997 *dataLen = 0; 998 return IPMI_CC_REQ_DATA_LEN_INVALID; 999 } 1000 *dataLen = 0; // default to 0 in case of an error 1001 1002 uint8_t reqDev = *(static_cast<uint8_t*>(request)); 1003 if (reqDev == 0xFF) 1004 { 1005 return IPMI_CC_INVALID_FIELD_REQUEST; 1006 } 1007 ipmi_ret_t status = replaceCacheFru(reqDev); 1008 1009 if (status != IPMI_CC_OK) 1010 { 1011 return status; 1012 } 1013 1014 GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response); 1015 respPtr->inventorySizeLSB = fruCache.size() & 0xFF; 1016 respPtr->inventorySizeMSB = fruCache.size() >> 8; 1017 respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte); 1018 1019 *dataLen = sizeof(GetFRUAreaResp); 1020 return IPMI_CC_OK; 1021 } 1022 1023 void registerStorageFunctions() 1024 { 1025 // <Get FRU Inventory Area Info> 1026 ipmiPrintAndRegister( 1027 NETFUN_STORAGE, 1028 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo), 1029 NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR); 1030 1031 // <READ FRU Data> 1032 ipmiPrintAndRegister( 1033 NETFUN_STORAGE, 1034 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL, 1035 ipmiStorageReadFRUData, PRIVILEGE_OPERATOR); 1036 1037 // <WRITE FRU Data> 1038 ipmiPrintAndRegister( 1039 NETFUN_STORAGE, 1040 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData), 1041 NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR); 1042 1043 // <Reserve SDR Repo> 1044 ipmiPrintAndRegister( 1045 NETFUN_STORAGE, 1046 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR), 1047 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER); 1048 1049 // <Get Sdr> 1050 ipmiPrintAndRegister( 1051 NETFUN_STORAGE, 1052 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr, 1053 ipmiStorageGetSDR, PRIVILEGE_USER); 1054 return; 1055 } 1056 } // namespace storage 1057 } // namespace ipmi 1058