blob: edad39dfa97b9f670082e64bd632c5cde693f6d9 [file] [log] [blame]
Kuiying Wang3a044402019-02-19 15:00:11 +08001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#include "post_code.hpp"
ZhikuiRen993d4dd2020-01-29 14:25:44 -080017
Jonathan Doman8290e0f2022-11-23 15:04:17 -080018#include <cereal/access.hpp>
19#include <cereal/archives/binary.hpp>
20#include <cereal/cereal.hpp>
21#include <cereal/types/map.hpp>
22#include <cereal/types/tuple.hpp>
23#include <cereal/types/vector.hpp>
Amithash Prasadb6616cd2025-05-29 15:56:51 -070024#include <phosphor-logging/commit.hpp>
25#include <sdbusplus/bus.hpp>
26#include <sdbusplus/exception.hpp>
Jonathan Doman8290e0f2022-11-23 15:04:17 -080027
Jonathan Domanc7fed5c2022-11-23 11:14:42 -080028#include <iomanip>
ZhikuiRen993d4dd2020-01-29 14:25:44 -080029
Amithash Prasadb6616cd2025-05-29 15:56:51 -070030using nlohmann::json;
31
Bonnie Lo13cb8532022-12-15 17:00:40 +080032const static constexpr auto timeoutMicroSeconds = 1000000;
Amithash Prasadb6616cd2025-05-29 15:56:51 -070033/* systemd service to kick start a target. */
34constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
35constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
36constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
37
38void PostCodeEvent::raise() const
39{
40 json j = {{name, args}};
41 try
42 {
43 sdbusplus::exception::throw_via_json(j);
44 }
45 catch (sdbusplus::exception::generated_event_base& e)
46 {
47 auto path = lg2::commit(std::move(e));
48 std::cout << path.str << std::endl;
49 }
50}
51
52void from_json(const json& j, PostCodeEvent& event)
53{
54 j.at("name").get_to(event.name);
55 for (const auto& entry : j.at("arguments").items())
56 {
57 if (entry.value().is_string())
58 {
59 event.args[entry.key()] = entry.value().get<std::string>();
60 }
61 else if (entry.value().is_number_integer())
62 {
63 event.args[entry.key()] = entry.value().get<int>();
64 }
65 }
66}
67
68void from_json(const json& j, PostCodeHandler& handler)
69{
70 j.at("primary").get_to(handler.primary);
71 if (j.contains("secondary"))
72 {
73 secondarycode_t secondary;
74 j.at("secondary").get_to(secondary);
75 handler.secondary = secondary;
76 }
77 if (j.contains("targets"))
78 {
79 j.at("targets").get_to(handler.targets);
80 }
81 if (j.contains("event"))
82 {
83 PostCodeEvent event;
84 j.at("event").get_to(event);
85 handler.event = event;
86 }
87}
88
89const PostCodeHandler* PostCodeHandlers::find(postcode_t code)
90{
91 for (const auto& handler : handlers)
92 {
93 if (handler.primary == std::get<0>(code) &&
94 (!handler.secondary || *handler.secondary == std::get<1>(code)))
95 {
96 return &handler;
97 }
98 }
99 return nullptr;
100}
101
102void PostCodeHandlers::handle(postcode_t code)
103{
104 const PostCodeHandler* handler = find(code);
105 if (!handler)
106 {
107 return;
108 }
109 for (const auto& target : handler->targets)
110 {
111 auto bus = sdbusplus::bus::new_default();
112 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
113 SYSTEMD_INTERFACE, "StartUnit");
114 method.append(target);
115 method.append("replace");
116 bus.call_noreply(method);
117 }
118 if (handler->event)
119 {
120 (*(handler->event)).raise();
121 }
122}
123
124void PostCodeHandlers::load(const std::string& path)
125{
126 std::ifstream ifs(path);
127 handlers = json::parse(ifs).template get<std::vector<PostCodeHandler>>();
128 ifs.close();
129}
Bonnie Lo13cb8532022-12-15 17:00:40 +0800130
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800131void PostCode::deleteAll()
132{
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800133 std::uintmax_t n = fs::remove_all(postCodeListPath);
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800134 std::cerr << "clearPostCodes deleted " << n << " files in "
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800135 << postCodeListPath << std::endl;
136 fs::create_directories(postCodeListPath);
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800137 postCodes.clear();
Ravi Tejab84fea42021-06-03 02:09:15 -0500138 currentBootCycleIndex = 0;
139 currentBootCycleCount(0);
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800140}
141
Manojkiran Eda84a4c192021-02-25 15:23:42 +0530142std::vector<postcode_t> PostCode::getPostCodes(uint16_t index)
Kuiying Wang3a044402019-02-19 15:00:11 +0800143{
Manojkiran Eda84a4c192021-02-25 15:23:42 +0530144 std::vector<postcode_t> codesVec;
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800145 if (1 == index && !postCodes.empty())
146 {
Manojkiran Edade8d3a52021-12-05 12:51:07 +0530147 std::transform(postCodes.begin(), postCodes.end(),
148 std::back_inserter(codesVec),
149 [](const auto& kv) { return kv.second; });
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800150 }
151 else
152 {
153 uint16_t bootNum = getBootNum(index);
Kuiying Wang3a044402019-02-19 15:00:11 +0800154
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800155 decltype(postCodes) codes;
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800156 deserializePostCodes(postCodeListPath / std::to_string(bootNum), codes);
Manojkiran Edade8d3a52021-12-05 12:51:07 +0530157 std::transform(codes.begin(), codes.end(), std::back_inserter(codesVec),
158 [](const auto& kv) { return kv.second; });
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800159 }
160 return codesVec;
161}
162
Patrick Williams4e1910b2025-02-01 08:22:19 -0500163std::map<uint64_t, postcode_t> PostCode::getPostCodesWithTimeStamp(
164 uint16_t index)
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800165{
166 if (1 == index && !postCodes.empty())
167 {
Kuiying Wang3a044402019-02-19 15:00:11 +0800168 return postCodes;
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800169 }
170
171 uint16_t bootNum = getBootNum(index);
172 decltype(postCodes) codes;
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800173 deserializePostCodes(postCodeListPath / std::to_string(bootNum), codes);
Kuiying Wang3a044402019-02-19 15:00:11 +0800174 return codes;
175}
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800176
Manojkiran Eda84a4c192021-02-25 15:23:42 +0530177void PostCode::savePostCodes(postcode_t code)
Kuiying Wang3a044402019-02-19 15:00:11 +0800178{
Bonnie Lo13cb8532022-12-15 17:00:40 +0800179 if (!timer)
180 {
Patrick Williams9c2e8712024-08-16 15:20:36 -0400181 timer = std::make_unique<sdbusplus::Timer>(event.get(), [this]() {
182 serialize(postCodeListPath);
183 });
Bonnie Lo13cb8532022-12-15 17:00:40 +0800184 }
185
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800186 // steady_clock is a monotonic clock that is guaranteed to never be adjusted
187 auto postCodeTimeSteady = std::chrono::steady_clock::now();
188 uint64_t tsUS = std::chrono::duration_cast<std::chrono::microseconds>(
189 std::chrono::system_clock::now().time_since_epoch())
190 .count();
191
192 if (postCodes.empty())
193 {
194 firstPostCodeTimeSteady = postCodeTimeSteady;
195 firstPostCodeUsSinceEpoch = tsUS; // uS since epoch for 1st post code
196 incrBootCycle();
197 }
198 else
199 {
200 // calculating tsUS so it is monotonic within the same boot
Alan Kuof5e52db2021-12-23 10:57:05 +0800201 tsUS = firstPostCodeUsSinceEpoch +
202 std::chrono::duration_cast<std::chrono::microseconds>(
203 postCodeTimeSteady - firstPostCodeTimeSteady)
204 .count();
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800205 }
206
207 postCodes.insert(std::make_pair(tsUS, code));
Bonnie Loc1819372022-10-27 17:14:55 +0800208 if (postCodes.size() > MAX_POST_CODE_SIZE_PER_CYCLE)
209 {
210 postCodes.erase(postCodes.begin());
211 }
Bonnie Lo13cb8532022-12-15 17:00:40 +0800212
213 if (!timer->isRunning())
214 {
215 timer->start(std::chrono::microseconds(timeoutMicroSeconds));
216 }
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800217
Delphine CC Chiua4c19b02023-03-01 13:15:45 +0800218 if (strlen(POSTCODE_DISPLAY_PATH) > 0)
219 {
Patrick Williams9c2e8712024-08-16 15:20:36 -0400220 std::string postCodeDisplayPath =
221 POSTCODE_DISPLAY_PATH + std::to_string(node);
Delphine CC Chiua4c19b02023-03-01 13:15:45 +0800222
223 std::ofstream postCodeDisplayFile(postCodeDisplayPath);
Potin Lai06b1dbe2024-09-16 15:09:06 +0800224 postCodeDisplayFile << "0x" << std::setfill('0') << std::hex;
225 for (const auto& byte : std::get<0>(code))
226 {
227 postCodeDisplayFile << std::setw(2) << static_cast<int>(byte);
228 }
Delphine CC Chiua4c19b02023-03-01 13:15:45 +0800229 postCodeDisplayFile.close();
230 }
231
Alan Kuo0171dd62021-04-15 10:47:32 +0800232#ifdef ENABLE_BIOS_POST_CODE_LOG
Alan Kuof5e52db2021-12-23 10:57:05 +0800233 uint64_t usTimeOffset = tsUS - firstPostCodeUsSinceEpoch;
Alan Kuo0171dd62021-04-15 10:47:32 +0800234 std::ostringstream hexCode;
Potin Lai06b1dbe2024-09-16 15:09:06 +0800235 hexCode << "0x" << std::setfill('0') << std::hex;
236 for (const auto& byte : std::get<0>(code))
237 {
238 hexCode << std::setw(2) << static_cast<int>(byte);
239 }
Alan Kuo0171dd62021-04-15 10:47:32 +0800240
241 std::ostringstream timeOffsetStr;
242 // Set Fixed-Point Notation
243 timeOffsetStr << std::fixed;
244 // Set precision to 4 digits
245 timeOffsetStr << std::setprecision(4);
246 // Add double to stream
247 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
248
249 phosphor::logging::log<phosphor::logging::level::INFO>(
250 "BIOS POST Code",
251 phosphor::logging::entry("REDFISH_MESSAGE_ID=%s",
Alan Kuof5e52db2021-12-23 10:57:05 +0800252 "OpenBMC.0.2.BIOSPOSTCode"),
Alan Kuo0171dd62021-04-15 10:47:32 +0800253 phosphor::logging::entry(
254 "REDFISH_MESSAGE_ARGS=%d,%s,%s", currentBootCycleIndex,
255 timeOffsetStr.str().c_str(), hexCode.str().c_str()));
256#endif
Amithash Prasadb6616cd2025-05-29 15:56:51 -0700257 postCodeHandlers.handle(code);
Alan Kuo0171dd62021-04-15 10:47:32 +0800258
Kuiying Wang3a044402019-02-19 15:00:11 +0800259 return;
260}
261
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800262fs::path PostCode::serialize(const fs::path& path)
Kuiying Wang3a044402019-02-19 15:00:11 +0800263{
264 try
265 {
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800266 std::ofstream osIdx(path / CurrentBootCycleIndexName, std::ios::binary);
Jonathan Doman8290e0f2022-11-23 15:04:17 -0800267 cereal::BinaryOutputArchive idxArchive(osIdx);
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800268 idxArchive(currentBootCycleIndex);
269
270 uint16_t count = currentBootCycleCount();
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800271 std::ofstream osCnt(path / CurrentBootCycleCountName, std::ios::binary);
Jonathan Doman8290e0f2022-11-23 15:04:17 -0800272 cereal::BinaryOutputArchive cntArchive(osCnt);
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800273 cntArchive(count);
Kuiying Wang3a044402019-02-19 15:00:11 +0800274
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800275 std::ofstream osPostCodes(path / std::to_string(currentBootCycleIndex));
Jonathan Doman8290e0f2022-11-23 15:04:17 -0800276 cereal::BinaryOutputArchive oarchivePostCodes(osPostCodes);
Kuiying Wang3a044402019-02-19 15:00:11 +0800277 oarchivePostCodes(postCodes);
278 }
Patrick Williams7cc8ea62021-10-06 12:40:56 -0500279 catch (const cereal::Exception& e)
Kuiying Wang3a044402019-02-19 15:00:11 +0800280 {
281 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
282 return "";
283 }
284 catch (const fs::filesystem_error& e)
285 {
286 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
287 return "";
288 }
289 return path;
290}
291
292bool PostCode::deserialize(const fs::path& path, uint16_t& index)
293{
294 try
295 {
296 if (fs::exists(path))
297 {
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800298 std::ifstream is(path, std::ios::in | std::ios::binary);
Jonathan Doman8290e0f2022-11-23 15:04:17 -0800299 cereal::BinaryInputArchive iarchive(is);
Kuiying Wang3a044402019-02-19 15:00:11 +0800300 iarchive(index);
301 return true;
302 }
303 return false;
304 }
Patrick Williams7cc8ea62021-10-06 12:40:56 -0500305 catch (const cereal::Exception& e)
Kuiying Wang3a044402019-02-19 15:00:11 +0800306 {
307 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
308 return false;
309 }
310 catch (const fs::filesystem_error& e)
311 {
312 return false;
313 }
314
315 return false;
316}
317
318bool PostCode::deserializePostCodes(const fs::path& path,
Manojkiran Eda84a4c192021-02-25 15:23:42 +0530319 std::map<uint64_t, postcode_t>& codes)
Kuiying Wang3a044402019-02-19 15:00:11 +0800320{
321 try
322 {
323 if (fs::exists(path))
324 {
Jonathan Domanc7fed5c2022-11-23 11:14:42 -0800325 std::ifstream is(path, std::ios::in | std::ios::binary);
Jonathan Doman8290e0f2022-11-23 15:04:17 -0800326 cereal::BinaryInputArchive iarchive(is);
Kuiying Wang3a044402019-02-19 15:00:11 +0800327 iarchive(codes);
328 return true;
329 }
330 return false;
331 }
Patrick Williams7cc8ea62021-10-06 12:40:56 -0500332 catch (const cereal::Exception& e)
Kuiying Wang3a044402019-02-19 15:00:11 +0800333 {
334 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
335 return false;
336 }
337 catch (const fs::filesystem_error& e)
338 {
339 return false;
340 }
Kuiying Wang3a044402019-02-19 15:00:11 +0800341 return false;
342}
ZhikuiRen993d4dd2020-01-29 14:25:44 -0800343
344void PostCode::incrBootCycle()
345{
346 if (currentBootCycleIndex >= maxBootCycleNum())
347 {
348 currentBootCycleIndex = 1;
349 }
350 else
351 {
352 currentBootCycleIndex++;
353 }
354 currentBootCycleCount(std::min(
355 maxBootCycleNum(), static_cast<uint16_t>(currentBootCycleCount() + 1)));
356}
357
358uint16_t PostCode::getBootNum(const uint16_t index) const
359{
360 // bootNum assumes the oldest archive is boot number 1
361 // and the current boot number equals bootCycleCount
362 // map bootNum back to bootIndex that was used to archive postcode
363 uint16_t bootNum = currentBootCycleIndex;
364 if (index > bootNum) // need to wrap around
365 {
366 bootNum = (maxBootCycleNum() + currentBootCycleIndex) - index + 1;
367 }
368 else
369 {
370 bootNum = currentBootCycleIndex - index + 1;
371 }
372 return bootNum;
Kumar Thangavelfd45f782020-09-01 22:59:00 +0530373}