blob: 7be7a459dab880ad54b696bcb5c9b169b85ce9d6 [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 Pulie63eeda2019-07-05 16:25:38 +053021
22static std::array<std::string, 5> listVersionPaths = {
23 "bmc_active", "bmc_recovery", "bios_active", "bios_recovery", "cpld"};
24
AppaRao Puli88aa33b2019-07-18 23:49:55 +053025// Caches the last Recovery/Panic Count to
26// identify any new Recovery/panic actions.
27/* TODO: When BMC Reset's, these values will be lost
28 * Persist this info using settingsd */
29static uint8_t lastRecoveryCount = 0;
30static uint8_t lastPanicCount = 0;
31static uint8_t lastMajorErr = 0;
32static uint8_t lastMinorErr = 0;
33
34static bool stateTimerRunning = false;
35std::unique_ptr<boost::asio::steady_timer> stateTimer = nullptr;
36
37// Recovery reason map. { <CPLD association>, <Recovery Reason> }
38static std::map<uint8_t, std::string> recoveryReasonMap = {
39 {0x01, "PCH active authentication failure"},
40 {0x02, "PCH recovery authentication failure"},
41 {0x03, "ACM launch failure"},
42 {0x04, "IBB launch failure"},
43 {0x05, "OBB launch failure"},
44 {0x06, "BMC active authentication failure"},
45 {0x07, "BMC recovery authentication failure"},
46 {0x08, "BMC launch failure"},
47 {0x09, "CPLD watchdog expired"}};
48
49// Panic Reason map. { <CPLD association>, <Panic reason> }
50static std::map<uint8_t, std::string> panicReasonMap = {
51 {0x01, "CPLD WDT expired"},
52 {0x02, "BMC WDT expired"},
53 {0x03, "ME WDT expired"},
54 {0x04, "ACM WDT expired"},
55 {0x05, "IBB WDT expired"},
56 {0x06, "OBB WDT expired"},
57 {0x07, "BMC active authentication failure"},
58 {0x08, "BMC recovery authentication failure"},
59 {0x09, "PCH active authentication failure"},
60 {0x0A, "PCH recovery authentication failure"},
61 {0x0B, "IBB authentication failure"},
62 {0x0C, "OBB authentication failure"},
63 {0x0D, "BMC authentication failure"},
64 {0x0E, "PCH active update intent"},
65 {0x0F, "BMC active update intent"},
66 {0x10, "PCH recovery update intent"},
67 {0x11, "BMC recovery update intent"}};
68
69static void logLastRecoveryEvent()
70{
71 uint8_t reason = 0;
72 if (0 !=
73 intel::pfr::readCpldReg(intel::pfr::ActionType::recoveryReason, reason))
74 {
75 return;
76 }
77
78 std::map<uint8_t, std::string>::const_iterator it =
79 recoveryReasonMap.find(reason);
80 if (it == recoveryReasonMap.end())
81 {
82 // No matching found. So just return without logging event.
83 return;
84 }
85
86 sd_journal_send("MESSAGE=%s", "Platform firmware recovered.", "PRIORITY=%i",
87 LOG_ERR, "REDFISH_MESSAGE_ID=%s",
88 "OpenBMC.0.1.PlatformFWRecovered",
89 "REDFISH_MESSAGE_ARGS=%s", it->second.c_str(), NULL);
90}
91
92static void logLastPanicEvent()
93{
94 uint8_t reason = 0;
95 if (0 !=
96 intel::pfr::readCpldReg(intel::pfr::ActionType::panicReason, reason))
97 {
98 return;
99 }
100
101 std::map<uint8_t, std::string>::const_iterator it =
102 panicReasonMap.find(reason);
103 if (it == panicReasonMap.end())
104 {
105 // No matching found. So just return without logging event.
106 return;
107 }
108
109 sd_journal_send("MESSAGE=%s", "Platform panic event triggered.",
110 "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s",
111 "OpenBMC.0.1.PlatformFWPanicTriggered",
112 "REDFISH_MESSAGE_ARGS=%s", it->second.c_str(), NULL);
113}
114
115static void checkAndLogEvents()
116{
117 uint8_t currPanicCount = 0;
118 if (0 == intel::pfr::readCpldReg(intel::pfr::ActionType::panicCount,
119 currPanicCount))
120 {
121 if (lastPanicCount != currPanicCount)
122 {
123 // Update cached data and log redfish event by reading reason.
124 lastPanicCount = currPanicCount;
125 logLastPanicEvent();
126 }
127 }
128
129 uint8_t currRecoveryCount = 0;
130 if (0 == intel::pfr::readCpldReg(intel::pfr::ActionType::recoveryCount,
131 currRecoveryCount))
132 {
133 if (lastRecoveryCount != currRecoveryCount)
134 {
135 // Update cached data and log redfish event by reading reason.
136 lastRecoveryCount = currRecoveryCount;
137 logLastRecoveryEvent();
138 }
139 }
140
141 uint8_t majorErr = 0;
142 uint8_t minorErr = 0;
143 if ((0 == intel::pfr::readCpldReg(intel::pfr::ActionType::majorError,
144 majorErr)) ||
145 (0 ==
146 intel::pfr::readCpldReg(intel::pfr::ActionType::minorError, minorErr)))
147 {
148 if ((lastMajorErr != majorErr) || (lastMinorErr != minorErr))
149 {
150 lastMajorErr = majorErr;
151 lastMinorErr = minorErr;
152
153 if (majorErr || minorErr)
154 {
155 sd_journal_send(
156 "MESSAGE=%s", "Error occurred on platform firmware.",
157 "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s",
158 "OpenBMC.0.1.PlatformFWErrorOccurred",
159 "REDFISH_MESSAGE_ARGS=%i,%i", majorErr, minorErr, NULL);
160 }
161 }
162 }
163}
164
165static void monitorPlatformStateChange(
166 sdbusplus::asio::object_server& server,
167 std::shared_ptr<sdbusplus::asio::connection>& conn)
168{
169 constexpr size_t pollTimeout = 10; // seconds
170 stateTimer->expires_after(std::chrono::seconds(pollTimeout));
171 stateTimer->async_wait(
172 [&server, &conn](const boost::system::error_code& ec) {
173 if (ec == boost::asio::error::operation_aborted)
174 {
175 // Timer reset.
176 return;
177 }
178 if (ec)
179 {
180 // Platform State Monitor - Timer cancelled.
181 return;
182 }
183 checkAndLogEvents();
184 monitorPlatformStateChange(server, conn);
185 });
186}
187
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530188int main()
189{
190 // setup connection to dbus
191 boost::asio::io_service io;
192 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530193 stateTimer = std::make_unique<boost::asio::steady_timer>(io);
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530194 conn->request_name("xyz.openbmc_project.Intel.PFR.Manager");
195 auto server = sdbusplus::asio::object_server(conn, true);
196
197 // Create Intel PFR attributes object and interface
198 intel::pfr::PfrConfig obj(server, conn);
199
200 // Create Software objects using Versions interface
201 for (const auto& path : listVersionPaths)
202 {
203 intel::pfr::PfrVersion obj(server, conn, path);
204 }
205
AppaRao Puli88aa33b2019-07-18 23:49:55 +0530206 // Capture the Chassis state and Start the monitor timer
207 // if state changed to 'On'. Run timer until OS boot.
208 // Stop timer if state changed to 'Off'.
209 static auto matchChassisState = sdbusplus::bus::match::match(
210 static_cast<sdbusplus::bus::bus&>(*conn),
211 "type='signal',member='PropertiesChanged', "
212 "interface='org.freedesktop.DBus.Properties', "
213 "sender='xyz.openbmc_project.State.Chassis', "
214 "arg0namespace='xyz.openbmc_project.State.Chassis'",
215 [&server, &conn](sdbusplus::message::message& message) {
216 std::string intfName;
217 std::map<std::string, std::variant<std::string>> properties;
218 message.read(intfName, properties);
219
220 const auto it = properties.find("CurrentPowerState");
221 if (it != properties.end())
222 {
223 const std::string* state =
224 std::get_if<std::string>(&it->second);
225 if (state != nullptr)
226 {
227 if ((*state ==
228 "xyz.openbmc_project.State.Chassis.PowerState.On") &&
229 (!stateTimerRunning))
230 {
231 stateTimerRunning = true;
232 monitorPlatformStateChange(server, conn);
233 }
234 else if ((*state == "xyz.openbmc_project.State.Chassis."
235 "PowerState.Off") &&
236 (stateTimerRunning))
237 {
238 stateTimer->cancel();
239 checkAndLogEvents();
240 stateTimerRunning = false;
241 }
242 }
243 }
244 });
245
246 // Capture the Host state and Start the monitor timer
247 // if state changed to 'Running'. Run timer until OS boot.
248 // Stop timer if state changed to 'Off'.
249 static auto matchHostState = sdbusplus::bus::match::match(
250 static_cast<sdbusplus::bus::bus&>(*conn),
251 "type='signal',member='PropertiesChanged', "
252 "interface='org.freedesktop.DBus.Properties', "
253 "sender='xyz.openbmc_project.State.Chassis', "
254 "arg0namespace='xyz.openbmc_project.State.Host'",
255 [&server, &conn](sdbusplus::message::message& message) {
256 std::string intfName;
257 std::map<std::string, std::variant<std::string>> properties;
258 message.read(intfName, properties);
259
260 const auto it = properties.find("CurrentHostState");
261 if (it != properties.end())
262 {
263 const std::string* state =
264 std::get_if<std::string>(&it->second);
265 if (state != nullptr)
266 {
267 if ((*state ==
268 "xyz.openbmc_project.State.Host.HostState.Running") &&
269 (!stateTimerRunning))
270 {
271 stateTimerRunning = true;
272 monitorPlatformStateChange(server, conn);
273 }
274 else if (((*state == "xyz.openbmc_project.State.Host."
275 "HostState.Off") ||
276 (*state == "xyz.openbmc_project.State.Host."
277 "HostState.Quiesced")) &&
278 (stateTimerRunning))
279 {
280 stateTimer->cancel();
281 checkAndLogEvents();
282 stateTimerRunning = false;
283 }
284 }
285 }
286 });
287
288 // Capture the OS state change and stop monitor timer
289 // if OS boots completly or becomes Inactive.
290 // start timer in other cases to mnitor states.
291 static auto matchOsState = sdbusplus::bus::match::match(
292 static_cast<sdbusplus::bus::bus&>(*conn),
293 "type='signal',member='PropertiesChanged', "
294 "interface='org.freedesktop.DBus.Properties', "
295 "sender='xyz.openbmc_project.State.Chassis', "
296 "arg0namespace='xyz.openbmc_project.State.OperatingSystem.Status'",
297 [&server, &conn](sdbusplus::message::message& message) {
298 std::string intfName;
299 std::map<std::string, std::variant<std::string>> properties;
300 message.read(intfName, properties);
301
302 const auto it = properties.find("OperatingSystemState");
303 if (it != properties.end())
304 {
305 const std::string* state =
306 std::get_if<std::string>(&it->second);
307 if (state != nullptr)
308 {
309 if (((*state == "BootComplete") ||
310 (*state == "Inactive")) &&
311 (stateTimerRunning))
312 {
313 stateTimer->cancel();
314 checkAndLogEvents();
315 stateTimerRunning = false;
316 }
317 else if (!stateTimerRunning)
318 {
319 stateTimerRunning = true;
320 monitorPlatformStateChange(server, conn);
321 }
322 }
323 }
324 });
325
326 // First time, check and log events if any.
327 checkAndLogEvents();
328
AppaRao Pulie63eeda2019-07-05 16:25:38 +0530329 phosphor::logging::log<phosphor::logging::level::INFO>(
330 "Intel PFR service started successfully");
331
332 io.run();
333
334 return 0;
335}