1711d51d8SMatt Spinler /** 2711d51d8SMatt Spinler * Copyright © 2019 IBM Corporation 3711d51d8SMatt Spinler * 4711d51d8SMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License"); 5711d51d8SMatt Spinler * you may not use this file except in compliance with the License. 6711d51d8SMatt Spinler * You may obtain a copy of the License at 7711d51d8SMatt Spinler * 8711d51d8SMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0 9711d51d8SMatt Spinler * 10711d51d8SMatt Spinler * Unless required by applicable law or agreed to in writing, software 11711d51d8SMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS, 12711d51d8SMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13711d51d8SMatt Spinler * See the License for the specific language governing permissions and 14711d51d8SMatt Spinler * limitations under the License. 15711d51d8SMatt Spinler */ 1689fa082aSMatt Spinler #include "repository.hpp" 1789fa082aSMatt Spinler 1889fa082aSMatt Spinler #include <fstream> 1989fa082aSMatt Spinler #include <phosphor-logging/log.hpp> 2089fa082aSMatt Spinler #include <xyz/openbmc_project/Common/File/error.hpp> 2189fa082aSMatt Spinler 2289fa082aSMatt Spinler namespace openpower 2389fa082aSMatt Spinler { 2489fa082aSMatt Spinler namespace pels 2589fa082aSMatt Spinler { 2689fa082aSMatt Spinler 2789fa082aSMatt Spinler namespace fs = std::filesystem; 2889fa082aSMatt Spinler using namespace phosphor::logging; 2989fa082aSMatt Spinler namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 3089fa082aSMatt Spinler 31*8d5f3a2bSMatt Spinler Repository::Repository(const std::filesystem::path& basePath, size_t repoSize, 32*8d5f3a2bSMatt Spinler size_t maxNumPELs) : 33*8d5f3a2bSMatt Spinler _logPath(basePath / "logs"), 34*8d5f3a2bSMatt Spinler _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs) 3589fa082aSMatt Spinler { 3689fa082aSMatt Spinler if (!fs::exists(_logPath)) 3789fa082aSMatt Spinler { 3889fa082aSMatt Spinler fs::create_directories(_logPath); 3989fa082aSMatt Spinler } 40475e574dSMatt Spinler 41475e574dSMatt Spinler restore(); 42475e574dSMatt Spinler } 43475e574dSMatt Spinler 44475e574dSMatt Spinler void Repository::restore() 45475e574dSMatt Spinler { 46475e574dSMatt Spinler for (auto& dirEntry : fs::directory_iterator(_logPath)) 47475e574dSMatt Spinler { 48475e574dSMatt Spinler try 49475e574dSMatt Spinler { 50475e574dSMatt Spinler if (!fs::is_regular_file(dirEntry.path())) 51475e574dSMatt Spinler { 52475e574dSMatt Spinler continue; 53475e574dSMatt Spinler } 54475e574dSMatt Spinler 55475e574dSMatt Spinler std::ifstream file{dirEntry.path()}; 56475e574dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 57475e574dSMatt Spinler std::istreambuf_iterator<char>()}; 58475e574dSMatt Spinler file.close(); 59475e574dSMatt Spinler 6007eefc54SMatt Spinler PEL pel{data}; 61475e574dSMatt Spinler if (pel.valid()) 62475e574dSMatt Spinler { 63a3c12a48SMatt Spinler // If the host hasn't acked it, reset the host state so 64a3c12a48SMatt Spinler // it will get sent up again. 65a3c12a48SMatt Spinler if (pel.hostTransmissionState() == TransmissionState::sent) 66a3c12a48SMatt Spinler { 67a3c12a48SMatt Spinler pel.setHostTransmissionState(TransmissionState::newPEL); 68a3c12a48SMatt Spinler try 69a3c12a48SMatt Spinler { 70a3c12a48SMatt Spinler write(pel, dirEntry.path()); 71a3c12a48SMatt Spinler } 72a3c12a48SMatt Spinler catch (std::exception& e) 73a3c12a48SMatt Spinler { 74a3c12a48SMatt Spinler log<level::ERR>( 75a3c12a48SMatt Spinler "Failed to save PEL after updating host state", 76a3c12a48SMatt Spinler entry("PELID=0x%X", pel.id())); 77a3c12a48SMatt Spinler } 78a3c12a48SMatt Spinler } 79a3c12a48SMatt Spinler 80346f99a1SMatt Spinler PELAttributes attributes{ 81346f99a1SMatt Spinler dirEntry.path(), pel.userHeader().actionFlags(), 82346f99a1SMatt Spinler pel.hostTransmissionState(), pel.hmcTransmissionState()}; 830ff00485SMatt Spinler 84475e574dSMatt Spinler using pelID = LogID::Pel; 85475e574dSMatt Spinler using obmcID = LogID::Obmc; 860ff00485SMatt Spinler _pelAttributes.emplace( 87475e574dSMatt Spinler LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 880ff00485SMatt Spinler attributes); 89475e574dSMatt Spinler } 90475e574dSMatt Spinler else 91475e574dSMatt Spinler { 92475e574dSMatt Spinler log<level::ERR>( 93475e574dSMatt Spinler "Found invalid PEL file while restoring. Removing.", 94475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str())); 95475e574dSMatt Spinler fs::remove(dirEntry.path()); 96475e574dSMatt Spinler } 97475e574dSMatt Spinler } 98475e574dSMatt Spinler catch (std::exception& e) 99475e574dSMatt Spinler { 100475e574dSMatt Spinler log<level::ERR>("Hit exception while restoring PEL File", 101475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str()), 102475e574dSMatt Spinler entry("ERROR=%s", e.what())); 103475e574dSMatt Spinler } 104475e574dSMatt Spinler } 10589fa082aSMatt Spinler } 10689fa082aSMatt Spinler 10789fa082aSMatt Spinler std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 10889fa082aSMatt Spinler { 10989fa082aSMatt Spinler char name[50]; 11089fa082aSMatt Spinler sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 11189fa082aSMatt Spinler time.yearLSB, time.month, time.day, time.hour, time.minutes, 11289fa082aSMatt Spinler time.seconds, time.hundredths, pelID); 11389fa082aSMatt Spinler return std::string{name}; 11489fa082aSMatt Spinler } 11589fa082aSMatt Spinler 11689fa082aSMatt Spinler void Repository::add(std::unique_ptr<PEL>& pel) 11789fa082aSMatt Spinler { 118df43a305SMatt Spinler pel->setHostTransmissionState(TransmissionState::newPEL); 119df43a305SMatt Spinler pel->setHMCTransmissionState(TransmissionState::newPEL); 120df43a305SMatt Spinler 12189fa082aSMatt Spinler auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 122ab1b97feSMatt Spinler 123ab1b97feSMatt Spinler write(*(pel.get()), path); 124ab1b97feSMatt Spinler 125346f99a1SMatt Spinler PELAttributes attributes{path, pel->userHeader().actionFlags(), 126346f99a1SMatt Spinler pel->hostTransmissionState(), 127346f99a1SMatt Spinler pel->hmcTransmissionState()}; 128ab1b97feSMatt Spinler 129ab1b97feSMatt Spinler using pelID = LogID::Pel; 130ab1b97feSMatt Spinler using obmcID = LogID::Obmc; 131ab1b97feSMatt Spinler _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 132ab1b97feSMatt Spinler attributes); 133ab1b97feSMatt Spinler 134ab1b97feSMatt Spinler processAddCallbacks(*pel); 135ab1b97feSMatt Spinler } 136ab1b97feSMatt Spinler 137ab1b97feSMatt Spinler void Repository::write(const PEL& pel, const fs::path& path) 138ab1b97feSMatt Spinler { 13989fa082aSMatt Spinler std::ofstream file{path, std::ios::binary}; 14089fa082aSMatt Spinler 14189fa082aSMatt Spinler if (!file.good()) 14289fa082aSMatt Spinler { 14389fa082aSMatt Spinler // If this fails, the filesystem is probably full so it isn't like 14489fa082aSMatt Spinler // we could successfully create yet another error log here. 14589fa082aSMatt Spinler auto e = errno; 14689fa082aSMatt Spinler fs::remove(path); 14789fa082aSMatt Spinler log<level::ERR>("Unable to open PEL file for writing", 14889fa082aSMatt Spinler entry("ERRNO=%d", e), entry("PATH=%s", path.c_str())); 14989fa082aSMatt Spinler throw file_error::Open(); 15089fa082aSMatt Spinler } 15189fa082aSMatt Spinler 152ab1b97feSMatt Spinler auto data = pel.data(); 15389fa082aSMatt Spinler file.write(reinterpret_cast<const char*>(data.data()), data.size()); 15489fa082aSMatt Spinler 15589fa082aSMatt Spinler if (file.fail()) 15689fa082aSMatt Spinler { 15789fa082aSMatt Spinler // Same note as above about not being able to create an error log 15889fa082aSMatt Spinler // for this case even if we wanted. 15989fa082aSMatt Spinler auto e = errno; 16089fa082aSMatt Spinler file.close(); 16189fa082aSMatt Spinler fs::remove(path); 16289fa082aSMatt Spinler log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e), 16389fa082aSMatt Spinler entry("PATH=%s", path.c_str())); 16489fa082aSMatt Spinler throw file_error::Write(); 16589fa082aSMatt Spinler } 166475e574dSMatt Spinler } 167475e574dSMatt Spinler 168475e574dSMatt Spinler void Repository::remove(const LogID& id) 169475e574dSMatt Spinler { 170475e574dSMatt Spinler auto pel = findPEL(id); 1710ff00485SMatt Spinler if (pel != _pelAttributes.end()) 172475e574dSMatt Spinler { 1735f5352e5SMatt Spinler log<level::DEBUG>("Removing PEL from repository", 1745f5352e5SMatt Spinler entry("PEL_ID=0x%X", pel->first.pelID.id), 1755f5352e5SMatt Spinler entry("OBMC_LOG_ID=%d", pel->first.obmcID.id)); 1760ff00485SMatt Spinler fs::remove(pel->second.path); 1770ff00485SMatt Spinler _pelAttributes.erase(pel); 178421f6531SMatt Spinler 1795f5352e5SMatt Spinler processDeleteCallbacks(pel->first.pelID.id); 1805f5352e5SMatt Spinler } 1815f5352e5SMatt Spinler else 1825f5352e5SMatt Spinler { 1835f5352e5SMatt Spinler log<level::DEBUG>("Could not find PEL to remove", 1845f5352e5SMatt Spinler entry("PEL_ID=0x%X", id.pelID.id), 1855f5352e5SMatt Spinler entry("OBMC_LOG_ID=%d", id.obmcID.id)); 186475e574dSMatt Spinler } 18789fa082aSMatt Spinler } 18889fa082aSMatt Spinler 1892813f36dSMatt Spinler std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 1902813f36dSMatt Spinler { 1912813f36dSMatt Spinler auto pel = findPEL(id); 1920ff00485SMatt Spinler if (pel != _pelAttributes.end()) 1932813f36dSMatt Spinler { 1940ff00485SMatt Spinler std::ifstream file{pel->second.path.c_str()}; 1952813f36dSMatt Spinler if (!file.good()) 1962813f36dSMatt Spinler { 1972813f36dSMatt Spinler auto e = errno; 1982813f36dSMatt Spinler log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e), 1990ff00485SMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 2002813f36dSMatt Spinler throw file_error::Open(); 2012813f36dSMatt Spinler } 2022813f36dSMatt Spinler 2032813f36dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2042813f36dSMatt Spinler std::istreambuf_iterator<char>()}; 2052813f36dSMatt Spinler return data; 2062813f36dSMatt Spinler } 2072813f36dSMatt Spinler 2082813f36dSMatt Spinler return std::nullopt; 2092813f36dSMatt Spinler } 2102813f36dSMatt Spinler 2116d51224bSMatt Spinler std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id) 2126d51224bSMatt Spinler { 2136d51224bSMatt Spinler auto pel = findPEL(id); 2146d51224bSMatt Spinler if (pel != _pelAttributes.end()) 2156d51224bSMatt Spinler { 2166d51224bSMatt Spinler FILE* fp = fopen(pel->second.path.c_str(), "rb"); 2176d51224bSMatt Spinler 2186d51224bSMatt Spinler if (fp == nullptr) 2196d51224bSMatt Spinler { 2206d51224bSMatt Spinler auto e = errno; 2216d51224bSMatt Spinler log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e), 2226d51224bSMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 2236d51224bSMatt Spinler throw file_error::Open(); 2246d51224bSMatt Spinler } 2256d51224bSMatt Spinler 2266d51224bSMatt Spinler // Must leave the file open here. It will be closed by sdbusplus 2276d51224bSMatt Spinler // when it sends it back over D-Bus. 2286d51224bSMatt Spinler 2296d51224bSMatt Spinler return fileno(fp); 2306d51224bSMatt Spinler } 2316d51224bSMatt Spinler return std::nullopt; 2326d51224bSMatt Spinler } 2336d51224bSMatt Spinler 2341ea78801SMatt Spinler void Repository::for_each(ForEachFunc func) const 2351ea78801SMatt Spinler { 2360ff00485SMatt Spinler for (const auto& [id, attributes] : _pelAttributes) 2371ea78801SMatt Spinler { 2380ff00485SMatt Spinler std::ifstream file{attributes.path}; 2391ea78801SMatt Spinler 2401ea78801SMatt Spinler if (!file.good()) 2411ea78801SMatt Spinler { 2421ea78801SMatt Spinler auto e = errno; 2431ea78801SMatt Spinler log<level::ERR>("Repository::for_each: Unable to open PEL file", 2441ea78801SMatt Spinler entry("ERRNO=%d", e), 2450ff00485SMatt Spinler entry("PATH=%s", attributes.path.c_str())); 2461ea78801SMatt Spinler continue; 2471ea78801SMatt Spinler } 2481ea78801SMatt Spinler 2491ea78801SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2501ea78801SMatt Spinler std::istreambuf_iterator<char>()}; 2511ea78801SMatt Spinler file.close(); 2521ea78801SMatt Spinler 2531ea78801SMatt Spinler PEL pel{data}; 2541ea78801SMatt Spinler 2551ea78801SMatt Spinler try 2561ea78801SMatt Spinler { 2571ea78801SMatt Spinler if (func(pel)) 2581ea78801SMatt Spinler { 2591ea78801SMatt Spinler break; 2601ea78801SMatt Spinler } 2611ea78801SMatt Spinler } 2621ea78801SMatt Spinler catch (std::exception& e) 2631ea78801SMatt Spinler { 2641ea78801SMatt Spinler log<level::ERR>("Repository::for_each function exception", 2651ea78801SMatt Spinler entry("ERROR=%s", e.what())); 2661ea78801SMatt Spinler } 2671ea78801SMatt Spinler } 2681ea78801SMatt Spinler } 2691ea78801SMatt Spinler 270421f6531SMatt Spinler void Repository::processAddCallbacks(const PEL& pel) const 271421f6531SMatt Spinler { 272421f6531SMatt Spinler for (auto& [name, func] : _addSubscriptions) 273421f6531SMatt Spinler { 274421f6531SMatt Spinler try 275421f6531SMatt Spinler { 276421f6531SMatt Spinler func(pel); 277421f6531SMatt Spinler } 278421f6531SMatt Spinler catch (std::exception& e) 279421f6531SMatt Spinler { 280421f6531SMatt Spinler log<level::ERR>("PEL Repository add callback exception", 281421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 282421f6531SMatt Spinler entry("ERROR=%s", e.what())); 283421f6531SMatt Spinler } 284421f6531SMatt Spinler } 285421f6531SMatt Spinler } 286421f6531SMatt Spinler 287421f6531SMatt Spinler void Repository::processDeleteCallbacks(uint32_t id) const 288421f6531SMatt Spinler { 289421f6531SMatt Spinler for (auto& [name, func] : _deleteSubscriptions) 290421f6531SMatt Spinler { 291421f6531SMatt Spinler try 292421f6531SMatt Spinler { 293421f6531SMatt Spinler func(id); 294421f6531SMatt Spinler } 295421f6531SMatt Spinler catch (std::exception& e) 296421f6531SMatt Spinler { 297421f6531SMatt Spinler log<level::ERR>("PEL Repository delete callback exception", 298421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 299421f6531SMatt Spinler entry("ERROR=%s", e.what())); 300421f6531SMatt Spinler } 301421f6531SMatt Spinler } 302421f6531SMatt Spinler } 303421f6531SMatt Spinler 3040ff00485SMatt Spinler std::optional<std::reference_wrapper<const Repository::PELAttributes>> 3050ff00485SMatt Spinler Repository::getPELAttributes(const LogID& id) const 3060ff00485SMatt Spinler { 3070ff00485SMatt Spinler auto pel = findPEL(id); 3080ff00485SMatt Spinler if (pel != _pelAttributes.end()) 3090ff00485SMatt Spinler { 3100ff00485SMatt Spinler return pel->second; 3110ff00485SMatt Spinler } 3120ff00485SMatt Spinler 3130ff00485SMatt Spinler return std::nullopt; 3140ff00485SMatt Spinler } 3150ff00485SMatt Spinler 31629d18c11SMatt Spinler void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state) 31729d18c11SMatt Spinler { 31829d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 31929d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 32029d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 32129d18c11SMatt Spinler 32229d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hostState != state)) 32329d18c11SMatt Spinler { 32429d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 32529d18c11SMatt Spinler pel.setHostTransmissionState(state); 32629d18c11SMatt Spinler }; 32729d18c11SMatt Spinler 32829d18c11SMatt Spinler try 32929d18c11SMatt Spinler { 33029d18c11SMatt Spinler updatePEL(attr->second.path, func); 33129d18c11SMatt Spinler 33229d18c11SMatt Spinler attr->second.hostState = state; 33329d18c11SMatt Spinler } 33429d18c11SMatt Spinler catch (std::exception& e) 33529d18c11SMatt Spinler { 33629d18c11SMatt Spinler log<level::ERR>("Unable to update PEL host transmission state", 33729d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 33829d18c11SMatt Spinler entry("ERROR=%s", e.what())); 33929d18c11SMatt Spinler } 34029d18c11SMatt Spinler } 34129d18c11SMatt Spinler } 34229d18c11SMatt Spinler 34329d18c11SMatt Spinler void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state) 34429d18c11SMatt Spinler { 34529d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 34629d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 34729d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 34829d18c11SMatt Spinler 34929d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state)) 35029d18c11SMatt Spinler { 35129d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 35229d18c11SMatt Spinler pel.setHMCTransmissionState(state); 35329d18c11SMatt Spinler }; 35429d18c11SMatt Spinler 35529d18c11SMatt Spinler try 35629d18c11SMatt Spinler { 35729d18c11SMatt Spinler updatePEL(attr->second.path, func); 35829d18c11SMatt Spinler 35929d18c11SMatt Spinler attr->second.hmcState = state; 36029d18c11SMatt Spinler } 36129d18c11SMatt Spinler catch (std::exception& e) 36229d18c11SMatt Spinler { 36329d18c11SMatt Spinler log<level::ERR>("Unable to update PEL HMC transmission state", 36429d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 36529d18c11SMatt Spinler entry("ERROR=%s", e.what())); 36629d18c11SMatt Spinler } 36729d18c11SMatt Spinler } 36829d18c11SMatt Spinler } 36929d18c11SMatt Spinler 37029d18c11SMatt Spinler void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc) 37129d18c11SMatt Spinler { 37229d18c11SMatt Spinler std::ifstream file{path}; 37329d18c11SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 37429d18c11SMatt Spinler std::istreambuf_iterator<char>()}; 37529d18c11SMatt Spinler file.close(); 37629d18c11SMatt Spinler 37729d18c11SMatt Spinler PEL pel{data}; 37829d18c11SMatt Spinler 37929d18c11SMatt Spinler if (pel.valid()) 38029d18c11SMatt Spinler { 38129d18c11SMatt Spinler updateFunc(pel); 38229d18c11SMatt Spinler 38329d18c11SMatt Spinler write(pel, path); 38429d18c11SMatt Spinler } 38529d18c11SMatt Spinler else 38629d18c11SMatt Spinler { 38729d18c11SMatt Spinler throw std::runtime_error( 38829d18c11SMatt Spinler "Unable to read a valid PEL when trying to update it"); 38929d18c11SMatt Spinler } 39029d18c11SMatt Spinler } 39129d18c11SMatt Spinler 39289fa082aSMatt Spinler } // namespace pels 39389fa082aSMatt Spinler } // namespace openpower 394