xref: /openbmc/bmcweb/features/redfish/lib/systems_logservices_postcodes.hpp (revision 8d2f868c9e553d8d7b77a3cbdbf32f7c889da8cb)
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