blob: f044d8ad824699ccb38f2f696c2736ced7fa2914 [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 Puli88aa33b2019-07-18 23:49:55 +053055// Recovery reason map. { <CPLD association>, <Recovery Reason> }
56static std::map<uint8_t, std::string> recoveryReasonMap = {
57 {0x01, "PCH active authentication failure"},
58 {0x02, "PCH recovery authentication failure"},
59 {0x03, "ACM launch failure"},
60 {0x04, "IBB launch failure"},
61 {0x05, "OBB launch failure"},
62 {0x06, "BMC active authentication failure"},
63 {0x07, "BMC recovery authentication failure"},
64 {0x08, "BMC launch failure"},
65 {0x09, "CPLD watchdog expired"}};
66
67// Panic Reason map. { <CPLD association>, <Panic reason> }
68static std::map<uint8_t, std::string> panicReasonMap = {
69 {0x01, "CPLD WDT expired"},
70 {0x02, "BMC WDT expired"},
71 {0x03, "ME WDT expired"},
72 {0x04, "ACM WDT expired"},
73 {0x05, "IBB WDT expired"},
74 {0x06, "OBB WDT expired"},
75 {0x07, "BMC active authentication failure"},
76 {0x08, "BMC recovery authentication failure"},
77 {0x09, "PCH active authentication failure"},
78 {0x0A, "PCH recovery authentication failure"},
79 {0x0B, "IBB authentication failure"},
80 {0x0C, "OBB authentication failure"},
81 {0x0D, "BMC authentication failure"},
82 {0x0E, "PCH active update intent"},
83 {0x0F, "BMC active update intent"},
84 {0x10, "PCH recovery update intent"},
85 {0x11, "BMC recovery update intent"}};
86
AppaRao Pulie4e95652019-07-19 16:52:01 +053087static void updateDbusPropertiesCache()
88{
89 for (const auto& pfrVerObj : pfrVersionObjects)
90 {
91 pfrVerObj->updateVersion();
92 }
93
94 // Update provisoningStatus properties
95 pfrConfigObject->updateProvisioningStatus();
96
97 phosphor::logging::log<phosphor::logging::level::INFO>(
98 "PFR Manager service cache data updated.");
99}
100
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530101static void logLastRecoveryEvent()
102{
103 uint8_t reason = 0;
104 if (0 !=
105 intel::pfr::readCpldReg(intel::pfr::ActionType::recoveryReason, reason))
106 {
107 return;
108 }
109
110 std::map<uint8_t, std::string>::const_iterator it =
111 recoveryReasonMap.find(reason);
112 if (it == recoveryReasonMap.end())
113 {
114 // No matching found. So just return without logging event.
115 return;
116 }
117
118 sd_journal_send("MESSAGE=%s", "Platform firmware recovered.", "PRIORITY=%i",
119 LOG_ERR, "REDFISH_MESSAGE_ID=%s",
120 "OpenBMC.0.1.PlatformFWRecovered",
121 "REDFISH_MESSAGE_ARGS=%s", it->second.c_str(), NULL);
122}
123
124static void logLastPanicEvent()
125{
126 uint8_t reason = 0;
127 if (0 !=
128 intel::pfr::readCpldReg(intel::pfr::ActionType::panicReason, reason))
129 {
130 return;
131 }
132
133 std::map<uint8_t, std::string>::const_iterator it =
134 panicReasonMap.find(reason);
135 if (it == panicReasonMap.end())
136 {
137 // No matching found. So just return without logging event.
138 return;
139 }
140
141 sd_journal_send("MESSAGE=%s", "Platform panic event triggered.",
142 "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s",
143 "OpenBMC.0.1.PlatformFWPanicTriggered",
144 "REDFISH_MESSAGE_ARGS=%s", it->second.c_str(), NULL);
145}
146
147static void checkAndLogEvents()
148{
149 uint8_t currPanicCount = 0;
150 if (0 == intel::pfr::readCpldReg(intel::pfr::ActionType::panicCount,
151 currPanicCount))
152 {
153 if (lastPanicCount != currPanicCount)
154 {
155 // Update cached data and log redfish event by reading reason.
156 lastPanicCount = currPanicCount;
157 logLastPanicEvent();
158 }
159 }
160
161 uint8_t currRecoveryCount = 0;
162 if (0 == intel::pfr::readCpldReg(intel::pfr::ActionType::recoveryCount,
163 currRecoveryCount))
164 {
165 if (lastRecoveryCount != currRecoveryCount)
166 {
167 // Update cached data and log redfish event by reading reason.
168 lastRecoveryCount = currRecoveryCount;
169 logLastRecoveryEvent();
170 }
171 }
172
173 uint8_t majorErr = 0;
174 uint8_t minorErr = 0;
175 if ((0 == intel::pfr::readCpldReg(intel::pfr::ActionType::majorError,
176 majorErr)) ||
177 (0 ==
178 intel::pfr::readCpldReg(intel::pfr::ActionType::minorError, minorErr)))
179 {
180 if ((lastMajorErr != majorErr) || (lastMinorErr != minorErr))
181 {
182 lastMajorErr = majorErr;
183 lastMinorErr = minorErr;
184
185 if (majorErr || minorErr)
186 {
187 sd_journal_send(
188 "MESSAGE=%s", "Error occurred on platform firmware.",
189 "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s",
190 "OpenBMC.0.1.PlatformFWErrorOccurred",
191 "REDFISH_MESSAGE_ARGS=%i,%i", majorErr, minorErr, NULL);
192 }
193 }
194 }
195}
196
197static void monitorPlatformStateChange(
198 sdbusplus::asio::object_server& server,
199 std::shared_ptr<sdbusplus::asio::connection>& conn)
200{
201 constexpr size_t pollTimeout = 10; // seconds
202 stateTimer->expires_after(std::chrono::seconds(pollTimeout));
203 stateTimer->async_wait(
204 [&server, &conn](const boost::system::error_code& ec) {
205 if (ec == boost::asio::error::operation_aborted)
206 {
207 // Timer reset.
208 return;
209 }
210 if (ec)
211 {
212 // Platform State Monitor - Timer cancelled.
213 return;
214 }
215 checkAndLogEvents();
216 monitorPlatformStateChange(server, conn);
217 });
218}
219
AppaRao Puli46cead92019-07-22 16:50:09 +0530220void checkAndSetCheckpoint(sdbusplus::asio::object_server& server,
221 std::shared_ptr<sdbusplus::asio::connection>& conn)
222{
223 // Check whether systemd completed all the loading.
224 conn->async_method_call(
225 [&server, &conn](boost::system::error_code ec,
226 const std::variant<uint64_t>& value) {
227 if (ec)
228 {
229 phosphor::logging::log<phosphor::logging::level::ERR>(
230 "async_method_call error: FinishTimestamp failed");
231 return;
232 }
233 if (std::get<uint64_t>(value))
234 {
235 if (!finishedSettingChkPoint)
236 {
237 finishedSettingChkPoint = true;
238 intel::pfr::setBMCBootCheckpoint(bmcBootFinishedChkPoint);
239 }
240 }
241 else
242 {
243 // FIX-ME: Latest up-stream sync caused issue in receiving
244 // StartupFinished signal. Unable to get StartupFinished signal
245 // from systemd1 hence using poll method too, to trigger it
246 // properly.
247 constexpr size_t pollTimeout = 10; // seconds
248 initTimer->expires_after(std::chrono::seconds(pollTimeout));
249 initTimer->async_wait([&server, &conn](
250 const boost::system::error_code& ec) {
251 if (ec == boost::asio::error::operation_aborted)
252 {
253 // Timer reset.
254 return;
255 }
256 if (ec)
257 {
258 phosphor::logging::log<phosphor::logging::level::ERR>(
259 "Set boot Checkpoint - async wait error.");
260 return;
261 }
262 checkAndSetCheckpoint(server, conn);
263 });
264 }
265 },
266 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
267 "org.freedesktop.DBus.Properties", "Get",
268 "org.freedesktop.systemd1.Manager", "FinishTimestamp");
269}
270
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530271int main()
272{
273 // setup connection to dbus
274 boost::asio::io_service io;
275 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530276 stateTimer = std::make_unique<boost::asio::steady_timer>(io);
AppaRao Puli46cead92019-07-22 16:50:09 +0530277 initTimer = std::make_unique<boost::asio::steady_timer>(io);
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530278 conn->request_name("xyz.openbmc_project.Intel.PFR.Manager");
AppaRao Pulie4e95652019-07-19 16:52:01 +0530279 auto server = sdbusplus::asio::object_server(conn);
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530280
281 // Create Intel PFR attributes object and interface
AppaRao Pulie4e95652019-07-19 16:52:01 +0530282 pfrConfigObject = std::make_unique<intel::pfr::PfrConfig>(server, conn);
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530283
AppaRao Pulie4e95652019-07-19 16:52:01 +0530284 pfrVersionObjects.clear();
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530285 // Create Software objects using Versions interface
AppaRao Pulie4e95652019-07-19 16:52:01 +0530286 for (const auto& entry : verComponentList)
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530287 {
AppaRao Pulie4e95652019-07-19 16:52:01 +0530288 pfrVersionObjects.emplace_back(std::make_unique<intel::pfr::PfrVersion>(
289 server, conn, std::get<0>(entry), std::get<1>(entry),
290 std::get<2>(entry)));
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530291 }
292
AppaRao Puli46cead92019-07-22 16:50:09 +0530293 // Monitor Boot finished signal and set the checkpoint 9 to
294 // notify CPLD about BMC boot finish.
295 auto bootFinishedSignal = std::make_unique<sdbusplus::bus::match::match>(
296 static_cast<sdbusplus::bus::bus&>(*conn),
297 "type='signal',"
298 "member='StartupFinished',path='/org/freedesktop/systemd1',"
299 "interface='org.freedesktop.systemd1.Manager'",
300 [&server, &conn](sdbusplus::message::message& msg) {
301 if (!finishedSettingChkPoint)
302 {
303 finishedSettingChkPoint = true;
304 intel::pfr::setBMCBootCheckpoint(bmcBootFinishedChkPoint);
305 }
306 });
307 checkAndSetCheckpoint(server, conn);
308
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530309 // Capture the Chassis state and Start the monitor timer
310 // if state changed to 'On'. Run timer until OS boot.
311 // Stop timer if state changed to 'Off'.
312 static auto matchChassisState = sdbusplus::bus::match::match(
313 static_cast<sdbusplus::bus::bus&>(*conn),
314 "type='signal',member='PropertiesChanged', "
315 "interface='org.freedesktop.DBus.Properties', "
316 "sender='xyz.openbmc_project.State.Chassis', "
317 "arg0namespace='xyz.openbmc_project.State.Chassis'",
318 [&server, &conn](sdbusplus::message::message& message) {
319 std::string intfName;
320 std::map<std::string, std::variant<std::string>> properties;
321 message.read(intfName, properties);
322
323 const auto it = properties.find("CurrentPowerState");
324 if (it != properties.end())
325 {
326 const std::string* state =
327 std::get_if<std::string>(&it->second);
328 if (state != nullptr)
329 {
330 if ((*state ==
331 "xyz.openbmc_project.State.Chassis.PowerState.On") &&
332 (!stateTimerRunning))
333 {
334 stateTimerRunning = true;
335 monitorPlatformStateChange(server, conn);
336 }
337 else if ((*state == "xyz.openbmc_project.State.Chassis."
338 "PowerState.Off") &&
339 (stateTimerRunning))
340 {
341 stateTimer->cancel();
342 checkAndLogEvents();
343 stateTimerRunning = false;
344 }
345 }
AppaRao Pulie4e95652019-07-19 16:52:01 +0530346
347 // Update the D-Bus properties when chassis state changes.
348 updateDbusPropertiesCache();
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530349 }
350 });
351
352 // Capture the Host state and Start the monitor timer
353 // if state changed to 'Running'. Run timer until OS boot.
354 // Stop timer if state changed to 'Off'.
355 static auto matchHostState = sdbusplus::bus::match::match(
356 static_cast<sdbusplus::bus::bus&>(*conn),
357 "type='signal',member='PropertiesChanged', "
358 "interface='org.freedesktop.DBus.Properties', "
359 "sender='xyz.openbmc_project.State.Chassis', "
360 "arg0namespace='xyz.openbmc_project.State.Host'",
361 [&server, &conn](sdbusplus::message::message& message) {
362 std::string intfName;
363 std::map<std::string, std::variant<std::string>> properties;
364 message.read(intfName, properties);
365
366 const auto it = properties.find("CurrentHostState");
367 if (it != properties.end())
368 {
369 const std::string* state =
370 std::get_if<std::string>(&it->second);
371 if (state != nullptr)
372 {
373 if ((*state ==
374 "xyz.openbmc_project.State.Host.HostState.Running") &&
375 (!stateTimerRunning))
376 {
377 stateTimerRunning = true;
378 monitorPlatformStateChange(server, conn);
379 }
380 else if (((*state == "xyz.openbmc_project.State.Host."
381 "HostState.Off") ||
382 (*state == "xyz.openbmc_project.State.Host."
383 "HostState.Quiesced")) &&
384 (stateTimerRunning))
385 {
386 stateTimer->cancel();
387 checkAndLogEvents();
388 stateTimerRunning = false;
389 }
390 }
AppaRao Pulie4e95652019-07-19 16:52:01 +0530391
392 // Update the D-Bus properties when host state changes.
393 updateDbusPropertiesCache();
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530394 }
395 });
396
397 // Capture the OS state change and stop monitor timer
398 // if OS boots completly or becomes Inactive.
399 // start timer in other cases to mnitor states.
400 static auto matchOsState = sdbusplus::bus::match::match(
401 static_cast<sdbusplus::bus::bus&>(*conn),
402 "type='signal',member='PropertiesChanged', "
403 "interface='org.freedesktop.DBus.Properties', "
404 "sender='xyz.openbmc_project.State.Chassis', "
405 "arg0namespace='xyz.openbmc_project.State.OperatingSystem.Status'",
406 [&server, &conn](sdbusplus::message::message& message) {
407 std::string intfName;
408 std::map<std::string, std::variant<std::string>> properties;
409 message.read(intfName, properties);
410
411 const auto it = properties.find("OperatingSystemState");
412 if (it != properties.end())
413 {
414 const std::string* state =
415 std::get_if<std::string>(&it->second);
416 if (state != nullptr)
417 {
418 if (((*state == "BootComplete") ||
419 (*state == "Inactive")) &&
420 (stateTimerRunning))
421 {
422 stateTimer->cancel();
423 checkAndLogEvents();
424 stateTimerRunning = false;
425 }
426 else if (!stateTimerRunning)
427 {
428 stateTimerRunning = true;
429 monitorPlatformStateChange(server, conn);
430 }
431 }
432 }
433 });
434
435 // First time, check and log events if any.
436 checkAndLogEvents();
437
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530438 phosphor::logging::log<phosphor::logging::level::INFO>(
439 "Intel PFR service started successfully");
440
441 io.run();
442
443 return 0;
444}