blob: cc5f9ff3b070bd4544c9a560fb54ad7322dceb61 [file] [log] [blame]
AppaRao Pulie63eeda2019-07-05 16:25:38 +05301/*
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
AppaRao Puli88aa33b2019-07-18 23:49:55 +053017#include <systemd/sd-journal.h>
18
AppaRao Pulie63eeda2019-07-05 16:25:38 +053019#include "pfr_mgr.hpp"
AppaRao Puli88aa33b2019-07-18 23:49:55 +053020#include "pfr.hpp"
AppaRao Puli46cead92019-07-22 16:50:09 +053021#include <boost/asio.hpp>
AppaRao Pulie63eeda2019-07-05 16:25:38 +053022
23static std::array<std::string, 5> listVersionPaths = {
24 "bmc_active", "bmc_recovery", "bios_active", "bios_recovery", "cpld"};
25
AppaRao Puli88aa33b2019-07-18 23:49:55 +053026// Caches the last Recovery/Panic Count to
27// identify any new Recovery/panic actions.
28/* TODO: When BMC Reset's, these values will be lost
29 * Persist this info using settingsd */
30static uint8_t lastRecoveryCount = 0;
31static uint8_t lastPanicCount = 0;
32static uint8_t lastMajorErr = 0;
33static uint8_t lastMinorErr = 0;
34
35static bool stateTimerRunning = false;
AppaRao Puli46cead92019-07-22 16:50:09 +053036bool finishedSettingChkPoint = false;
37static constexpr uint8_t bmcBootFinishedChkPoint = 0x09;
38
AppaRao Puli88aa33b2019-07-18 23:49:55 +053039std::unique_ptr<boost::asio::steady_timer> stateTimer = nullptr;
AppaRao Puli46cead92019-07-22 16:50:09 +053040std::unique_ptr<boost::asio::steady_timer> initTimer = nullptr;
AppaRao Puli88aa33b2019-07-18 23:49:55 +053041
42// Recovery reason map. { <CPLD association>, <Recovery Reason> }
43static std::map<uint8_t, std::string> recoveryReasonMap = {
44 {0x01, "PCH active authentication failure"},
45 {0x02, "PCH recovery authentication failure"},
46 {0x03, "ACM launch failure"},
47 {0x04, "IBB launch failure"},
48 {0x05, "OBB launch failure"},
49 {0x06, "BMC active authentication failure"},
50 {0x07, "BMC recovery authentication failure"},
51 {0x08, "BMC launch failure"},
52 {0x09, "CPLD watchdog expired"}};
53
54// Panic Reason map. { <CPLD association>, <Panic reason> }
55static std::map<uint8_t, std::string> panicReasonMap = {
56 {0x01, "CPLD WDT expired"},
57 {0x02, "BMC WDT expired"},
58 {0x03, "ME WDT expired"},
59 {0x04, "ACM WDT expired"},
60 {0x05, "IBB WDT expired"},
61 {0x06, "OBB WDT expired"},
62 {0x07, "BMC active authentication failure"},
63 {0x08, "BMC recovery authentication failure"},
64 {0x09, "PCH active authentication failure"},
65 {0x0A, "PCH recovery authentication failure"},
66 {0x0B, "IBB authentication failure"},
67 {0x0C, "OBB authentication failure"},
68 {0x0D, "BMC authentication failure"},
69 {0x0E, "PCH active update intent"},
70 {0x0F, "BMC active update intent"},
71 {0x10, "PCH recovery update intent"},
72 {0x11, "BMC recovery update intent"}};
73
74static void logLastRecoveryEvent()
75{
76 uint8_t reason = 0;
77 if (0 !=
78 intel::pfr::readCpldReg(intel::pfr::ActionType::recoveryReason, reason))
79 {
80 return;
81 }
82
83 std::map<uint8_t, std::string>::const_iterator it =
84 recoveryReasonMap.find(reason);
85 if (it == recoveryReasonMap.end())
86 {
87 // No matching found. So just return without logging event.
88 return;
89 }
90
91 sd_journal_send("MESSAGE=%s", "Platform firmware recovered.", "PRIORITY=%i",
92 LOG_ERR, "REDFISH_MESSAGE_ID=%s",
93 "OpenBMC.0.1.PlatformFWRecovered",
94 "REDFISH_MESSAGE_ARGS=%s", it->second.c_str(), NULL);
95}
96
97static void logLastPanicEvent()
98{
99 uint8_t reason = 0;
100 if (0 !=
101 intel::pfr::readCpldReg(intel::pfr::ActionType::panicReason, reason))
102 {
103 return;
104 }
105
106 std::map<uint8_t, std::string>::const_iterator it =
107 panicReasonMap.find(reason);
108 if (it == panicReasonMap.end())
109 {
110 // No matching found. So just return without logging event.
111 return;
112 }
113
114 sd_journal_send("MESSAGE=%s", "Platform panic event triggered.",
115 "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s",
116 "OpenBMC.0.1.PlatformFWPanicTriggered",
117 "REDFISH_MESSAGE_ARGS=%s", it->second.c_str(), NULL);
118}
119
120static void checkAndLogEvents()
121{
122 uint8_t currPanicCount = 0;
123 if (0 == intel::pfr::readCpldReg(intel::pfr::ActionType::panicCount,
124 currPanicCount))
125 {
126 if (lastPanicCount != currPanicCount)
127 {
128 // Update cached data and log redfish event by reading reason.
129 lastPanicCount = currPanicCount;
130 logLastPanicEvent();
131 }
132 }
133
134 uint8_t currRecoveryCount = 0;
135 if (0 == intel::pfr::readCpldReg(intel::pfr::ActionType::recoveryCount,
136 currRecoveryCount))
137 {
138 if (lastRecoveryCount != currRecoveryCount)
139 {
140 // Update cached data and log redfish event by reading reason.
141 lastRecoveryCount = currRecoveryCount;
142 logLastRecoveryEvent();
143 }
144 }
145
146 uint8_t majorErr = 0;
147 uint8_t minorErr = 0;
148 if ((0 == intel::pfr::readCpldReg(intel::pfr::ActionType::majorError,
149 majorErr)) ||
150 (0 ==
151 intel::pfr::readCpldReg(intel::pfr::ActionType::minorError, minorErr)))
152 {
153 if ((lastMajorErr != majorErr) || (lastMinorErr != minorErr))
154 {
155 lastMajorErr = majorErr;
156 lastMinorErr = minorErr;
157
158 if (majorErr || minorErr)
159 {
160 sd_journal_send(
161 "MESSAGE=%s", "Error occurred on platform firmware.",
162 "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s",
163 "OpenBMC.0.1.PlatformFWErrorOccurred",
164 "REDFISH_MESSAGE_ARGS=%i,%i", majorErr, minorErr, NULL);
165 }
166 }
167 }
168}
169
170static void monitorPlatformStateChange(
171 sdbusplus::asio::object_server& server,
172 std::shared_ptr<sdbusplus::asio::connection>& conn)
173{
174 constexpr size_t pollTimeout = 10; // seconds
175 stateTimer->expires_after(std::chrono::seconds(pollTimeout));
176 stateTimer->async_wait(
177 [&server, &conn](const boost::system::error_code& ec) {
178 if (ec == boost::asio::error::operation_aborted)
179 {
180 // Timer reset.
181 return;
182 }
183 if (ec)
184 {
185 // Platform State Monitor - Timer cancelled.
186 return;
187 }
188 checkAndLogEvents();
189 monitorPlatformStateChange(server, conn);
190 });
191}
192
AppaRao Puli46cead92019-07-22 16:50:09 +0530193void checkAndSetCheckpoint(sdbusplus::asio::object_server& server,
194 std::shared_ptr<sdbusplus::asio::connection>& conn)
195{
196 // Check whether systemd completed all the loading.
197 conn->async_method_call(
198 [&server, &conn](boost::system::error_code ec,
199 const std::variant<uint64_t>& value) {
200 if (ec)
201 {
202 phosphor::logging::log<phosphor::logging::level::ERR>(
203 "async_method_call error: FinishTimestamp failed");
204 return;
205 }
206 if (std::get<uint64_t>(value))
207 {
208 if (!finishedSettingChkPoint)
209 {
210 finishedSettingChkPoint = true;
211 intel::pfr::setBMCBootCheckpoint(bmcBootFinishedChkPoint);
212 }
213 }
214 else
215 {
216 // FIX-ME: Latest up-stream sync caused issue in receiving
217 // StartupFinished signal. Unable to get StartupFinished signal
218 // from systemd1 hence using poll method too, to trigger it
219 // properly.
220 constexpr size_t pollTimeout = 10; // seconds
221 initTimer->expires_after(std::chrono::seconds(pollTimeout));
222 initTimer->async_wait([&server, &conn](
223 const boost::system::error_code& ec) {
224 if (ec == boost::asio::error::operation_aborted)
225 {
226 // Timer reset.
227 return;
228 }
229 if (ec)
230 {
231 phosphor::logging::log<phosphor::logging::level::ERR>(
232 "Set boot Checkpoint - async wait error.");
233 return;
234 }
235 checkAndSetCheckpoint(server, conn);
236 });
237 }
238 },
239 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
240 "org.freedesktop.DBus.Properties", "Get",
241 "org.freedesktop.systemd1.Manager", "FinishTimestamp");
242}
243
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530244int main()
245{
246 // setup connection to dbus
247 boost::asio::io_service io;
248 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530249 stateTimer = std::make_unique<boost::asio::steady_timer>(io);
AppaRao Puli46cead92019-07-22 16:50:09 +0530250 initTimer = std::make_unique<boost::asio::steady_timer>(io);
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530251 conn->request_name("xyz.openbmc_project.Intel.PFR.Manager");
252 auto server = sdbusplus::asio::object_server(conn, true);
253
254 // Create Intel PFR attributes object and interface
255 intel::pfr::PfrConfig obj(server, conn);
256
257 // Create Software objects using Versions interface
258 for (const auto& path : listVersionPaths)
259 {
260 intel::pfr::PfrVersion obj(server, conn, path);
261 }
262
AppaRao Puli46cead92019-07-22 16:50:09 +0530263 // Monitor Boot finished signal and set the checkpoint 9 to
264 // notify CPLD about BMC boot finish.
265 auto bootFinishedSignal = std::make_unique<sdbusplus::bus::match::match>(
266 static_cast<sdbusplus::bus::bus&>(*conn),
267 "type='signal',"
268 "member='StartupFinished',path='/org/freedesktop/systemd1',"
269 "interface='org.freedesktop.systemd1.Manager'",
270 [&server, &conn](sdbusplus::message::message& msg) {
271 if (!finishedSettingChkPoint)
272 {
273 finishedSettingChkPoint = true;
274 intel::pfr::setBMCBootCheckpoint(bmcBootFinishedChkPoint);
275 }
276 });
277 checkAndSetCheckpoint(server, conn);
278
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530279 // Capture the Chassis state and Start the monitor timer
280 // if state changed to 'On'. Run timer until OS boot.
281 // Stop timer if state changed to 'Off'.
282 static auto matchChassisState = sdbusplus::bus::match::match(
283 static_cast<sdbusplus::bus::bus&>(*conn),
284 "type='signal',member='PropertiesChanged', "
285 "interface='org.freedesktop.DBus.Properties', "
286 "sender='xyz.openbmc_project.State.Chassis', "
287 "arg0namespace='xyz.openbmc_project.State.Chassis'",
288 [&server, &conn](sdbusplus::message::message& message) {
289 std::string intfName;
290 std::map<std::string, std::variant<std::string>> properties;
291 message.read(intfName, properties);
292
293 const auto it = properties.find("CurrentPowerState");
294 if (it != properties.end())
295 {
296 const std::string* state =
297 std::get_if<std::string>(&it->second);
298 if (state != nullptr)
299 {
300 if ((*state ==
301 "xyz.openbmc_project.State.Chassis.PowerState.On") &&
302 (!stateTimerRunning))
303 {
304 stateTimerRunning = true;
305 monitorPlatformStateChange(server, conn);
306 }
307 else if ((*state == "xyz.openbmc_project.State.Chassis."
308 "PowerState.Off") &&
309 (stateTimerRunning))
310 {
311 stateTimer->cancel();
312 checkAndLogEvents();
313 stateTimerRunning = false;
314 }
315 }
316 }
317 });
318
319 // Capture the Host state and Start the monitor timer
320 // if state changed to 'Running'. Run timer until OS boot.
321 // Stop timer if state changed to 'Off'.
322 static auto matchHostState = sdbusplus::bus::match::match(
323 static_cast<sdbusplus::bus::bus&>(*conn),
324 "type='signal',member='PropertiesChanged', "
325 "interface='org.freedesktop.DBus.Properties', "
326 "sender='xyz.openbmc_project.State.Chassis', "
327 "arg0namespace='xyz.openbmc_project.State.Host'",
328 [&server, &conn](sdbusplus::message::message& message) {
329 std::string intfName;
330 std::map<std::string, std::variant<std::string>> properties;
331 message.read(intfName, properties);
332
333 const auto it = properties.find("CurrentHostState");
334 if (it != properties.end())
335 {
336 const std::string* state =
337 std::get_if<std::string>(&it->second);
338 if (state != nullptr)
339 {
340 if ((*state ==
341 "xyz.openbmc_project.State.Host.HostState.Running") &&
342 (!stateTimerRunning))
343 {
344 stateTimerRunning = true;
345 monitorPlatformStateChange(server, conn);
346 }
347 else if (((*state == "xyz.openbmc_project.State.Host."
348 "HostState.Off") ||
349 (*state == "xyz.openbmc_project.State.Host."
350 "HostState.Quiesced")) &&
351 (stateTimerRunning))
352 {
353 stateTimer->cancel();
354 checkAndLogEvents();
355 stateTimerRunning = false;
356 }
357 }
358 }
359 });
360
361 // Capture the OS state change and stop monitor timer
362 // if OS boots completly or becomes Inactive.
363 // start timer in other cases to mnitor states.
364 static auto matchOsState = sdbusplus::bus::match::match(
365 static_cast<sdbusplus::bus::bus&>(*conn),
366 "type='signal',member='PropertiesChanged', "
367 "interface='org.freedesktop.DBus.Properties', "
368 "sender='xyz.openbmc_project.State.Chassis', "
369 "arg0namespace='xyz.openbmc_project.State.OperatingSystem.Status'",
370 [&server, &conn](sdbusplus::message::message& message) {
371 std::string intfName;
372 std::map<std::string, std::variant<std::string>> properties;
373 message.read(intfName, properties);
374
375 const auto it = properties.find("OperatingSystemState");
376 if (it != properties.end())
377 {
378 const std::string* state =
379 std::get_if<std::string>(&it->second);
380 if (state != nullptr)
381 {
382 if (((*state == "BootComplete") ||
383 (*state == "Inactive")) &&
384 (stateTimerRunning))
385 {
386 stateTimer->cancel();
387 checkAndLogEvents();
388 stateTimerRunning = false;
389 }
390 else if (!stateTimerRunning)
391 {
392 stateTimerRunning = true;
393 monitorPlatformStateChange(server, conn);
394 }
395 }
396 }
397 });
398
399 // First time, check and log events if any.
400 checkAndLogEvents();
401
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530402 phosphor::logging::log<phosphor::logging::level::INFO>(
403 "Intel PFR service started successfully");
404
405 io.run();
406
407 return 0;
408}