13f7c5e40SJason M. Bills /* 23f7c5e40SJason M. Bills // Copyright (c) 2017 2018 Intel Corporation 33f7c5e40SJason M. Bills // 43f7c5e40SJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License"); 53f7c5e40SJason M. Bills // you may not use this file except in compliance with the License. 63f7c5e40SJason M. Bills // You may obtain a copy of the License at 73f7c5e40SJason M. Bills // 83f7c5e40SJason M. Bills // http://www.apache.org/licenses/LICENSE-2.0 93f7c5e40SJason M. Bills // 103f7c5e40SJason M. Bills // Unless required by applicable law or agreed to in writing, software 113f7c5e40SJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS, 123f7c5e40SJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133f7c5e40SJason M. Bills // See the License for the specific language governing permissions and 143f7c5e40SJason M. Bills // limitations under the License. 153f7c5e40SJason M. Bills */ 163f7c5e40SJason M. Bills 17*6d9c83fdSJason M. Bills #include <ipmid/api.h> 183f7c5e40SJason M. Bills 193f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp> 203f7c5e40SJason M. Bills #include <commandutils.hpp> 213f7c5e40SJason M. Bills #include <iostream> 223f7c5e40SJason M. Bills #include <phosphor-logging/log.hpp> 233f7c5e40SJason M. Bills #include <sdbusplus/message/types.hpp> 243f7c5e40SJason M. Bills #include <sdbusplus/timer.hpp> 253f7c5e40SJason M. Bills #include <storagecommands.hpp> 263f7c5e40SJason M. Bills 273f7c5e40SJason M. Bills namespace ipmi 283f7c5e40SJason M. Bills { 293f7c5e40SJason M. Bills 303f7c5e40SJason M. Bills namespace storage 313f7c5e40SJason M. Bills { 323f7c5e40SJason M. Bills 33e2d1aee3SJason M. Bills constexpr static const size_t maxMessageSize = 64; 343f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16; 353f7c5e40SJason M. Bills using ManagedObjectType = boost::container::flat_map< 363f7c5e40SJason M. Bills sdbusplus::message::object_path, 373f7c5e40SJason M. Bills boost::container::flat_map< 383f7c5e40SJason M. Bills std::string, boost::container::flat_map<std::string, DbusVariant>>>; 393f7c5e40SJason M. Bills using ManagedEntry = std::pair< 403f7c5e40SJason M. Bills sdbusplus::message::object_path, 413f7c5e40SJason M. Bills boost::container::flat_map< 423f7c5e40SJason M. Bills std::string, boost::container::flat_map<std::string, DbusVariant>>>; 433f7c5e40SJason M. Bills 443bcba457SJames Feist constexpr static const char* fruDeviceServiceName = 453bcba457SJames Feist "xyz.openbmc_project.FruDevice"; 46e2d1aee3SJason M. Bills constexpr static const size_t cacheTimeoutSeconds = 10; 473f7c5e40SJason M. Bills 483f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache; 493f7c5e40SJason M. Bills static uint8_t cacheBus = 0xFF; 503f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF; 513f7c5e40SJason M. Bills 523f7c5e40SJason M. Bills std::unique_ptr<phosphor::Timer> cacheTimer = nullptr; 533f7c5e40SJason M. Bills 543f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a 553f7c5e40SJason M. Bills // collision to verify our dev-id 563f7c5e40SJason M. Bills boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes; 573f7c5e40SJason M. Bills 58e2d1aee3SJason M. Bills void registerStorageFunctions() __attribute__((constructor)); 593f7c5e40SJason M. Bills static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); 603f7c5e40SJason M. Bills 613f7c5e40SJason M. Bills bool writeFru() 623f7c5e40SJason M. Bills { 633f7c5e40SJason M. Bills sdbusplus::message::message writeFru = dbus.new_method_call( 643f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 653f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "WriteFru"); 663f7c5e40SJason M. Bills writeFru.append(cacheBus, cacheAddr, fruCache); 673f7c5e40SJason M. Bills try 683f7c5e40SJason M. Bills { 693f7c5e40SJason M. Bills sdbusplus::message::message writeFruResp = dbus.call(writeFru); 703f7c5e40SJason M. Bills } 713f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 723f7c5e40SJason M. Bills { 733f7c5e40SJason M. Bills // todo: log sel? 743f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 753f7c5e40SJason M. Bills "error writing fru"); 763f7c5e40SJason M. Bills return false; 773f7c5e40SJason M. Bills } 783f7c5e40SJason M. Bills return true; 793f7c5e40SJason M. Bills } 803f7c5e40SJason M. Bills 81e2d1aee3SJason M. Bills void createTimer() 82e2d1aee3SJason M. Bills { 83e2d1aee3SJason M. Bills if (cacheTimer == nullptr) 84e2d1aee3SJason M. Bills { 85e2d1aee3SJason M. Bills cacheTimer = std::make_unique<phosphor::Timer>(writeFru); 86e2d1aee3SJason M. Bills } 87e2d1aee3SJason M. Bills } 88e2d1aee3SJason M. Bills 893f7c5e40SJason M. Bills ipmi_ret_t replaceCacheFru(uint8_t devId) 903f7c5e40SJason M. Bills { 913f7c5e40SJason M. Bills static uint8_t lastDevId = 0xFF; 923f7c5e40SJason M. Bills 933f7c5e40SJason M. Bills bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired(); 943f7c5e40SJason M. Bills if (lastDevId == devId && timerRunning) 953f7c5e40SJason M. Bills { 963f7c5e40SJason M. Bills return IPMI_CC_OK; // cache already up to date 973f7c5e40SJason M. Bills } 983f7c5e40SJason M. Bills // if timer is running, stop it and writeFru manually 993f7c5e40SJason M. Bills else if (timerRunning) 1003f7c5e40SJason M. Bills { 1013f7c5e40SJason M. Bills cacheTimer->stop(); 1023f7c5e40SJason M. Bills writeFru(); 1033f7c5e40SJason M. Bills } 1043f7c5e40SJason M. Bills 1053f7c5e40SJason M. Bills sdbusplus::message::message getObjects = dbus.new_method_call( 1063f7c5e40SJason M. Bills fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 1073f7c5e40SJason M. Bills "GetManagedObjects"); 1083f7c5e40SJason M. Bills ManagedObjectType frus; 1093f7c5e40SJason M. Bills try 1103f7c5e40SJason M. Bills { 1113f7c5e40SJason M. Bills sdbusplus::message::message resp = dbus.call(getObjects); 1123f7c5e40SJason M. Bills resp.read(frus); 1133f7c5e40SJason M. Bills } 1143f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 1153f7c5e40SJason M. Bills { 1163f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 1173f7c5e40SJason M. Bills "replaceCacheFru: error getting managed objects"); 1183f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 1193f7c5e40SJason M. Bills } 1203f7c5e40SJason M. Bills 1213f7c5e40SJason M. Bills deviceHashes.clear(); 1223f7c5e40SJason M. Bills 1233f7c5e40SJason M. Bills // hash the object paths to create unique device id's. increment on 1243f7c5e40SJason M. Bills // collision 1253f7c5e40SJason M. Bills std::hash<std::string> hasher; 1263f7c5e40SJason M. Bills for (const auto& fru : frus) 1273f7c5e40SJason M. Bills { 1283f7c5e40SJason M. Bills auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice"); 1293f7c5e40SJason M. Bills if (fruIface == fru.second.end()) 1303f7c5e40SJason M. Bills { 1313f7c5e40SJason M. Bills continue; 1323f7c5e40SJason M. Bills } 1333f7c5e40SJason M. Bills 1343f7c5e40SJason M. Bills auto busFind = fruIface->second.find("BUS"); 1353f7c5e40SJason M. Bills auto addrFind = fruIface->second.find("ADDRESS"); 1363f7c5e40SJason M. Bills if (busFind == fruIface->second.end() || 1373f7c5e40SJason M. Bills addrFind == fruIface->second.end()) 1383f7c5e40SJason M. Bills { 1393f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::INFO>( 1403f7c5e40SJason M. Bills "fru device missing Bus or Address", 1413f7c5e40SJason M. Bills phosphor::logging::entry("FRU=%s", fru.first.str.c_str())); 1423f7c5e40SJason M. Bills continue; 1433f7c5e40SJason M. Bills } 1443f7c5e40SJason M. Bills 1453f7c5e40SJason M. Bills uint8_t fruBus = 1463f7c5e40SJason M. Bills sdbusplus::message::variant_ns::get<uint32_t>(busFind->second); 1473f7c5e40SJason M. Bills uint8_t fruAddr = 1483f7c5e40SJason M. Bills sdbusplus::message::variant_ns::get<uint32_t>(addrFind->second); 1493f7c5e40SJason M. Bills 1503f7c5e40SJason M. Bills uint8_t fruHash = 0; 1513f7c5e40SJason M. Bills if (fruBus != 0 || fruAddr != 0) 1523f7c5e40SJason M. Bills { 1533f7c5e40SJason M. Bills fruHash = hasher(fru.first.str); 1543f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for baseboard 1553f7c5e40SJason M. Bills if (fruHash == 0 || fruHash == 0xFF) 1563f7c5e40SJason M. Bills { 1573f7c5e40SJason M. Bills fruHash = 1; 1583f7c5e40SJason M. Bills } 1593f7c5e40SJason M. Bills } 1603f7c5e40SJason M. Bills std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr); 1613f7c5e40SJason M. Bills 1623f7c5e40SJason M. Bills bool emplacePassed = false; 1633f7c5e40SJason M. Bills while (!emplacePassed) 1643f7c5e40SJason M. Bills { 1653f7c5e40SJason M. Bills auto resp = deviceHashes.emplace(fruHash, newDev); 1663f7c5e40SJason M. Bills emplacePassed = resp.second; 1673f7c5e40SJason M. Bills if (!emplacePassed) 1683f7c5e40SJason M. Bills { 1693f7c5e40SJason M. Bills fruHash++; 1703f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for 1713f7c5e40SJason M. Bills // baseboard 1723f7c5e40SJason M. Bills if (fruHash == 0XFF) 1733f7c5e40SJason M. Bills { 1743f7c5e40SJason M. Bills fruHash = 0x1; 1753f7c5e40SJason M. Bills } 1763f7c5e40SJason M. Bills } 1773f7c5e40SJason M. Bills } 1783f7c5e40SJason M. Bills } 1793f7c5e40SJason M. Bills auto deviceFind = deviceHashes.find(devId); 1803f7c5e40SJason M. Bills if (deviceFind == deviceHashes.end()) 1813f7c5e40SJason M. Bills { 1823f7c5e40SJason M. Bills return IPMI_CC_SENSOR_INVALID; 1833f7c5e40SJason M. Bills } 1843f7c5e40SJason M. Bills 1853f7c5e40SJason M. Bills fruCache.clear(); 1863f7c5e40SJason M. Bills sdbusplus::message::message getRawFru = dbus.new_method_call( 1873f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 1883f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "GetRawFru"); 1893f7c5e40SJason M. Bills cacheBus = deviceFind->second.first; 1903f7c5e40SJason M. Bills cacheAddr = deviceFind->second.second; 1913f7c5e40SJason M. Bills getRawFru.append(cacheBus, cacheAddr); 1923f7c5e40SJason M. Bills try 1933f7c5e40SJason M. Bills { 1943f7c5e40SJason M. Bills sdbusplus::message::message getRawResp = dbus.call(getRawFru); 1953f7c5e40SJason M. Bills getRawResp.read(fruCache); 1963f7c5e40SJason M. Bills } 1973f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 1983f7c5e40SJason M. Bills { 1993f7c5e40SJason M. Bills lastDevId = 0xFF; 2003f7c5e40SJason M. Bills cacheBus = 0xFF; 2013f7c5e40SJason M. Bills cacheAddr = 0xFF; 2023f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 2033f7c5e40SJason M. Bills } 2043f7c5e40SJason M. Bills 2053f7c5e40SJason M. Bills lastDevId = devId; 2063f7c5e40SJason M. Bills return IPMI_CC_OK; 2073f7c5e40SJason M. Bills } 2083f7c5e40SJason M. Bills 209e2d1aee3SJason M. Bills ipmi_ret_t ipmiStorageReadFRUData(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 210e2d1aee3SJason M. Bills ipmi_request_t request, 211e2d1aee3SJason M. Bills ipmi_response_t response, 212e2d1aee3SJason M. Bills ipmi_data_len_t dataLen, 213e2d1aee3SJason M. Bills ipmi_context_t context) 214e2d1aee3SJason M. Bills { 215e2d1aee3SJason M. Bills if (*dataLen != 4) 216e2d1aee3SJason M. Bills { 217e2d1aee3SJason M. Bills *dataLen = 0; 218e2d1aee3SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 219e2d1aee3SJason M. Bills } 220e2d1aee3SJason M. Bills *dataLen = 0; // default to 0 in case of an error 221e2d1aee3SJason M. Bills 222e2d1aee3SJason M. Bills auto req = static_cast<GetFRUAreaReq*>(request); 223e2d1aee3SJason M. Bills 224e2d1aee3SJason M. Bills if (req->countToRead > maxMessageSize - 1) 225e2d1aee3SJason M. Bills { 226e2d1aee3SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 227e2d1aee3SJason M. Bills } 228e2d1aee3SJason M. Bills ipmi_ret_t status = replaceCacheFru(req->fruDeviceID); 229e2d1aee3SJason M. Bills 230e2d1aee3SJason M. Bills if (status != IPMI_CC_OK) 231e2d1aee3SJason M. Bills { 232e2d1aee3SJason M. Bills return status; 233e2d1aee3SJason M. Bills } 234e2d1aee3SJason M. Bills 235e2d1aee3SJason M. Bills size_t fromFRUByteLen = 0; 236e2d1aee3SJason M. Bills if (req->countToRead + req->fruInventoryOffset < fruCache.size()) 237e2d1aee3SJason M. Bills { 238e2d1aee3SJason M. Bills fromFRUByteLen = req->countToRead; 239e2d1aee3SJason M. Bills } 240e2d1aee3SJason M. Bills else if (fruCache.size() > req->fruInventoryOffset) 241e2d1aee3SJason M. Bills { 242e2d1aee3SJason M. Bills fromFRUByteLen = fruCache.size() - req->fruInventoryOffset; 243e2d1aee3SJason M. Bills } 244e2d1aee3SJason M. Bills size_t padByteLen = req->countToRead - fromFRUByteLen; 245e2d1aee3SJason M. Bills uint8_t* respPtr = static_cast<uint8_t*>(response); 246e2d1aee3SJason M. Bills *respPtr = req->countToRead; 247e2d1aee3SJason M. Bills std::copy(fruCache.begin() + req->fruInventoryOffset, 248e2d1aee3SJason M. Bills fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen, 249e2d1aee3SJason M. Bills ++respPtr); 250e2d1aee3SJason M. Bills // if longer than the fru is requested, fill with 0xFF 251e2d1aee3SJason M. Bills if (padByteLen) 252e2d1aee3SJason M. Bills { 253e2d1aee3SJason M. Bills respPtr += fromFRUByteLen; 254e2d1aee3SJason M. Bills std::fill(respPtr, respPtr + padByteLen, 0xFF); 255e2d1aee3SJason M. Bills } 256e2d1aee3SJason M. Bills *dataLen = fromFRUByteLen + 1; 257e2d1aee3SJason M. Bills 258e2d1aee3SJason M. Bills return IPMI_CC_OK; 259e2d1aee3SJason M. Bills } 260e2d1aee3SJason M. Bills 261e2d1aee3SJason M. Bills ipmi_ret_t ipmiStorageWriteFRUData(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 262e2d1aee3SJason M. Bills ipmi_request_t request, 263e2d1aee3SJason M. Bills ipmi_response_t response, 264e2d1aee3SJason M. Bills ipmi_data_len_t dataLen, 265e2d1aee3SJason M. Bills ipmi_context_t context) 266e2d1aee3SJason M. Bills { 267e2d1aee3SJason M. Bills if (*dataLen < 4 || 268e2d1aee3SJason M. Bills *dataLen >= 269e2d1aee3SJason M. Bills 0xFF + 3) // count written return is one byte, so limit to one byte 270e2d1aee3SJason M. Bills // of data after the three request data bytes 271e2d1aee3SJason M. Bills { 272e2d1aee3SJason M. Bills *dataLen = 0; 273e2d1aee3SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 274e2d1aee3SJason M. Bills } 275e2d1aee3SJason M. Bills 276e2d1aee3SJason M. Bills auto req = static_cast<WriteFRUDataReq*>(request); 277e2d1aee3SJason M. Bills size_t writeLen = *dataLen - 3; 278e2d1aee3SJason M. Bills *dataLen = 0; // default to 0 in case of an error 279e2d1aee3SJason M. Bills 280e2d1aee3SJason M. Bills ipmi_ret_t status = replaceCacheFru(req->fruDeviceID); 281e2d1aee3SJason M. Bills if (status != IPMI_CC_OK) 282e2d1aee3SJason M. Bills { 283e2d1aee3SJason M. Bills return status; 284e2d1aee3SJason M. Bills } 285e2d1aee3SJason M. Bills int lastWriteAddr = req->fruInventoryOffset + writeLen; 286e2d1aee3SJason M. Bills if (fruCache.size() < lastWriteAddr) 287e2d1aee3SJason M. Bills { 288e2d1aee3SJason M. Bills fruCache.resize(req->fruInventoryOffset + writeLen); 289e2d1aee3SJason M. Bills } 290e2d1aee3SJason M. Bills 291e2d1aee3SJason M. Bills std::copy(req->data, req->data + writeLen, 292e2d1aee3SJason M. Bills fruCache.begin() + req->fruInventoryOffset); 293e2d1aee3SJason M. Bills 294e2d1aee3SJason M. Bills bool atEnd = false; 295e2d1aee3SJason M. Bills 296e2d1aee3SJason M. Bills if (fruCache.size() >= sizeof(FRUHeader)) 297e2d1aee3SJason M. Bills { 298e2d1aee3SJason M. Bills 299e2d1aee3SJason M. Bills FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data()); 300e2d1aee3SJason M. Bills 301e2d1aee3SJason M. Bills int lastRecordStart = std::max( 302e2d1aee3SJason M. Bills header->internalOffset, 303e2d1aee3SJason M. Bills std::max(header->chassisOffset, 304e2d1aee3SJason M. Bills std::max(header->boardOffset, header->productOffset))); 305e2d1aee3SJason M. Bills // TODO: Handle Multi-Record FRUs? 306e2d1aee3SJason M. Bills 307e2d1aee3SJason M. Bills lastRecordStart *= 8; // header starts in are multiples of 8 bytes 308e2d1aee3SJason M. Bills 309e2d1aee3SJason M. Bills // get the length of the area in multiples of 8 bytes 310e2d1aee3SJason M. Bills if (lastWriteAddr > (lastRecordStart + 1)) 311e2d1aee3SJason M. Bills { 312e2d1aee3SJason M. Bills // second byte in record area is the length 313e2d1aee3SJason M. Bills int areaLength(fruCache[lastRecordStart + 1]); 314e2d1aee3SJason M. Bills areaLength *= 8; // it is in multiples of 8 bytes 315e2d1aee3SJason M. Bills 316e2d1aee3SJason M. Bills if (lastWriteAddr >= (areaLength + lastRecordStart)) 317e2d1aee3SJason M. Bills { 318e2d1aee3SJason M. Bills atEnd = true; 319e2d1aee3SJason M. Bills } 320e2d1aee3SJason M. Bills } 321e2d1aee3SJason M. Bills } 322e2d1aee3SJason M. Bills uint8_t* respPtr = static_cast<uint8_t*>(response); 323e2d1aee3SJason M. Bills if (atEnd) 324e2d1aee3SJason M. Bills { 325e2d1aee3SJason M. Bills // cancel timer, we're at the end so might as well send it 326e2d1aee3SJason M. Bills cacheTimer->stop(); 327e2d1aee3SJason M. Bills if (!writeFru()) 328e2d1aee3SJason M. Bills { 329e2d1aee3SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 330e2d1aee3SJason M. Bills } 331e2d1aee3SJason M. Bills *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF)); 332e2d1aee3SJason M. Bills } 333e2d1aee3SJason M. Bills else 334e2d1aee3SJason M. Bills { 335e2d1aee3SJason M. Bills // start a timer, if no further data is sent in cacheTimeoutSeconds 336e2d1aee3SJason M. Bills // seconds, check to see if it is valid 337e2d1aee3SJason M. Bills createTimer(); 338e2d1aee3SJason M. Bills cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>( 339e2d1aee3SJason M. Bills std::chrono::seconds(cacheTimeoutSeconds))); 340e2d1aee3SJason M. Bills *respPtr = 0; 341e2d1aee3SJason M. Bills } 342e2d1aee3SJason M. Bills 343e2d1aee3SJason M. Bills *dataLen = 1; 344e2d1aee3SJason M. Bills 345e2d1aee3SJason M. Bills return IPMI_CC_OK; 346e2d1aee3SJason M. Bills } 347e2d1aee3SJason M. Bills 348e2d1aee3SJason M. Bills ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 349e2d1aee3SJason M. Bills ipmi_request_t request, 350e2d1aee3SJason M. Bills ipmi_response_t response, 351e2d1aee3SJason M. Bills ipmi_data_len_t dataLen, 352e2d1aee3SJason M. Bills ipmi_context_t context) 353e2d1aee3SJason M. Bills { 354e2d1aee3SJason M. Bills if (*dataLen != 1) 355e2d1aee3SJason M. Bills { 356e2d1aee3SJason M. Bills *dataLen = 0; 357e2d1aee3SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 358e2d1aee3SJason M. Bills } 359e2d1aee3SJason M. Bills *dataLen = 0; // default to 0 in case of an error 360e2d1aee3SJason M. Bills 361e2d1aee3SJason M. Bills uint8_t reqDev = *(static_cast<uint8_t*>(request)); 362e2d1aee3SJason M. Bills if (reqDev == 0xFF) 363e2d1aee3SJason M. Bills { 364e2d1aee3SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 365e2d1aee3SJason M. Bills } 366e2d1aee3SJason M. Bills ipmi_ret_t status = replaceCacheFru(reqDev); 367e2d1aee3SJason M. Bills 368e2d1aee3SJason M. Bills if (status != IPMI_CC_OK) 369e2d1aee3SJason M. Bills { 370e2d1aee3SJason M. Bills return status; 371e2d1aee3SJason M. Bills } 372e2d1aee3SJason M. Bills 373e2d1aee3SJason M. Bills GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response); 374e2d1aee3SJason M. Bills respPtr->inventorySizeLSB = fruCache.size() & 0xFF; 375e2d1aee3SJason M. Bills respPtr->inventorySizeMSB = fruCache.size() >> 8; 376e2d1aee3SJason M. Bills respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte); 377e2d1aee3SJason M. Bills 378e2d1aee3SJason M. Bills *dataLen = sizeof(GetFRUAreaResp); 379e2d1aee3SJason M. Bills return IPMI_CC_OK; 380e2d1aee3SJason M. Bills } 381e2d1aee3SJason M. Bills 3823f7c5e40SJason M. Bills ipmi_ret_t getFruSdrCount(size_t& count) 3833f7c5e40SJason M. Bills { 3843f7c5e40SJason M. Bills ipmi_ret_t ret = replaceCacheFru(0); 3853f7c5e40SJason M. Bills if (ret != IPMI_CC_OK) 3863f7c5e40SJason M. Bills { 3873f7c5e40SJason M. Bills return ret; 3883f7c5e40SJason M. Bills } 3893f7c5e40SJason M. Bills count = deviceHashes.size(); 3903f7c5e40SJason M. Bills return IPMI_CC_OK; 3913f7c5e40SJason M. Bills } 3923f7c5e40SJason M. Bills 3933f7c5e40SJason M. Bills ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp) 3943f7c5e40SJason M. Bills { 3953f7c5e40SJason M. Bills ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list 3963f7c5e40SJason M. Bills if (ret != IPMI_CC_OK) 3973f7c5e40SJason M. Bills { 3983f7c5e40SJason M. Bills return ret; 3993f7c5e40SJason M. Bills } 4003f7c5e40SJason M. Bills if (deviceHashes.size() < index) 4013f7c5e40SJason M. Bills { 4023f7c5e40SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 4033f7c5e40SJason M. Bills } 4043f7c5e40SJason M. Bills auto device = deviceHashes.begin() + index; 4053f7c5e40SJason M. Bills uint8_t& bus = device->second.first; 4063f7c5e40SJason M. Bills uint8_t& address = device->second.second; 4073f7c5e40SJason M. Bills 4083f7c5e40SJason M. Bills ManagedObjectType frus; 4093f7c5e40SJason M. Bills 4103f7c5e40SJason M. Bills sdbusplus::message::message getObjects = dbus.new_method_call( 4113f7c5e40SJason M. Bills fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 4123f7c5e40SJason M. Bills "GetManagedObjects"); 4133f7c5e40SJason M. Bills try 4143f7c5e40SJason M. Bills { 4153f7c5e40SJason M. Bills sdbusplus::message::message resp = dbus.call(getObjects); 4163f7c5e40SJason M. Bills resp.read(frus); 4173f7c5e40SJason M. Bills } 4183f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 4193f7c5e40SJason M. Bills { 4203f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 4213f7c5e40SJason M. Bills } 4223f7c5e40SJason M. Bills boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr; 4233f7c5e40SJason M. Bills auto fru = 4243f7c5e40SJason M. Bills std::find_if(frus.begin(), frus.end(), 4253f7c5e40SJason M. Bills [bus, address, &fruData](ManagedEntry& entry) { 4263f7c5e40SJason M. Bills auto findFruDevice = 4273f7c5e40SJason M. Bills entry.second.find("xyz.openbmc_project.FruDevice"); 4283f7c5e40SJason M. Bills if (findFruDevice == entry.second.end()) 4293f7c5e40SJason M. Bills { 4303f7c5e40SJason M. Bills return false; 4313f7c5e40SJason M. Bills } 4323f7c5e40SJason M. Bills fruData = &(findFruDevice->second); 4333f7c5e40SJason M. Bills auto findBus = findFruDevice->second.find("BUS"); 4343f7c5e40SJason M. Bills auto findAddress = 4353f7c5e40SJason M. Bills findFruDevice->second.find("ADDRESS"); 4363f7c5e40SJason M. Bills if (findBus == findFruDevice->second.end() || 4373f7c5e40SJason M. Bills findAddress == findFruDevice->second.end()) 4383f7c5e40SJason M. Bills { 4393f7c5e40SJason M. Bills return false; 4403f7c5e40SJason M. Bills } 4413f7c5e40SJason M. Bills if (sdbusplus::message::variant_ns::get<uint32_t>( 4423f7c5e40SJason M. Bills findBus->second) != bus) 4433f7c5e40SJason M. Bills { 4443f7c5e40SJason M. Bills return false; 4453f7c5e40SJason M. Bills } 4463f7c5e40SJason M. Bills if (sdbusplus::message::variant_ns::get<uint32_t>( 4473f7c5e40SJason M. Bills findAddress->second) != address) 4483f7c5e40SJason M. Bills { 4493f7c5e40SJason M. Bills return false; 4503f7c5e40SJason M. Bills } 4513f7c5e40SJason M. Bills return true; 4523f7c5e40SJason M. Bills }); 4533f7c5e40SJason M. Bills if (fru == frus.end()) 4543f7c5e40SJason M. Bills { 4553f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 4563f7c5e40SJason M. Bills } 4573f7c5e40SJason M. Bills std::string name; 4583f7c5e40SJason M. Bills auto findProductName = fruData->find("BOARD_PRODUCT_NAME"); 4593f7c5e40SJason M. Bills auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME"); 4603f7c5e40SJason M. Bills if (findProductName != fruData->end()) 4613f7c5e40SJason M. Bills { 4623f7c5e40SJason M. Bills name = sdbusplus::message::variant_ns::get<std::string>( 4633f7c5e40SJason M. Bills findProductName->second); 4643f7c5e40SJason M. Bills } 4653f7c5e40SJason M. Bills else if (findBoardName != fruData->end()) 4663f7c5e40SJason M. Bills { 4673f7c5e40SJason M. Bills name = sdbusplus::message::variant_ns::get<std::string>( 4683f7c5e40SJason M. Bills findBoardName->second); 4693f7c5e40SJason M. Bills } 4703f7c5e40SJason M. Bills else 4713f7c5e40SJason M. Bills { 4723f7c5e40SJason M. Bills name = "UNKNOWN"; 4733f7c5e40SJason M. Bills } 4743f7c5e40SJason M. Bills if (name.size() > maxFruSdrNameSize) 4753f7c5e40SJason M. Bills { 4763f7c5e40SJason M. Bills name = name.substr(0, maxFruSdrNameSize); 4773f7c5e40SJason M. Bills } 4783f7c5e40SJason M. Bills size_t sizeDiff = maxFruSdrNameSize - name.size(); 4793f7c5e40SJason M. Bills 4803f7c5e40SJason M. Bills resp.header.record_id_lsb = 0x0; // calling code is to implement these 4813f7c5e40SJason M. Bills resp.header.record_id_msb = 0x0; 4823f7c5e40SJason M. Bills resp.header.sdr_version = ipmiSdrVersion; 4833f7c5e40SJason M. Bills resp.header.record_type = 0x11; // FRU Device Locator 4843f7c5e40SJason M. Bills resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff; 4853f7c5e40SJason M. Bills resp.key.deviceAddress = 0x20; 4863f7c5e40SJason M. Bills resp.key.fruID = device->first; 4873f7c5e40SJason M. Bills resp.key.accessLun = 0x80; // logical / physical fru device 4883f7c5e40SJason M. Bills resp.key.channelNumber = 0x0; 4893f7c5e40SJason M. Bills resp.body.reserved = 0x0; 4903f7c5e40SJason M. Bills resp.body.deviceType = 0x10; 4913f7c5e40SJason M. Bills resp.body.entityID = 0x0; 4923f7c5e40SJason M. Bills resp.body.entityInstance = 0x1; 4933f7c5e40SJason M. Bills resp.body.oem = 0x0; 4943f7c5e40SJason M. Bills resp.body.deviceIDLen = name.size(); 4953f7c5e40SJason M. Bills name.copy(resp.body.deviceID, name.size()); 4963f7c5e40SJason M. Bills 4973f7c5e40SJason M. Bills return IPMI_CC_OK; 4983f7c5e40SJason M. Bills } 499e2d1aee3SJason M. Bills 500e2d1aee3SJason M. Bills void registerStorageFunctions() 501e2d1aee3SJason M. Bills { 502e2d1aee3SJason M. Bills // <Get FRU Inventory Area Info> 503e2d1aee3SJason M. Bills ipmiPrintAndRegister( 504e2d1aee3SJason M. Bills NETFUN_STORAGE, 505e2d1aee3SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo), 506e2d1aee3SJason M. Bills NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR); 507e2d1aee3SJason M. Bills 508e2d1aee3SJason M. Bills // <Add READ FRU Data 509e2d1aee3SJason M. Bills ipmiPrintAndRegister( 510e2d1aee3SJason M. Bills NETFUN_STORAGE, 511e2d1aee3SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL, 512e2d1aee3SJason M. Bills ipmiStorageReadFRUData, PRIVILEGE_OPERATOR); 513e2d1aee3SJason M. Bills 514e2d1aee3SJason M. Bills // <Add WRITE FRU Data 515e2d1aee3SJason M. Bills ipmiPrintAndRegister( 516e2d1aee3SJason M. Bills NETFUN_STORAGE, 517e2d1aee3SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData), 518e2d1aee3SJason M. Bills NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR); 519e2d1aee3SJason M. Bills } 5203f7c5e40SJason M. Bills } // namespace storage 5213f7c5e40SJason M. Bills } // namespace ipmi