blob: f44733c076cb34d939f6383c01957872b1c5b1aa [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
AppaRao Puli88aa33b2019-07-18 23:49:55 +053023// Caches the last Recovery/Panic Count to
24// identify any new Recovery/panic actions.
25/* TODO: When BMC Reset's, these values will be lost
26 * Persist this info using settingsd */
27static uint8_t lastRecoveryCount = 0;
28static uint8_t lastPanicCount = 0;
29static uint8_t lastMajorErr = 0;
30static uint8_t lastMinorErr = 0;
31
32static bool stateTimerRunning = false;
AppaRao Puli46cead92019-07-22 16:50:09 +053033bool finishedSettingChkPoint = false;
34static constexpr uint8_t bmcBootFinishedChkPoint = 0x09;
35
AppaRao Puli88aa33b2019-07-18 23:49:55 +053036std::unique_ptr<boost::asio::steady_timer> stateTimer = nullptr;
AppaRao Puli46cead92019-07-22 16:50:09 +053037std::unique_ptr<boost::asio::steady_timer> initTimer = nullptr;
AppaRao Puli88aa33b2019-07-18 23:49:55 +053038
AppaRao Pulie4e95652019-07-19 16:52:01 +053039std::vector<std::unique_ptr<intel::pfr::PfrVersion>> pfrVersionObjects;
40std::unique_ptr<intel::pfr::PfrConfig> pfrConfigObject;
41
42using namespace intel::pfr;
43// List holds <ObjPath> <ImageType> <VersionPurpose>
44static std::vector<std::tuple<std::string, ImageType, std::string>>
45 verComponentList = {
46 std::make_tuple("bmc_active", ImageType::bmcActive, versionPurposeBMC),
47 std::make_tuple("bmc_recovery", ImageType::bmcRecovery,
48 versionPurposeBMC),
49 std::make_tuple("bios_active", ImageType::biosActive,
50 versionPurposeHost),
51 std::make_tuple("bios_recovery", ImageType::biosRecovery,
52 versionPurposeHost),
53 std::make_tuple("cpld", ImageType::cpld, versionPurposeOther)};
54
AppaRao Pulie90f1282019-11-05 01:07:05 +053055// Recovery reason map.
56// {<CPLD association>,{<Redfish MessageID>, <Recovery Reason>}}
57static const boost::container::flat_map<uint8_t,
58 std::pair<std::string, std::string>>
59 recoveryReasonMap = {
60 {0x01,
61 {"BIOSFirmwareRecoveryReason",
62 "PCH active image authentication failure"}},
63 {0x02,
64 {"BIOSFirmwareRecoveryReason",
65 "PCH recovery image authentication failure"}},
66 {0x03, {"MEFirmwareRecoveryReason", "ME launch failure"}},
67 {0x04, {"BIOSFirmwareRecoveryReason", "ACM launch failure"}},
68 {0x05, {"BIOSFirmwareRecoveryReason", "IBB launch failure"}},
69 {0x06, {"BIOSFirmwareRecoveryReason", "OBB launch failure"}},
70 {0x07,
71 {"BMCFirmwareRecoveryReason",
72 "BMC active image authentication failure"}},
73 {0x08,
74 {"BMCFirmwareRecoveryReason",
75 "BMC recovery image authentication failure"}},
76 {0x09, {"BMCFirmwareRecoveryReason", "BMC launch failure"}},
77 {0x0A, {"CPLDFirmwareRecoveryReason", "CPLD watchdog expired"}}};
AppaRao Puli88aa33b2019-07-18 23:49:55 +053078
AppaRao Pulie90f1282019-11-05 01:07:05 +053079// Panic Reason map.
80// {<CPLD association>, {<Redfish MessageID>, <Panic reason> })
81static const boost::container::flat_map<uint8_t,
82 std::pair<std::string, std::string>>
83 panicReasonMap = {
84 {0x01, {"CPLDFirmwarePanicReason", "CPLD watchdog expired"}},
85 {0x02, {"BMCFirmwarePanicReason", "BMC watchdog expired"}},
86 {0x03, {"MEFirmwarePanicReason", "ME watchdog expired"}},
87 {0x04, {"BIOSFirmwarePanicReason", "ACM watchdog expired"}},
88 {0x05, {"BIOSFirmwarePanicReason", "IBB watchdog expired"}},
89 {0x06, {"BIOSFirmwarePanicReason", "OBB watchdog expired"}},
90 {0x07,
91 {"BMCFirmwarePanicReason", "BMC active image authentication failure"}},
92 {0x08,
93 {"BMCFirmwarePanicReason",
94 "BMC recovery image authentication failure"}},
95 {0x09,
96 {"BIOSFirmwarePanicReason",
97 "PCH active image authentication failure"}},
98 {0x0A,
99 {"BIOSFirmwarePanicReason",
100 "PCH recovery image authentication failure"}},
101 {0x0B, {"MEFirmwarePanicReason", "ME authentication failure"}},
102 {0x0C,
103 {"BIOSFirmwarePanicReason",
104 "ACM or IBB or OBB authentication failure"}},
105 {0x0D, {"BIOSFirmwarePanicReason", "PCH update intent"}},
106 {0x0E, {"BMCFirmwarePanicReason", "BMC update intent"}},
107 {0x0F, {"BMCFirmwarePanicReason", "BMC reset detected"}}};
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530108
AppaRao Pulie4e95652019-07-19 16:52:01 +0530109static void updateDbusPropertiesCache()
110{
111 for (const auto& pfrVerObj : pfrVersionObjects)
112 {
113 pfrVerObj->updateVersion();
114 }
115
116 // Update provisoningStatus properties
117 pfrConfigObject->updateProvisioningStatus();
118
119 phosphor::logging::log<phosphor::logging::level::INFO>(
120 "PFR Manager service cache data updated.");
121}
122
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530123static void logLastRecoveryEvent()
124{
125 uint8_t reason = 0;
126 if (0 !=
127 intel::pfr::readCpldReg(intel::pfr::ActionType::recoveryReason, reason))
128 {
129 return;
130 }
131
AppaRao Pulie90f1282019-11-05 01:07:05 +0530132 auto it = recoveryReasonMap.find(reason);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530133 if (it == recoveryReasonMap.end())
134 {
135 // No matching found. So just return without logging event.
136 return;
137 }
AppaRao Pulie90f1282019-11-05 01:07:05 +0530138 std::string msgId = "OpenBMC.0.1." + it->second.first;
139 sd_journal_send("MESSAGE=%s", "Platform firmware recovery occurred.",
140 "PRIORITY=%i", LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
141 msgId.c_str(), "REDFISH_MESSAGE_ARGS=%s",
142 it->second.second.c_str(), NULL);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530143}
144
145static void logLastPanicEvent()
146{
147 uint8_t reason = 0;
148 if (0 !=
149 intel::pfr::readCpldReg(intel::pfr::ActionType::panicReason, reason))
150 {
151 return;
152 }
153
AppaRao Pulie90f1282019-11-05 01:07:05 +0530154 auto it = panicReasonMap.find(reason);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530155 if (it == panicReasonMap.end())
156 {
157 // No matching found. So just return without logging event.
158 return;
159 }
160
AppaRao Pulie90f1282019-11-05 01:07:05 +0530161 std::string msgId = "OpenBMC.0.1." + it->second.first;
162 sd_journal_send("MESSAGE=%s", "Platform firmware panic occurred.",
163 "PRIORITY=%i", LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
164 msgId.c_str(), "REDFISH_MESSAGE_ARGS=%s",
165 it->second.second.c_str(), NULL);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530166}
167
168static void checkAndLogEvents()
169{
170 uint8_t currPanicCount = 0;
171 if (0 == intel::pfr::readCpldReg(intel::pfr::ActionType::panicCount,
172 currPanicCount))
173 {
174 if (lastPanicCount != currPanicCount)
175 {
176 // Update cached data and log redfish event by reading reason.
177 lastPanicCount = currPanicCount;
178 logLastPanicEvent();
179 }
180 }
181
182 uint8_t currRecoveryCount = 0;
183 if (0 == intel::pfr::readCpldReg(intel::pfr::ActionType::recoveryCount,
184 currRecoveryCount))
185 {
186 if (lastRecoveryCount != currRecoveryCount)
187 {
188 // Update cached data and log redfish event by reading reason.
189 lastRecoveryCount = currRecoveryCount;
190 logLastRecoveryEvent();
191 }
192 }
193
194 uint8_t majorErr = 0;
195 uint8_t minorErr = 0;
196 if ((0 == intel::pfr::readCpldReg(intel::pfr::ActionType::majorError,
197 majorErr)) ||
198 (0 ==
199 intel::pfr::readCpldReg(intel::pfr::ActionType::minorError, minorErr)))
200 {
201 if ((lastMajorErr != majorErr) || (lastMinorErr != minorErr))
202 {
203 lastMajorErr = majorErr;
204 lastMinorErr = minorErr;
205
206 if (majorErr || minorErr)
207 {
AppaRao Pulidbe184d2019-10-09 18:04:22 +0530208 std::string errorStr =
209 toHexString(majorErr) + "." + toHexString(minorErr);
210 sd_journal_send("MESSAGE=%s",
211 "Error occurred on platform firmware.",
212 "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s",
213 "OpenBMC.0.1.PlatformFirmwareError",
214 "REDFISH_MESSAGE_ARGS=%s", errorStr, NULL);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530215 }
216 }
217 }
218}
219
220static void monitorPlatformStateChange(
221 sdbusplus::asio::object_server& server,
222 std::shared_ptr<sdbusplus::asio::connection>& conn)
223{
224 constexpr size_t pollTimeout = 10; // seconds
225 stateTimer->expires_after(std::chrono::seconds(pollTimeout));
226 stateTimer->async_wait(
227 [&server, &conn](const boost::system::error_code& ec) {
228 if (ec == boost::asio::error::operation_aborted)
229 {
230 // Timer reset.
231 return;
232 }
233 if (ec)
234 {
235 // Platform State Monitor - Timer cancelled.
236 return;
237 }
238 checkAndLogEvents();
239 monitorPlatformStateChange(server, conn);
240 });
241}
242
AppaRao Puli46cead92019-07-22 16:50:09 +0530243void checkAndSetCheckpoint(sdbusplus::asio::object_server& server,
244 std::shared_ptr<sdbusplus::asio::connection>& conn)
245{
246 // Check whether systemd completed all the loading.
247 conn->async_method_call(
248 [&server, &conn](boost::system::error_code ec,
249 const std::variant<uint64_t>& value) {
250 if (ec)
251 {
252 phosphor::logging::log<phosphor::logging::level::ERR>(
253 "async_method_call error: FinishTimestamp failed");
254 return;
255 }
256 if (std::get<uint64_t>(value))
257 {
258 if (!finishedSettingChkPoint)
259 {
260 finishedSettingChkPoint = true;
261 intel::pfr::setBMCBootCheckpoint(bmcBootFinishedChkPoint);
262 }
263 }
264 else
265 {
266 // FIX-ME: Latest up-stream sync caused issue in receiving
267 // StartupFinished signal. Unable to get StartupFinished signal
268 // from systemd1 hence using poll method too, to trigger it
269 // properly.
270 constexpr size_t pollTimeout = 10; // seconds
271 initTimer->expires_after(std::chrono::seconds(pollTimeout));
272 initTimer->async_wait([&server, &conn](
273 const boost::system::error_code& ec) {
274 if (ec == boost::asio::error::operation_aborted)
275 {
276 // Timer reset.
277 return;
278 }
279 if (ec)
280 {
281 phosphor::logging::log<phosphor::logging::level::ERR>(
282 "Set boot Checkpoint - async wait error.");
283 return;
284 }
285 checkAndSetCheckpoint(server, conn);
286 });
287 }
288 },
289 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
290 "org.freedesktop.DBus.Properties", "Get",
291 "org.freedesktop.systemd1.Manager", "FinishTimestamp");
292}
293
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530294int main()
295{
296 // setup connection to dbus
297 boost::asio::io_service io;
298 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530299 stateTimer = std::make_unique<boost::asio::steady_timer>(io);
AppaRao Puli46cead92019-07-22 16:50:09 +0530300 initTimer = std::make_unique<boost::asio::steady_timer>(io);
AppaRao Pulicc1ed682019-10-01 12:29:40 +0530301 conn->request_name("xyz.openbmc_project.PFR.Manager");
AppaRao Pulie4e95652019-07-19 16:52:01 +0530302 auto server = sdbusplus::asio::object_server(conn);
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530303
AppaRao Pulicc1ed682019-10-01 12:29:40 +0530304 // Create PFR attributes object and interface
AppaRao Pulie4e95652019-07-19 16:52:01 +0530305 pfrConfigObject = std::make_unique<intel::pfr::PfrConfig>(server, conn);
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530306
AppaRao Pulie4e95652019-07-19 16:52:01 +0530307 pfrVersionObjects.clear();
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530308 // Create Software objects using Versions interface
AppaRao Pulie4e95652019-07-19 16:52:01 +0530309 for (const auto& entry : verComponentList)
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530310 {
AppaRao Pulie4e95652019-07-19 16:52:01 +0530311 pfrVersionObjects.emplace_back(std::make_unique<intel::pfr::PfrVersion>(
312 server, conn, std::get<0>(entry), std::get<1>(entry),
313 std::get<2>(entry)));
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530314 }
315
AppaRao Puli46cead92019-07-22 16:50:09 +0530316 // Monitor Boot finished signal and set the checkpoint 9 to
317 // notify CPLD about BMC boot finish.
318 auto bootFinishedSignal = std::make_unique<sdbusplus::bus::match::match>(
319 static_cast<sdbusplus::bus::bus&>(*conn),
320 "type='signal',"
321 "member='StartupFinished',path='/org/freedesktop/systemd1',"
322 "interface='org.freedesktop.systemd1.Manager'",
323 [&server, &conn](sdbusplus::message::message& msg) {
324 if (!finishedSettingChkPoint)
325 {
326 finishedSettingChkPoint = true;
327 intel::pfr::setBMCBootCheckpoint(bmcBootFinishedChkPoint);
328 }
329 });
330 checkAndSetCheckpoint(server, conn);
331
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530332 // Capture the Chassis state and Start the monitor timer
333 // if state changed to 'On'. Run timer until OS boot.
334 // Stop timer if state changed to 'Off'.
335 static auto matchChassisState = sdbusplus::bus::match::match(
336 static_cast<sdbusplus::bus::bus&>(*conn),
337 "type='signal',member='PropertiesChanged', "
338 "interface='org.freedesktop.DBus.Properties', "
339 "sender='xyz.openbmc_project.State.Chassis', "
340 "arg0namespace='xyz.openbmc_project.State.Chassis'",
341 [&server, &conn](sdbusplus::message::message& message) {
342 std::string intfName;
343 std::map<std::string, std::variant<std::string>> properties;
344 message.read(intfName, properties);
345
346 const auto it = properties.find("CurrentPowerState");
347 if (it != properties.end())
348 {
349 const std::string* state =
350 std::get_if<std::string>(&it->second);
351 if (state != nullptr)
352 {
353 if ((*state ==
354 "xyz.openbmc_project.State.Chassis.PowerState.On") &&
355 (!stateTimerRunning))
356 {
357 stateTimerRunning = true;
358 monitorPlatformStateChange(server, conn);
359 }
360 else if ((*state == "xyz.openbmc_project.State.Chassis."
361 "PowerState.Off") &&
362 (stateTimerRunning))
363 {
364 stateTimer->cancel();
365 checkAndLogEvents();
366 stateTimerRunning = false;
367 }
368 }
AppaRao Pulie4e95652019-07-19 16:52:01 +0530369
370 // Update the D-Bus properties when chassis state changes.
371 updateDbusPropertiesCache();
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530372 }
373 });
374
375 // Capture the Host state and Start the monitor timer
376 // if state changed to 'Running'. Run timer until OS boot.
377 // Stop timer if state changed to 'Off'.
378 static auto matchHostState = sdbusplus::bus::match::match(
379 static_cast<sdbusplus::bus::bus&>(*conn),
380 "type='signal',member='PropertiesChanged', "
381 "interface='org.freedesktop.DBus.Properties', "
382 "sender='xyz.openbmc_project.State.Chassis', "
383 "arg0namespace='xyz.openbmc_project.State.Host'",
384 [&server, &conn](sdbusplus::message::message& message) {
385 std::string intfName;
386 std::map<std::string, std::variant<std::string>> properties;
387 message.read(intfName, properties);
388
389 const auto it = properties.find("CurrentHostState");
390 if (it != properties.end())
391 {
392 const std::string* state =
393 std::get_if<std::string>(&it->second);
394 if (state != nullptr)
395 {
396 if ((*state ==
397 "xyz.openbmc_project.State.Host.HostState.Running") &&
398 (!stateTimerRunning))
399 {
400 stateTimerRunning = true;
401 monitorPlatformStateChange(server, conn);
402 }
403 else if (((*state == "xyz.openbmc_project.State.Host."
404 "HostState.Off") ||
405 (*state == "xyz.openbmc_project.State.Host."
406 "HostState.Quiesced")) &&
407 (stateTimerRunning))
408 {
409 stateTimer->cancel();
410 checkAndLogEvents();
411 stateTimerRunning = false;
412 }
413 }
AppaRao Pulie4e95652019-07-19 16:52:01 +0530414
415 // Update the D-Bus properties when host state changes.
416 updateDbusPropertiesCache();
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530417 }
418 });
419
420 // Capture the OS state change and stop monitor timer
421 // if OS boots completly or becomes Inactive.
422 // start timer in other cases to mnitor states.
423 static auto matchOsState = sdbusplus::bus::match::match(
424 static_cast<sdbusplus::bus::bus&>(*conn),
425 "type='signal',member='PropertiesChanged', "
426 "interface='org.freedesktop.DBus.Properties', "
427 "sender='xyz.openbmc_project.State.Chassis', "
428 "arg0namespace='xyz.openbmc_project.State.OperatingSystem.Status'",
429 [&server, &conn](sdbusplus::message::message& message) {
430 std::string intfName;
431 std::map<std::string, std::variant<std::string>> properties;
432 message.read(intfName, properties);
433
434 const auto it = properties.find("OperatingSystemState");
435 if (it != properties.end())
436 {
437 const std::string* state =
438 std::get_if<std::string>(&it->second);
439 if (state != nullptr)
440 {
441 if (((*state == "BootComplete") ||
442 (*state == "Inactive")) &&
443 (stateTimerRunning))
444 {
445 stateTimer->cancel();
446 checkAndLogEvents();
447 stateTimerRunning = false;
448 }
449 else if (!stateTimerRunning)
450 {
451 stateTimerRunning = true;
452 monitorPlatformStateChange(server, conn);
453 }
454 }
455 }
456 });
457
458 // First time, check and log events if any.
459 checkAndLogEvents();
460
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530461 phosphor::logging::log<phosphor::logging::level::INFO>(
462 "Intel PFR service started successfully");
463
464 io.run();
465
466 return 0;
467}