13f7c5e40SJason M. Bills /* 21a2fbddfSJason M. Bills // Copyright (c) 2017-2019 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 17ca99ef59SPatrick Venture #include "storagecommands.hpp" 18ca99ef59SPatrick Venture 19ca99ef59SPatrick Venture #include "commandutils.hpp" 2021a1b5f8SPeter Foley #include "fruutils.hpp" 21ca99ef59SPatrick Venture #include "ipmi_to_redfish_hooks.hpp" 22ca99ef59SPatrick Venture #include "sdrutils.hpp" 23c2a07d4bSPatrick Venture #include "types.hpp" 24ca99ef59SPatrick Venture 251d4d54ddSJason M. Bills #include <boost/algorithm/string.hpp> 263f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp> 27*ee27fe09SJohnathan Mantey #include <boost/process.hpp> 282a265d54SJames Feist #include <ipmid/api.hpp> 292569025eSJames Feist #include <ipmid/message.hpp> 303f7c5e40SJason M. Bills #include <phosphor-logging/log.hpp> 313f7c5e40SJason M. Bills #include <sdbusplus/message/types.hpp> 323f7c5e40SJason M. Bills #include <sdbusplus/timer.hpp> 33fcd2d3a9SJames Feist 34fcd2d3a9SJames Feist #include <filesystem> 35f23fd543SArchana Kakani #include <fstream> 36*ee27fe09SJohnathan Mantey #include <functional> 37fcd2d3a9SJames Feist #include <iostream> 38c04e2e70SJason M. Bills #include <stdexcept> 39*ee27fe09SJohnathan Mantey #include <string_view> 403f7c5e40SJason M. Bills 419ce789fcSPatrick Venture static constexpr bool DEBUG = false; 429ce789fcSPatrick Venture 431d4d54ddSJason M. Bills namespace intel_oem::ipmi::sel 441d4d54ddSJason M. Bills { 451d4d54ddSJason M. Bills static const std::filesystem::path selLogDir = "/var/log"; 461d4d54ddSJason M. Bills static const std::string selLogFilename = "ipmi_sel"; 471d4d54ddSJason M. Bills 481d4d54ddSJason M. Bills static int getFileTimestamp(const std::filesystem::path& file) 491d4d54ddSJason M. Bills { 501d4d54ddSJason M. Bills struct stat st; 511d4d54ddSJason M. Bills 521d4d54ddSJason M. Bills if (stat(file.c_str(), &st) >= 0) 531d4d54ddSJason M. Bills { 541d4d54ddSJason M. Bills return st.st_mtime; 551d4d54ddSJason M. Bills } 561d4d54ddSJason M. Bills return ::ipmi::sel::invalidTimeStamp; 571d4d54ddSJason M. Bills } 581d4d54ddSJason M. Bills 591d4d54ddSJason M. Bills namespace erase_time 607944c307SJason M. Bills { 617944c307SJason M. Bills static constexpr const char* selEraseTimestamp = "/var/lib/ipmi/sel_erase_time"; 627944c307SJason M. Bills 637944c307SJason M. Bills void save() 647944c307SJason M. Bills { 657944c307SJason M. Bills // open the file, creating it if necessary 667944c307SJason M. Bills int fd = open(selEraseTimestamp, O_WRONLY | O_CREAT | O_CLOEXEC, 0644); 677944c307SJason M. Bills if (fd < 0) 687944c307SJason M. Bills { 697944c307SJason M. Bills std::cerr << "Failed to open file\n"; 707944c307SJason M. Bills return; 717944c307SJason M. Bills } 727944c307SJason M. Bills 737944c307SJason M. Bills // update the file timestamp to the current time 747944c307SJason M. Bills if (futimens(fd, NULL) < 0) 757944c307SJason M. Bills { 767944c307SJason M. Bills std::cerr << "Failed to update timestamp: " 777944c307SJason M. Bills << std::string(strerror(errno)); 787944c307SJason M. Bills } 797944c307SJason M. Bills close(fd); 807944c307SJason M. Bills } 817944c307SJason M. Bills 827944c307SJason M. Bills int get() 837944c307SJason M. Bills { 841d4d54ddSJason M. Bills return getFileTimestamp(selEraseTimestamp); 857944c307SJason M. Bills } 861d4d54ddSJason M. Bills } // namespace erase_time 871d4d54ddSJason M. Bills } // namespace intel_oem::ipmi::sel 887944c307SJason M. Bills 893f7c5e40SJason M. Bills namespace ipmi 903f7c5e40SJason M. Bills { 913f7c5e40SJason M. Bills 923f7c5e40SJason M. Bills namespace storage 933f7c5e40SJason M. Bills { 943f7c5e40SJason M. Bills 95e2d1aee3SJason M. Bills constexpr static const size_t maxMessageSize = 64; 963f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16; 97e4f710d7SJames Feist using ObjectType = boost::container::flat_map< 98e4f710d7SJames Feist std::string, boost::container::flat_map<std::string, DbusVariant>>; 99e4f710d7SJames Feist using ManagedObjectType = 100e4f710d7SJames Feist boost::container::flat_map<sdbusplus::message::object_path, ObjectType>; 101e4f710d7SJames Feist using ManagedEntry = std::pair<sdbusplus::message::object_path, ObjectType>; 1023f7c5e40SJason M. Bills 1033bcba457SJames Feist constexpr static const char* fruDeviceServiceName = 1043bcba457SJames Feist "xyz.openbmc_project.FruDevice"; 1052569025eSJames Feist constexpr static const size_t writeTimeoutSeconds = 10; 106358e7dfaSAnoop S constexpr static const char* chassisTypeRackMount = "23"; 1073f7c5e40SJason M. Bills 1084ed6f2c1SJason M. Bills // event direction is bit[7] of eventType where 1b = Deassertion event 1094ed6f2c1SJason M. Bills constexpr static const uint8_t deassertionEvent = 0x80; 110c04e2e70SJason M. Bills 1113f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache; 11207574006SJohnathan Mantey static uint16_t cacheBus = 0xFFFF; 1133f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF; 114e4f710d7SJames Feist static uint8_t lastDevId = 0xFF; 1153f7c5e40SJason M. Bills 11607574006SJohnathan Mantey static uint16_t writeBus = 0xFFFF; 1172569025eSJames Feist static uint8_t writeAddr = 0XFF; 1182569025eSJames Feist 119f0feb49cSPatrick Williams std::unique_ptr<sdbusplus::Timer> writeTimer = nullptr; 120f944d2e5SPatrick Williams static std::vector<sdbusplus::bus::match_t> fruMatches; 1213f7c5e40SJason M. Bills 1222569025eSJames Feist ManagedObjectType frus; 1232569025eSJames Feist 1243f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a 1253f7c5e40SJason M. Bills // collision to verify our dev-id 12607574006SJohnathan Mantey boost::container::flat_map<uint8_t, std::pair<uint16_t, uint8_t>> deviceHashes; 1273f7c5e40SJason M. Bills 128e2d1aee3SJason M. Bills void registerStorageFunctions() __attribute__((constructor)); 1293f7c5e40SJason M. Bills 1303f7c5e40SJason M. Bills bool writeFru() 1313f7c5e40SJason M. Bills { 13207574006SJohnathan Mantey if (writeBus == 0xFFFF && writeAddr == 0xFF) 1332569025eSJames Feist { 1342569025eSJames Feist return true; 1352569025eSJames Feist } 13615419dd5SVernon Mauery std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 137f944d2e5SPatrick Williams sdbusplus::message_t writeFru = dbus->new_method_call( 1383f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 1393f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "WriteFru"); 1402569025eSJames Feist writeFru.append(writeBus, writeAddr, fruCache); 1413f7c5e40SJason M. Bills try 1423f7c5e40SJason M. Bills { 143f944d2e5SPatrick Williams sdbusplus::message_t writeFruResp = dbus->call(writeFru); 1443f7c5e40SJason M. Bills } 145bd51e6a9SPatrick Williams catch (const sdbusplus::exception_t&) 1463f7c5e40SJason M. Bills { 1473f7c5e40SJason M. Bills // todo: log sel? 1483f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 1493f7c5e40SJason M. Bills "error writing fru"); 1503f7c5e40SJason M. Bills return false; 1513f7c5e40SJason M. Bills } 15207574006SJohnathan Mantey writeBus = 0xFFFF; 1532569025eSJames Feist writeAddr = 0xFF; 1543f7c5e40SJason M. Bills return true; 1553f7c5e40SJason M. Bills } 1563f7c5e40SJason M. Bills 1572569025eSJames Feist void createTimers() 158e2d1aee3SJason M. Bills { 159f0feb49cSPatrick Williams writeTimer = std::make_unique<sdbusplus::Timer>(writeFru); 160e2d1aee3SJason M. Bills } 161e2d1aee3SJason M. Bills 162e4f710d7SJames Feist void recalculateHashes() 1633f7c5e40SJason M. Bills { 1643f7c5e40SJason M. Bills deviceHashes.clear(); 1653f7c5e40SJason M. Bills // hash the object paths to create unique device id's. increment on 1663f7c5e40SJason M. Bills // collision 1673f7c5e40SJason M. Bills std::hash<std::string> hasher; 1683f7c5e40SJason M. Bills for (const auto& fru : frus) 1693f7c5e40SJason M. Bills { 1703f7c5e40SJason M. Bills auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice"); 1713f7c5e40SJason M. Bills if (fruIface == fru.second.end()) 1723f7c5e40SJason M. Bills { 1733f7c5e40SJason M. Bills continue; 1743f7c5e40SJason M. Bills } 1753f7c5e40SJason M. Bills 1763f7c5e40SJason M. Bills auto busFind = fruIface->second.find("BUS"); 1773f7c5e40SJason M. Bills auto addrFind = fruIface->second.find("ADDRESS"); 1783f7c5e40SJason M. Bills if (busFind == fruIface->second.end() || 1793f7c5e40SJason M. Bills addrFind == fruIface->second.end()) 1803f7c5e40SJason M. Bills { 1813f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::INFO>( 1823f7c5e40SJason M. Bills "fru device missing Bus or Address", 1833f7c5e40SJason M. Bills phosphor::logging::entry("FRU=%s", fru.first.str.c_str())); 1843f7c5e40SJason M. Bills continue; 1853f7c5e40SJason M. Bills } 1863f7c5e40SJason M. Bills 18707574006SJohnathan Mantey uint16_t fruBus = std::get<uint32_t>(busFind->second); 1888166c8d7SVernon Mauery uint8_t fruAddr = std::get<uint32_t>(addrFind->second); 189358e7dfaSAnoop S auto chassisFind = fruIface->second.find("CHASSIS_TYPE"); 190358e7dfaSAnoop S std::string chassisType; 191358e7dfaSAnoop S if (chassisFind != fruIface->second.end()) 192358e7dfaSAnoop S { 193358e7dfaSAnoop S chassisType = std::get<std::string>(chassisFind->second); 194358e7dfaSAnoop S } 1953f7c5e40SJason M. Bills 1963f7c5e40SJason M. Bills uint8_t fruHash = 0; 197358e7dfaSAnoop S if (chassisType.compare(chassisTypeRackMount) != 0) 1983f7c5e40SJason M. Bills { 1993f7c5e40SJason M. Bills fruHash = hasher(fru.first.str); 2003f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for baseboard 2013f7c5e40SJason M. Bills if (fruHash == 0 || fruHash == 0xFF) 2023f7c5e40SJason M. Bills { 2033f7c5e40SJason M. Bills fruHash = 1; 2043f7c5e40SJason M. Bills } 2053f7c5e40SJason M. Bills } 20607574006SJohnathan Mantey std::pair<uint16_t, uint8_t> newDev(fruBus, fruAddr); 2073f7c5e40SJason M. Bills 2083f7c5e40SJason M. Bills bool emplacePassed = false; 2093f7c5e40SJason M. Bills while (!emplacePassed) 2103f7c5e40SJason M. Bills { 2113f7c5e40SJason M. Bills auto resp = deviceHashes.emplace(fruHash, newDev); 2123f7c5e40SJason M. Bills emplacePassed = resp.second; 2133f7c5e40SJason M. Bills if (!emplacePassed) 2143f7c5e40SJason M. Bills { 2153f7c5e40SJason M. Bills fruHash++; 2163f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for 2173f7c5e40SJason M. Bills // baseboard 2183f7c5e40SJason M. Bills if (fruHash == 0XFF) 2193f7c5e40SJason M. Bills { 2203f7c5e40SJason M. Bills fruHash = 0x1; 2213f7c5e40SJason M. Bills } 2223f7c5e40SJason M. Bills } 2233f7c5e40SJason M. Bills } 2243f7c5e40SJason M. Bills } 225e4f710d7SJames Feist } 226e4f710d7SJames Feist 227e4f710d7SJames Feist void replaceCacheFru(const std::shared_ptr<sdbusplus::asio::connection>& bus, 228f23fd543SArchana Kakani boost::asio::yield_context& yield) 229e4f710d7SJames Feist { 230e4f710d7SJames Feist boost::system::error_code ec; 231e4f710d7SJames Feist 232*ee27fe09SJohnathan Mantey frus = bus->yield_method_call<ManagedObjectType>( 233*ee27fe09SJohnathan Mantey yield, ec, fruDeviceServiceName, "/", 234*ee27fe09SJohnathan Mantey "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 235e4f710d7SJames Feist if (ec) 236e4f710d7SJames Feist { 237e4f710d7SJames Feist phosphor::logging::log<phosphor::logging::level::ERR>( 238*ee27fe09SJohnathan Mantey "GetMangagedObjects for getSensorMap failed", 239e4f710d7SJames Feist phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 240e4f710d7SJames Feist 241e4f710d7SJames Feist return; 242e4f710d7SJames Feist } 243e4f710d7SJames Feist recalculateHashes(); 244e4f710d7SJames Feist } 245e4f710d7SJames Feist 246dcff1506SVernon Mauery ipmi::Cc getFru(ipmi::Context::ptr& ctx, uint8_t devId) 247e4f710d7SJames Feist { 248e4f710d7SJames Feist if (lastDevId == devId && devId != 0xFF) 249e4f710d7SJames Feist { 250e4f710d7SJames Feist return ipmi::ccSuccess; 251e4f710d7SJames Feist } 252e4f710d7SJames Feist 2533f7c5e40SJason M. Bills auto deviceFind = deviceHashes.find(devId); 254*ee27fe09SJohnathan Mantey if (deviceFind == deviceHashes.end()) 2553f7c5e40SJason M. Bills { 2563f7c5e40SJason M. Bills return IPMI_CC_SENSOR_INVALID; 2573f7c5e40SJason M. Bills } 2583f7c5e40SJason M. Bills 2593432a0acSPavanKumarIntel if (writeTimer->isRunning()) 2603432a0acSPavanKumarIntel { 2613432a0acSPavanKumarIntel phosphor::logging::log<phosphor::logging::level::ERR>( 2623432a0acSPavanKumarIntel "Couldn't get raw fru as fru is updating"); 2633432a0acSPavanKumarIntel return ipmi::ccBusy; 2643432a0acSPavanKumarIntel } 2653f7c5e40SJason M. Bills fruCache.clear(); 2662569025eSJames Feist 2673f7c5e40SJason M. Bills cacheBus = deviceFind->second.first; 2683f7c5e40SJason M. Bills cacheAddr = deviceFind->second.second; 2692569025eSJames Feist 270e4f710d7SJames Feist boost::system::error_code ec; 271e4f710d7SJames Feist 2722569025eSJames Feist fruCache = ctx->bus->yield_method_call<std::vector<uint8_t>>( 273*ee27fe09SJohnathan Mantey ctx->yield, ec, fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 2742569025eSJames Feist "xyz.openbmc_project.FruDeviceManager", "GetRawFru", cacheBus, 2752569025eSJames Feist cacheAddr); 276*ee27fe09SJohnathan Mantey if (ec) 2773f7c5e40SJason M. Bills { 2782569025eSJames Feist phosphor::logging::log<phosphor::logging::level::ERR>( 2792569025eSJames Feist "Couldn't get raw fru", 2802569025eSJames Feist phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 28107574006SJohnathan Mantey cacheBus = 0xFFFF; 2823f7c5e40SJason M. Bills cacheAddr = 0xFF; 2832569025eSJames Feist return ipmi::ccResponseError; 2843f7c5e40SJason M. Bills } 2853f7c5e40SJason M. Bills 2863f7c5e40SJason M. Bills lastDevId = devId; 2872569025eSJames Feist return ipmi::ccSuccess; 2883f7c5e40SJason M. Bills } 2893f7c5e40SJason M. Bills 290e4f710d7SJames Feist void writeFruIfRunning() 291e4f710d7SJames Feist { 292e4f710d7SJames Feist if (!writeTimer->isRunning()) 293e4f710d7SJames Feist { 294e4f710d7SJames Feist return; 295e4f710d7SJames Feist } 296e4f710d7SJames Feist writeTimer->stop(); 297e4f710d7SJames Feist writeFru(); 298e4f710d7SJames Feist } 299e4f710d7SJames Feist 300e4f710d7SJames Feist void startMatch(void) 301e4f710d7SJames Feist { 302e4f710d7SJames Feist if (fruMatches.size()) 303e4f710d7SJames Feist { 304e4f710d7SJames Feist return; 305e4f710d7SJames Feist } 306e4f710d7SJames Feist 307e4f710d7SJames Feist fruMatches.reserve(2); 308e4f710d7SJames Feist 309e4f710d7SJames Feist auto bus = getSdBus(); 310e4f710d7SJames Feist fruMatches.emplace_back(*bus, 311e4f710d7SJames Feist "type='signal',arg0path='/xyz/openbmc_project/" 312e4f710d7SJames Feist "FruDevice/',member='InterfacesAdded'", 313f944d2e5SPatrick Williams [](sdbusplus::message_t& message) { 314e4f710d7SJames Feist sdbusplus::message::object_path path; 315e4f710d7SJames Feist ObjectType object; 316e4f710d7SJames Feist try 317e4f710d7SJames Feist { 318e4f710d7SJames Feist message.read(path, object); 319e4f710d7SJames Feist } 320bd51e6a9SPatrick Williams catch (const sdbusplus::exception_t&) 321e4f710d7SJames Feist { 322e4f710d7SJames Feist return; 323e4f710d7SJames Feist } 324b37abfb2SPatrick Williams auto findType = object.find("xyz.openbmc_project.FruDevice"); 325e4f710d7SJames Feist if (findType == object.end()) 326e4f710d7SJames Feist { 327e4f710d7SJames Feist return; 328e4f710d7SJames Feist } 329e4f710d7SJames Feist writeFruIfRunning(); 330e4f710d7SJames Feist frus[path] = object; 331e4f710d7SJames Feist recalculateHashes(); 332e4f710d7SJames Feist lastDevId = 0xFF; 333e4f710d7SJames Feist }); 334e4f710d7SJames Feist 335e4f710d7SJames Feist fruMatches.emplace_back(*bus, 336e4f710d7SJames Feist "type='signal',arg0path='/xyz/openbmc_project/" 337e4f710d7SJames Feist "FruDevice/',member='InterfacesRemoved'", 338f944d2e5SPatrick Williams [](sdbusplus::message_t& message) { 339e4f710d7SJames Feist sdbusplus::message::object_path path; 340e4f710d7SJames Feist std::set<std::string> interfaces; 341e4f710d7SJames Feist try 342e4f710d7SJames Feist { 343e4f710d7SJames Feist message.read(path, interfaces); 344e4f710d7SJames Feist } 345bd51e6a9SPatrick Williams catch (const sdbusplus::exception_t&) 346e4f710d7SJames Feist { 347e4f710d7SJames Feist return; 348e4f710d7SJames Feist } 349b37abfb2SPatrick Williams auto findType = interfaces.find("xyz.openbmc_project.FruDevice"); 350e4f710d7SJames Feist if (findType == interfaces.end()) 351e4f710d7SJames Feist { 352e4f710d7SJames Feist return; 353e4f710d7SJames Feist } 354e4f710d7SJames Feist writeFruIfRunning(); 355e4f710d7SJames Feist frus.erase(path); 356e4f710d7SJames Feist recalculateHashes(); 357e4f710d7SJames Feist lastDevId = 0xFF; 358e4f710d7SJames Feist }); 359e4f710d7SJames Feist 360e4f710d7SJames Feist // call once to populate 361e4f710d7SJames Feist boost::asio::spawn(*getIoContext(), [](boost::asio::yield_context yield) { 362e4f710d7SJames Feist replaceCacheFru(getSdBus(), yield); 363e4f710d7SJames Feist }); 364e4f710d7SJames Feist } 365e4f710d7SJames Feist 3665f4194eeSjayaprakash Mutyala /** @brief implements the read FRU data command 3675f4194eeSjayaprakash Mutyala * @param fruDeviceId - FRU Device ID 3685f4194eeSjayaprakash Mutyala * @param fruInventoryOffset - FRU Inventory Offset to write 3695f4194eeSjayaprakash Mutyala * @param countToRead - Count to read 3705f4194eeSjayaprakash Mutyala * 3715f4194eeSjayaprakash Mutyala * @returns ipmi completion code plus response data 3725f4194eeSjayaprakash Mutyala * - countWritten - Count written 3735f4194eeSjayaprakash Mutyala */ 3745f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t, // Count 3755f4194eeSjayaprakash Mutyala std::vector<uint8_t> // Requested data 3765f4194eeSjayaprakash Mutyala > 377dcff1506SVernon Mauery ipmiStorageReadFruData(ipmi::Context::ptr& ctx, uint8_t fruDeviceId, 3782569025eSJames Feist uint16_t fruInventoryOffset, uint8_t countToRead) 379e2d1aee3SJason M. Bills { 3805f4194eeSjayaprakash Mutyala if (fruDeviceId == 0xFF) 381e2d1aee3SJason M. Bills { 3825f4194eeSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 383e2d1aee3SJason M. Bills } 384e2d1aee3SJason M. Bills 385e4f710d7SJames Feist ipmi::Cc status = getFru(ctx, fruDeviceId); 386e2d1aee3SJason M. Bills 3875f4194eeSjayaprakash Mutyala if (status != ipmi::ccSuccess) 3885f4194eeSjayaprakash Mutyala { 3895f4194eeSjayaprakash Mutyala return ipmi::response(status); 390e2d1aee3SJason M. Bills } 391e2d1aee3SJason M. Bills 3925f4194eeSjayaprakash Mutyala size_t fromFruByteLen = 0; 3935f4194eeSjayaprakash Mutyala if (countToRead + fruInventoryOffset < fruCache.size()) 394e2d1aee3SJason M. Bills { 3955f4194eeSjayaprakash Mutyala fromFruByteLen = countToRead; 3965f4194eeSjayaprakash Mutyala } 3975f4194eeSjayaprakash Mutyala else if (fruCache.size() > fruInventoryOffset) 398e2d1aee3SJason M. Bills { 3995f4194eeSjayaprakash Mutyala fromFruByteLen = fruCache.size() - fruInventoryOffset; 4005f4194eeSjayaprakash Mutyala } 4015f4194eeSjayaprakash Mutyala else 4025f4194eeSjayaprakash Mutyala { 4039210838eSsrikanta mondal return ipmi::responseReqDataLenExceeded(); 404e2d1aee3SJason M. Bills } 405e2d1aee3SJason M. Bills 4065f4194eeSjayaprakash Mutyala std::vector<uint8_t> requestedData; 407e2d1aee3SJason M. Bills 4085f4194eeSjayaprakash Mutyala requestedData.insert( 4095f4194eeSjayaprakash Mutyala requestedData.begin(), fruCache.begin() + fruInventoryOffset, 4105f4194eeSjayaprakash Mutyala fruCache.begin() + fruInventoryOffset + fromFruByteLen); 4115f4194eeSjayaprakash Mutyala 41270b17f93SPatrick Venture return ipmi::responseSuccess(static_cast<uint8_t>(requestedData.size()), 41370b17f93SPatrick Venture requestedData); 414e2d1aee3SJason M. Bills } 4155f4194eeSjayaprakash Mutyala 4165f4194eeSjayaprakash Mutyala /** @brief implements the write FRU data command 4175f4194eeSjayaprakash Mutyala * @param fruDeviceId - FRU Device ID 4185f4194eeSjayaprakash Mutyala * @param fruInventoryOffset - FRU Inventory Offset to write 4195f4194eeSjayaprakash Mutyala * @param dataToWrite - Data to write 4205f4194eeSjayaprakash Mutyala * 4215f4194eeSjayaprakash Mutyala * @returns ipmi completion code plus response data 4225f4194eeSjayaprakash Mutyala * - countWritten - Count written 4235f4194eeSjayaprakash Mutyala */ 4245f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t> 425dcff1506SVernon Mauery ipmiStorageWriteFruData(ipmi::Context::ptr& ctx, uint8_t fruDeviceId, 4262569025eSJames Feist uint16_t fruInventoryOffset, 4275f4194eeSjayaprakash Mutyala std::vector<uint8_t>& dataToWrite) 4285f4194eeSjayaprakash Mutyala { 4295f4194eeSjayaprakash Mutyala if (fruDeviceId == 0xFF) 4305f4194eeSjayaprakash Mutyala { 4315f4194eeSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 4325f4194eeSjayaprakash Mutyala } 4335f4194eeSjayaprakash Mutyala 4345f4194eeSjayaprakash Mutyala size_t writeLen = dataToWrite.size(); 4355f4194eeSjayaprakash Mutyala 436e4f710d7SJames Feist ipmi::Cc status = getFru(ctx, fruDeviceId); 4375f4194eeSjayaprakash Mutyala if (status != ipmi::ccSuccess) 4385f4194eeSjayaprakash Mutyala { 4395f4194eeSjayaprakash Mutyala return ipmi::response(status); 4405f4194eeSjayaprakash Mutyala } 441dcff1506SVernon Mauery size_t lastWriteAddr = fruInventoryOffset + writeLen; 442e2d1aee3SJason M. Bills if (fruCache.size() < lastWriteAddr) 443e2d1aee3SJason M. Bills { 4445f4194eeSjayaprakash Mutyala fruCache.resize(fruInventoryOffset + writeLen); 445e2d1aee3SJason M. Bills } 446e2d1aee3SJason M. Bills 4475f4194eeSjayaprakash Mutyala std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen, 4485f4194eeSjayaprakash Mutyala fruCache.begin() + fruInventoryOffset); 449e2d1aee3SJason M. Bills 45021a1b5f8SPeter Foley bool atEnd = validateBasicFruContent(fruCache, lastWriteAddr); 4515f4194eeSjayaprakash Mutyala uint8_t countWritten = 0; 4522569025eSJames Feist 4532569025eSJames Feist writeBus = cacheBus; 4542569025eSJames Feist writeAddr = cacheAddr; 455e2d1aee3SJason M. Bills if (atEnd) 456e2d1aee3SJason M. Bills { 457e2d1aee3SJason M. Bills // cancel timer, we're at the end so might as well send it 4582569025eSJames Feist writeTimer->stop(); 459e2d1aee3SJason M. Bills if (!writeFru()) 460e2d1aee3SJason M. Bills { 4615f4194eeSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 462e2d1aee3SJason M. Bills } 4635f4194eeSjayaprakash Mutyala countWritten = std::min(fruCache.size(), static_cast<size_t>(0xFF)); 464e2d1aee3SJason M. Bills } 465e2d1aee3SJason M. Bills else 466e2d1aee3SJason M. Bills { 4672569025eSJames Feist // start a timer, if no further data is sent to check to see if it is 4682569025eSJames Feist // valid 4692569025eSJames Feist writeTimer->start(std::chrono::duration_cast<std::chrono::microseconds>( 4702569025eSJames Feist std::chrono::seconds(writeTimeoutSeconds))); 4715f4194eeSjayaprakash Mutyala countWritten = 0; 472e2d1aee3SJason M. Bills } 473e2d1aee3SJason M. Bills 4745f4194eeSjayaprakash Mutyala return ipmi::responseSuccess(countWritten); 475e2d1aee3SJason M. Bills } 476e2d1aee3SJason M. Bills 477d33acd6bSjayaprakash Mutyala /** @brief implements the get FRU inventory area info command 478d33acd6bSjayaprakash Mutyala * @param fruDeviceId - FRU Device ID 479d33acd6bSjayaprakash Mutyala * 480d33acd6bSjayaprakash Mutyala * @returns IPMI completion code plus response data 481d33acd6bSjayaprakash Mutyala * - inventorySize - Number of possible allocation units 482d33acd6bSjayaprakash Mutyala * - accessType - Allocation unit size in bytes. 483d33acd6bSjayaprakash Mutyala */ 484d33acd6bSjayaprakash Mutyala ipmi::RspType<uint16_t, // inventorySize 485d33acd6bSjayaprakash Mutyala uint8_t> // accessType 486dcff1506SVernon Mauery ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr& ctx, uint8_t fruDeviceId) 487e2d1aee3SJason M. Bills { 488d33acd6bSjayaprakash Mutyala if (fruDeviceId == 0xFF) 489e2d1aee3SJason M. Bills { 490d33acd6bSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 491e2d1aee3SJason M. Bills } 492e2d1aee3SJason M. Bills 4931e2ab061SJayaprakash Mutyala ipmi::Cc ret = getFru(ctx, fruDeviceId); 4941e2ab061SJayaprakash Mutyala if (ret != ipmi::ccSuccess) 4951e2ab061SJayaprakash Mutyala { 4961e2ab061SJayaprakash Mutyala return ipmi::response(ret); 4971e2ab061SJayaprakash Mutyala } 498e2d1aee3SJason M. Bills 499d33acd6bSjayaprakash Mutyala constexpr uint8_t accessType = 500d33acd6bSjayaprakash Mutyala static_cast<uint8_t>(GetFRUAreaAccessType::byte); 501e2d1aee3SJason M. Bills 502d33acd6bSjayaprakash Mutyala return ipmi::responseSuccess(fruCache.size(), accessType); 503e2d1aee3SJason M. Bills } 504e2d1aee3SJason M. Bills 505dcff1506SVernon Mauery ipmi::Cc getFruSdrCount(ipmi::Context::ptr&, size_t& count) 5063f7c5e40SJason M. Bills { 5073f7c5e40SJason M. Bills count = deviceHashes.size(); 508dcff1506SVernon Mauery return ipmi::ccSuccess; 5093f7c5e40SJason M. Bills } 5103f7c5e40SJason M. Bills 511dcff1506SVernon Mauery ipmi::Cc getFruSdrs(ipmi::Context::ptr& ctx, size_t index, 5122569025eSJames Feist get_sdr::SensorDataFruRecord& resp) 5133f7c5e40SJason M. Bills { 5143f7c5e40SJason M. Bills if (deviceHashes.size() < index) 5153f7c5e40SJason M. Bills { 516dcff1506SVernon Mauery return ipmi::ccInvalidFieldRequest; 5173f7c5e40SJason M. Bills } 5183f7c5e40SJason M. Bills auto device = deviceHashes.begin() + index; 51907574006SJohnathan Mantey uint16_t& bus = device->second.first; 5203f7c5e40SJason M. Bills uint8_t& address = device->second.second; 5213f7c5e40SJason M. Bills 5223f7c5e40SJason M. Bills boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr; 523b37abfb2SPatrick Williams auto fru = std::find_if(frus.begin(), frus.end(), 5243f7c5e40SJason M. Bills [bus, address, &fruData](ManagedEntry& entry) { 525b37abfb2SPatrick Williams auto findFruDevice = entry.second.find("xyz.openbmc_project.FruDevice"); 5263f7c5e40SJason M. Bills if (findFruDevice == entry.second.end()) 5273f7c5e40SJason M. Bills { 5283f7c5e40SJason M. Bills return false; 5293f7c5e40SJason M. Bills } 5303f7c5e40SJason M. Bills fruData = &(findFruDevice->second); 5313f7c5e40SJason M. Bills auto findBus = findFruDevice->second.find("BUS"); 532b37abfb2SPatrick Williams auto findAddress = findFruDevice->second.find("ADDRESS"); 5333f7c5e40SJason M. Bills if (findBus == findFruDevice->second.end() || 5343f7c5e40SJason M. Bills findAddress == findFruDevice->second.end()) 5353f7c5e40SJason M. Bills { 5363f7c5e40SJason M. Bills return false; 5373f7c5e40SJason M. Bills } 5388166c8d7SVernon Mauery if (std::get<uint32_t>(findBus->second) != bus) 5393f7c5e40SJason M. Bills { 5403f7c5e40SJason M. Bills return false; 5413f7c5e40SJason M. Bills } 5428166c8d7SVernon Mauery if (std::get<uint32_t>(findAddress->second) != address) 5433f7c5e40SJason M. Bills { 5443f7c5e40SJason M. Bills return false; 5453f7c5e40SJason M. Bills } 5463f7c5e40SJason M. Bills return true; 5473f7c5e40SJason M. Bills }); 5483f7c5e40SJason M. Bills if (fru == frus.end()) 5493f7c5e40SJason M. Bills { 550dcff1506SVernon Mauery return ipmi::ccResponseError; 5513f7c5e40SJason M. Bills } 5529ce789fcSPatrick Venture 5532569025eSJames Feist #ifdef USING_ENTITY_MANAGER_DECORATORS 5542569025eSJames Feist 5559ce789fcSPatrick Venture boost::container::flat_map<std::string, DbusVariant>* entityData = nullptr; 5569ce789fcSPatrick Venture 5572569025eSJames Feist // todo: this should really use caching, this is a very inefficient lookup 5582569025eSJames Feist boost::system::error_code ec; 5592569025eSJames Feist ManagedObjectType entities = ctx->bus->yield_method_call<ManagedObjectType>( 5609d2894d9SNan Zhou ctx->yield, ec, "xyz.openbmc_project.EntityManager", 5619d2894d9SNan Zhou "/xyz/openbmc_project/inventory", "org.freedesktop.DBus.ObjectManager", 5629d2894d9SNan Zhou "GetManagedObjects"); 5632569025eSJames Feist 5642569025eSJames Feist if (ec) 5659ce789fcSPatrick Venture { 5662569025eSJames Feist phosphor::logging::log<phosphor::logging::level::ERR>( 5672569025eSJames Feist "GetMangagedObjects for getSensorMap failed", 5682569025eSJames Feist phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 5699ce789fcSPatrick Venture 5702569025eSJames Feist return ipmi::ccResponseError; 5712569025eSJames Feist } 5729ce789fcSPatrick Venture 573b37abfb2SPatrick Williams auto entity = 574b37abfb2SPatrick Williams std::find_if(entities.begin(), entities.end(), 5759ce789fcSPatrick Venture [bus, address, &entityData](ManagedEntry& entry) { 5769ce789fcSPatrick Venture auto findFruDevice = entry.second.find( 5779ce789fcSPatrick Venture "xyz.openbmc_project.Inventory.Decorator.FruDevice"); 5789ce789fcSPatrick Venture if (findFruDevice == entry.second.end()) 5799ce789fcSPatrick Venture { 5809ce789fcSPatrick Venture return false; 5819ce789fcSPatrick Venture } 5829ce789fcSPatrick Venture 5839ce789fcSPatrick Venture // Integer fields added via Entity-Manager json are uint64_ts by 5849ce789fcSPatrick Venture // default. 5859ce789fcSPatrick Venture auto findBus = findFruDevice->second.find("Bus"); 5869ce789fcSPatrick Venture auto findAddress = findFruDevice->second.find("Address"); 5879ce789fcSPatrick Venture 5889ce789fcSPatrick Venture if (findBus == findFruDevice->second.end() || 5899ce789fcSPatrick Venture findAddress == findFruDevice->second.end()) 5909ce789fcSPatrick Venture { 5919ce789fcSPatrick Venture return false; 5929ce789fcSPatrick Venture } 5939ce789fcSPatrick Venture if ((std::get<uint64_t>(findBus->second) != bus) || 5949ce789fcSPatrick Venture (std::get<uint64_t>(findAddress->second) != address)) 5959ce789fcSPatrick Venture { 5969ce789fcSPatrick Venture return false; 5979ce789fcSPatrick Venture } 5989ce789fcSPatrick Venture 599b4b020c4SPatrick Venture // At this point we found the device entry and should return 600b4b020c4SPatrick Venture // true. 601b37abfb2SPatrick Williams auto findIpmiDevice = 602b37abfb2SPatrick Williams entry.second.find("xyz.openbmc_project.Inventory.Decorator.Ipmi"); 603b4b020c4SPatrick Venture if (findIpmiDevice != entry.second.end()) 6049ce789fcSPatrick Venture { 605b4b020c4SPatrick Venture entityData = &(findIpmiDevice->second); 6069ce789fcSPatrick Venture } 6079ce789fcSPatrick Venture 6089ce789fcSPatrick Venture return true; 6099ce789fcSPatrick Venture }); 6109ce789fcSPatrick Venture 6119ce789fcSPatrick Venture if (entity == entities.end()) 6129ce789fcSPatrick Venture { 6139ce789fcSPatrick Venture if constexpr (DEBUG) 6149ce789fcSPatrick Venture { 6159ce789fcSPatrick Venture std::fprintf(stderr, "Ipmi or FruDevice Decorator interface " 6169ce789fcSPatrick Venture "not found for Fru\n"); 6179ce789fcSPatrick Venture } 6189ce789fcSPatrick Venture } 6192569025eSJames Feist 6202569025eSJames Feist #endif 6219ce789fcSPatrick Venture 6223f7c5e40SJason M. Bills std::string name; 6233f7c5e40SJason M. Bills auto findProductName = fruData->find("BOARD_PRODUCT_NAME"); 6243f7c5e40SJason M. Bills auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME"); 6253f7c5e40SJason M. Bills if (findProductName != fruData->end()) 6263f7c5e40SJason M. Bills { 6278166c8d7SVernon Mauery name = std::get<std::string>(findProductName->second); 6283f7c5e40SJason M. Bills } 6293f7c5e40SJason M. Bills else if (findBoardName != fruData->end()) 6303f7c5e40SJason M. Bills { 6318166c8d7SVernon Mauery name = std::get<std::string>(findBoardName->second); 6323f7c5e40SJason M. Bills } 6333f7c5e40SJason M. Bills else 6343f7c5e40SJason M. Bills { 6353f7c5e40SJason M. Bills name = "UNKNOWN"; 6363f7c5e40SJason M. Bills } 6373f7c5e40SJason M. Bills if (name.size() > maxFruSdrNameSize) 6383f7c5e40SJason M. Bills { 6393f7c5e40SJason M. Bills name = name.substr(0, maxFruSdrNameSize); 6403f7c5e40SJason M. Bills } 6413f7c5e40SJason M. Bills size_t sizeDiff = maxFruSdrNameSize - name.size(); 6423f7c5e40SJason M. Bills 6433f7c5e40SJason M. Bills resp.header.record_id_lsb = 0x0; // calling code is to implement these 6443f7c5e40SJason M. Bills resp.header.record_id_msb = 0x0; 6453f7c5e40SJason M. Bills resp.header.sdr_version = ipmiSdrVersion; 64673d0135dSPatrick Venture resp.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD; 6473f7c5e40SJason M. Bills resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff; 6483f7c5e40SJason M. Bills resp.key.deviceAddress = 0x20; 6493f7c5e40SJason M. Bills resp.key.fruID = device->first; 6503f7c5e40SJason M. Bills resp.key.accessLun = 0x80; // logical / physical fru device 6513f7c5e40SJason M. Bills resp.key.channelNumber = 0x0; 6523f7c5e40SJason M. Bills resp.body.reserved = 0x0; 6533f7c5e40SJason M. Bills resp.body.deviceType = 0x10; 6544f86d1f2SJames Feist resp.body.deviceTypeModifier = 0x0; 6559ce789fcSPatrick Venture 6569ce789fcSPatrick Venture uint8_t entityID = 0; 6579ce789fcSPatrick Venture uint8_t entityInstance = 0x1; 6589ce789fcSPatrick Venture 6592569025eSJames Feist #ifdef USING_ENTITY_MANAGER_DECORATORS 6609ce789fcSPatrick Venture if (entityData) 6619ce789fcSPatrick Venture { 6629ce789fcSPatrick Venture auto entityIdProperty = entityData->find("EntityId"); 6639ce789fcSPatrick Venture auto entityInstanceProperty = entityData->find("EntityInstance"); 6649ce789fcSPatrick Venture 6659ce789fcSPatrick Venture if (entityIdProperty != entityData->end()) 6669ce789fcSPatrick Venture { 6679ce789fcSPatrick Venture entityID = static_cast<uint8_t>( 6689ce789fcSPatrick Venture std::get<uint64_t>(entityIdProperty->second)); 6699ce789fcSPatrick Venture } 6709ce789fcSPatrick Venture if (entityInstanceProperty != entityData->end()) 6719ce789fcSPatrick Venture { 6729ce789fcSPatrick Venture entityInstance = static_cast<uint8_t>( 6739ce789fcSPatrick Venture std::get<uint64_t>(entityInstanceProperty->second)); 6749ce789fcSPatrick Venture } 6759ce789fcSPatrick Venture } 6762569025eSJames Feist #endif 6779ce789fcSPatrick Venture 6789ce789fcSPatrick Venture resp.body.entityID = entityID; 6799ce789fcSPatrick Venture resp.body.entityInstance = entityInstance; 6809ce789fcSPatrick Venture 6813f7c5e40SJason M. Bills resp.body.oem = 0x0; 6823f7c5e40SJason M. Bills resp.body.deviceIDLen = name.size(); 6833f7c5e40SJason M. Bills name.copy(resp.body.deviceID, name.size()); 6843f7c5e40SJason M. Bills 685dcff1506SVernon Mauery return ipmi::ccSuccess; 6863f7c5e40SJason M. Bills } 687e2d1aee3SJason M. Bills 6881d4d54ddSJason M. Bills static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles) 689c04e2e70SJason M. Bills { 6901d4d54ddSJason M. Bills // Loop through the directory looking for ipmi_sel log files 6911d4d54ddSJason M. Bills for (const std::filesystem::directory_entry& dirEnt : 6921d4d54ddSJason M. Bills std::filesystem::directory_iterator(intel_oem::ipmi::sel::selLogDir)) 693c04e2e70SJason M. Bills { 6941d4d54ddSJason M. Bills std::string filename = dirEnt.path().filename(); 6951d4d54ddSJason M. Bills if (boost::starts_with(filename, intel_oem::ipmi::sel::selLogFilename)) 6961d4d54ddSJason M. Bills { 6971d4d54ddSJason M. Bills // If we find an ipmi_sel log file, save the path 6981d4d54ddSJason M. Bills selLogFiles.emplace_back(intel_oem::ipmi::sel::selLogDir / 6991d4d54ddSJason M. Bills filename); 700c04e2e70SJason M. Bills } 701c04e2e70SJason M. Bills } 7021d4d54ddSJason M. Bills // As the log files rotate, they are appended with a ".#" that is higher for 7031d4d54ddSJason M. Bills // the older logs. Since we don't expect more than 10 log files, we 7041d4d54ddSJason M. Bills // can just sort the list to get them in order from newest to oldest 7051d4d54ddSJason M. Bills std::sort(selLogFiles.begin(), selLogFiles.end()); 706c04e2e70SJason M. Bills 7071d4d54ddSJason M. Bills return !selLogFiles.empty(); 708c04e2e70SJason M. Bills } 709c04e2e70SJason M. Bills 7101d4d54ddSJason M. Bills static int countSELEntries() 7111d4d54ddSJason M. Bills { 7121d4d54ddSJason M. Bills // Get the list of ipmi_sel log files 7131d4d54ddSJason M. Bills std::vector<std::filesystem::path> selLogFiles; 7141d4d54ddSJason M. Bills if (!getSELLogFiles(selLogFiles)) 7151d4d54ddSJason M. Bills { 7161d4d54ddSJason M. Bills return 0; 7171d4d54ddSJason M. Bills } 7181d4d54ddSJason M. Bills int numSELEntries = 0; 7191d4d54ddSJason M. Bills // Loop through each log file and count the number of logs 7201d4d54ddSJason M. Bills for (const std::filesystem::path& file : selLogFiles) 7211d4d54ddSJason M. Bills { 7221d4d54ddSJason M. Bills std::ifstream logStream(file); 7231d4d54ddSJason M. Bills if (!logStream.is_open()) 7241d4d54ddSJason M. Bills { 7251d4d54ddSJason M. Bills continue; 7261d4d54ddSJason M. Bills } 7271d4d54ddSJason M. Bills 7281d4d54ddSJason M. Bills std::string line; 7291d4d54ddSJason M. Bills while (std::getline(logStream, line)) 7301d4d54ddSJason M. Bills { 7311d4d54ddSJason M. Bills numSELEntries++; 7321d4d54ddSJason M. Bills } 7331d4d54ddSJason M. Bills } 7341d4d54ddSJason M. Bills return numSELEntries; 7351d4d54ddSJason M. Bills } 7361d4d54ddSJason M. Bills 7371d4d54ddSJason M. Bills static bool findSELEntry(const int recordID, 738ff7e15b2SPatrick Venture const std::vector<std::filesystem::path>& selLogFiles, 7391d4d54ddSJason M. Bills std::string& entry) 7401d4d54ddSJason M. Bills { 7411d4d54ddSJason M. Bills // Record ID is the first entry field following the timestamp. It is 7421d4d54ddSJason M. Bills // preceded by a space and followed by a comma 7431d4d54ddSJason M. Bills std::string search = " " + std::to_string(recordID) + ","; 7441d4d54ddSJason M. Bills 7451d4d54ddSJason M. Bills // Loop through the ipmi_sel log entries 7461d4d54ddSJason M. Bills for (const std::filesystem::path& file : selLogFiles) 7471d4d54ddSJason M. Bills { 7481d4d54ddSJason M. Bills std::ifstream logStream(file); 7491d4d54ddSJason M. Bills if (!logStream.is_open()) 7501d4d54ddSJason M. Bills { 7511d4d54ddSJason M. Bills continue; 7521d4d54ddSJason M. Bills } 7531d4d54ddSJason M. Bills 7541d4d54ddSJason M. Bills while (std::getline(logStream, entry)) 7551d4d54ddSJason M. Bills { 7561d4d54ddSJason M. Bills // Check if the record ID matches 7571d4d54ddSJason M. Bills if (entry.find(search) != std::string::npos) 7581d4d54ddSJason M. Bills { 7591d4d54ddSJason M. Bills return true; 7601d4d54ddSJason M. Bills } 7611d4d54ddSJason M. Bills } 7621d4d54ddSJason M. Bills } 7631d4d54ddSJason M. Bills return false; 7641d4d54ddSJason M. Bills } 7651d4d54ddSJason M. Bills 7661d4d54ddSJason M. Bills static uint16_t 7671d4d54ddSJason M. Bills getNextRecordID(const uint16_t recordID, 768ff7e15b2SPatrick Venture const std::vector<std::filesystem::path>& selLogFiles) 7691d4d54ddSJason M. Bills { 7701d4d54ddSJason M. Bills uint16_t nextRecordID = recordID + 1; 7711d4d54ddSJason M. Bills std::string entry; 7721d4d54ddSJason M. Bills if (findSELEntry(nextRecordID, selLogFiles, entry)) 7731d4d54ddSJason M. Bills { 7741d4d54ddSJason M. Bills return nextRecordID; 7751d4d54ddSJason M. Bills } 7761d4d54ddSJason M. Bills else 7771d4d54ddSJason M. Bills { 7781d4d54ddSJason M. Bills return ipmi::sel::lastEntry; 7791d4d54ddSJason M. Bills } 780c04e2e70SJason M. Bills } 781c04e2e70SJason M. Bills 782ff7e15b2SPatrick Venture static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data) 783c04e2e70SJason M. Bills { 784c04e2e70SJason M. Bills for (unsigned int i = 0; i < hexStr.size(); i += 2) 785c04e2e70SJason M. Bills { 786c04e2e70SJason M. Bills try 787c04e2e70SJason M. Bills { 788c04e2e70SJason M. Bills data.push_back(static_cast<uint8_t>( 789c04e2e70SJason M. Bills std::stoul(hexStr.substr(i, 2), nullptr, 16))); 790c04e2e70SJason M. Bills } 791bd51e6a9SPatrick Williams catch (const std::invalid_argument& e) 792c04e2e70SJason M. Bills { 793c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 794c04e2e70SJason M. Bills return -1; 795c04e2e70SJason M. Bills } 796bd51e6a9SPatrick Williams catch (const std::out_of_range& e) 797c04e2e70SJason M. Bills { 798c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 799c04e2e70SJason M. Bills return -1; 800c04e2e70SJason M. Bills } 801c04e2e70SJason M. Bills } 802c04e2e70SJason M. Bills return 0; 803c04e2e70SJason M. Bills } 804c04e2e70SJason M. Bills 8051d4d54ddSJason M. Bills ipmi::RspType<uint8_t, // SEL version 8061d4d54ddSJason M. Bills uint16_t, // SEL entry count 8071d4d54ddSJason M. Bills uint16_t, // free space 8081d4d54ddSJason M. Bills uint32_t, // last add timestamp 8091d4d54ddSJason M. Bills uint32_t, // last erase timestamp 8101d4d54ddSJason M. Bills uint8_t> // operation support 8111d4d54ddSJason M. Bills ipmiStorageGetSELInfo() 812c04e2e70SJason M. Bills { 8131d4d54ddSJason M. Bills constexpr uint8_t selVersion = ipmi::sel::selVersion; 8141d4d54ddSJason M. Bills uint16_t entries = countSELEntries(); 8151d4d54ddSJason M. Bills uint32_t addTimeStamp = intel_oem::ipmi::sel::getFileTimestamp( 8161d4d54ddSJason M. Bills intel_oem::ipmi::sel::selLogDir / intel_oem::ipmi::sel::selLogFilename); 8171d4d54ddSJason M. Bills uint32_t eraseTimeStamp = intel_oem::ipmi::sel::erase_time::get(); 8181d4d54ddSJason M. Bills constexpr uint8_t operationSupport = 8191d4d54ddSJason M. Bills intel_oem::ipmi::sel::selOperationSupport; 8201d4d54ddSJason M. Bills constexpr uint16_t freeSpace = 8211d4d54ddSJason M. Bills 0xffff; // Spec indicates that more than 64kB is free 822c04e2e70SJason M. Bills 8231d4d54ddSJason M. Bills return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp, 8241d4d54ddSJason M. Bills eraseTimeStamp, operationSupport); 825c04e2e70SJason M. Bills } 826c04e2e70SJason M. Bills 8271d4d54ddSJason M. Bills using systemEventType = std::tuple< 8281d4d54ddSJason M. Bills uint32_t, // Timestamp 8291d4d54ddSJason M. Bills uint16_t, // Generator ID 8301d4d54ddSJason M. Bills uint8_t, // EvM Rev 8311d4d54ddSJason M. Bills uint8_t, // Sensor Type 8321d4d54ddSJason M. Bills uint8_t, // Sensor Number 8331d4d54ddSJason M. Bills uint7_t, // Event Type 8341d4d54ddSJason M. Bills bool, // Event Direction 8351d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data 8361d4d54ddSJason M. Bills using oemTsEventType = std::tuple< 8371d4d54ddSJason M. Bills uint32_t, // Timestamp 8381d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>; // Event Data 8391d4d54ddSJason M. Bills using oemEventType = 8401d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>; // Event Data 8411d4d54ddSJason M. Bills 8421d4d54ddSJason M. Bills ipmi::RspType<uint16_t, // Next Record ID 8431d4d54ddSJason M. Bills uint16_t, // Record ID 8441d4d54ddSJason M. Bills uint8_t, // Record Type 8451d4d54ddSJason M. Bills std::variant<systemEventType, oemTsEventType, 8461d4d54ddSJason M. Bills oemEventType>> // Record Content 8471d4d54ddSJason M. Bills ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID, 8481d4d54ddSJason M. Bills uint8_t offset, uint8_t size) 849c04e2e70SJason M. Bills { 8501d4d54ddSJason M. Bills // Only support getting the entire SEL record. If a partial size or non-zero 8511d4d54ddSJason M. Bills // offset is requested, return an error 8521d4d54ddSJason M. Bills if (offset != 0 || size != ipmi::sel::entireRecord) 853c04e2e70SJason M. Bills { 8541d4d54ddSJason M. Bills return ipmi::responseRetBytesUnavailable(); 855c04e2e70SJason M. Bills } 856c04e2e70SJason M. Bills 8571d4d54ddSJason M. Bills // Check the reservation ID if one is provided or required (only if the 8581d4d54ddSJason M. Bills // offset is non-zero) 8591d4d54ddSJason M. Bills if (reservationID != 0 || offset != 0) 860c04e2e70SJason M. Bills { 8611d4d54ddSJason M. Bills if (!checkSELReservation(reservationID)) 862c04e2e70SJason M. Bills { 8631d4d54ddSJason M. Bills return ipmi::responseInvalidReservationId(); 864c04e2e70SJason M. Bills } 865c04e2e70SJason M. Bills } 866c04e2e70SJason M. Bills 8671d4d54ddSJason M. Bills // Get the ipmi_sel log files 8681d4d54ddSJason M. Bills std::vector<std::filesystem::path> selLogFiles; 8691d4d54ddSJason M. Bills if (!getSELLogFiles(selLogFiles)) 870c04e2e70SJason M. Bills { 8711d4d54ddSJason M. Bills return ipmi::responseSensorInvalid(); 872c04e2e70SJason M. Bills } 873c04e2e70SJason M. Bills 8741d4d54ddSJason M. Bills std::string targetEntry; 875c04e2e70SJason M. Bills 876c04e2e70SJason M. Bills if (targetID == ipmi::sel::firstEntry) 877c04e2e70SJason M. Bills { 8781d4d54ddSJason M. Bills // The first entry will be at the top of the oldest log file 8791d4d54ddSJason M. Bills std::ifstream logStream(selLogFiles.back()); 8801d4d54ddSJason M. Bills if (!logStream.is_open()) 881c04e2e70SJason M. Bills { 8821d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 883c04e2e70SJason M. Bills } 8841d4d54ddSJason M. Bills 8851d4d54ddSJason M. Bills if (!std::getline(logStream, targetEntry)) 8861d4d54ddSJason M. Bills { 8871d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 888c04e2e70SJason M. Bills } 889c04e2e70SJason M. Bills } 890c04e2e70SJason M. Bills else if (targetID == ipmi::sel::lastEntry) 891c04e2e70SJason M. Bills { 8921d4d54ddSJason M. Bills // The last entry will be at the bottom of the newest log file 8931d4d54ddSJason M. Bills std::ifstream logStream(selLogFiles.front()); 8941d4d54ddSJason M. Bills if (!logStream.is_open()) 895c04e2e70SJason M. Bills { 8961d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 897c04e2e70SJason M. Bills } 898c04e2e70SJason M. Bills 8991d4d54ddSJason M. Bills std::string line; 9001d4d54ddSJason M. Bills while (std::getline(logStream, line)) 901c04e2e70SJason M. Bills { 9021d4d54ddSJason M. Bills targetEntry = line; 903c04e2e70SJason M. Bills } 904c04e2e70SJason M. Bills } 905c04e2e70SJason M. Bills else 906c04e2e70SJason M. Bills { 9071d4d54ddSJason M. Bills if (!findSELEntry(targetID, selLogFiles, targetEntry)) 908c04e2e70SJason M. Bills { 9091d4d54ddSJason M. Bills return ipmi::responseSensorInvalid(); 9101d4d54ddSJason M. Bills } 911c04e2e70SJason M. Bills } 912c04e2e70SJason M. Bills 91352aaa7d5SJason M. Bills // The format of the ipmi_sel message is "<Timestamp> 91452aaa7d5SJason M. Bills // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]". 91552aaa7d5SJason M. Bills // First get the Timestamp 91652aaa7d5SJason M. Bills size_t space = targetEntry.find_first_of(" "); 91752aaa7d5SJason M. Bills if (space == std::string::npos) 91852aaa7d5SJason M. Bills { 91952aaa7d5SJason M. Bills return ipmi::responseUnspecifiedError(); 92052aaa7d5SJason M. Bills } 92152aaa7d5SJason M. Bills std::string entryTimestamp = targetEntry.substr(0, space); 92252aaa7d5SJason M. Bills // Then get the log contents 92352aaa7d5SJason M. Bills size_t entryStart = targetEntry.find_first_not_of(" ", space); 92452aaa7d5SJason M. Bills if (entryStart == std::string::npos) 92552aaa7d5SJason M. Bills { 92652aaa7d5SJason M. Bills return ipmi::responseUnspecifiedError(); 92752aaa7d5SJason M. Bills } 92852aaa7d5SJason M. Bills std::string_view entry(targetEntry); 92952aaa7d5SJason M. Bills entry.remove_prefix(entryStart); 93052aaa7d5SJason M. Bills // Use split to separate the entry into its fields 9311d4d54ddSJason M. Bills std::vector<std::string> targetEntryFields; 93252aaa7d5SJason M. Bills boost::split(targetEntryFields, entry, boost::is_any_of(","), 9331d4d54ddSJason M. Bills boost::token_compress_on); 93452aaa7d5SJason M. Bills if (targetEntryFields.size() < 3) 9351d4d54ddSJason M. Bills { 9361d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 937c04e2e70SJason M. Bills } 9381a2fbddfSJason M. Bills std::string& recordIDStr = targetEntryFields[0]; 9391a2fbddfSJason M. Bills std::string& recordTypeStr = targetEntryFields[1]; 9401a2fbddfSJason M. Bills std::string& eventDataStr = targetEntryFields[2]; 941c04e2e70SJason M. Bills 9421a2fbddfSJason M. Bills uint16_t recordID; 9431a2fbddfSJason M. Bills uint8_t recordType; 9441a2fbddfSJason M. Bills try 9451a2fbddfSJason M. Bills { 9461a2fbddfSJason M. Bills recordID = std::stoul(recordIDStr); 9471a2fbddfSJason M. Bills recordType = std::stoul(recordTypeStr, nullptr, 16); 9481a2fbddfSJason M. Bills } 9491a2fbddfSJason M. Bills catch (const std::invalid_argument&) 9501a2fbddfSJason M. Bills { 9511a2fbddfSJason M. Bills return ipmi::responseUnspecifiedError(); 9521a2fbddfSJason M. Bills } 9531d4d54ddSJason M. Bills uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles); 9541d4d54ddSJason M. Bills std::vector<uint8_t> eventDataBytes; 9551a2fbddfSJason M. Bills if (fromHexStr(eventDataStr, eventDataBytes) < 0) 9561d4d54ddSJason M. Bills { 9571d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 9581d4d54ddSJason M. Bills } 9591d4d54ddSJason M. Bills 9601d4d54ddSJason M. Bills if (recordType == intel_oem::ipmi::sel::systemEvent) 9611d4d54ddSJason M. Bills { 9621d4d54ddSJason M. Bills // Get the timestamp 9631d4d54ddSJason M. Bills std::tm timeStruct = {}; 96452aaa7d5SJason M. Bills std::istringstream entryStream(entryTimestamp); 9651d4d54ddSJason M. Bills 9661d4d54ddSJason M. Bills uint32_t timestamp = ipmi::sel::invalidTimeStamp; 9671d4d54ddSJason M. Bills if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 9681d4d54ddSJason M. Bills { 9691d4d54ddSJason M. Bills timestamp = std::mktime(&timeStruct); 9701d4d54ddSJason M. Bills } 9711d4d54ddSJason M. Bills 9721d4d54ddSJason M. Bills // Set the event message revision 9731d4d54ddSJason M. Bills uint8_t evmRev = intel_oem::ipmi::sel::eventMsgRev; 9741d4d54ddSJason M. Bills 9751a2fbddfSJason M. Bills uint16_t generatorID = 0; 9761a2fbddfSJason M. Bills uint8_t sensorType = 0; 977308c3a8bSJohnathan Mantey uint16_t sensorAndLun = 0; 9781a2fbddfSJason M. Bills uint8_t sensorNum = 0xFF; 9791a2fbddfSJason M. Bills uint7_t eventType = 0; 9801a2fbddfSJason M. Bills bool eventDir = 0; 9811a2fbddfSJason M. Bills // System type events should have six fields 9821a2fbddfSJason M. Bills if (targetEntryFields.size() >= 6) 9831a2fbddfSJason M. Bills { 9841a2fbddfSJason M. Bills std::string& generatorIDStr = targetEntryFields[3]; 9851a2fbddfSJason M. Bills std::string& sensorPath = targetEntryFields[4]; 9861a2fbddfSJason M. Bills std::string& eventDirStr = targetEntryFields[5]; 9871a2fbddfSJason M. Bills 9881a2fbddfSJason M. Bills // Get the generator ID 9891a2fbddfSJason M. Bills try 9901a2fbddfSJason M. Bills { 9911a2fbddfSJason M. Bills generatorID = std::stoul(generatorIDStr, nullptr, 16); 9921a2fbddfSJason M. Bills } 9931a2fbddfSJason M. Bills catch (const std::invalid_argument&) 9941a2fbddfSJason M. Bills { 9951a2fbddfSJason M. Bills std::cerr << "Invalid Generator ID\n"; 9961a2fbddfSJason M. Bills } 9971a2fbddfSJason M. Bills 9981d4d54ddSJason M. Bills // Get the sensor type, sensor number, and event type for the sensor 9991a2fbddfSJason M. Bills sensorType = getSensorTypeFromPath(sensorPath); 1000308c3a8bSJohnathan Mantey sensorAndLun = getSensorNumberFromPath(sensorPath); 1001308c3a8bSJohnathan Mantey sensorNum = static_cast<uint8_t>(sensorAndLun); 1002308c3a8bSJohnathan Mantey generatorID |= sensorAndLun >> 8; 10031a2fbddfSJason M. Bills eventType = getSensorEventTypeFromPath(sensorPath); 10041d4d54ddSJason M. Bills 10051d4d54ddSJason M. Bills // Get the event direction 10061a2fbddfSJason M. Bills try 10071a2fbddfSJason M. Bills { 10081a2fbddfSJason M. Bills eventDir = std::stoul(eventDirStr) ? 0 : 1; 10091a2fbddfSJason M. Bills } 10101a2fbddfSJason M. Bills catch (const std::invalid_argument&) 10111a2fbddfSJason M. Bills { 10121a2fbddfSJason M. Bills std::cerr << "Invalid Event Direction\n"; 10131a2fbddfSJason M. Bills } 10141a2fbddfSJason M. Bills } 10151d4d54ddSJason M. Bills 10161d4d54ddSJason M. Bills // Only keep the eventData bytes that fit in the record 10171d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{}; 10181d4d54ddSJason M. Bills std::copy_n(eventDataBytes.begin(), 10191d4d54ddSJason M. Bills std::min(eventDataBytes.size(), eventData.size()), 10201d4d54ddSJason M. Bills eventData.begin()); 10211d4d54ddSJason M. Bills 10221d4d54ddSJason M. Bills return ipmi::responseSuccess( 10231d4d54ddSJason M. Bills nextRecordID, recordID, recordType, 10241d4d54ddSJason M. Bills systemEventType{timestamp, generatorID, evmRev, sensorType, 10251d4d54ddSJason M. Bills sensorNum, eventType, eventDir, eventData}); 10261d4d54ddSJason M. Bills } 10271d4d54ddSJason M. Bills else if (recordType >= intel_oem::ipmi::sel::oemTsEventFirst && 10281d4d54ddSJason M. Bills recordType <= intel_oem::ipmi::sel::oemTsEventLast) 10291d4d54ddSJason M. Bills { 10301d4d54ddSJason M. Bills // Get the timestamp 10311d4d54ddSJason M. Bills std::tm timeStruct = {}; 103252aaa7d5SJason M. Bills std::istringstream entryStream(entryTimestamp); 10331d4d54ddSJason M. Bills 10341d4d54ddSJason M. Bills uint32_t timestamp = ipmi::sel::invalidTimeStamp; 10351d4d54ddSJason M. Bills if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 10361d4d54ddSJason M. Bills { 10371d4d54ddSJason M. Bills timestamp = std::mktime(&timeStruct); 10381d4d54ddSJason M. Bills } 10391d4d54ddSJason M. Bills 10401d4d54ddSJason M. Bills // Only keep the bytes that fit in the record 10411d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize> eventData{}; 10421d4d54ddSJason M. Bills std::copy_n(eventDataBytes.begin(), 10431d4d54ddSJason M. Bills std::min(eventDataBytes.size(), eventData.size()), 10441d4d54ddSJason M. Bills eventData.begin()); 10451d4d54ddSJason M. Bills 10461d4d54ddSJason M. Bills return ipmi::responseSuccess(nextRecordID, recordID, recordType, 10471d4d54ddSJason M. Bills oemTsEventType{timestamp, eventData}); 10481d4d54ddSJason M. Bills } 1049c5136aaaSPatrick Venture else if (recordType >= intel_oem::ipmi::sel::oemEventFirst) 10501d4d54ddSJason M. Bills { 10511d4d54ddSJason M. Bills // Only keep the bytes that fit in the record 10521d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize> eventData{}; 10531d4d54ddSJason M. Bills std::copy_n(eventDataBytes.begin(), 10541d4d54ddSJason M. Bills std::min(eventDataBytes.size(), eventData.size()), 10551d4d54ddSJason M. Bills eventData.begin()); 10561d4d54ddSJason M. Bills 10571d4d54ddSJason M. Bills return ipmi::responseSuccess(nextRecordID, recordID, recordType, 10581d4d54ddSJason M. Bills eventData); 10591d4d54ddSJason M. Bills } 10601d4d54ddSJason M. Bills 10611d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 1062c04e2e70SJason M. Bills } 1063c04e2e70SJason M. Bills 10646dd8f047SJason M. Bills ipmi::RspType<uint16_t> ipmiStorageAddSELEntry( 10656dd8f047SJason M. Bills uint16_t recordID, uint8_t recordType, uint32_t timestamp, 10666dd8f047SJason M. Bills uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum, 10676dd8f047SJason M. Bills uint8_t eventType, uint8_t eventData1, uint8_t eventData2, 10686dd8f047SJason M. Bills uint8_t eventData3) 1069c04e2e70SJason M. Bills { 1070c04e2e70SJason M. Bills // Per the IPMI spec, need to cancel any reservation when a SEL entry is 1071c04e2e70SJason M. Bills // added 1072c04e2e70SJason M. Bills cancelSELReservation(); 1073c04e2e70SJason M. Bills 10746dd8f047SJason M. Bills // Send this request to the Redfish hooks to log it as a Redfish message 10756dd8f047SJason M. Bills // instead. There is no need to add it to the SEL, so just return success. 10766dd8f047SJason M. Bills intel_oem::ipmi::sel::checkRedfishHooks( 10776dd8f047SJason M. Bills recordID, recordType, timestamp, generatorID, evmRev, sensorType, 10786dd8f047SJason M. Bills sensorNum, eventType, eventData1, eventData2, eventData3); 107999b78ec8SJason M. Bills 10806dd8f047SJason M. Bills uint16_t responseID = 0xFFFF; 10816dd8f047SJason M. Bills return ipmi::responseSuccess(responseID); 1082c04e2e70SJason M. Bills } 1083c04e2e70SJason M. Bills 1084dcff1506SVernon Mauery ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr&, 10851d4d54ddSJason M. Bills uint16_t reservationID, 10861d4d54ddSJason M. Bills const std::array<uint8_t, 3>& clr, 10871d4d54ddSJason M. Bills uint8_t eraseOperation) 1088c04e2e70SJason M. Bills { 10891d4d54ddSJason M. Bills if (!checkSELReservation(reservationID)) 1090c04e2e70SJason M. Bills { 10911d4d54ddSJason M. Bills return ipmi::responseInvalidReservationId(); 1092c04e2e70SJason M. Bills } 1093c04e2e70SJason M. Bills 10941d4d54ddSJason M. Bills static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'}; 10951d4d54ddSJason M. Bills if (clr != clrExpected) 1096c04e2e70SJason M. Bills { 10971d4d54ddSJason M. Bills return ipmi::responseInvalidFieldRequest(); 1098c04e2e70SJason M. Bills } 1099c04e2e70SJason M. Bills 11001d4d54ddSJason M. Bills // Erasure status cannot be fetched, so always return erasure status as 11011d4d54ddSJason M. Bills // `erase completed`. 11021d4d54ddSJason M. Bills if (eraseOperation == ipmi::sel::getEraseStatus) 1103c04e2e70SJason M. Bills { 11041d4d54ddSJason M. Bills return ipmi::responseSuccess(ipmi::sel::eraseComplete); 1105c04e2e70SJason M. Bills } 1106c04e2e70SJason M. Bills 11071d4d54ddSJason M. Bills // Check that initiate erase is correct 11081d4d54ddSJason M. Bills if (eraseOperation != ipmi::sel::initiateErase) 11091d4d54ddSJason M. Bills { 11101d4d54ddSJason M. Bills return ipmi::responseInvalidFieldRequest(); 11111d4d54ddSJason M. Bills } 11121d4d54ddSJason M. Bills 11131d4d54ddSJason M. Bills // Per the IPMI spec, need to cancel any reservation when the SEL is 11141d4d54ddSJason M. Bills // cleared 1115c04e2e70SJason M. Bills cancelSELReservation(); 1116c04e2e70SJason M. Bills 11177944c307SJason M. Bills // Save the erase time 11187944c307SJason M. Bills intel_oem::ipmi::sel::erase_time::save(); 11197944c307SJason M. Bills 11201d4d54ddSJason M. Bills // Clear the SEL by deleting the log files 11211d4d54ddSJason M. Bills std::vector<std::filesystem::path> selLogFiles; 11221d4d54ddSJason M. Bills if (getSELLogFiles(selLogFiles)) 1123c04e2e70SJason M. Bills { 11241d4d54ddSJason M. Bills for (const std::filesystem::path& file : selLogFiles) 11251d4d54ddSJason M. Bills { 11261d4d54ddSJason M. Bills std::error_code ec; 11271d4d54ddSJason M. Bills std::filesystem::remove(file, ec); 1128c04e2e70SJason M. Bills } 1129c04e2e70SJason M. Bills } 1130c04e2e70SJason M. Bills 11311d4d54ddSJason M. Bills // Reload rsyslog so it knows to start new log files 113215419dd5SVernon Mauery std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1133f944d2e5SPatrick Williams sdbusplus::message_t rsyslogReload = dbus->new_method_call( 11341d4d54ddSJason M. Bills "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 11351d4d54ddSJason M. Bills "org.freedesktop.systemd1.Manager", "ReloadUnit"); 11361d4d54ddSJason M. Bills rsyslogReload.append("rsyslog.service", "replace"); 11371d4d54ddSJason M. Bills try 11381d4d54ddSJason M. Bills { 1139f944d2e5SPatrick Williams sdbusplus::message_t reloadResponse = dbus->call(rsyslogReload); 11401d4d54ddSJason M. Bills } 1141bd51e6a9SPatrick Williams catch (const sdbusplus::exception_t& e) 11421d4d54ddSJason M. Bills { 11431d4d54ddSJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 1144c04e2e70SJason M. Bills } 1145c04e2e70SJason M. Bills 11461d4d54ddSJason M. Bills return ipmi::responseSuccess(ipmi::sel::eraseComplete); 11471d4d54ddSJason M. Bills } 11481d4d54ddSJason M. Bills 11491a47462eSJason M. Bills ipmi::RspType<uint32_t> ipmiStorageGetSELTime() 11501a47462eSJason M. Bills { 11511a47462eSJason M. Bills struct timespec selTime = {}; 11521a47462eSJason M. Bills 11531a47462eSJason M. Bills if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 11541a47462eSJason M. Bills { 11551a47462eSJason M. Bills return ipmi::responseUnspecifiedError(); 11561a47462eSJason M. Bills } 11571a47462eSJason M. Bills 11581a47462eSJason M. Bills return ipmi::responseSuccess(selTime.tv_sec); 11591a47462eSJason M. Bills } 11601a47462eSJason M. Bills 1161dcff1506SVernon Mauery ipmi::RspType<> ipmiStorageSetSELTime([[maybe_unused]] uint32_t selTime) 1162cac97a53SJason M. Bills { 1163cac97a53SJason M. Bills // Set SEL Time is not supported 11641d4d54ddSJason M. Bills return ipmi::responseInvalidCommand(); 1165cac97a53SJason M. Bills } 1166cac97a53SJason M. Bills 116774c50c64SJames Feist std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId) 116874c50c64SJames Feist { 116974c50c64SJames Feist std::vector<uint8_t> resp; 117074c50c64SJames Feist if (index == 0) 117174c50c64SJames Feist { 117274c50c64SJames Feist std::string bmcName = "Basbrd Mgmt Ctlr"; 1173f4d5e05eSJohnathan Mantey Type12Record bmc(recordId, 0x20, 0, 0, 0xbf, 0x2e, 1, 0, bmcName); 117474c50c64SJames Feist uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc); 117574c50c64SJames Feist resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record)); 117674c50c64SJames Feist } 117774c50c64SJames Feist else if (index == 1) 117874c50c64SJames Feist { 117974c50c64SJames Feist std::string meName = "Mgmt Engine"; 1180f4d5e05eSJohnathan Mantey Type12Record me(recordId, 0x2c, 6, 0x24, 0x21, 0x2e, 2, 0, meName); 118174c50c64SJames Feist uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me); 118274c50c64SJames Feist resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record)); 118374c50c64SJames Feist } 118474c50c64SJames Feist else 118574c50c64SJames Feist { 118674c50c64SJames Feist throw std::runtime_error("getType12SDRs:: Illegal index " + 118774c50c64SJames Feist std::to_string(index)); 118874c50c64SJames Feist } 118974c50c64SJames Feist 119074c50c64SJames Feist return resp; 119174c50c64SJames Feist } 119274c50c64SJames Feist 1193fee5e4c7SYong Li std::vector<uint8_t> getNMDiscoverySDR(uint16_t index, uint16_t recordId) 1194fee5e4c7SYong Li { 1195fee5e4c7SYong Li std::vector<uint8_t> resp; 1196fee5e4c7SYong Li if (index == 0) 1197fee5e4c7SYong Li { 1198fee5e4c7SYong Li NMDiscoveryRecord nm = {}; 1199fee5e4c7SYong Li nm.header.record_id_lsb = recordId; 1200fee5e4c7SYong Li nm.header.record_id_msb = recordId >> 8; 1201fee5e4c7SYong Li nm.header.sdr_version = ipmiSdrVersion; 1202fee5e4c7SYong Li nm.header.record_type = 0xC0; 1203fee5e4c7SYong Li nm.header.record_length = 0xB; 1204fee5e4c7SYong Li nm.oemID0 = 0x57; 1205fee5e4c7SYong Li nm.oemID1 = 0x1; 1206fee5e4c7SYong Li nm.oemID2 = 0x0; 1207fee5e4c7SYong Li nm.subType = 0x0D; 1208fee5e4c7SYong Li nm.version = 0x1; 120980d4d5f9SMatt Simmering nm.targetAddress = 0x2C; 1210fee5e4c7SYong Li nm.channelNumber = 0x60; 1211fee5e4c7SYong Li nm.healthEventSensor = 0x19; 1212fee5e4c7SYong Li nm.exceptionEventSensor = 0x18; 1213fee5e4c7SYong Li nm.operationalCapSensor = 0x1A; 1214fee5e4c7SYong Li nm.thresholdExceededSensor = 0x1B; 1215fee5e4c7SYong Li 1216fee5e4c7SYong Li uint8_t* nmPtr = reinterpret_cast<uint8_t*>(&nm); 1217fee5e4c7SYong Li resp.insert(resp.end(), nmPtr, nmPtr + sizeof(NMDiscoveryRecord)); 1218fee5e4c7SYong Li } 1219fee5e4c7SYong Li else 1220fee5e4c7SYong Li { 1221fee5e4c7SYong Li throw std::runtime_error("getNMDiscoverySDR:: Illegal index " + 1222fee5e4c7SYong Li std::to_string(index)); 1223fee5e4c7SYong Li } 1224fee5e4c7SYong Li 1225fee5e4c7SYong Li return resp; 1226fee5e4c7SYong Li } 1227fee5e4c7SYong Li 1228e2d1aee3SJason M. Bills void registerStorageFunctions() 1229e2d1aee3SJason M. Bills { 12302569025eSJames Feist createTimers(); 1231e4f710d7SJames Feist startMatch(); 1232e4f710d7SJames Feist 1233e2d1aee3SJason M. Bills // <Get FRU Inventory Area Info> 1234d33acd6bSjayaprakash Mutyala ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage, 1235d33acd6bSjayaprakash Mutyala ipmi::storage::cmdGetFruInventoryAreaInfo, 1236d33acd6bSjayaprakash Mutyala ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo); 1237c04e2e70SJason M. Bills // <READ FRU Data> 12385f4194eeSjayaprakash Mutyala ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 12395f4194eeSjayaprakash Mutyala ipmi::storage::cmdReadFruData, ipmi::Privilege::User, 12405f4194eeSjayaprakash Mutyala ipmiStorageReadFruData); 1241e2d1aee3SJason M. Bills 1242c04e2e70SJason M. Bills // <WRITE FRU Data> 12435f4194eeSjayaprakash Mutyala ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 12445f4194eeSjayaprakash Mutyala ipmi::storage::cmdWriteFruData, 12455f4194eeSjayaprakash Mutyala ipmi::Privilege::Operator, ipmiStorageWriteFruData); 1246c04e2e70SJason M. Bills 1247c04e2e70SJason M. Bills // <Get SEL Info> 12481d4d54ddSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1249542498e9SJason M. Bills ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User, 1250542498e9SJason M. Bills ipmiStorageGetSELInfo); 1251c04e2e70SJason M. Bills 1252c04e2e70SJason M. Bills // <Get SEL Entry> 12531d4d54ddSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1254542498e9SJason M. Bills ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User, 1255542498e9SJason M. Bills ipmiStorageGetSELEntry); 1256c04e2e70SJason M. Bills 1257c04e2e70SJason M. Bills // <Add SEL Entry> 12586dd8f047SJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 125998bbf69aSVernon Mauery ipmi::storage::cmdAddSelEntry, 12606dd8f047SJason M. Bills ipmi::Privilege::Operator, ipmiStorageAddSELEntry); 1261c04e2e70SJason M. Bills 1262c04e2e70SJason M. Bills // <Clear SEL> 12631d4d54ddSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 12641d4d54ddSJason M. Bills ipmi::storage::cmdClearSel, ipmi::Privilege::Operator, 12651d4d54ddSJason M. Bills ipmiStorageClearSEL); 1266cac97a53SJason M. Bills 12671a47462eSJason M. Bills // <Get SEL Time> 12681a47462eSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1269542498e9SJason M. Bills ipmi::storage::cmdGetSelTime, ipmi::Privilege::User, 1270542498e9SJason M. Bills ipmiStorageGetSELTime); 12711a47462eSJason M. Bills 1272cac97a53SJason M. Bills // <Set SEL Time> 12731d4d54ddSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 12741d4d54ddSJason M. Bills ipmi::storage::cmdSetSelTime, 12751d4d54ddSJason M. Bills ipmi::Privilege::Operator, ipmiStorageSetSELTime); 1276e2d1aee3SJason M. Bills } 12773f7c5e40SJason M. Bills } // namespace storage 12783f7c5e40SJason M. Bills } // namespace ipmi 1279