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