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 176d9c83fdSJason M. Bills #include <ipmid/api.h> 183f7c5e40SJason M. Bills 193f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp> 20c04e2e70SJason M. Bills #include <boost/process.hpp> 213f7c5e40SJason M. Bills #include <commandutils.hpp> 223f7c5e40SJason M. Bills #include <iostream> 23c04e2e70SJason M. Bills #include <phosphor-ipmi-host/selutility.hpp> 243f7c5e40SJason M. Bills #include <phosphor-logging/log.hpp> 253f7c5e40SJason M. Bills #include <sdbusplus/message/types.hpp> 263f7c5e40SJason M. Bills #include <sdbusplus/timer.hpp> 27c04e2e70SJason M. Bills #include <sdrutils.hpp> 28c04e2e70SJason M. Bills #include <stdexcept> 293f7c5e40SJason M. Bills #include <storagecommands.hpp> 30c04e2e70SJason M. Bills #include <string_view> 313f7c5e40SJason M. Bills 323f7c5e40SJason M. Bills namespace ipmi 333f7c5e40SJason M. Bills { 343f7c5e40SJason M. Bills 353f7c5e40SJason M. Bills namespace storage 363f7c5e40SJason M. Bills { 373f7c5e40SJason M. Bills 38e2d1aee3SJason M. Bills constexpr static const size_t maxMessageSize = 64; 393f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16; 403f7c5e40SJason M. Bills using ManagedObjectType = boost::container::flat_map< 413f7c5e40SJason M. Bills sdbusplus::message::object_path, 423f7c5e40SJason M. Bills boost::container::flat_map< 433f7c5e40SJason M. Bills std::string, boost::container::flat_map<std::string, DbusVariant>>>; 443f7c5e40SJason M. Bills using ManagedEntry = std::pair< 453f7c5e40SJason M. Bills sdbusplus::message::object_path, 463f7c5e40SJason M. Bills boost::container::flat_map< 473f7c5e40SJason M. Bills std::string, boost::container::flat_map<std::string, DbusVariant>>>; 483f7c5e40SJason M. Bills 493bcba457SJames Feist constexpr static const char* fruDeviceServiceName = 503bcba457SJames Feist "xyz.openbmc_project.FruDevice"; 51e2d1aee3SJason M. Bills constexpr static const size_t cacheTimeoutSeconds = 10; 523f7c5e40SJason M. Bills 53c04e2e70SJason M. Bills constexpr static const uint8_t deassertionEvent = 1; 54c04e2e70SJason M. Bills 553f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache; 563f7c5e40SJason M. Bills static uint8_t cacheBus = 0xFF; 573f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF; 583f7c5e40SJason M. Bills 593f7c5e40SJason M. Bills std::unique_ptr<phosphor::Timer> cacheTimer = nullptr; 603f7c5e40SJason M. Bills 613f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a 623f7c5e40SJason M. Bills // collision to verify our dev-id 633f7c5e40SJason M. Bills boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes; 643f7c5e40SJason M. Bills 65e2d1aee3SJason M. Bills void registerStorageFunctions() __attribute__((constructor)); 663f7c5e40SJason M. Bills static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); 673f7c5e40SJason M. Bills 683f7c5e40SJason M. Bills bool writeFru() 693f7c5e40SJason M. Bills { 703f7c5e40SJason M. Bills sdbusplus::message::message writeFru = dbus.new_method_call( 713f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 723f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "WriteFru"); 733f7c5e40SJason M. Bills writeFru.append(cacheBus, cacheAddr, fruCache); 743f7c5e40SJason M. Bills try 753f7c5e40SJason M. Bills { 763f7c5e40SJason M. Bills sdbusplus::message::message writeFruResp = dbus.call(writeFru); 773f7c5e40SJason M. Bills } 783f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 793f7c5e40SJason M. Bills { 803f7c5e40SJason M. Bills // todo: log sel? 813f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 823f7c5e40SJason M. Bills "error writing fru"); 833f7c5e40SJason M. Bills return false; 843f7c5e40SJason M. Bills } 853f7c5e40SJason M. Bills return true; 863f7c5e40SJason M. Bills } 873f7c5e40SJason M. Bills 88e2d1aee3SJason M. Bills void createTimer() 89e2d1aee3SJason M. Bills { 90e2d1aee3SJason M. Bills if (cacheTimer == nullptr) 91e2d1aee3SJason M. Bills { 92e2d1aee3SJason M. Bills cacheTimer = std::make_unique<phosphor::Timer>(writeFru); 93e2d1aee3SJason M. Bills } 94e2d1aee3SJason M. Bills } 95e2d1aee3SJason M. Bills 963f7c5e40SJason M. Bills ipmi_ret_t replaceCacheFru(uint8_t devId) 973f7c5e40SJason M. Bills { 983f7c5e40SJason M. Bills static uint8_t lastDevId = 0xFF; 993f7c5e40SJason M. Bills 1003f7c5e40SJason M. Bills bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired(); 1013f7c5e40SJason M. Bills if (lastDevId == devId && timerRunning) 1023f7c5e40SJason M. Bills { 1033f7c5e40SJason M. Bills return IPMI_CC_OK; // cache already up to date 1043f7c5e40SJason M. Bills } 1053f7c5e40SJason M. Bills // if timer is running, stop it and writeFru manually 1063f7c5e40SJason M. Bills else if (timerRunning) 1073f7c5e40SJason M. Bills { 1083f7c5e40SJason M. Bills cacheTimer->stop(); 1093f7c5e40SJason M. Bills writeFru(); 1103f7c5e40SJason M. Bills } 1113f7c5e40SJason M. Bills 1123f7c5e40SJason M. Bills sdbusplus::message::message getObjects = dbus.new_method_call( 1133f7c5e40SJason M. Bills fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 1143f7c5e40SJason M. Bills "GetManagedObjects"); 1153f7c5e40SJason M. Bills ManagedObjectType frus; 1163f7c5e40SJason M. Bills try 1173f7c5e40SJason M. Bills { 1183f7c5e40SJason M. Bills sdbusplus::message::message resp = dbus.call(getObjects); 1193f7c5e40SJason M. Bills resp.read(frus); 1203f7c5e40SJason M. Bills } 1213f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 1223f7c5e40SJason M. Bills { 1233f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 1243f7c5e40SJason M. Bills "replaceCacheFru: error getting managed objects"); 1253f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 1263f7c5e40SJason M. Bills } 1273f7c5e40SJason M. Bills 1283f7c5e40SJason M. Bills deviceHashes.clear(); 1293f7c5e40SJason M. Bills 1303f7c5e40SJason M. Bills // hash the object paths to create unique device id's. increment on 1313f7c5e40SJason M. Bills // collision 1323f7c5e40SJason M. Bills std::hash<std::string> hasher; 1333f7c5e40SJason M. Bills for (const auto& fru : frus) 1343f7c5e40SJason M. Bills { 1353f7c5e40SJason M. Bills auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice"); 1363f7c5e40SJason M. Bills if (fruIface == fru.second.end()) 1373f7c5e40SJason M. Bills { 1383f7c5e40SJason M. Bills continue; 1393f7c5e40SJason M. Bills } 1403f7c5e40SJason M. Bills 1413f7c5e40SJason M. Bills auto busFind = fruIface->second.find("BUS"); 1423f7c5e40SJason M. Bills auto addrFind = fruIface->second.find("ADDRESS"); 1433f7c5e40SJason M. Bills if (busFind == fruIface->second.end() || 1443f7c5e40SJason M. Bills addrFind == fruIface->second.end()) 1453f7c5e40SJason M. Bills { 1463f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::INFO>( 1473f7c5e40SJason M. Bills "fru device missing Bus or Address", 1483f7c5e40SJason M. Bills phosphor::logging::entry("FRU=%s", fru.first.str.c_str())); 1493f7c5e40SJason M. Bills continue; 1503f7c5e40SJason M. Bills } 1513f7c5e40SJason M. Bills 1523f7c5e40SJason M. Bills uint8_t fruBus = 1533f7c5e40SJason M. Bills sdbusplus::message::variant_ns::get<uint32_t>(busFind->second); 1543f7c5e40SJason M. Bills uint8_t fruAddr = 1553f7c5e40SJason M. Bills sdbusplus::message::variant_ns::get<uint32_t>(addrFind->second); 1563f7c5e40SJason M. Bills 1573f7c5e40SJason M. Bills uint8_t fruHash = 0; 1583f7c5e40SJason M. Bills if (fruBus != 0 || fruAddr != 0) 1593f7c5e40SJason M. Bills { 1603f7c5e40SJason M. Bills fruHash = hasher(fru.first.str); 1613f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for baseboard 1623f7c5e40SJason M. Bills if (fruHash == 0 || fruHash == 0xFF) 1633f7c5e40SJason M. Bills { 1643f7c5e40SJason M. Bills fruHash = 1; 1653f7c5e40SJason M. Bills } 1663f7c5e40SJason M. Bills } 1673f7c5e40SJason M. Bills std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr); 1683f7c5e40SJason M. Bills 1693f7c5e40SJason M. Bills bool emplacePassed = false; 1703f7c5e40SJason M. Bills while (!emplacePassed) 1713f7c5e40SJason M. Bills { 1723f7c5e40SJason M. Bills auto resp = deviceHashes.emplace(fruHash, newDev); 1733f7c5e40SJason M. Bills emplacePassed = resp.second; 1743f7c5e40SJason M. Bills if (!emplacePassed) 1753f7c5e40SJason M. Bills { 1763f7c5e40SJason M. Bills fruHash++; 1773f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for 1783f7c5e40SJason M. Bills // baseboard 1793f7c5e40SJason M. Bills if (fruHash == 0XFF) 1803f7c5e40SJason M. Bills { 1813f7c5e40SJason M. Bills fruHash = 0x1; 1823f7c5e40SJason M. Bills } 1833f7c5e40SJason M. Bills } 1843f7c5e40SJason M. Bills } 1853f7c5e40SJason M. Bills } 1863f7c5e40SJason M. Bills auto deviceFind = deviceHashes.find(devId); 1873f7c5e40SJason M. Bills if (deviceFind == deviceHashes.end()) 1883f7c5e40SJason M. Bills { 1893f7c5e40SJason M. Bills return IPMI_CC_SENSOR_INVALID; 1903f7c5e40SJason M. Bills } 1913f7c5e40SJason M. Bills 1923f7c5e40SJason M. Bills fruCache.clear(); 1933f7c5e40SJason M. Bills sdbusplus::message::message getRawFru = dbus.new_method_call( 1943f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 1953f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "GetRawFru"); 1963f7c5e40SJason M. Bills cacheBus = deviceFind->second.first; 1973f7c5e40SJason M. Bills cacheAddr = deviceFind->second.second; 1983f7c5e40SJason M. Bills getRawFru.append(cacheBus, cacheAddr); 1993f7c5e40SJason M. Bills try 2003f7c5e40SJason M. Bills { 2013f7c5e40SJason M. Bills sdbusplus::message::message getRawResp = dbus.call(getRawFru); 2023f7c5e40SJason M. Bills getRawResp.read(fruCache); 2033f7c5e40SJason M. Bills } 2043f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 2053f7c5e40SJason M. Bills { 2063f7c5e40SJason M. Bills lastDevId = 0xFF; 2073f7c5e40SJason M. Bills cacheBus = 0xFF; 2083f7c5e40SJason M. Bills cacheAddr = 0xFF; 2093f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 2103f7c5e40SJason M. Bills } 2113f7c5e40SJason M. Bills 2123f7c5e40SJason M. Bills lastDevId = devId; 2133f7c5e40SJason M. Bills return IPMI_CC_OK; 2143f7c5e40SJason M. Bills } 2153f7c5e40SJason M. Bills 216e2d1aee3SJason M. Bills ipmi_ret_t ipmiStorageReadFRUData(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 217e2d1aee3SJason M. Bills ipmi_request_t request, 218e2d1aee3SJason M. Bills ipmi_response_t response, 219e2d1aee3SJason M. Bills ipmi_data_len_t dataLen, 220e2d1aee3SJason M. Bills ipmi_context_t context) 221e2d1aee3SJason M. Bills { 222e2d1aee3SJason M. Bills if (*dataLen != 4) 223e2d1aee3SJason M. Bills { 224e2d1aee3SJason M. Bills *dataLen = 0; 225e2d1aee3SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 226e2d1aee3SJason M. Bills } 227e2d1aee3SJason M. Bills *dataLen = 0; // default to 0 in case of an error 228e2d1aee3SJason M. Bills 229e2d1aee3SJason M. Bills auto req = static_cast<GetFRUAreaReq*>(request); 230e2d1aee3SJason M. Bills 231e2d1aee3SJason M. Bills if (req->countToRead > maxMessageSize - 1) 232e2d1aee3SJason M. Bills { 233e2d1aee3SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 234e2d1aee3SJason M. Bills } 235e2d1aee3SJason M. Bills ipmi_ret_t status = replaceCacheFru(req->fruDeviceID); 236e2d1aee3SJason M. Bills 237e2d1aee3SJason M. Bills if (status != IPMI_CC_OK) 238e2d1aee3SJason M. Bills { 239e2d1aee3SJason M. Bills return status; 240e2d1aee3SJason M. Bills } 241e2d1aee3SJason M. Bills 242e2d1aee3SJason M. Bills size_t fromFRUByteLen = 0; 243e2d1aee3SJason M. Bills if (req->countToRead + req->fruInventoryOffset < fruCache.size()) 244e2d1aee3SJason M. Bills { 245e2d1aee3SJason M. Bills fromFRUByteLen = req->countToRead; 246e2d1aee3SJason M. Bills } 247e2d1aee3SJason M. Bills else if (fruCache.size() > req->fruInventoryOffset) 248e2d1aee3SJason M. Bills { 249e2d1aee3SJason M. Bills fromFRUByteLen = fruCache.size() - req->fruInventoryOffset; 250e2d1aee3SJason M. Bills } 251e2d1aee3SJason M. Bills size_t padByteLen = req->countToRead - fromFRUByteLen; 252e2d1aee3SJason M. Bills uint8_t* respPtr = static_cast<uint8_t*>(response); 253e2d1aee3SJason M. Bills *respPtr = req->countToRead; 254e2d1aee3SJason M. Bills std::copy(fruCache.begin() + req->fruInventoryOffset, 255e2d1aee3SJason M. Bills fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen, 256e2d1aee3SJason M. Bills ++respPtr); 257e2d1aee3SJason M. Bills // if longer than the fru is requested, fill with 0xFF 258e2d1aee3SJason M. Bills if (padByteLen) 259e2d1aee3SJason M. Bills { 260e2d1aee3SJason M. Bills respPtr += fromFRUByteLen; 261e2d1aee3SJason M. Bills std::fill(respPtr, respPtr + padByteLen, 0xFF); 262e2d1aee3SJason M. Bills } 263e2d1aee3SJason M. Bills *dataLen = fromFRUByteLen + 1; 264e2d1aee3SJason M. Bills 265e2d1aee3SJason M. Bills return IPMI_CC_OK; 266e2d1aee3SJason M. Bills } 267e2d1aee3SJason M. Bills 268e2d1aee3SJason M. Bills ipmi_ret_t ipmiStorageWriteFRUData(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 269e2d1aee3SJason M. Bills ipmi_request_t request, 270e2d1aee3SJason M. Bills ipmi_response_t response, 271e2d1aee3SJason M. Bills ipmi_data_len_t dataLen, 272e2d1aee3SJason M. Bills ipmi_context_t context) 273e2d1aee3SJason M. Bills { 274e2d1aee3SJason M. Bills if (*dataLen < 4 || 275e2d1aee3SJason M. Bills *dataLen >= 276e2d1aee3SJason M. Bills 0xFF + 3) // count written return is one byte, so limit to one byte 277e2d1aee3SJason M. Bills // of data after the three request data bytes 278e2d1aee3SJason M. Bills { 279e2d1aee3SJason M. Bills *dataLen = 0; 280e2d1aee3SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 281e2d1aee3SJason M. Bills } 282e2d1aee3SJason M. Bills 283e2d1aee3SJason M. Bills auto req = static_cast<WriteFRUDataReq*>(request); 284e2d1aee3SJason M. Bills size_t writeLen = *dataLen - 3; 285e2d1aee3SJason M. Bills *dataLen = 0; // default to 0 in case of an error 286e2d1aee3SJason M. Bills 287e2d1aee3SJason M. Bills ipmi_ret_t status = replaceCacheFru(req->fruDeviceID); 288e2d1aee3SJason M. Bills if (status != IPMI_CC_OK) 289e2d1aee3SJason M. Bills { 290e2d1aee3SJason M. Bills return status; 291e2d1aee3SJason M. Bills } 292e2d1aee3SJason M. Bills int lastWriteAddr = req->fruInventoryOffset + writeLen; 293e2d1aee3SJason M. Bills if (fruCache.size() < lastWriteAddr) 294e2d1aee3SJason M. Bills { 295e2d1aee3SJason M. Bills fruCache.resize(req->fruInventoryOffset + writeLen); 296e2d1aee3SJason M. Bills } 297e2d1aee3SJason M. Bills 298e2d1aee3SJason M. Bills std::copy(req->data, req->data + writeLen, 299e2d1aee3SJason M. Bills fruCache.begin() + req->fruInventoryOffset); 300e2d1aee3SJason M. Bills 301e2d1aee3SJason M. Bills bool atEnd = false; 302e2d1aee3SJason M. Bills 303e2d1aee3SJason M. Bills if (fruCache.size() >= sizeof(FRUHeader)) 304e2d1aee3SJason M. Bills { 305e2d1aee3SJason M. Bills 306e2d1aee3SJason M. Bills FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data()); 307e2d1aee3SJason M. Bills 308e2d1aee3SJason M. Bills int lastRecordStart = std::max( 309e2d1aee3SJason M. Bills header->internalOffset, 310e2d1aee3SJason M. Bills std::max(header->chassisOffset, 311e2d1aee3SJason M. Bills std::max(header->boardOffset, header->productOffset))); 312e2d1aee3SJason M. Bills // TODO: Handle Multi-Record FRUs? 313e2d1aee3SJason M. Bills 314e2d1aee3SJason M. Bills lastRecordStart *= 8; // header starts in are multiples of 8 bytes 315e2d1aee3SJason M. Bills 316e2d1aee3SJason M. Bills // get the length of the area in multiples of 8 bytes 317e2d1aee3SJason M. Bills if (lastWriteAddr > (lastRecordStart + 1)) 318e2d1aee3SJason M. Bills { 319e2d1aee3SJason M. Bills // second byte in record area is the length 320e2d1aee3SJason M. Bills int areaLength(fruCache[lastRecordStart + 1]); 321e2d1aee3SJason M. Bills areaLength *= 8; // it is in multiples of 8 bytes 322e2d1aee3SJason M. Bills 323e2d1aee3SJason M. Bills if (lastWriteAddr >= (areaLength + lastRecordStart)) 324e2d1aee3SJason M. Bills { 325e2d1aee3SJason M. Bills atEnd = true; 326e2d1aee3SJason M. Bills } 327e2d1aee3SJason M. Bills } 328e2d1aee3SJason M. Bills } 329e2d1aee3SJason M. Bills uint8_t* respPtr = static_cast<uint8_t*>(response); 330e2d1aee3SJason M. Bills if (atEnd) 331e2d1aee3SJason M. Bills { 332e2d1aee3SJason M. Bills // cancel timer, we're at the end so might as well send it 333e2d1aee3SJason M. Bills cacheTimer->stop(); 334e2d1aee3SJason M. Bills if (!writeFru()) 335e2d1aee3SJason M. Bills { 336e2d1aee3SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 337e2d1aee3SJason M. Bills } 338e2d1aee3SJason M. Bills *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF)); 339e2d1aee3SJason M. Bills } 340e2d1aee3SJason M. Bills else 341e2d1aee3SJason M. Bills { 342e2d1aee3SJason M. Bills // start a timer, if no further data is sent in cacheTimeoutSeconds 343e2d1aee3SJason M. Bills // seconds, check to see if it is valid 344e2d1aee3SJason M. Bills createTimer(); 345e2d1aee3SJason M. Bills cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>( 346e2d1aee3SJason M. Bills std::chrono::seconds(cacheTimeoutSeconds))); 347e2d1aee3SJason M. Bills *respPtr = 0; 348e2d1aee3SJason M. Bills } 349e2d1aee3SJason M. Bills 350e2d1aee3SJason M. Bills *dataLen = 1; 351e2d1aee3SJason M. Bills 352e2d1aee3SJason M. Bills return IPMI_CC_OK; 353e2d1aee3SJason M. Bills } 354e2d1aee3SJason M. Bills 355e2d1aee3SJason M. Bills ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 356e2d1aee3SJason M. Bills ipmi_request_t request, 357e2d1aee3SJason M. Bills ipmi_response_t response, 358e2d1aee3SJason M. Bills ipmi_data_len_t dataLen, 359e2d1aee3SJason M. Bills ipmi_context_t context) 360e2d1aee3SJason M. Bills { 361e2d1aee3SJason M. Bills if (*dataLen != 1) 362e2d1aee3SJason M. Bills { 363e2d1aee3SJason M. Bills *dataLen = 0; 364e2d1aee3SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 365e2d1aee3SJason M. Bills } 366e2d1aee3SJason M. Bills *dataLen = 0; // default to 0 in case of an error 367e2d1aee3SJason M. Bills 368e2d1aee3SJason M. Bills uint8_t reqDev = *(static_cast<uint8_t*>(request)); 369e2d1aee3SJason M. Bills if (reqDev == 0xFF) 370e2d1aee3SJason M. Bills { 371e2d1aee3SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 372e2d1aee3SJason M. Bills } 373e2d1aee3SJason M. Bills ipmi_ret_t status = replaceCacheFru(reqDev); 374e2d1aee3SJason M. Bills 375e2d1aee3SJason M. Bills if (status != IPMI_CC_OK) 376e2d1aee3SJason M. Bills { 377e2d1aee3SJason M. Bills return status; 378e2d1aee3SJason M. Bills } 379e2d1aee3SJason M. Bills 380e2d1aee3SJason M. Bills GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response); 381e2d1aee3SJason M. Bills respPtr->inventorySizeLSB = fruCache.size() & 0xFF; 382e2d1aee3SJason M. Bills respPtr->inventorySizeMSB = fruCache.size() >> 8; 383e2d1aee3SJason M. Bills respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte); 384e2d1aee3SJason M. Bills 385e2d1aee3SJason M. Bills *dataLen = sizeof(GetFRUAreaResp); 386e2d1aee3SJason M. Bills return IPMI_CC_OK; 387e2d1aee3SJason M. Bills } 388e2d1aee3SJason M. Bills 3893f7c5e40SJason M. Bills ipmi_ret_t getFruSdrCount(size_t& count) 3903f7c5e40SJason M. Bills { 3913f7c5e40SJason M. Bills ipmi_ret_t ret = replaceCacheFru(0); 3923f7c5e40SJason M. Bills if (ret != IPMI_CC_OK) 3933f7c5e40SJason M. Bills { 3943f7c5e40SJason M. Bills return ret; 3953f7c5e40SJason M. Bills } 3963f7c5e40SJason M. Bills count = deviceHashes.size(); 3973f7c5e40SJason M. Bills return IPMI_CC_OK; 3983f7c5e40SJason M. Bills } 3993f7c5e40SJason M. Bills 4003f7c5e40SJason M. Bills ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp) 4013f7c5e40SJason M. Bills { 4023f7c5e40SJason M. Bills ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list 4033f7c5e40SJason M. Bills if (ret != IPMI_CC_OK) 4043f7c5e40SJason M. Bills { 4053f7c5e40SJason M. Bills return ret; 4063f7c5e40SJason M. Bills } 4073f7c5e40SJason M. Bills if (deviceHashes.size() < index) 4083f7c5e40SJason M. Bills { 4093f7c5e40SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 4103f7c5e40SJason M. Bills } 4113f7c5e40SJason M. Bills auto device = deviceHashes.begin() + index; 4123f7c5e40SJason M. Bills uint8_t& bus = device->second.first; 4133f7c5e40SJason M. Bills uint8_t& address = device->second.second; 4143f7c5e40SJason M. Bills 4153f7c5e40SJason M. Bills ManagedObjectType frus; 4163f7c5e40SJason M. Bills 4173f7c5e40SJason M. Bills sdbusplus::message::message getObjects = dbus.new_method_call( 4183f7c5e40SJason M. Bills fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 4193f7c5e40SJason M. Bills "GetManagedObjects"); 4203f7c5e40SJason M. Bills try 4213f7c5e40SJason M. Bills { 4223f7c5e40SJason M. Bills sdbusplus::message::message resp = dbus.call(getObjects); 4233f7c5e40SJason M. Bills resp.read(frus); 4243f7c5e40SJason M. Bills } 4253f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 4263f7c5e40SJason M. Bills { 4273f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 4283f7c5e40SJason M. Bills } 4293f7c5e40SJason M. Bills boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr; 4303f7c5e40SJason M. Bills auto fru = 4313f7c5e40SJason M. Bills std::find_if(frus.begin(), frus.end(), 4323f7c5e40SJason M. Bills [bus, address, &fruData](ManagedEntry& entry) { 4333f7c5e40SJason M. Bills auto findFruDevice = 4343f7c5e40SJason M. Bills entry.second.find("xyz.openbmc_project.FruDevice"); 4353f7c5e40SJason M. Bills if (findFruDevice == entry.second.end()) 4363f7c5e40SJason M. Bills { 4373f7c5e40SJason M. Bills return false; 4383f7c5e40SJason M. Bills } 4393f7c5e40SJason M. Bills fruData = &(findFruDevice->second); 4403f7c5e40SJason M. Bills auto findBus = findFruDevice->second.find("BUS"); 4413f7c5e40SJason M. Bills auto findAddress = 4423f7c5e40SJason M. Bills findFruDevice->second.find("ADDRESS"); 4433f7c5e40SJason M. Bills if (findBus == findFruDevice->second.end() || 4443f7c5e40SJason M. Bills findAddress == findFruDevice->second.end()) 4453f7c5e40SJason M. Bills { 4463f7c5e40SJason M. Bills return false; 4473f7c5e40SJason M. Bills } 4483f7c5e40SJason M. Bills if (sdbusplus::message::variant_ns::get<uint32_t>( 4493f7c5e40SJason M. Bills findBus->second) != bus) 4503f7c5e40SJason M. Bills { 4513f7c5e40SJason M. Bills return false; 4523f7c5e40SJason M. Bills } 4533f7c5e40SJason M. Bills if (sdbusplus::message::variant_ns::get<uint32_t>( 4543f7c5e40SJason M. Bills findAddress->second) != address) 4553f7c5e40SJason M. Bills { 4563f7c5e40SJason M. Bills return false; 4573f7c5e40SJason M. Bills } 4583f7c5e40SJason M. Bills return true; 4593f7c5e40SJason M. Bills }); 4603f7c5e40SJason M. Bills if (fru == frus.end()) 4613f7c5e40SJason M. Bills { 4623f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 4633f7c5e40SJason M. Bills } 4643f7c5e40SJason M. Bills std::string name; 4653f7c5e40SJason M. Bills auto findProductName = fruData->find("BOARD_PRODUCT_NAME"); 4663f7c5e40SJason M. Bills auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME"); 4673f7c5e40SJason M. Bills if (findProductName != fruData->end()) 4683f7c5e40SJason M. Bills { 4693f7c5e40SJason M. Bills name = sdbusplus::message::variant_ns::get<std::string>( 4703f7c5e40SJason M. Bills findProductName->second); 4713f7c5e40SJason M. Bills } 4723f7c5e40SJason M. Bills else if (findBoardName != fruData->end()) 4733f7c5e40SJason M. Bills { 4743f7c5e40SJason M. Bills name = sdbusplus::message::variant_ns::get<std::string>( 4753f7c5e40SJason M. Bills findBoardName->second); 4763f7c5e40SJason M. Bills } 4773f7c5e40SJason M. Bills else 4783f7c5e40SJason M. Bills { 4793f7c5e40SJason M. Bills name = "UNKNOWN"; 4803f7c5e40SJason M. Bills } 4813f7c5e40SJason M. Bills if (name.size() > maxFruSdrNameSize) 4823f7c5e40SJason M. Bills { 4833f7c5e40SJason M. Bills name = name.substr(0, maxFruSdrNameSize); 4843f7c5e40SJason M. Bills } 4853f7c5e40SJason M. Bills size_t sizeDiff = maxFruSdrNameSize - name.size(); 4863f7c5e40SJason M. Bills 4873f7c5e40SJason M. Bills resp.header.record_id_lsb = 0x0; // calling code is to implement these 4883f7c5e40SJason M. Bills resp.header.record_id_msb = 0x0; 4893f7c5e40SJason M. Bills resp.header.sdr_version = ipmiSdrVersion; 4903f7c5e40SJason M. Bills resp.header.record_type = 0x11; // FRU Device Locator 4913f7c5e40SJason M. Bills resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff; 4923f7c5e40SJason M. Bills resp.key.deviceAddress = 0x20; 4933f7c5e40SJason M. Bills resp.key.fruID = device->first; 4943f7c5e40SJason M. Bills resp.key.accessLun = 0x80; // logical / physical fru device 4953f7c5e40SJason M. Bills resp.key.channelNumber = 0x0; 4963f7c5e40SJason M. Bills resp.body.reserved = 0x0; 4973f7c5e40SJason M. Bills resp.body.deviceType = 0x10; 4983f7c5e40SJason M. Bills resp.body.entityID = 0x0; 4993f7c5e40SJason M. Bills resp.body.entityInstance = 0x1; 5003f7c5e40SJason M. Bills resp.body.oem = 0x0; 5013f7c5e40SJason M. Bills resp.body.deviceIDLen = name.size(); 5023f7c5e40SJason M. Bills name.copy(resp.body.deviceID, name.size()); 5033f7c5e40SJason M. Bills 5043f7c5e40SJason M. Bills return IPMI_CC_OK; 5053f7c5e40SJason M. Bills } 506e2d1aee3SJason M. Bills 507c04e2e70SJason M. Bills ipmi_ret_t ipmiStorageGetSELInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 508c04e2e70SJason M. Bills ipmi_request_t request, 509c04e2e70SJason M. Bills ipmi_response_t response, 510c04e2e70SJason M. Bills ipmi_data_len_t data_len, 511c04e2e70SJason M. Bills ipmi_context_t context) 512c04e2e70SJason M. Bills { 513c04e2e70SJason M. Bills if (*data_len != 0) 514c04e2e70SJason M. Bills { 515c04e2e70SJason M. Bills *data_len = 0; 516c04e2e70SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 517c04e2e70SJason M. Bills } 518c04e2e70SJason M. Bills ipmi::sel::GetSELInfoResponse* responseData = 519c04e2e70SJason M. Bills static_cast<ipmi::sel::GetSELInfoResponse*>(response); 520c04e2e70SJason M. Bills 521c04e2e70SJason M. Bills responseData->selVersion = ipmi::sel::selVersion; 522c04e2e70SJason M. Bills // Last erase timestamp is not available from log manager. 523c04e2e70SJason M. Bills responseData->eraseTimeStamp = ipmi::sel::invalidTimeStamp; 524c04e2e70SJason M. Bills responseData->addTimeStamp = ipmi::sel::invalidTimeStamp; 525c04e2e70SJason M. Bills responseData->operationSupport = intel_oem::ipmi::sel::selOperationSupport; 526c04e2e70SJason M. Bills responseData->entries = 0; 527c04e2e70SJason M. Bills 528c04e2e70SJason M. Bills // Open the journal 529c04e2e70SJason M. Bills sd_journal* journalTmp = nullptr; 530c04e2e70SJason M. Bills if (int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); ret < 0) 531c04e2e70SJason M. Bills { 532c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 533c04e2e70SJason M. Bills "Failed to open journal: ", 534c04e2e70SJason M. Bills phosphor::logging::entry("ERRNO=%s", strerror(-ret))); 535c04e2e70SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 536c04e2e70SJason M. Bills } 537c04e2e70SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 538c04e2e70SJason M. Bills journalTmp, sd_journal_close); 539c04e2e70SJason M. Bills journalTmp = nullptr; 540c04e2e70SJason M. Bills 541c04e2e70SJason M. Bills // Filter the journal based on the SEL MESSAGE_ID 542c04e2e70SJason M. Bills std::string match = 543c04e2e70SJason M. Bills "MESSAGE_ID=" + std::string(intel_oem::ipmi::sel::selMessageId); 544c04e2e70SJason M. Bills sd_journal_add_match(journal.get(), match.c_str(), 0); 545c04e2e70SJason M. Bills 546c04e2e70SJason M. Bills // Count the number of SEL Entries in the journal and get the timestamp of 547c04e2e70SJason M. Bills // the newest entry 548c04e2e70SJason M. Bills bool timestampRecorded = false; 549c04e2e70SJason M. Bills SD_JOURNAL_FOREACH_BACKWARDS(journal.get()) 550c04e2e70SJason M. Bills { 551c04e2e70SJason M. Bills if (!timestampRecorded) 552c04e2e70SJason M. Bills { 553c04e2e70SJason M. Bills uint64_t timestamp; 554c04e2e70SJason M. Bills if (int ret = 555c04e2e70SJason M. Bills sd_journal_get_realtime_usec(journal.get(), ×tamp); 556c04e2e70SJason M. Bills ret < 0) 557c04e2e70SJason M. Bills { 558c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 559c04e2e70SJason M. Bills "Failed to read timestamp: ", 560c04e2e70SJason M. Bills phosphor::logging::entry("ERRNO=%s", strerror(-ret))); 561c04e2e70SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 562c04e2e70SJason M. Bills } 563c04e2e70SJason M. Bills timestamp /= (1000 * 1000); // convert from us to s 564c04e2e70SJason M. Bills responseData->addTimeStamp = static_cast<uint32_t>(timestamp); 565c04e2e70SJason M. Bills timestampRecorded = true; 566c04e2e70SJason M. Bills } 567c04e2e70SJason M. Bills responseData->entries++; 568c04e2e70SJason M. Bills } 569c04e2e70SJason M. Bills 570c04e2e70SJason M. Bills *data_len = sizeof(ipmi::sel::GetSELInfoResponse); 571c04e2e70SJason M. Bills return IPMI_CC_OK; 572c04e2e70SJason M. Bills } 573c04e2e70SJason M. Bills 574c04e2e70SJason M. Bills static int fromHexStr(const std::string hexStr, std::vector<uint8_t>& data) 575c04e2e70SJason M. Bills { 576c04e2e70SJason M. Bills for (unsigned int i = 0; i < hexStr.size(); i += 2) 577c04e2e70SJason M. Bills { 578c04e2e70SJason M. Bills try 579c04e2e70SJason M. Bills { 580c04e2e70SJason M. Bills data.push_back(static_cast<uint8_t>( 581c04e2e70SJason M. Bills std::stoul(hexStr.substr(i, 2), nullptr, 16))); 582c04e2e70SJason M. Bills } 583c04e2e70SJason M. Bills catch (std::invalid_argument& e) 584c04e2e70SJason M. Bills { 585c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 586c04e2e70SJason M. Bills return -1; 587c04e2e70SJason M. Bills } 588c04e2e70SJason M. Bills catch (std::out_of_range& e) 589c04e2e70SJason M. Bills { 590c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 591c04e2e70SJason M. Bills return -1; 592c04e2e70SJason M. Bills } 593c04e2e70SJason M. Bills } 594c04e2e70SJason M. Bills return 0; 595c04e2e70SJason M. Bills } 596c04e2e70SJason M. Bills 597c04e2e70SJason M. Bills static int getJournalMetadata(sd_journal* journal, 598c04e2e70SJason M. Bills const std::string_view& field, 599c04e2e70SJason M. Bills std::string& contents) 600c04e2e70SJason M. Bills { 601c04e2e70SJason M. Bills const char* data = nullptr; 602c04e2e70SJason M. Bills size_t length = 0; 603c04e2e70SJason M. Bills 604c04e2e70SJason M. Bills // Get the metadata from the requested field of the journal entry 605c04e2e70SJason M. Bills if (int ret = sd_journal_get_data(journal, field.data(), 606c04e2e70SJason M. Bills (const void**)&data, &length); 607c04e2e70SJason M. Bills ret < 0) 608c04e2e70SJason M. Bills { 609c04e2e70SJason M. Bills return ret; 610c04e2e70SJason M. Bills } 611c04e2e70SJason M. Bills std::string_view metadata(data, length); 612c04e2e70SJason M. Bills // Only use the content after the "=" character. 613c04e2e70SJason M. Bills metadata.remove_prefix(std::min(metadata.find("=") + 1, metadata.size())); 614c04e2e70SJason M. Bills contents = std::string(metadata); 615c04e2e70SJason M. Bills return 0; 616c04e2e70SJason M. Bills } 617c04e2e70SJason M. Bills 618c04e2e70SJason M. Bills static int getJournalMetadata(sd_journal* journal, 619c04e2e70SJason M. Bills const std::string_view& field, const int& base, 620c04e2e70SJason M. Bills int& contents) 621c04e2e70SJason M. Bills { 622c04e2e70SJason M. Bills std::string metadata; 623c04e2e70SJason M. Bills // Get the metadata from the requested field of the journal entry 624c04e2e70SJason M. Bills if (int ret = getJournalMetadata(journal, field, metadata); ret < 0) 625c04e2e70SJason M. Bills { 626c04e2e70SJason M. Bills return ret; 627c04e2e70SJason M. Bills } 628c04e2e70SJason M. Bills try 629c04e2e70SJason M. Bills { 630c04e2e70SJason M. Bills contents = static_cast<int>(std::stoul(metadata, nullptr, base)); 631c04e2e70SJason M. Bills } 632c04e2e70SJason M. Bills catch (std::invalid_argument& e) 633c04e2e70SJason M. Bills { 634c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 635c04e2e70SJason M. Bills return -1; 636c04e2e70SJason M. Bills } 637c04e2e70SJason M. Bills catch (std::out_of_range& e) 638c04e2e70SJason M. Bills { 639c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 640c04e2e70SJason M. Bills return -1; 641c04e2e70SJason M. Bills } 642c04e2e70SJason M. Bills return 0; 643c04e2e70SJason M. Bills } 644c04e2e70SJason M. Bills 645c04e2e70SJason M. Bills static int getJournalSelData(sd_journal* journal, std::vector<uint8_t>& evtData) 646c04e2e70SJason M. Bills { 647c04e2e70SJason M. Bills std::string evtDataStr; 648c04e2e70SJason M. Bills // Get the OEM data from the IPMI_SEL_DATA field 649c04e2e70SJason M. Bills if (int ret = getJournalMetadata(journal, "IPMI_SEL_DATA", evtDataStr); 650c04e2e70SJason M. Bills ret < 0) 651c04e2e70SJason M. Bills { 652c04e2e70SJason M. Bills return ret; 653c04e2e70SJason M. Bills } 654c04e2e70SJason M. Bills return fromHexStr(evtDataStr, evtData); 655c04e2e70SJason M. Bills } 656c04e2e70SJason M. Bills 657c04e2e70SJason M. Bills ipmi_ret_t ipmiStorageGetSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 658c04e2e70SJason M. Bills ipmi_request_t request, 659c04e2e70SJason M. Bills ipmi_response_t response, 660c04e2e70SJason M. Bills ipmi_data_len_t data_len, 661c04e2e70SJason M. Bills ipmi_context_t context) 662c04e2e70SJason M. Bills { 663c04e2e70SJason M. Bills if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest)) 664c04e2e70SJason M. Bills { 665c04e2e70SJason M. Bills *data_len = 0; 666c04e2e70SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 667c04e2e70SJason M. Bills } 668c04e2e70SJason M. Bills *data_len = 0; // Default to 0 in case of errors 669c04e2e70SJason M. Bills auto requestData = 670c04e2e70SJason M. Bills static_cast<const ipmi::sel::GetSELEntryRequest*>(request); 671c04e2e70SJason M. Bills 672c04e2e70SJason M. Bills if (requestData->reservationID != 0 || requestData->offset != 0) 673c04e2e70SJason M. Bills { 674c04e2e70SJason M. Bills if (!checkSELReservation(requestData->reservationID)) 675c04e2e70SJason M. Bills { 676c04e2e70SJason M. Bills return IPMI_CC_INVALID_RESERVATION_ID; 677c04e2e70SJason M. Bills } 678c04e2e70SJason M. Bills } 679c04e2e70SJason M. Bills 680c04e2e70SJason M. Bills GetSELEntryResponse record{}; 681c04e2e70SJason M. Bills // Default as the last entry 682c04e2e70SJason M. Bills record.nextRecordID = ipmi::sel::lastEntry; 683c04e2e70SJason M. Bills 684c04e2e70SJason M. Bills // Check for the requested SEL Entry. 685c04e2e70SJason M. Bills sd_journal* journalTmp; 686c04e2e70SJason M. Bills if (int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); ret < 0) 687c04e2e70SJason M. Bills { 688c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 689c04e2e70SJason M. Bills "Failed to open journal: ", 690c04e2e70SJason M. Bills phosphor::logging::entry("ERRNO=%s", strerror(-ret))); 691c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 692c04e2e70SJason M. Bills } 693c04e2e70SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 694c04e2e70SJason M. Bills journalTmp, sd_journal_close); 695c04e2e70SJason M. Bills journalTmp = nullptr; 696c04e2e70SJason M. Bills 697c04e2e70SJason M. Bills std::string match = 698c04e2e70SJason M. Bills "MESSAGE_ID=" + std::string(intel_oem::ipmi::sel::selMessageId); 699c04e2e70SJason M. Bills sd_journal_add_match(journal.get(), match.c_str(), 0); 700c04e2e70SJason M. Bills 701c04e2e70SJason M. Bills // Get the requested target SEL record ID if first or last is requested. 702c04e2e70SJason M. Bills int targetID = requestData->selRecordID; 703c04e2e70SJason M. Bills if (targetID == ipmi::sel::firstEntry) 704c04e2e70SJason M. Bills { 705c04e2e70SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 706c04e2e70SJason M. Bills { 707c04e2e70SJason M. Bills // Get the record ID from the IPMI_SEL_RECORD_ID field of the first 708c04e2e70SJason M. Bills // entry 709c04e2e70SJason M. Bills if (getJournalMetadata(journal.get(), "IPMI_SEL_RECORD_ID", 10, 710c04e2e70SJason M. Bills targetID) < 0) 711c04e2e70SJason M. Bills { 712c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 713c04e2e70SJason M. Bills } 714c04e2e70SJason M. Bills break; 715c04e2e70SJason M. Bills } 716c04e2e70SJason M. Bills } 717c04e2e70SJason M. Bills else if (targetID == ipmi::sel::lastEntry) 718c04e2e70SJason M. Bills { 719c04e2e70SJason M. Bills SD_JOURNAL_FOREACH_BACKWARDS(journal.get()) 720c04e2e70SJason M. Bills { 721c04e2e70SJason M. Bills // Get the record ID from the IPMI_SEL_RECORD_ID field of the first 722c04e2e70SJason M. Bills // entry 723c04e2e70SJason M. Bills if (getJournalMetadata(journal.get(), "IPMI_SEL_RECORD_ID", 10, 724c04e2e70SJason M. Bills targetID) < 0) 725c04e2e70SJason M. Bills { 726c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 727c04e2e70SJason M. Bills } 728c04e2e70SJason M. Bills break; 729c04e2e70SJason M. Bills } 730c04e2e70SJason M. Bills } 731c04e2e70SJason M. Bills // Find the requested ID 732c04e2e70SJason M. Bills match = "IPMI_SEL_RECORD_ID=" + std::to_string(targetID); 733c04e2e70SJason M. Bills sd_journal_add_match(journal.get(), match.c_str(), 0); 734c04e2e70SJason M. Bills // And find the next ID (wrapping to Record ID 1 when necessary) 735c04e2e70SJason M. Bills int nextID = targetID + 1; 736c04e2e70SJason M. Bills if (nextID == ipmi::sel::lastEntry) 737c04e2e70SJason M. Bills { 738c04e2e70SJason M. Bills nextID = 1; 739c04e2e70SJason M. Bills } 740c04e2e70SJason M. Bills match = "IPMI_SEL_RECORD_ID=" + std::to_string(nextID); 741c04e2e70SJason M. Bills sd_journal_add_match(journal.get(), match.c_str(), 0); 742c04e2e70SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 743c04e2e70SJason M. Bills { 744c04e2e70SJason M. Bills // Get the record ID from the IPMI_SEL_RECORD_ID field 745c04e2e70SJason M. Bills int id = 0; 746c04e2e70SJason M. Bills if (getJournalMetadata(journal.get(), "IPMI_SEL_RECORD_ID", 10, id) < 0) 747c04e2e70SJason M. Bills { 748c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 749c04e2e70SJason M. Bills } 750c04e2e70SJason M. Bills if (id == targetID) 751c04e2e70SJason M. Bills { 752c04e2e70SJason M. Bills // Found the desired record, so fill in the data 753c04e2e70SJason M. Bills record.recordID = id; 754c04e2e70SJason M. Bills 755c04e2e70SJason M. Bills int recordType = 0; 756c04e2e70SJason M. Bills // Get the record type from the IPMI_SEL_RECORD_TYPE field 757c04e2e70SJason M. Bills if (getJournalMetadata(journal.get(), "IPMI_SEL_RECORD_TYPE", 16, 758c04e2e70SJason M. Bills recordType) < 0) 759c04e2e70SJason M. Bills { 760c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 761c04e2e70SJason M. Bills } 762c04e2e70SJason M. Bills record.recordType = recordType; 763c04e2e70SJason M. Bills // The rest of the record depends on the record type 764c04e2e70SJason M. Bills if (record.recordType == intel_oem::ipmi::sel::systemEvent) 765c04e2e70SJason M. Bills { 766c04e2e70SJason M. Bills // Get the timestamp 767c04e2e70SJason M. Bills uint64_t ts = 0; 768c04e2e70SJason M. Bills if (sd_journal_get_realtime_usec(journal.get(), &ts) < 0) 769c04e2e70SJason M. Bills { 770c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 771c04e2e70SJason M. Bills } 772c04e2e70SJason M. Bills record.record.system.timestamp = static_cast<uint32_t>( 773c04e2e70SJason M. Bills ts / 1000 / 1000); // Convert from us to s 774c04e2e70SJason M. Bills 775c04e2e70SJason M. Bills int generatorID = 0; 776c04e2e70SJason M. Bills // Get the generator ID from the IPMI_SEL_GENERATOR_ID field 777c04e2e70SJason M. Bills if (getJournalMetadata(journal.get(), "IPMI_SEL_GENERATOR_ID", 778c04e2e70SJason M. Bills 16, generatorID) < 0) 779c04e2e70SJason M. Bills { 780c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 781c04e2e70SJason M. Bills } 782c04e2e70SJason M. Bills record.record.system.generatorID = generatorID; 783c04e2e70SJason M. Bills 784c04e2e70SJason M. Bills // Set the event message revision 785c04e2e70SJason M. Bills record.record.system.eventMsgRevision = 786c04e2e70SJason M. Bills intel_oem::ipmi::sel::eventMsgRev; 787c04e2e70SJason M. Bills 788c04e2e70SJason M. Bills std::string path; 789c04e2e70SJason M. Bills // Get the IPMI_SEL_SENSOR_PATH field 790c04e2e70SJason M. Bills if (getJournalMetadata(journal.get(), "IPMI_SEL_SENSOR_PATH", 791c04e2e70SJason M. Bills path) < 0) 792c04e2e70SJason M. Bills { 793c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 794c04e2e70SJason M. Bills } 795c04e2e70SJason M. Bills record.record.system.sensorType = getSensorTypeFromPath(path); 796c04e2e70SJason M. Bills record.record.system.sensorNum = getSensorNumberFromPath(path); 797c04e2e70SJason M. Bills record.record.system.eventType = 798c04e2e70SJason M. Bills getSensorEventTypeFromPath(path); 799c04e2e70SJason M. Bills 800c04e2e70SJason M. Bills int eventDir = 0; 801c04e2e70SJason M. Bills // Get the event direction from the IPMI_SEL_EVENT_DIR field 802c04e2e70SJason M. Bills if (getJournalMetadata(journal.get(), "IPMI_SEL_EVENT_DIR", 16, 803c04e2e70SJason M. Bills eventDir) < 0) 804c04e2e70SJason M. Bills { 805c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 806c04e2e70SJason M. Bills } 807c04e2e70SJason M. Bills // Set the event direction 808c04e2e70SJason M. Bills if (eventDir == 0) 809c04e2e70SJason M. Bills { 810c04e2e70SJason M. Bills record.record.system.eventDir = deassertionEvent; 811c04e2e70SJason M. Bills } 812c04e2e70SJason M. Bills 813c04e2e70SJason M. Bills std::vector<uint8_t> evtData; 814c04e2e70SJason M. Bills // Get the event data from the IPMI_SEL_DATA field 815c04e2e70SJason M. Bills if (getJournalSelData(journal.get(), evtData) < 0) 816c04e2e70SJason M. Bills { 817c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 818c04e2e70SJason M. Bills } 819c04e2e70SJason M. Bills record.record.system.eventData[0] = evtData[0]; 820c04e2e70SJason M. Bills record.record.system.eventData[1] = evtData[1]; 821c04e2e70SJason M. Bills record.record.system.eventData[2] = evtData[2]; 822c04e2e70SJason M. Bills } 823c04e2e70SJason M. Bills else if (record.recordType >= 824c04e2e70SJason M. Bills intel_oem::ipmi::sel::oemTsEventFirst && 825c04e2e70SJason M. Bills record.recordType <= intel_oem::ipmi::sel::oemTsEventLast) 826c04e2e70SJason M. Bills { 827c04e2e70SJason M. Bills // Get the timestamp 828c04e2e70SJason M. Bills uint64_t timestamp = 0; 829c04e2e70SJason M. Bills if (sd_journal_get_realtime_usec(journal.get(), ×tamp) < 0) 830c04e2e70SJason M. Bills { 831c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 832c04e2e70SJason M. Bills } 833c04e2e70SJason M. Bills record.record.oemTs.timestamp = static_cast<uint32_t>( 834c04e2e70SJason M. Bills timestamp / 1000 / 1000); // Convert from us to s 835c04e2e70SJason M. Bills 836c04e2e70SJason M. Bills std::vector<uint8_t> evtData; 837c04e2e70SJason M. Bills // Get the OEM data from the IPMI_SEL_DATA field 838c04e2e70SJason M. Bills if (getJournalSelData(journal.get(), evtData) < 0) 839c04e2e70SJason M. Bills { 840c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 841c04e2e70SJason M. Bills } 842c04e2e70SJason M. Bills // Only keep the bytes that fit in the record 843c04e2e70SJason M. Bills std::copy_n(evtData.begin(), 844c04e2e70SJason M. Bills std::min(evtData.size(), 845c04e2e70SJason M. Bills intel_oem::ipmi::sel::oemTsEventSize), 846c04e2e70SJason M. Bills record.record.oemTs.eventData); 847c04e2e70SJason M. Bills } 848c04e2e70SJason M. Bills else if (record.recordType >= intel_oem::ipmi::sel::oemEventFirst && 849c04e2e70SJason M. Bills record.recordType <= intel_oem::ipmi::sel::oemEventLast) 850c04e2e70SJason M. Bills { 851c04e2e70SJason M. Bills std::vector<uint8_t> evtData; 852c04e2e70SJason M. Bills // Get the OEM data from the IPMI_SEL_DATA field 853c04e2e70SJason M. Bills if (getJournalSelData(journal.get(), evtData) < 0) 854c04e2e70SJason M. Bills { 855c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 856c04e2e70SJason M. Bills } 857c04e2e70SJason M. Bills // Only keep the bytes that fit in the record 858c04e2e70SJason M. Bills std::copy_n(evtData.begin(), 859c04e2e70SJason M. Bills std::min(evtData.size(), 860c04e2e70SJason M. Bills intel_oem::ipmi::sel::oemEventSize), 861c04e2e70SJason M. Bills record.record.oem.eventData); 862c04e2e70SJason M. Bills } 863c04e2e70SJason M. Bills } 864c04e2e70SJason M. Bills else if (id == nextID) 865c04e2e70SJason M. Bills { 866c04e2e70SJason M. Bills record.nextRecordID = id; 867c04e2e70SJason M. Bills } 868c04e2e70SJason M. Bills } 869c04e2e70SJason M. Bills 870c04e2e70SJason M. Bills // If we didn't find the requested record, return an error 871c04e2e70SJason M. Bills if (record.recordID == 0) 872c04e2e70SJason M. Bills { 873c04e2e70SJason M. Bills return IPMI_CC_SENSOR_INVALID; 874c04e2e70SJason M. Bills } 875c04e2e70SJason M. Bills 876c04e2e70SJason M. Bills if (requestData->readLength == ipmi::sel::entireRecord) 877c04e2e70SJason M. Bills { 878c04e2e70SJason M. Bills std::copy(&record, &record + 1, 879c04e2e70SJason M. Bills static_cast<GetSELEntryResponse*>(response)); 880c04e2e70SJason M. Bills *data_len = sizeof(record); 881c04e2e70SJason M. Bills } 882c04e2e70SJason M. Bills else 883c04e2e70SJason M. Bills { 884c04e2e70SJason M. Bills if (requestData->offset >= ipmi::sel::selRecordSize || 885c04e2e70SJason M. Bills requestData->readLength > ipmi::sel::selRecordSize) 886c04e2e70SJason M. Bills { 887c04e2e70SJason M. Bills return IPMI_CC_PARM_OUT_OF_RANGE; 888c04e2e70SJason M. Bills } 889c04e2e70SJason M. Bills 890c04e2e70SJason M. Bills auto diff = ipmi::sel::selRecordSize - requestData->offset; 891c04e2e70SJason M. Bills auto readLength = 892c04e2e70SJason M. Bills std::min(diff, static_cast<int>(requestData->readLength)); 893c04e2e70SJason M. Bills 894c04e2e70SJason M. Bills *static_cast<uint16_t*>(response) = record.nextRecordID; 895c04e2e70SJason M. Bills std::copy_n( 896c04e2e70SJason M. Bills reinterpret_cast<uint8_t*>(&record.recordID) + requestData->offset, 897c04e2e70SJason M. Bills readLength, 898c04e2e70SJason M. Bills static_cast<uint8_t*>(response) + sizeof(record.nextRecordID)); 899c04e2e70SJason M. Bills *data_len = sizeof(record.nextRecordID) + readLength; 900c04e2e70SJason M. Bills } 901c04e2e70SJason M. Bills 902c04e2e70SJason M. Bills return IPMI_CC_OK; 903c04e2e70SJason M. Bills } 904c04e2e70SJason M. Bills 905c04e2e70SJason M. Bills ipmi_ret_t ipmiStorageAddSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 906c04e2e70SJason M. Bills ipmi_request_t request, 907c04e2e70SJason M. Bills ipmi_response_t response, 908c04e2e70SJason M. Bills ipmi_data_len_t data_len, 909c04e2e70SJason M. Bills ipmi_context_t context) 910c04e2e70SJason M. Bills { 911c04e2e70SJason M. Bills static constexpr char const* ipmiSELObject = 912c04e2e70SJason M. Bills "xyz.openbmc_project.Logging.IPMI"; 913c04e2e70SJason M. Bills static constexpr char const* ipmiSELPath = 914c04e2e70SJason M. Bills "/xyz/openbmc_project/Logging/IPMI"; 915c04e2e70SJason M. Bills static constexpr char const* ipmiSELAddInterface = 916c04e2e70SJason M. Bills "xyz.openbmc_project.Logging.IPMI"; 917c04e2e70SJason M. Bills static const std::string ipmiSELAddMessage = 918c04e2e70SJason M. Bills "IPMI SEL entry logged using IPMI Add SEL Entry command."; 919c04e2e70SJason M. Bills uint16_t recordID = 0; 920c04e2e70SJason M. Bills sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 921c04e2e70SJason M. Bills 922c04e2e70SJason M. Bills if (*data_len != sizeof(AddSELRequest)) 923c04e2e70SJason M. Bills { 924c04e2e70SJason M. Bills *data_len = 0; 925c04e2e70SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 926c04e2e70SJason M. Bills } 927c04e2e70SJason M. Bills AddSELRequest* req = static_cast<AddSELRequest*>(request); 928c04e2e70SJason M. Bills 929c04e2e70SJason M. Bills // Per the IPMI spec, need to cancel any reservation when a SEL entry is 930c04e2e70SJason M. Bills // added 931c04e2e70SJason M. Bills cancelSELReservation(); 932c04e2e70SJason M. Bills 933c04e2e70SJason M. Bills if (req->recordType == intel_oem::ipmi::sel::systemEvent) 934c04e2e70SJason M. Bills { 935c04e2e70SJason M. Bills std::string sensorPath = 936c04e2e70SJason M. Bills getPathFromSensorNumber(req->record.system.sensorNum); 937c04e2e70SJason M. Bills std::vector<uint8_t> eventData( 938c04e2e70SJason M. Bills req->record.system.eventData, 939c04e2e70SJason M. Bills req->record.system.eventData + 940c04e2e70SJason M. Bills intel_oem::ipmi::sel::systemEventSize); 941c04e2e70SJason M. Bills bool assert = req->record.system.eventDir ? false : true; 942c04e2e70SJason M. Bills uint16_t genId = req->record.system.generatorID; 943c04e2e70SJason M. Bills sdbusplus::message::message writeSEL = bus.new_method_call( 944c04e2e70SJason M. Bills ipmiSELObject, ipmiSELPath, ipmiSELAddInterface, "IpmiSelAdd"); 945c04e2e70SJason M. Bills writeSEL.append(ipmiSELAddMessage, sensorPath, eventData, assert, 946c04e2e70SJason M. Bills genId); 947c04e2e70SJason M. Bills try 948c04e2e70SJason M. Bills { 949c04e2e70SJason M. Bills sdbusplus::message::message writeSELResp = bus.call(writeSEL); 950c04e2e70SJason M. Bills writeSELResp.read(recordID); 951c04e2e70SJason M. Bills } 952c04e2e70SJason M. Bills catch (sdbusplus::exception_t& e) 953c04e2e70SJason M. Bills { 954c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 955c04e2e70SJason M. Bills *data_len = 0; 956c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 957c04e2e70SJason M. Bills } 958c04e2e70SJason M. Bills } 959c04e2e70SJason M. Bills else if (req->recordType >= intel_oem::ipmi::sel::oemTsEventFirst && 960c04e2e70SJason M. Bills req->recordType <= intel_oem::ipmi::sel::oemEventLast) 961c04e2e70SJason M. Bills { 962c04e2e70SJason M. Bills std::vector<uint8_t> eventData; 963c04e2e70SJason M. Bills if (req->recordType <= intel_oem::ipmi::sel::oemTsEventLast) 964c04e2e70SJason M. Bills { 965c04e2e70SJason M. Bills eventData = 966c04e2e70SJason M. Bills std::vector<uint8_t>(req->record.oemTs.eventData, 967c04e2e70SJason M. Bills req->record.oemTs.eventData + 968c04e2e70SJason M. Bills intel_oem::ipmi::sel::oemTsEventSize); 969c04e2e70SJason M. Bills } 970c04e2e70SJason M. Bills else 971c04e2e70SJason M. Bills { 972c04e2e70SJason M. Bills eventData = std::vector<uint8_t>( 973c04e2e70SJason M. Bills req->record.oem.eventData, 974c04e2e70SJason M. Bills req->record.oem.eventData + intel_oem::ipmi::sel::oemEventSize); 975c04e2e70SJason M. Bills } 976c04e2e70SJason M. Bills sdbusplus::message::message writeSEL = bus.new_method_call( 977c04e2e70SJason M. Bills ipmiSELObject, ipmiSELPath, ipmiSELAddInterface, "IpmiSelAddOem"); 978c04e2e70SJason M. Bills writeSEL.append(ipmiSELAddMessage, eventData, req->recordType); 979c04e2e70SJason M. Bills try 980c04e2e70SJason M. Bills { 981c04e2e70SJason M. Bills sdbusplus::message::message writeSELResp = bus.call(writeSEL); 982c04e2e70SJason M. Bills writeSELResp.read(recordID); 983c04e2e70SJason M. Bills } 984c04e2e70SJason M. Bills catch (sdbusplus::exception_t& e) 985c04e2e70SJason M. Bills { 986c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 987c04e2e70SJason M. Bills *data_len = 0; 988c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 989c04e2e70SJason M. Bills } 990c04e2e70SJason M. Bills } 991c04e2e70SJason M. Bills else 992c04e2e70SJason M. Bills { 993c04e2e70SJason M. Bills *data_len = 0; 994c04e2e70SJason M. Bills return IPMI_CC_PARM_OUT_OF_RANGE; 995c04e2e70SJason M. Bills } 996c04e2e70SJason M. Bills 997c04e2e70SJason M. Bills *static_cast<uint16_t*>(response) = recordID; 998c04e2e70SJason M. Bills *data_len = sizeof(recordID); 999c04e2e70SJason M. Bills return IPMI_CC_OK; 1000c04e2e70SJason M. Bills } 1001c04e2e70SJason M. Bills 1002c04e2e70SJason M. Bills ipmi_ret_t ipmiStorageClearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1003c04e2e70SJason M. Bills ipmi_request_t request, ipmi_response_t response, 1004c04e2e70SJason M. Bills ipmi_data_len_t data_len, ipmi_context_t context) 1005c04e2e70SJason M. Bills { 1006c04e2e70SJason M. Bills if (*data_len != sizeof(ipmi::sel::ClearSELRequest)) 1007c04e2e70SJason M. Bills { 1008c04e2e70SJason M. Bills *data_len = 0; 1009c04e2e70SJason M. Bills return IPMI_CC_REQ_DATA_LEN_INVALID; 1010c04e2e70SJason M. Bills } 1011c04e2e70SJason M. Bills auto requestData = static_cast<const ipmi::sel::ClearSELRequest*>(request); 1012c04e2e70SJason M. Bills 1013c04e2e70SJason M. Bills if (!checkSELReservation(requestData->reservationID)) 1014c04e2e70SJason M. Bills { 1015c04e2e70SJason M. Bills *data_len = 0; 1016c04e2e70SJason M. Bills return IPMI_CC_INVALID_RESERVATION_ID; 1017c04e2e70SJason M. Bills } 1018c04e2e70SJason M. Bills 1019c04e2e70SJason M. Bills if (requestData->charC != 'C' || requestData->charL != 'L' || 1020c04e2e70SJason M. Bills requestData->charR != 'R') 1021c04e2e70SJason M. Bills { 1022c04e2e70SJason M. Bills *data_len = 0; 1023c04e2e70SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 1024c04e2e70SJason M. Bills } 1025c04e2e70SJason M. Bills 1026c04e2e70SJason M. Bills uint8_t eraseProgress = ipmi::sel::eraseComplete; 1027c04e2e70SJason M. Bills 1028c04e2e70SJason M. Bills /* 1029c04e2e70SJason M. Bills * Erasure status cannot be fetched from DBUS, so always return erasure 1030c04e2e70SJason M. Bills * status as `erase completed`. 1031c04e2e70SJason M. Bills */ 1032c04e2e70SJason M. Bills if (requestData->eraseOperation == ipmi::sel::getEraseStatus) 1033c04e2e70SJason M. Bills { 1034c04e2e70SJason M. Bills *static_cast<uint8_t*>(response) = eraseProgress; 1035c04e2e70SJason M. Bills *data_len = sizeof(eraseProgress); 1036c04e2e70SJason M. Bills return IPMI_CC_OK; 1037c04e2e70SJason M. Bills } 1038c04e2e70SJason M. Bills 1039c04e2e70SJason M. Bills // Per the IPMI spec, need to cancel any reservation when the SEL is cleared 1040c04e2e70SJason M. Bills cancelSELReservation(); 1041c04e2e70SJason M. Bills 1042c04e2e70SJason M. Bills // Clear the SEL by by rotating the journal to start a new file then 1043c04e2e70SJason M. Bills // vacuuming to keep only the new file 1044*c49adb70SJason M. Bills if (boost::process::system("/bin/journalctl", "--rotate") != 0) 1045c04e2e70SJason M. Bills { 1046c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 1047c04e2e70SJason M. Bills } 1048*c49adb70SJason M. Bills if (boost::process::system("/bin/journalctl", "--vacuum-files=1") != 0) 1049c04e2e70SJason M. Bills { 1050c04e2e70SJason M. Bills return IPMI_CC_UNSPECIFIED_ERROR; 1051c04e2e70SJason M. Bills } 1052c04e2e70SJason M. Bills 1053c04e2e70SJason M. Bills *static_cast<uint8_t*>(response) = eraseProgress; 1054c04e2e70SJason M. Bills *data_len = sizeof(eraseProgress); 1055c04e2e70SJason M. Bills return IPMI_CC_OK; 1056c04e2e70SJason M. Bills } 1057c04e2e70SJason M. Bills 1058cac97a53SJason M. Bills ipmi_ret_t ipmiStorageSetSELTime(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1059cac97a53SJason M. Bills ipmi_request_t request, 1060cac97a53SJason M. Bills ipmi_response_t response, 1061cac97a53SJason M. Bills ipmi_data_len_t data_len, 1062cac97a53SJason M. Bills ipmi_context_t context) 1063cac97a53SJason M. Bills { 1064cac97a53SJason M. Bills // Set SEL Time is not supported 1065cac97a53SJason M. Bills *data_len = 0; 1066cac97a53SJason M. Bills return IPMI_CC_INVALID; 1067cac97a53SJason M. Bills } 1068cac97a53SJason M. Bills 1069e2d1aee3SJason M. Bills void registerStorageFunctions() 1070e2d1aee3SJason M. Bills { 1071e2d1aee3SJason M. Bills // <Get FRU Inventory Area Info> 1072e2d1aee3SJason M. Bills ipmiPrintAndRegister( 1073e2d1aee3SJason M. Bills NETFUN_STORAGE, 1074e2d1aee3SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo), 1075e2d1aee3SJason M. Bills NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR); 1076e2d1aee3SJason M. Bills 1077c04e2e70SJason M. Bills // <READ FRU Data> 1078e2d1aee3SJason M. Bills ipmiPrintAndRegister( 1079e2d1aee3SJason M. Bills NETFUN_STORAGE, 1080e2d1aee3SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL, 1081e2d1aee3SJason M. Bills ipmiStorageReadFRUData, PRIVILEGE_OPERATOR); 1082e2d1aee3SJason M. Bills 1083c04e2e70SJason M. Bills // <WRITE FRU Data> 1084e2d1aee3SJason M. Bills ipmiPrintAndRegister( 1085e2d1aee3SJason M. Bills NETFUN_STORAGE, 1086e2d1aee3SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData), 1087e2d1aee3SJason M. Bills NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR); 1088c04e2e70SJason M. Bills 1089c04e2e70SJason M. Bills // <Get SEL Info> 1090c04e2e70SJason M. Bills ipmiPrintAndRegister( 1091c04e2e70SJason M. Bills NETFUN_STORAGE, 1092c04e2e70SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSELInfo), NULL, 1093c04e2e70SJason M. Bills ipmiStorageGetSELInfo, PRIVILEGE_OPERATOR); 1094c04e2e70SJason M. Bills 1095c04e2e70SJason M. Bills // <Get SEL Entry> 1096c04e2e70SJason M. Bills ipmiPrintAndRegister( 1097c04e2e70SJason M. Bills NETFUN_STORAGE, 1098c04e2e70SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSELEntry), NULL, 1099c04e2e70SJason M. Bills ipmiStorageGetSELEntry, PRIVILEGE_OPERATOR); 1100c04e2e70SJason M. Bills 1101c04e2e70SJason M. Bills // <Add SEL Entry> 1102c04e2e70SJason M. Bills ipmiPrintAndRegister( 1103c04e2e70SJason M. Bills NETFUN_STORAGE, 1104c04e2e70SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdAddSEL), NULL, 1105c04e2e70SJason M. Bills ipmiStorageAddSELEntry, PRIVILEGE_OPERATOR); 1106c04e2e70SJason M. Bills 1107c04e2e70SJason M. Bills // <Clear SEL> 1108c04e2e70SJason M. Bills ipmiPrintAndRegister( 1109c04e2e70SJason M. Bills NETFUN_STORAGE, 1110c04e2e70SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdClearSEL), NULL, 1111c04e2e70SJason M. Bills ipmiStorageClearSEL, PRIVILEGE_OPERATOR); 1112cac97a53SJason M. Bills 1113cac97a53SJason M. Bills // <Set SEL Time> 1114cac97a53SJason M. Bills ipmiPrintAndRegister( 1115cac97a53SJason M. Bills NETFUN_STORAGE, 1116cac97a53SJason M. Bills static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdSetSELTime), NULL, 1117cac97a53SJason M. Bills ipmiStorageSetSELTime, PRIVILEGE_OPERATOR); 1118e2d1aee3SJason M. Bills } 11193f7c5e40SJason M. Bills } // namespace storage 11203f7c5e40SJason M. Bills } // namespace ipmi 1121