blob: fabd150eccd4a504e9d3b38b9c1fc9edd08d398e [file] [log] [blame]
Charles Hsudbd77b92020-10-29 11:20:34 +08001/*
2// Copyright (c) 2018 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
17#pragma once
18#include <sel_logger.hpp>
19#include <sensorutils.hpp>
20
21#include <iostream>
22#include <string_view>
23#include <variant>
24
25enum class watchdogEventOffsets : uint8_t
26{
27 noAction = 0x00,
28 hardReset = 0x01,
29 powerDown = 0x02,
30 powerCycle = 0x03,
31};
32
33enum class watchdogTimerUseOffsets : uint8_t
34{
35 reserved = 0x00,
36 BIOSFRB2 = 0x01,
37 BIOSPOST = 0x02,
38 OSLoad = 0x03,
39 SMSOS = 0x04,
40 OEM = 0x05,
41 unspecified = 0x0f,
42};
43
44enum class watchdogInterruptTypeOffsets : uint8_t
45{
46 none = 0x00,
47 SMI = 0x01,
48 NMI = 0x02,
49 messageInterrupt = 0x03,
50 unspecified = 0x0f,
51};
52
53static constexpr const uint8_t wdtNologBit = (1 << 7);
54static constexpr int interruptTypeBits = 4;
55
56inline static sdbusplus::bus::match::match
57 startWatchdogEventMonitor(std::shared_ptr<sdbusplus::asio::connection> conn)
58{
59 auto watchdogEventMatcherCallback = [conn](
60 sdbusplus::message::message& msg) {
61 // This static set of std::pair<path, event> tracks asserted events to
62 // avoid duplicate logs or deasserts logged without an assert
63 static boost::container::flat_set<std::pair<std::string, std::string>>
64 assertedEvents;
65
66 // SEL event data is three bytes where 0xFF means unspecified
67 std::vector<uint8_t> eventData(selEvtDataMaxSize, 0xFF);
68
69 // Get the event type and assertion details from the message
70 std::string watchdogInterface;
71 boost::container::flat_map<std::string, std::variant<bool>>
72 propertiesChanged;
73
74 msg.read(watchdogInterface, propertiesChanged);
75
76 if (propertiesChanged.begin()->first != "Enabled")
77 {
78 return;
79 }
80
81 bool* pval = std::get_if<bool>(&propertiesChanged.begin()->second);
82
83 if (!pval)
84 {
85 std::cerr << "watchdog event direction has invalid type\n";
86 return;
87 }
88 bool assert = *pval;
89
90 sdbusplus::message::message getWatchdogStatus =
91 conn->new_method_call(msg.get_sender(), msg.get_path(),
92 "org.freedesktop.DBus.Properties", "GetAll");
93 getWatchdogStatus.append("xyz.openbmc_project.State.Watchdog");
94 boost::container::flat_map<std::string,
95 std::variant<std::string, uint64_t, bool>>
96 watchdogStatus;
97
98 try
99 {
100 sdbusplus::message::message getWatchdogStatusResp =
101 conn->call(getWatchdogStatus);
102 getWatchdogStatusResp.read(watchdogStatus);
103 }
104 catch (sdbusplus::exception_t&)
105 {
106 std::cerr << "error getting watchdog status from " << msg.get_path()
107 << "\n";
108 return;
109 }
110
111 auto getExpireAction = watchdogStatus.find("ExpireAction");
112 std::string_view expireAction;
113 if (getExpireAction != watchdogStatus.end())
114 {
115 expireAction = std::get<std::string>(getExpireAction->second);
116 expireAction.remove_prefix(std::min(
117 expireAction.find_last_of(".") + 1, expireAction.size()));
118 }
119 if (expireAction == "HardReset")
120 {
121 eventData[0] =
122 static_cast<uint8_t>(watchdogEventOffsets::hardReset);
123 }
124 else if (expireAction == "PowerOff")
125 {
126 eventData[0] =
127 static_cast<uint8_t>(watchdogEventOffsets::powerDown);
128 }
129 else if (expireAction == "PowerCycle")
130 {
131 eventData[0] =
132 static_cast<uint8_t>(watchdogEventOffsets::powerCycle);
133 }
134 else if (expireAction == "None")
135 {
136 eventData[0] = static_cast<uint8_t>(watchdogEventOffsets::noAction);
137 }
138
139 auto getPreTimeoutInterrupt =
140 watchdogStatus.find("PreTimeoutInterrupt");
141 std::string_view preTimeoutInterrupt;
142 if (getPreTimeoutInterrupt != watchdogStatus.end())
143 {
144 preTimeoutInterrupt =
145 std::get<std::string>(getPreTimeoutInterrupt->second);
146 preTimeoutInterrupt.remove_prefix(
147 std::min(preTimeoutInterrupt.find_last_of(".") + 1,
148 preTimeoutInterrupt.size()));
149 }
150 if (preTimeoutInterrupt == "None")
151 {
152 eventData[1] &=
153 (static_cast<uint8_t>(watchdogInterruptTypeOffsets::none)
154 << interruptTypeBits);
155 }
156 else if (preTimeoutInterrupt == "SMI")
157 {
158 eventData[1] &=
159 (static_cast<uint8_t>(watchdogInterruptTypeOffsets::SMI)
160 << interruptTypeBits);
161 }
162 else if (preTimeoutInterrupt == "NMI")
163 {
164 eventData[1] &=
165 (static_cast<uint8_t>(watchdogInterruptTypeOffsets::NMI)
166 << interruptTypeBits);
167 }
168 else if (preTimeoutInterrupt == "MI")
169 {
170 eventData[1] &= (static_cast<uint8_t>(
171 watchdogInterruptTypeOffsets::messageInterrupt)
172 << interruptTypeBits);
173 }
174
175 auto getCurrentTimerUse = watchdogStatus.find("CurrentTimerUse");
176 std::string_view currentTimerUse;
177 if (getCurrentTimerUse != watchdogStatus.end())
178 {
179 currentTimerUse = std::get<std::string>(getCurrentTimerUse->second);
180 currentTimerUse.remove_prefix(std::min(
181 currentTimerUse.find_last_of(".") + 1, currentTimerUse.size()));
182 }
183 if (currentTimerUse == "BIOSFRB2")
184 {
185 eventData[1] |=
186 static_cast<uint8_t>(watchdogTimerUseOffsets::BIOSFRB2);
187 }
188 else if (currentTimerUse == "BIOSPOST")
189 {
190 eventData[1] |=
191 static_cast<uint8_t>(watchdogTimerUseOffsets::BIOSPOST);
192 }
193 else if (currentTimerUse == "OSLoad")
194 {
195 eventData[1] |=
196 static_cast<uint8_t>(watchdogTimerUseOffsets::OSLoad);
197 }
198 else if (currentTimerUse == "SMSOS")
199 {
200 eventData[1] |=
201 static_cast<uint8_t>(watchdogTimerUseOffsets::SMSOS);
202 }
203 else if (currentTimerUse == "OEM")
204 {
205 eventData[1] |= static_cast<uint8_t>(watchdogTimerUseOffsets::OEM);
206 }
207 else
208 {
209 eventData[1] |=
210 static_cast<uint8_t>(watchdogTimerUseOffsets::unspecified);
211 }
212
213 auto getWatchdogInterval = watchdogStatus.find("Interval");
Jason M. Billse9da2592021-06-28 07:04:37 -0700214 uint64_t watchdogInterval = 0;
Charles Hsudbd77b92020-10-29 11:20:34 +0800215 if (getWatchdogInterval != watchdogStatus.end())
216 {
217 watchdogInterval = std::get<uint64_t>(getWatchdogInterval->second);
218 }
219
220 // get watchdog status porperties
221 static bool wdt_nolog;
222 sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
223 uint8_t netFn = 0x06;
224 uint8_t lun = 0x00;
225 uint8_t cmd = 0x25;
226 std::vector<uint8_t> commandData;
227 std::map<std::string, std::variant<int>> options;
228
229 auto ipmiCall = bus.new_method_call(
230 "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
231 "xyz.openbmc_project.Ipmi.Server", "execute");
232 ipmiCall.append(netFn, lun, cmd, commandData, options);
233 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
234 rsp;
235 auto ipmiReply = bus.call(ipmiCall);
236 ipmiReply.read(rsp);
237 auto& [rnetFn, rlun, rcmd, cc, responseData] = rsp;
238
239 std::string direction;
240 std::string eventMessageArgs;
241 if (assert)
242 {
243 direction = " enable ";
244 eventMessageArgs = "Enabled";
245 wdt_nolog = responseData[0] & wdtNologBit;
246 }
247 else
248 {
249 direction = " disable ";
250 eventMessageArgs = "Disabled";
251 }
252
253 // Set Watchdog Timer byte1[7]-1b=don't log
254 if (!wdt_nolog)
255 {
256 // Construct a human-readable message of this event for the log
257 std::string journalMsg(
258 std::string(currentTimerUse) + std::string(direction) +
259 "watchdog countdown " +
260 std::to_string(watchdogInterval / 1000) + " seconds " +
261 std::string(expireAction) + " action");
262
263 std::string redfishMessageID = "OpenBMC.0.1.IPMIWatchdog";
264
265 selAddSystemRecord(
266 journalMsg, std::string(msg.get_path()), eventData, assert,
267 selBMCGenID, "REDFISH_MESSAGE_ID=%s", redfishMessageID.c_str(),
268 "REDFISH_MESSAGE_ARGS=%s", eventMessageArgs.c_str(), NULL);
269 }
270 };
271 sdbusplus::bus::match::match watchdogEventMatcher(
272 static_cast<sdbusplus::bus::bus&>(*conn),
273 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
274 "PropertiesChanged',arg0namespace='xyz.openbmc_project.State."
275 "Watchdog'",
276 std::move(watchdogEventMatcherCallback));
277 return watchdogEventMatcher;
278}