1*8d2f868cSEd Tanous #pragma once 2*8d2f868cSEd Tanous 3*8d2f868cSEd Tanous #include "app.hpp" 4*8d2f868cSEd Tanous #include "generated/enums/log_service.hpp" 5*8d2f868cSEd Tanous #include "query.hpp" 6*8d2f868cSEd Tanous #include "registries/openbmc_message_registry.hpp" 7*8d2f868cSEd Tanous #include "registries/privilege_registry.hpp" 8*8d2f868cSEd Tanous #include "utils/time_utils.hpp" 9*8d2f868cSEd Tanous 10*8d2f868cSEd Tanous #include <cstdint> 11*8d2f868cSEd Tanous #include <memory> 12*8d2f868cSEd Tanous #include <string_view> 13*8d2f868cSEd Tanous #include <utility> 14*8d2f868cSEd Tanous #include <vector> 15*8d2f868cSEd Tanous 16*8d2f868cSEd Tanous namespace redfish 17*8d2f868cSEd Tanous { 18*8d2f868cSEd Tanous /**************************************************** 19*8d2f868cSEd Tanous * Redfish PostCode interfaces 20*8d2f868cSEd Tanous * using DBUS interface: getPostCodesTS 21*8d2f868cSEd Tanous ******************************************************/ 22*8d2f868cSEd Tanous inline void requestRoutesPostCodesLogService(App& app) 23*8d2f868cSEd Tanous { 24*8d2f868cSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/") 25*8d2f868cSEd Tanous .privileges(redfish::privileges::getLogService) 26*8d2f868cSEd Tanous .methods( 27*8d2f868cSEd Tanous boost::beast::http::verb:: 28*8d2f868cSEd Tanous get)([&app](const crow::Request& req, 29*8d2f868cSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 30*8d2f868cSEd Tanous const std::string& systemName) { 31*8d2f868cSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 32*8d2f868cSEd Tanous { 33*8d2f868cSEd Tanous return; 34*8d2f868cSEd Tanous } 35*8d2f868cSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 36*8d2f868cSEd Tanous { 37*8d2f868cSEd Tanous // Option currently returns no systems. TBD 38*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 39*8d2f868cSEd Tanous systemName); 40*8d2f868cSEd Tanous return; 41*8d2f868cSEd Tanous } 42*8d2f868cSEd Tanous if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 43*8d2f868cSEd Tanous { 44*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 45*8d2f868cSEd Tanous systemName); 46*8d2f868cSEd Tanous return; 47*8d2f868cSEd Tanous } 48*8d2f868cSEd Tanous asyncResp->res.jsonValue["@odata.id"] = 49*8d2f868cSEd Tanous std::format("/redfish/v1/Systems/{}/LogServices/PostCodes", 50*8d2f868cSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 51*8d2f868cSEd Tanous asyncResp->res.jsonValue["@odata.type"] = 52*8d2f868cSEd Tanous "#LogService.v1_2_0.LogService"; 53*8d2f868cSEd Tanous asyncResp->res.jsonValue["Name"] = "POST Code Log Service"; 54*8d2f868cSEd Tanous asyncResp->res.jsonValue["Description"] = "POST Code Log Service"; 55*8d2f868cSEd Tanous asyncResp->res.jsonValue["Id"] = "PostCodes"; 56*8d2f868cSEd Tanous asyncResp->res.jsonValue["OverWritePolicy"] = 57*8d2f868cSEd Tanous log_service::OverWritePolicy::WrapsWhenFull; 58*8d2f868cSEd Tanous asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( 59*8d2f868cSEd Tanous "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", 60*8d2f868cSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 61*8d2f868cSEd Tanous 62*8d2f868cSEd Tanous std::pair<std::string, std::string> redfishDateTimeOffset = 63*8d2f868cSEd Tanous redfish::time_utils::getDateTimeOffsetNow(); 64*8d2f868cSEd Tanous asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; 65*8d2f868cSEd Tanous asyncResp->res.jsonValue["DateTimeLocalOffset"] = 66*8d2f868cSEd Tanous redfishDateTimeOffset.second; 67*8d2f868cSEd Tanous 68*8d2f868cSEd Tanous asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] 69*8d2f868cSEd Tanous ["target"] = std::format( 70*8d2f868cSEd Tanous "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog", 71*8d2f868cSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 72*8d2f868cSEd Tanous }); 73*8d2f868cSEd Tanous } 74*8d2f868cSEd Tanous 75*8d2f868cSEd Tanous inline void requestRoutesPostCodesClear(App& app) 76*8d2f868cSEd Tanous { 77*8d2f868cSEd Tanous BMCWEB_ROUTE( 78*8d2f868cSEd Tanous app, 79*8d2f868cSEd Tanous "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/") 80*8d2f868cSEd Tanous // The following privilege is incorrect; It should be ConfigureManager 81*8d2f868cSEd Tanous //.privileges(redfish::privileges::postLogService) 82*8d2f868cSEd Tanous .privileges({{"ConfigureComponents"}}) 83*8d2f868cSEd Tanous .methods(boost::beast::http::verb::post)( 84*8d2f868cSEd Tanous [&app](const crow::Request& req, 85*8d2f868cSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 86*8d2f868cSEd Tanous const std::string& systemName) { 87*8d2f868cSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 88*8d2f868cSEd Tanous { 89*8d2f868cSEd Tanous return; 90*8d2f868cSEd Tanous } 91*8d2f868cSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 92*8d2f868cSEd Tanous { 93*8d2f868cSEd Tanous // Option currently returns no systems. TBD 94*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 95*8d2f868cSEd Tanous systemName); 96*8d2f868cSEd Tanous return; 97*8d2f868cSEd Tanous } 98*8d2f868cSEd Tanous if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 99*8d2f868cSEd Tanous { 100*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 101*8d2f868cSEd Tanous systemName); 102*8d2f868cSEd Tanous return; 103*8d2f868cSEd Tanous } 104*8d2f868cSEd Tanous BMCWEB_LOG_DEBUG("Do delete all postcodes entries."); 105*8d2f868cSEd Tanous 106*8d2f868cSEd Tanous // Make call to post-code service to request clear all 107*8d2f868cSEd Tanous crow::connections::systemBus->async_method_call( 108*8d2f868cSEd Tanous [asyncResp](const boost::system::error_code& ec) { 109*8d2f868cSEd Tanous if (ec) 110*8d2f868cSEd Tanous { 111*8d2f868cSEd Tanous // TODO Handle for specific error code 112*8d2f868cSEd Tanous BMCWEB_LOG_ERROR( 113*8d2f868cSEd Tanous "doClearPostCodes resp_handler got error {}", 114*8d2f868cSEd Tanous ec); 115*8d2f868cSEd Tanous asyncResp->res.result(boost::beast::http::status:: 116*8d2f868cSEd Tanous internal_server_error); 117*8d2f868cSEd Tanous messages::internalError(asyncResp->res); 118*8d2f868cSEd Tanous return; 119*8d2f868cSEd Tanous } 120*8d2f868cSEd Tanous messages::success(asyncResp->res); 121*8d2f868cSEd Tanous }, 122*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode0", 123*8d2f868cSEd Tanous "/xyz/openbmc_project/State/Boot/PostCode0", 124*8d2f868cSEd Tanous "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 125*8d2f868cSEd Tanous }); 126*8d2f868cSEd Tanous } 127*8d2f868cSEd Tanous 128*8d2f868cSEd Tanous /** 129*8d2f868cSEd Tanous * @brief Parse post code ID and get the current value and index value 130*8d2f868cSEd Tanous * eg: postCodeID=B1-2, currentValue=1, index=2 131*8d2f868cSEd Tanous * 132*8d2f868cSEd Tanous * @param[in] postCodeID Post Code ID 133*8d2f868cSEd Tanous * @param[out] currentValue Current value 134*8d2f868cSEd Tanous * @param[out] index Index value 135*8d2f868cSEd Tanous * 136*8d2f868cSEd Tanous * @return bool true if the parsing is successful, false the parsing fails 137*8d2f868cSEd Tanous */ 138*8d2f868cSEd Tanous inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue, 139*8d2f868cSEd Tanous uint16_t& index) 140*8d2f868cSEd Tanous { 141*8d2f868cSEd Tanous std::vector<std::string> split; 142*8d2f868cSEd Tanous bmcweb::split(split, postCodeID, '-'); 143*8d2f868cSEd Tanous if (split.size() != 2) 144*8d2f868cSEd Tanous { 145*8d2f868cSEd Tanous return false; 146*8d2f868cSEd Tanous } 147*8d2f868cSEd Tanous std::string_view postCodeNumber = split[0]; 148*8d2f868cSEd Tanous if (postCodeNumber.size() < 2) 149*8d2f868cSEd Tanous { 150*8d2f868cSEd Tanous return false; 151*8d2f868cSEd Tanous } 152*8d2f868cSEd Tanous if (postCodeNumber[0] != 'B') 153*8d2f868cSEd Tanous { 154*8d2f868cSEd Tanous return false; 155*8d2f868cSEd Tanous } 156*8d2f868cSEd Tanous postCodeNumber.remove_prefix(1); 157*8d2f868cSEd Tanous auto [ptrIndex, ecIndex] = 158*8d2f868cSEd Tanous std::from_chars(postCodeNumber.begin(), postCodeNumber.end(), index); 159*8d2f868cSEd Tanous if (ptrIndex != postCodeNumber.end() || ecIndex != std::errc()) 160*8d2f868cSEd Tanous { 161*8d2f868cSEd Tanous return false; 162*8d2f868cSEd Tanous } 163*8d2f868cSEd Tanous 164*8d2f868cSEd Tanous std::string_view postCodeIndex = split[1]; 165*8d2f868cSEd Tanous 166*8d2f868cSEd Tanous auto [ptrValue, ecValue] = std::from_chars( 167*8d2f868cSEd Tanous postCodeIndex.begin(), postCodeIndex.end(), currentValue); 168*8d2f868cSEd Tanous 169*8d2f868cSEd Tanous return ptrValue == postCodeIndex.end() && ecValue == std::errc(); 170*8d2f868cSEd Tanous } 171*8d2f868cSEd Tanous 172*8d2f868cSEd Tanous static bool fillPostCodeEntry( 173*8d2f868cSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 174*8d2f868cSEd Tanous const boost::container::flat_map< 175*8d2f868cSEd Tanous uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode, 176*8d2f868cSEd Tanous const uint16_t bootIndex, const uint64_t codeIndex = 0, 177*8d2f868cSEd Tanous const uint64_t skip = 0, const uint64_t top = 0) 178*8d2f868cSEd Tanous { 179*8d2f868cSEd Tanous // Get the Message from the MessageRegistry 180*8d2f868cSEd Tanous const registries::Message* message = 181*8d2f868cSEd Tanous registries::getMessage("OpenBMC.0.2.BIOSPOSTCode"); 182*8d2f868cSEd Tanous if (message == nullptr) 183*8d2f868cSEd Tanous { 184*8d2f868cSEd Tanous BMCWEB_LOG_ERROR("Couldn't find known message?"); 185*8d2f868cSEd Tanous return false; 186*8d2f868cSEd Tanous } 187*8d2f868cSEd Tanous uint64_t currentCodeIndex = 0; 188*8d2f868cSEd Tanous uint64_t firstCodeTimeUs = 0; 189*8d2f868cSEd Tanous for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& 190*8d2f868cSEd Tanous code : postcode) 191*8d2f868cSEd Tanous { 192*8d2f868cSEd Tanous currentCodeIndex++; 193*8d2f868cSEd Tanous std::string postcodeEntryID = 194*8d2f868cSEd Tanous "B" + std::to_string(bootIndex) + "-" + 195*8d2f868cSEd Tanous std::to_string(currentCodeIndex); // 1 based index in EntryID string 196*8d2f868cSEd Tanous 197*8d2f868cSEd Tanous uint64_t usecSinceEpoch = code.first; 198*8d2f868cSEd Tanous uint64_t usTimeOffset = 0; 199*8d2f868cSEd Tanous 200*8d2f868cSEd Tanous if (1 == currentCodeIndex) 201*8d2f868cSEd Tanous { // already incremented 202*8d2f868cSEd Tanous firstCodeTimeUs = code.first; 203*8d2f868cSEd Tanous } 204*8d2f868cSEd Tanous else 205*8d2f868cSEd Tanous { 206*8d2f868cSEd Tanous usTimeOffset = code.first - firstCodeTimeUs; 207*8d2f868cSEd Tanous } 208*8d2f868cSEd Tanous 209*8d2f868cSEd Tanous // skip if no specific codeIndex is specified and currentCodeIndex does 210*8d2f868cSEd Tanous // not fall between top and skip 211*8d2f868cSEd Tanous if ((codeIndex == 0) && 212*8d2f868cSEd Tanous (currentCodeIndex <= skip || currentCodeIndex > top)) 213*8d2f868cSEd Tanous { 214*8d2f868cSEd Tanous continue; 215*8d2f868cSEd Tanous } 216*8d2f868cSEd Tanous 217*8d2f868cSEd Tanous // skip if a specific codeIndex is specified and does not match the 218*8d2f868cSEd Tanous // currentIndex 219*8d2f868cSEd Tanous if ((codeIndex > 0) && (currentCodeIndex != codeIndex)) 220*8d2f868cSEd Tanous { 221*8d2f868cSEd Tanous // This is done for simplicity. 1st entry is needed to calculate 222*8d2f868cSEd Tanous // time offset. To improve efficiency, one can get to the entry 223*8d2f868cSEd Tanous // directly (possibly with flatmap's nth method) 224*8d2f868cSEd Tanous continue; 225*8d2f868cSEd Tanous } 226*8d2f868cSEd Tanous 227*8d2f868cSEd Tanous // currentCodeIndex is within top and skip or equal to specified code 228*8d2f868cSEd Tanous // index 229*8d2f868cSEd Tanous 230*8d2f868cSEd Tanous // Get the Created time from the timestamp 231*8d2f868cSEd Tanous std::string entryTimeStr; 232*8d2f868cSEd Tanous entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch); 233*8d2f868cSEd Tanous 234*8d2f868cSEd Tanous // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex) 235*8d2f868cSEd Tanous std::ostringstream hexCode; 236*8d2f868cSEd Tanous hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex 237*8d2f868cSEd Tanous << std::get<0>(code.second); 238*8d2f868cSEd Tanous std::ostringstream timeOffsetStr; 239*8d2f868cSEd Tanous // Set Fixed -Point Notation 240*8d2f868cSEd Tanous timeOffsetStr << std::fixed; 241*8d2f868cSEd Tanous // Set precision to 4 digits 242*8d2f868cSEd Tanous timeOffsetStr << std::setprecision(4); 243*8d2f868cSEd Tanous // Add double to stream 244*8d2f868cSEd Tanous timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000; 245*8d2f868cSEd Tanous 246*8d2f868cSEd Tanous std::string bootIndexStr = std::to_string(bootIndex); 247*8d2f868cSEd Tanous std::string timeOffsetString = timeOffsetStr.str(); 248*8d2f868cSEd Tanous std::string hexCodeStr = hexCode.str(); 249*8d2f868cSEd Tanous 250*8d2f868cSEd Tanous std::array<std::string_view, 3> messageArgs = { 251*8d2f868cSEd Tanous bootIndexStr, timeOffsetString, hexCodeStr}; 252*8d2f868cSEd Tanous 253*8d2f868cSEd Tanous std::string msg = 254*8d2f868cSEd Tanous redfish::registries::fillMessageArgs(messageArgs, message->message); 255*8d2f868cSEd Tanous if (msg.empty()) 256*8d2f868cSEd Tanous { 257*8d2f868cSEd Tanous messages::internalError(asyncResp->res); 258*8d2f868cSEd Tanous return false; 259*8d2f868cSEd Tanous } 260*8d2f868cSEd Tanous 261*8d2f868cSEd Tanous // Get Severity template from message registry 262*8d2f868cSEd Tanous std::string severity; 263*8d2f868cSEd Tanous if (message != nullptr) 264*8d2f868cSEd Tanous { 265*8d2f868cSEd Tanous severity = message->messageSeverity; 266*8d2f868cSEd Tanous } 267*8d2f868cSEd Tanous 268*8d2f868cSEd Tanous // Format entry 269*8d2f868cSEd Tanous nlohmann::json::object_t bmcLogEntry; 270*8d2f868cSEd Tanous bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; 271*8d2f868cSEd Tanous bmcLogEntry["@odata.id"] = boost::urls::format( 272*8d2f868cSEd Tanous "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}", 273*8d2f868cSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID); 274*8d2f868cSEd Tanous bmcLogEntry["Name"] = "POST Code Log Entry"; 275*8d2f868cSEd Tanous bmcLogEntry["Id"] = postcodeEntryID; 276*8d2f868cSEd Tanous bmcLogEntry["Message"] = std::move(msg); 277*8d2f868cSEd Tanous bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode"; 278*8d2f868cSEd Tanous bmcLogEntry["MessageArgs"] = messageArgs; 279*8d2f868cSEd Tanous bmcLogEntry["EntryType"] = "Event"; 280*8d2f868cSEd Tanous bmcLogEntry["Severity"] = std::move(severity); 281*8d2f868cSEd Tanous bmcLogEntry["Created"] = entryTimeStr; 282*8d2f868cSEd Tanous if (!std::get<std::vector<uint8_t>>(code.second).empty()) 283*8d2f868cSEd Tanous { 284*8d2f868cSEd Tanous bmcLogEntry["AdditionalDataURI"] = 285*8d2f868cSEd Tanous std::format( 286*8d2f868cSEd Tanous "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/", 287*8d2f868cSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME) + 288*8d2f868cSEd Tanous postcodeEntryID + "/attachment"; 289*8d2f868cSEd Tanous } 290*8d2f868cSEd Tanous 291*8d2f868cSEd Tanous // codeIndex is only specified when querying single entry, return only 292*8d2f868cSEd Tanous // that entry in this case 293*8d2f868cSEd Tanous if (codeIndex != 0) 294*8d2f868cSEd Tanous { 295*8d2f868cSEd Tanous asyncResp->res.jsonValue.update(bmcLogEntry); 296*8d2f868cSEd Tanous return true; 297*8d2f868cSEd Tanous } 298*8d2f868cSEd Tanous 299*8d2f868cSEd Tanous nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; 300*8d2f868cSEd Tanous logEntryArray.emplace_back(std::move(bmcLogEntry)); 301*8d2f868cSEd Tanous } 302*8d2f868cSEd Tanous 303*8d2f868cSEd Tanous // Return value is always false when querying multiple entries 304*8d2f868cSEd Tanous return false; 305*8d2f868cSEd Tanous } 306*8d2f868cSEd Tanous 307*8d2f868cSEd Tanous static void 308*8d2f868cSEd Tanous getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 309*8d2f868cSEd Tanous const std::string& entryId) 310*8d2f868cSEd Tanous { 311*8d2f868cSEd Tanous uint16_t bootIndex = 0; 312*8d2f868cSEd Tanous uint64_t codeIndex = 0; 313*8d2f868cSEd Tanous if (!parsePostCode(entryId, codeIndex, bootIndex)) 314*8d2f868cSEd Tanous { 315*8d2f868cSEd Tanous // Requested ID was not found 316*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); 317*8d2f868cSEd Tanous return; 318*8d2f868cSEd Tanous } 319*8d2f868cSEd Tanous 320*8d2f868cSEd Tanous if (bootIndex == 0 || codeIndex == 0) 321*8d2f868cSEd Tanous { 322*8d2f868cSEd Tanous // 0 is an invalid index 323*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); 324*8d2f868cSEd Tanous return; 325*8d2f868cSEd Tanous } 326*8d2f868cSEd Tanous 327*8d2f868cSEd Tanous crow::connections::systemBus->async_method_call( 328*8d2f868cSEd Tanous [asyncResp, entryId, bootIndex, 329*8d2f868cSEd Tanous codeIndex](const boost::system::error_code& ec, 330*8d2f868cSEd Tanous const boost::container::flat_map< 331*8d2f868cSEd Tanous uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& 332*8d2f868cSEd Tanous postcode) { 333*8d2f868cSEd Tanous if (ec) 334*8d2f868cSEd Tanous { 335*8d2f868cSEd Tanous BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error"); 336*8d2f868cSEd Tanous messages::internalError(asyncResp->res); 337*8d2f868cSEd Tanous return; 338*8d2f868cSEd Tanous } 339*8d2f868cSEd Tanous 340*8d2f868cSEd Tanous if (postcode.empty()) 341*8d2f868cSEd Tanous { 342*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); 343*8d2f868cSEd Tanous return; 344*8d2f868cSEd Tanous } 345*8d2f868cSEd Tanous 346*8d2f868cSEd Tanous if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex)) 347*8d2f868cSEd Tanous { 348*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); 349*8d2f868cSEd Tanous return; 350*8d2f868cSEd Tanous } 351*8d2f868cSEd Tanous }, 352*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode0", 353*8d2f868cSEd Tanous "/xyz/openbmc_project/State/Boot/PostCode0", 354*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", 355*8d2f868cSEd Tanous bootIndex); 356*8d2f868cSEd Tanous } 357*8d2f868cSEd Tanous 358*8d2f868cSEd Tanous static void 359*8d2f868cSEd Tanous getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 360*8d2f868cSEd Tanous const uint16_t bootIndex, const uint16_t bootCount, 361*8d2f868cSEd Tanous const uint64_t entryCount, size_t skip, size_t top) 362*8d2f868cSEd Tanous { 363*8d2f868cSEd Tanous crow::connections::systemBus->async_method_call( 364*8d2f868cSEd Tanous [asyncResp, bootIndex, bootCount, entryCount, skip, 365*8d2f868cSEd Tanous top](const boost::system::error_code& ec, 366*8d2f868cSEd Tanous const boost::container::flat_map< 367*8d2f868cSEd Tanous uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& 368*8d2f868cSEd Tanous postcode) { 369*8d2f868cSEd Tanous if (ec) 370*8d2f868cSEd Tanous { 371*8d2f868cSEd Tanous BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error"); 372*8d2f868cSEd Tanous messages::internalError(asyncResp->res); 373*8d2f868cSEd Tanous return; 374*8d2f868cSEd Tanous } 375*8d2f868cSEd Tanous 376*8d2f868cSEd Tanous uint64_t endCount = entryCount; 377*8d2f868cSEd Tanous if (!postcode.empty()) 378*8d2f868cSEd Tanous { 379*8d2f868cSEd Tanous endCount = entryCount + postcode.size(); 380*8d2f868cSEd Tanous if (skip < endCount && (top + skip) > entryCount) 381*8d2f868cSEd Tanous { 382*8d2f868cSEd Tanous uint64_t thisBootSkip = 383*8d2f868cSEd Tanous std::max(static_cast<uint64_t>(skip), entryCount) - 384*8d2f868cSEd Tanous entryCount; 385*8d2f868cSEd Tanous uint64_t thisBootTop = 386*8d2f868cSEd Tanous std::min(static_cast<uint64_t>(top + skip), endCount) - 387*8d2f868cSEd Tanous entryCount; 388*8d2f868cSEd Tanous 389*8d2f868cSEd Tanous fillPostCodeEntry(asyncResp, postcode, bootIndex, 0, 390*8d2f868cSEd Tanous thisBootSkip, thisBootTop); 391*8d2f868cSEd Tanous } 392*8d2f868cSEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = endCount; 393*8d2f868cSEd Tanous } 394*8d2f868cSEd Tanous 395*8d2f868cSEd Tanous // continue to previous bootIndex 396*8d2f868cSEd Tanous if (bootIndex < bootCount) 397*8d2f868cSEd Tanous { 398*8d2f868cSEd Tanous getPostCodeForBoot(asyncResp, 399*8d2f868cSEd Tanous static_cast<uint16_t>(bootIndex + 1), 400*8d2f868cSEd Tanous bootCount, endCount, skip, top); 401*8d2f868cSEd Tanous } 402*8d2f868cSEd Tanous else if (skip + top < endCount) 403*8d2f868cSEd Tanous { 404*8d2f868cSEd Tanous asyncResp->res.jsonValue["Members@odata.nextLink"] = 405*8d2f868cSEd Tanous std::format( 406*8d2f868cSEd Tanous "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=", 407*8d2f868cSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME) + 408*8d2f868cSEd Tanous std::to_string(skip + top); 409*8d2f868cSEd Tanous } 410*8d2f868cSEd Tanous }, 411*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode0", 412*8d2f868cSEd Tanous "/xyz/openbmc_project/State/Boot/PostCode0", 413*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", 414*8d2f868cSEd Tanous bootIndex); 415*8d2f868cSEd Tanous } 416*8d2f868cSEd Tanous 417*8d2f868cSEd Tanous static void 418*8d2f868cSEd Tanous getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 419*8d2f868cSEd Tanous size_t skip, size_t top) 420*8d2f868cSEd Tanous { 421*8d2f868cSEd Tanous uint64_t entryCount = 0; 422*8d2f868cSEd Tanous sdbusplus::asio::getProperty<uint16_t>( 423*8d2f868cSEd Tanous *crow::connections::systemBus, 424*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode0", 425*8d2f868cSEd Tanous "/xyz/openbmc_project/State/Boot/PostCode0", 426*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount", 427*8d2f868cSEd Tanous [asyncResp, entryCount, skip, 428*8d2f868cSEd Tanous top](const boost::system::error_code& ec, const uint16_t bootCount) { 429*8d2f868cSEd Tanous if (ec) 430*8d2f868cSEd Tanous { 431*8d2f868cSEd Tanous BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 432*8d2f868cSEd Tanous messages::internalError(asyncResp->res); 433*8d2f868cSEd Tanous return; 434*8d2f868cSEd Tanous } 435*8d2f868cSEd Tanous getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top); 436*8d2f868cSEd Tanous }); 437*8d2f868cSEd Tanous } 438*8d2f868cSEd Tanous 439*8d2f868cSEd Tanous inline void requestRoutesPostCodesEntryCollection(App& app) 440*8d2f868cSEd Tanous { 441*8d2f868cSEd Tanous BMCWEB_ROUTE(app, 442*8d2f868cSEd Tanous "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/") 443*8d2f868cSEd Tanous .privileges(redfish::privileges::getLogEntryCollection) 444*8d2f868cSEd Tanous .methods(boost::beast::http::verb::get)( 445*8d2f868cSEd Tanous [&app](const crow::Request& req, 446*8d2f868cSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 447*8d2f868cSEd Tanous const std::string& systemName) { 448*8d2f868cSEd Tanous query_param::QueryCapabilities capabilities = { 449*8d2f868cSEd Tanous .canDelegateTop = true, 450*8d2f868cSEd Tanous .canDelegateSkip = true, 451*8d2f868cSEd Tanous }; 452*8d2f868cSEd Tanous query_param::Query delegatedQuery; 453*8d2f868cSEd Tanous if (!redfish::setUpRedfishRouteWithDelegation( 454*8d2f868cSEd Tanous app, req, asyncResp, delegatedQuery, capabilities)) 455*8d2f868cSEd Tanous { 456*8d2f868cSEd Tanous return; 457*8d2f868cSEd Tanous } 458*8d2f868cSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 459*8d2f868cSEd Tanous { 460*8d2f868cSEd Tanous // Option currently returns no systems. TBD 461*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 462*8d2f868cSEd Tanous systemName); 463*8d2f868cSEd Tanous return; 464*8d2f868cSEd Tanous } 465*8d2f868cSEd Tanous 466*8d2f868cSEd Tanous if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 467*8d2f868cSEd Tanous { 468*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 469*8d2f868cSEd Tanous systemName); 470*8d2f868cSEd Tanous return; 471*8d2f868cSEd Tanous } 472*8d2f868cSEd Tanous asyncResp->res.jsonValue["@odata.type"] = 473*8d2f868cSEd Tanous "#LogEntryCollection.LogEntryCollection"; 474*8d2f868cSEd Tanous asyncResp->res.jsonValue["@odata.id"] = std::format( 475*8d2f868cSEd Tanous "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", 476*8d2f868cSEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 477*8d2f868cSEd Tanous asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries"; 478*8d2f868cSEd Tanous asyncResp->res.jsonValue["Description"] = 479*8d2f868cSEd Tanous "Collection of POST Code Log Entries"; 480*8d2f868cSEd Tanous asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 481*8d2f868cSEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = 0; 482*8d2f868cSEd Tanous size_t skip = delegatedQuery.skip.value_or(0); 483*8d2f868cSEd Tanous size_t top = 484*8d2f868cSEd Tanous delegatedQuery.top.value_or(query_param::Query::maxTop); 485*8d2f868cSEd Tanous getCurrentBootNumber(asyncResp, skip, top); 486*8d2f868cSEd Tanous }); 487*8d2f868cSEd Tanous } 488*8d2f868cSEd Tanous 489*8d2f868cSEd Tanous inline void requestRoutesPostCodesEntryAdditionalData(App& app) 490*8d2f868cSEd Tanous { 491*8d2f868cSEd Tanous BMCWEB_ROUTE( 492*8d2f868cSEd Tanous app, 493*8d2f868cSEd Tanous "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/") 494*8d2f868cSEd Tanous .privileges(redfish::privileges::getLogEntry) 495*8d2f868cSEd Tanous .methods( 496*8d2f868cSEd Tanous boost::beast::http::verb:: 497*8d2f868cSEd Tanous get)([&app](const crow::Request& req, 498*8d2f868cSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 499*8d2f868cSEd Tanous const std::string& systemName, 500*8d2f868cSEd Tanous const std::string& postCodeID) { 501*8d2f868cSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 502*8d2f868cSEd Tanous { 503*8d2f868cSEd Tanous return; 504*8d2f868cSEd Tanous } 505*8d2f868cSEd Tanous if (!http_helpers::isContentTypeAllowed( 506*8d2f868cSEd Tanous req.getHeaderValue("Accept"), 507*8d2f868cSEd Tanous http_helpers::ContentType::OctetStream, true)) 508*8d2f868cSEd Tanous { 509*8d2f868cSEd Tanous asyncResp->res.result(boost::beast::http::status::bad_request); 510*8d2f868cSEd Tanous return; 511*8d2f868cSEd Tanous } 512*8d2f868cSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 513*8d2f868cSEd Tanous { 514*8d2f868cSEd Tanous // Option currently returns no systems. TBD 515*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 516*8d2f868cSEd Tanous systemName); 517*8d2f868cSEd Tanous return; 518*8d2f868cSEd Tanous } 519*8d2f868cSEd Tanous if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 520*8d2f868cSEd Tanous { 521*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 522*8d2f868cSEd Tanous systemName); 523*8d2f868cSEd Tanous return; 524*8d2f868cSEd Tanous } 525*8d2f868cSEd Tanous 526*8d2f868cSEd Tanous uint64_t currentValue = 0; 527*8d2f868cSEd Tanous uint16_t index = 0; 528*8d2f868cSEd Tanous if (!parsePostCode(postCodeID, currentValue, index)) 529*8d2f868cSEd Tanous { 530*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", 531*8d2f868cSEd Tanous postCodeID); 532*8d2f868cSEd Tanous return; 533*8d2f868cSEd Tanous } 534*8d2f868cSEd Tanous 535*8d2f868cSEd Tanous crow::connections::systemBus->async_method_call( 536*8d2f868cSEd Tanous [asyncResp, postCodeID, currentValue]( 537*8d2f868cSEd Tanous const boost::system::error_code& ec, 538*8d2f868cSEd Tanous const std::vector<std::tuple< 539*8d2f868cSEd Tanous uint64_t, std::vector<uint8_t>>>& postcodes) { 540*8d2f868cSEd Tanous if (ec.value() == EBADR) 541*8d2f868cSEd Tanous { 542*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", 543*8d2f868cSEd Tanous postCodeID); 544*8d2f868cSEd Tanous return; 545*8d2f868cSEd Tanous } 546*8d2f868cSEd Tanous if (ec) 547*8d2f868cSEd Tanous { 548*8d2f868cSEd Tanous BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 549*8d2f868cSEd Tanous messages::internalError(asyncResp->res); 550*8d2f868cSEd Tanous return; 551*8d2f868cSEd Tanous } 552*8d2f868cSEd Tanous 553*8d2f868cSEd Tanous size_t value = static_cast<size_t>(currentValue) - 1; 554*8d2f868cSEd Tanous if (value == std::string::npos || 555*8d2f868cSEd Tanous postcodes.size() < currentValue) 556*8d2f868cSEd Tanous { 557*8d2f868cSEd Tanous BMCWEB_LOG_WARNING("Wrong currentValue value"); 558*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", 559*8d2f868cSEd Tanous postCodeID); 560*8d2f868cSEd Tanous return; 561*8d2f868cSEd Tanous } 562*8d2f868cSEd Tanous 563*8d2f868cSEd Tanous const auto& [tID, c] = postcodes[value]; 564*8d2f868cSEd Tanous if (c.empty()) 565*8d2f868cSEd Tanous { 566*8d2f868cSEd Tanous BMCWEB_LOG_WARNING("No found post code data"); 567*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", 568*8d2f868cSEd Tanous postCodeID); 569*8d2f868cSEd Tanous return; 570*8d2f868cSEd Tanous } 571*8d2f868cSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 572*8d2f868cSEd Tanous const char* d = reinterpret_cast<const char*>(c.data()); 573*8d2f868cSEd Tanous std::string_view strData(d, c.size()); 574*8d2f868cSEd Tanous 575*8d2f868cSEd Tanous asyncResp->res.addHeader( 576*8d2f868cSEd Tanous boost::beast::http::field::content_type, 577*8d2f868cSEd Tanous "application/octet-stream"); 578*8d2f868cSEd Tanous asyncResp->res.addHeader( 579*8d2f868cSEd Tanous boost::beast::http::field::content_transfer_encoding, 580*8d2f868cSEd Tanous "Base64"); 581*8d2f868cSEd Tanous asyncResp->res.write(crow::utility::base64encode(strData)); 582*8d2f868cSEd Tanous }, 583*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode0", 584*8d2f868cSEd Tanous "/xyz/openbmc_project/State/Boot/PostCode0", 585*8d2f868cSEd Tanous "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", 586*8d2f868cSEd Tanous index); 587*8d2f868cSEd Tanous }); 588*8d2f868cSEd Tanous } 589*8d2f868cSEd Tanous 590*8d2f868cSEd Tanous inline void requestRoutesPostCodesEntry(App& app) 591*8d2f868cSEd Tanous { 592*8d2f868cSEd Tanous BMCWEB_ROUTE( 593*8d2f868cSEd Tanous app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/") 594*8d2f868cSEd Tanous .privileges(redfish::privileges::getLogEntry) 595*8d2f868cSEd Tanous .methods(boost::beast::http::verb::get)( 596*8d2f868cSEd Tanous [&app](const crow::Request& req, 597*8d2f868cSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 598*8d2f868cSEd Tanous const std::string& systemName, const std::string& targetID) { 599*8d2f868cSEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 600*8d2f868cSEd Tanous { 601*8d2f868cSEd Tanous return; 602*8d2f868cSEd Tanous } 603*8d2f868cSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 604*8d2f868cSEd Tanous { 605*8d2f868cSEd Tanous // Option currently returns no systems. TBD 606*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 607*8d2f868cSEd Tanous systemName); 608*8d2f868cSEd Tanous return; 609*8d2f868cSEd Tanous } 610*8d2f868cSEd Tanous if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 611*8d2f868cSEd Tanous { 612*8d2f868cSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 613*8d2f868cSEd Tanous systemName); 614*8d2f868cSEd Tanous return; 615*8d2f868cSEd Tanous } 616*8d2f868cSEd Tanous 617*8d2f868cSEd Tanous getPostCodeForEntry(asyncResp, targetID); 618*8d2f868cSEd Tanous }); 619*8d2f868cSEd Tanous } 620*8d2f868cSEd Tanous 621*8d2f868cSEd Tanous inline void requestRoutesSystemsLogServicesPostCode(App& app) 622*8d2f868cSEd Tanous { 623*8d2f868cSEd Tanous requestRoutesPostCodesClear(app); 624*8d2f868cSEd Tanous requestRoutesPostCodesEntry(app); 625*8d2f868cSEd Tanous requestRoutesPostCodesEntryAdditionalData(app); 626*8d2f868cSEd Tanous requestRoutesPostCodesEntryCollection(app); 627*8d2f868cSEd Tanous requestRoutesPostCodesLogService(app); 628*8d2f868cSEd Tanous } 629*8d2f868cSEd Tanous } // namespace redfish 630