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 3189fa082aSMatt Spinler Repository::Repository(const std::filesystem::path& basePath) : 3289fa082aSMatt Spinler _logPath(basePath / "logs") 3389fa082aSMatt Spinler { 3489fa082aSMatt Spinler if (!fs::exists(_logPath)) 3589fa082aSMatt Spinler { 3689fa082aSMatt Spinler fs::create_directories(_logPath); 3789fa082aSMatt Spinler } 38475e574dSMatt Spinler 39475e574dSMatt Spinler restore(); 40475e574dSMatt Spinler } 41475e574dSMatt Spinler 42475e574dSMatt Spinler void Repository::restore() 43475e574dSMatt Spinler { 44475e574dSMatt Spinler for (auto& dirEntry : fs::directory_iterator(_logPath)) 45475e574dSMatt Spinler { 46475e574dSMatt Spinler try 47475e574dSMatt Spinler { 48475e574dSMatt Spinler if (!fs::is_regular_file(dirEntry.path())) 49475e574dSMatt Spinler { 50475e574dSMatt Spinler continue; 51475e574dSMatt Spinler } 52475e574dSMatt Spinler 53475e574dSMatt Spinler std::ifstream file{dirEntry.path()}; 54475e574dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 55475e574dSMatt Spinler std::istreambuf_iterator<char>()}; 56475e574dSMatt Spinler file.close(); 57475e574dSMatt Spinler 5807eefc54SMatt Spinler PEL pel{data}; 59475e574dSMatt Spinler if (pel.valid()) 60475e574dSMatt Spinler { 61a3c12a48SMatt Spinler // If the host hasn't acked it, reset the host state so 62a3c12a48SMatt Spinler // it will get sent up again. 63a3c12a48SMatt Spinler if (pel.hostTransmissionState() == TransmissionState::sent) 64a3c12a48SMatt Spinler { 65a3c12a48SMatt Spinler pel.setHostTransmissionState(TransmissionState::newPEL); 66a3c12a48SMatt Spinler try 67a3c12a48SMatt Spinler { 68a3c12a48SMatt Spinler write(pel, dirEntry.path()); 69a3c12a48SMatt Spinler } 70a3c12a48SMatt Spinler catch (std::exception& e) 71a3c12a48SMatt Spinler { 72a3c12a48SMatt Spinler log<level::ERR>( 73a3c12a48SMatt Spinler "Failed to save PEL after updating host state", 74a3c12a48SMatt Spinler entry("PELID=0x%X", pel.id())); 75a3c12a48SMatt Spinler } 76a3c12a48SMatt Spinler } 77a3c12a48SMatt Spinler 78346f99a1SMatt Spinler PELAttributes attributes{ 79346f99a1SMatt Spinler dirEntry.path(), pel.userHeader().actionFlags(), 80346f99a1SMatt Spinler pel.hostTransmissionState(), pel.hmcTransmissionState()}; 810ff00485SMatt Spinler 82475e574dSMatt Spinler using pelID = LogID::Pel; 83475e574dSMatt Spinler using obmcID = LogID::Obmc; 840ff00485SMatt Spinler _pelAttributes.emplace( 85475e574dSMatt Spinler LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 860ff00485SMatt Spinler attributes); 87475e574dSMatt Spinler } 88475e574dSMatt Spinler else 89475e574dSMatt Spinler { 90475e574dSMatt Spinler log<level::ERR>( 91475e574dSMatt Spinler "Found invalid PEL file while restoring. Removing.", 92475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str())); 93475e574dSMatt Spinler fs::remove(dirEntry.path()); 94475e574dSMatt Spinler } 95475e574dSMatt Spinler } 96475e574dSMatt Spinler catch (std::exception& e) 97475e574dSMatt Spinler { 98475e574dSMatt Spinler log<level::ERR>("Hit exception while restoring PEL File", 99475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str()), 100475e574dSMatt Spinler entry("ERROR=%s", e.what())); 101475e574dSMatt Spinler } 102475e574dSMatt Spinler } 10389fa082aSMatt Spinler } 10489fa082aSMatt Spinler 10589fa082aSMatt Spinler std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 10689fa082aSMatt Spinler { 10789fa082aSMatt Spinler char name[50]; 10889fa082aSMatt Spinler sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 10989fa082aSMatt Spinler time.yearLSB, time.month, time.day, time.hour, time.minutes, 11089fa082aSMatt Spinler time.seconds, time.hundredths, pelID); 11189fa082aSMatt Spinler return std::string{name}; 11289fa082aSMatt Spinler } 11389fa082aSMatt Spinler 11489fa082aSMatt Spinler void Repository::add(std::unique_ptr<PEL>& pel) 11589fa082aSMatt Spinler { 116df43a305SMatt Spinler pel->setHostTransmissionState(TransmissionState::newPEL); 117df43a305SMatt Spinler pel->setHMCTransmissionState(TransmissionState::newPEL); 118df43a305SMatt Spinler 11989fa082aSMatt Spinler auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 120ab1b97feSMatt Spinler 121ab1b97feSMatt Spinler write(*(pel.get()), path); 122ab1b97feSMatt Spinler 123346f99a1SMatt Spinler PELAttributes attributes{path, pel->userHeader().actionFlags(), 124346f99a1SMatt Spinler pel->hostTransmissionState(), 125346f99a1SMatt Spinler pel->hmcTransmissionState()}; 126ab1b97feSMatt Spinler 127ab1b97feSMatt Spinler using pelID = LogID::Pel; 128ab1b97feSMatt Spinler using obmcID = LogID::Obmc; 129ab1b97feSMatt Spinler _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 130ab1b97feSMatt Spinler attributes); 131ab1b97feSMatt Spinler 132ab1b97feSMatt Spinler processAddCallbacks(*pel); 133ab1b97feSMatt Spinler } 134ab1b97feSMatt Spinler 135ab1b97feSMatt Spinler void Repository::write(const PEL& pel, const fs::path& path) 136ab1b97feSMatt Spinler { 13789fa082aSMatt Spinler std::ofstream file{path, std::ios::binary}; 13889fa082aSMatt Spinler 13989fa082aSMatt Spinler if (!file.good()) 14089fa082aSMatt Spinler { 14189fa082aSMatt Spinler // If this fails, the filesystem is probably full so it isn't like 14289fa082aSMatt Spinler // we could successfully create yet another error log here. 14389fa082aSMatt Spinler auto e = errno; 14489fa082aSMatt Spinler fs::remove(path); 14589fa082aSMatt Spinler log<level::ERR>("Unable to open PEL file for writing", 14689fa082aSMatt Spinler entry("ERRNO=%d", e), entry("PATH=%s", path.c_str())); 14789fa082aSMatt Spinler throw file_error::Open(); 14889fa082aSMatt Spinler } 14989fa082aSMatt Spinler 150ab1b97feSMatt Spinler auto data = pel.data(); 15189fa082aSMatt Spinler file.write(reinterpret_cast<const char*>(data.data()), data.size()); 15289fa082aSMatt Spinler 15389fa082aSMatt Spinler if (file.fail()) 15489fa082aSMatt Spinler { 15589fa082aSMatt Spinler // Same note as above about not being able to create an error log 15689fa082aSMatt Spinler // for this case even if we wanted. 15789fa082aSMatt Spinler auto e = errno; 15889fa082aSMatt Spinler file.close(); 15989fa082aSMatt Spinler fs::remove(path); 16089fa082aSMatt Spinler log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e), 16189fa082aSMatt Spinler entry("PATH=%s", path.c_str())); 16289fa082aSMatt Spinler throw file_error::Write(); 16389fa082aSMatt Spinler } 164475e574dSMatt Spinler } 165475e574dSMatt Spinler 166475e574dSMatt Spinler void Repository::remove(const LogID& id) 167475e574dSMatt Spinler { 168475e574dSMatt Spinler auto pel = findPEL(id); 1690ff00485SMatt Spinler if (pel != _pelAttributes.end()) 170475e574dSMatt Spinler { 1710ff00485SMatt Spinler fs::remove(pel->second.path); 1720ff00485SMatt Spinler _pelAttributes.erase(pel); 173421f6531SMatt Spinler 174421f6531SMatt Spinler processDeleteCallbacks(id.pelID.id); 175475e574dSMatt Spinler } 17689fa082aSMatt Spinler } 17789fa082aSMatt Spinler 1782813f36dSMatt Spinler std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 1792813f36dSMatt Spinler { 1802813f36dSMatt Spinler auto pel = findPEL(id); 1810ff00485SMatt Spinler if (pel != _pelAttributes.end()) 1822813f36dSMatt Spinler { 1830ff00485SMatt Spinler std::ifstream file{pel->second.path.c_str()}; 1842813f36dSMatt Spinler if (!file.good()) 1852813f36dSMatt Spinler { 1862813f36dSMatt Spinler auto e = errno; 1872813f36dSMatt Spinler log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e), 1880ff00485SMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 1892813f36dSMatt Spinler throw file_error::Open(); 1902813f36dSMatt Spinler } 1912813f36dSMatt Spinler 1922813f36dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 1932813f36dSMatt Spinler std::istreambuf_iterator<char>()}; 1942813f36dSMatt Spinler return data; 1952813f36dSMatt Spinler } 1962813f36dSMatt Spinler 1972813f36dSMatt Spinler return std::nullopt; 1982813f36dSMatt Spinler } 1992813f36dSMatt Spinler 2001ea78801SMatt Spinler void Repository::for_each(ForEachFunc func) const 2011ea78801SMatt Spinler { 2020ff00485SMatt Spinler for (const auto& [id, attributes] : _pelAttributes) 2031ea78801SMatt Spinler { 2040ff00485SMatt Spinler std::ifstream file{attributes.path}; 2051ea78801SMatt Spinler 2061ea78801SMatt Spinler if (!file.good()) 2071ea78801SMatt Spinler { 2081ea78801SMatt Spinler auto e = errno; 2091ea78801SMatt Spinler log<level::ERR>("Repository::for_each: Unable to open PEL file", 2101ea78801SMatt Spinler entry("ERRNO=%d", e), 2110ff00485SMatt Spinler entry("PATH=%s", attributes.path.c_str())); 2121ea78801SMatt Spinler continue; 2131ea78801SMatt Spinler } 2141ea78801SMatt Spinler 2151ea78801SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2161ea78801SMatt Spinler std::istreambuf_iterator<char>()}; 2171ea78801SMatt Spinler file.close(); 2181ea78801SMatt Spinler 2191ea78801SMatt Spinler PEL pel{data}; 2201ea78801SMatt Spinler 2211ea78801SMatt Spinler try 2221ea78801SMatt Spinler { 2231ea78801SMatt Spinler if (func(pel)) 2241ea78801SMatt Spinler { 2251ea78801SMatt Spinler break; 2261ea78801SMatt Spinler } 2271ea78801SMatt Spinler } 2281ea78801SMatt Spinler catch (std::exception& e) 2291ea78801SMatt Spinler { 2301ea78801SMatt Spinler log<level::ERR>("Repository::for_each function exception", 2311ea78801SMatt Spinler entry("ERROR=%s", e.what())); 2321ea78801SMatt Spinler } 2331ea78801SMatt Spinler } 2341ea78801SMatt Spinler } 2351ea78801SMatt Spinler 236421f6531SMatt Spinler void Repository::processAddCallbacks(const PEL& pel) const 237421f6531SMatt Spinler { 238421f6531SMatt Spinler for (auto& [name, func] : _addSubscriptions) 239421f6531SMatt Spinler { 240421f6531SMatt Spinler try 241421f6531SMatt Spinler { 242421f6531SMatt Spinler func(pel); 243421f6531SMatt Spinler } 244421f6531SMatt Spinler catch (std::exception& e) 245421f6531SMatt Spinler { 246421f6531SMatt Spinler log<level::ERR>("PEL Repository add callback exception", 247421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 248421f6531SMatt Spinler entry("ERROR=%s", e.what())); 249421f6531SMatt Spinler } 250421f6531SMatt Spinler } 251421f6531SMatt Spinler } 252421f6531SMatt Spinler 253421f6531SMatt Spinler void Repository::processDeleteCallbacks(uint32_t id) const 254421f6531SMatt Spinler { 255421f6531SMatt Spinler for (auto& [name, func] : _deleteSubscriptions) 256421f6531SMatt Spinler { 257421f6531SMatt Spinler try 258421f6531SMatt Spinler { 259421f6531SMatt Spinler func(id); 260421f6531SMatt Spinler } 261421f6531SMatt Spinler catch (std::exception& e) 262421f6531SMatt Spinler { 263421f6531SMatt Spinler log<level::ERR>("PEL Repository delete callback exception", 264421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 265421f6531SMatt Spinler entry("ERROR=%s", e.what())); 266421f6531SMatt Spinler } 267421f6531SMatt Spinler } 268421f6531SMatt Spinler } 269421f6531SMatt Spinler 2700ff00485SMatt Spinler std::optional<std::reference_wrapper<const Repository::PELAttributes>> 2710ff00485SMatt Spinler Repository::getPELAttributes(const LogID& id) const 2720ff00485SMatt Spinler { 2730ff00485SMatt Spinler auto pel = findPEL(id); 2740ff00485SMatt Spinler if (pel != _pelAttributes.end()) 2750ff00485SMatt Spinler { 2760ff00485SMatt Spinler return pel->second; 2770ff00485SMatt Spinler } 2780ff00485SMatt Spinler 2790ff00485SMatt Spinler return std::nullopt; 2800ff00485SMatt Spinler } 2810ff00485SMatt Spinler 282*29d18c11SMatt Spinler void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state) 283*29d18c11SMatt Spinler { 284*29d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 285*29d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 286*29d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 287*29d18c11SMatt Spinler 288*29d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hostState != state)) 289*29d18c11SMatt Spinler { 290*29d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 291*29d18c11SMatt Spinler pel.setHostTransmissionState(state); 292*29d18c11SMatt Spinler }; 293*29d18c11SMatt Spinler 294*29d18c11SMatt Spinler try 295*29d18c11SMatt Spinler { 296*29d18c11SMatt Spinler updatePEL(attr->second.path, func); 297*29d18c11SMatt Spinler 298*29d18c11SMatt Spinler attr->second.hostState = state; 299*29d18c11SMatt Spinler } 300*29d18c11SMatt Spinler catch (std::exception& e) 301*29d18c11SMatt Spinler { 302*29d18c11SMatt Spinler log<level::ERR>("Unable to update PEL host transmission state", 303*29d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 304*29d18c11SMatt Spinler entry("ERROR=%s", e.what())); 305*29d18c11SMatt Spinler } 306*29d18c11SMatt Spinler } 307*29d18c11SMatt Spinler } 308*29d18c11SMatt Spinler 309*29d18c11SMatt Spinler void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state) 310*29d18c11SMatt Spinler { 311*29d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 312*29d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 313*29d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 314*29d18c11SMatt Spinler 315*29d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state)) 316*29d18c11SMatt Spinler { 317*29d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 318*29d18c11SMatt Spinler pel.setHMCTransmissionState(state); 319*29d18c11SMatt Spinler }; 320*29d18c11SMatt Spinler 321*29d18c11SMatt Spinler try 322*29d18c11SMatt Spinler { 323*29d18c11SMatt Spinler updatePEL(attr->second.path, func); 324*29d18c11SMatt Spinler 325*29d18c11SMatt Spinler attr->second.hmcState = state; 326*29d18c11SMatt Spinler } 327*29d18c11SMatt Spinler catch (std::exception& e) 328*29d18c11SMatt Spinler { 329*29d18c11SMatt Spinler log<level::ERR>("Unable to update PEL HMC transmission state", 330*29d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 331*29d18c11SMatt Spinler entry("ERROR=%s", e.what())); 332*29d18c11SMatt Spinler } 333*29d18c11SMatt Spinler } 334*29d18c11SMatt Spinler } 335*29d18c11SMatt Spinler 336*29d18c11SMatt Spinler void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc) 337*29d18c11SMatt Spinler { 338*29d18c11SMatt Spinler std::ifstream file{path}; 339*29d18c11SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 340*29d18c11SMatt Spinler std::istreambuf_iterator<char>()}; 341*29d18c11SMatt Spinler file.close(); 342*29d18c11SMatt Spinler 343*29d18c11SMatt Spinler PEL pel{data}; 344*29d18c11SMatt Spinler 345*29d18c11SMatt Spinler if (pel.valid()) 346*29d18c11SMatt Spinler { 347*29d18c11SMatt Spinler updateFunc(pel); 348*29d18c11SMatt Spinler 349*29d18c11SMatt Spinler write(pel, path); 350*29d18c11SMatt Spinler } 351*29d18c11SMatt Spinler else 352*29d18c11SMatt Spinler { 353*29d18c11SMatt Spinler throw std::runtime_error( 354*29d18c11SMatt Spinler "Unable to read a valid PEL when trying to update it"); 355*29d18c11SMatt Spinler } 356*29d18c11SMatt Spinler } 357*29d18c11SMatt Spinler 35889fa082aSMatt Spinler } // namespace pels 35989fa082aSMatt Spinler } // namespace openpower 360