blob: 1ac521275d8defaea27f4000e3d9abc768a7cd70 [file] [log] [blame]
Vishwanatha Subbanna307d80b2017-06-28 15:56:09 +05301#include "occ_status.hpp"
Gunnar Mills94df8c92018-09-14 14:50:03 -05002
Vishwanatha Subbanna6add0b82017-07-21 19:02:37 +05303#include "occ_sensor.hpp"
Chris Cain78e86012021-03-04 16:15:31 -06004#include "powermode.hpp"
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +05305#include "utils.hpp"
Gunnar Mills94df8c92018-09-14 14:50:03 -05006
Chris Caina8857c52021-01-27 11:53:05 -06007#include <fmt/core.h>
8
Chris Cain78e86012021-03-04 16:15:31 -06009#ifdef POWER10
10#include <com/ibm/Host/Target/server.hpp>
11#endif
Gunnar Mills94df8c92018-09-14 14:50:03 -050012#include <phosphor-logging/log.hpp>
Chris Cain78e86012021-03-04 16:15:31 -060013
Vishwanatha Subbanna307d80b2017-06-28 15:56:09 +053014namespace open_power
15{
16namespace occ
17{
Chris Cain78e86012021-03-04 16:15:31 -060018
Chris Caina8857c52021-01-27 11:53:05 -060019using namespace phosphor::logging;
Vishwanatha Subbanna307d80b2017-06-28 15:56:09 +053020
21// Handles updates to occActive property
22bool Status::occActive(bool value)
23{
Vishwanatha Subbanna32e84e92017-06-28 19:17:28 +053024 if (value != this->occActive())
25 {
Chris Caina8857c52021-01-27 11:53:05 -060026 log<level::INFO>(fmt::format("Status::occActive OCC{} changed to {}",
27 instance, value)
28 .c_str());
Vishwanatha Subbanna32e84e92017-06-28 19:17:28 +053029 if (value)
30 {
31 // Bind the device
32 device.bind();
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053033
Edward A. James9fd2bdc2017-11-08 16:18:57 -060034 // Start watching for errors
35 addErrorWatch();
36
Chris Caina8857c52021-01-27 11:53:05 -060037 // Reset last OCC state
38 lastState = 0;
39
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053040 // Call into Manager to let know that we have bound
Edward A. James9fd2bdc2017-11-08 16:18:57 -060041 if (this->callBack)
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053042 {
43 this->callBack(value);
Edward A. James9fd2bdc2017-11-08 16:18:57 -060044 }
Vishwanatha Subbanna32e84e92017-06-28 19:17:28 +053045 }
46 else
47 {
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053048 // Call into Manager to let know that we will unbind.
Edward A. James9fd2bdc2017-11-08 16:18:57 -060049 if (this->callBack)
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053050 {
51 this->callBack(value);
Edward A. James9fd2bdc2017-11-08 16:18:57 -060052 }
53
54 // Stop watching for errors
55 removeErrorWatch();
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053056
57 // Do the unbind.
Vishwanatha Subbanna32e84e92017-06-28 19:17:28 +053058 device.unBind();
59 }
60 }
Edward A. James5e177972017-10-25 15:50:31 -050061 else if (value && !device.bound())
62 {
63 // Existing error watch is on a dead file descriptor.
Edward A. James9fd2bdc2017-11-08 16:18:57 -060064 removeErrorWatch();
Edward A. James5e177972017-10-25 15:50:31 -050065
66 /*
67 * In it's constructor, Status checks Device::bound() to see if OCC is
68 * active or not.
69 * Device::bound() checks for occX-dev0 directory.
70 * We will lose occX-dev0 directories during FSI rescan.
71 * So, if we start this application (and construct Status), and then
72 * later do FSI rescan, we will end up with occActive = true and device
73 * NOT bound. Lets correct that situation here.
74 */
75 device.bind();
76
77 // Add error watch again
Edward A. James9fd2bdc2017-11-08 16:18:57 -060078 addErrorWatch();
Edward A. James5e177972017-10-25 15:50:31 -050079 }
Eddie James6d6d1b32019-04-22 10:45:08 -050080 else if (!value && device.bound())
81 {
82 removeErrorWatch();
83
84 // In the event that the application never receives the active signal
85 // even though the OCC is active (this can occur if the BMC is rebooted
86 // with the host on, since the initial OCC driver probe will discover
87 // the OCCs), this application needs to be able to unbind the device
88 // when we get the OCC inactive signal.
89 device.unBind();
90 }
Vishwanatha Subbanna307d80b2017-06-28 15:56:09 +053091 return Base::Status::occActive(value);
92}
93
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053094// Callback handler when a device error is reported.
Eddie James482e31f2017-09-14 13:17:17 -050095void Status::deviceErrorHandler(bool error)
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053096{
Eddie James482e31f2017-09-14 13:17:17 -050097 // Make sure we have an error
98 if (error)
99 {
100 // This would deem OCC inactive
101 this->occActive(false);
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530102
Eddie James482e31f2017-09-14 13:17:17 -0500103 // Reset the OCC
104 this->resetOCC();
105 }
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530106}
107
108// Sends message to host control command handler to reset OCC
109void Status::resetOCC()
110{
Chris Caina8857c52021-01-27 11:53:05 -0600111 log<level::INFO>(
112 fmt::format(">>Status::resetOCC() - requesting reset for OCC{}",
113 instance)
114 .c_str());
Tom Joseph00325232020-07-29 17:51:48 +0530115#ifdef PLDM
116 if (resetCallBack)
117 {
118 this->resetCallBack(instance);
119 }
120#else
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530121 constexpr auto CONTROL_HOST_PATH = "/org/open_power/control/host0";
122 constexpr auto CONTROL_HOST_INTF = "org.open_power.Control.Host";
123
124 // This will throw exception on failure
George Liuf3b75142021-06-10 11:22:50 +0800125 auto service = utils::getService(CONTROL_HOST_PATH, CONTROL_HOST_INTF);
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530126
George Liuf3b75142021-06-10 11:22:50 +0800127 auto& bus = utils::getBus();
Gunnar Mills94df8c92018-09-14 14:50:03 -0500128 auto method = bus.new_method_call(service.c_str(), CONTROL_HOST_PATH,
129 CONTROL_HOST_INTF, "Execute");
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530130 // OCC Reset control command
Gunnar Mills94df8c92018-09-14 14:50:03 -0500131 method.append(convertForMessage(Control::Host::Command::OCCReset).c_str());
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530132
133 // OCC Sensor ID for callout reasons
Patrick Williamse0962702020-05-13 17:50:22 -0500134 method.append(std::variant<uint8_t>(std::get<0>(sensorMap.at(instance))));
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530135 bus.call_noreply(method);
136 return;
Tom Joseph00325232020-07-29 17:51:48 +0530137#endif
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530138}
139
140// Handler called by Host control command handler to convey the
141// status of the executed command
142void Status::hostControlEvent(sdbusplus::message::message& msg)
143{
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530144 std::string cmdCompleted{};
145 std::string cmdStatus{};
146
147 msg.read(cmdCompleted, cmdStatus);
148
149 log<level::DEBUG>("Host control signal values",
Gunnar Mills94df8c92018-09-14 14:50:03 -0500150 entry("COMMAND=%s", cmdCompleted.c_str()),
151 entry("STATUS=%s", cmdStatus.c_str()));
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530152
Gunnar Mills94df8c92018-09-14 14:50:03 -0500153 if (Control::Host::convertResultFromString(cmdStatus) !=
154 Control::Host::Result::Success)
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530155 {
Gunnar Mills94df8c92018-09-14 14:50:03 -0500156 if (Control::Host::convertCommandFromString(cmdCompleted) ==
157 Control::Host::Command::OCCReset)
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530158 {
Gunnar Mills85e65202018-04-08 15:01:54 -0500159 // Must be a Timeout. Log an Error trace
Alexander Filippov1d69e192019-03-21 18:12:07 +0300160 log<level::ERR>(
161 "Error resetting the OCC.", entry("PATH=%s", path.c_str()),
162 entry("SENSORID=0x%X", std::get<0>(sensorMap.at(instance))));
Vishwanatha Subbanna30e329a2017-07-24 23:13:14 +0530163 }
164 }
165 return;
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +0530166}
167
Chris Caina8857c52021-01-27 11:53:05 -0600168void Status::readOccState()
169{
170 unsigned int state;
171 const fs::path filename =
172 fs::path(DEV_PATH) /
173 fs::path(sysfsName + "." + std::to_string(instance + 1)) / "occ_state";
174
175 log<level::DEBUG>(
176 fmt::format("Status::readOccState: reading OCC{} state from {}",
177 instance, filename.c_str())
178 .c_str());
179
180 std::ifstream file(filename, std::ios::in);
181 const int open_errno = errno;
182 if (file)
183 {
184 file >> state;
185 if (state != lastState)
186 {
187 // Trace OCC state changes
188 log<level::INFO>(
189 fmt::format("Status::readOccState: OCC{} state 0x{:02X}",
190 instance, state)
191 .c_str());
192 lastState = state;
Chris Cain78e86012021-03-04 16:15:31 -0600193
194#ifdef POWER10
195 if ((OccState(state) == OccState::ACTIVE) && (device.master()))
196 {
197 // Kernel detected that the master OCC went to active state
198 occsWentActive();
199 }
200#endif
Chris Caina8857c52021-01-27 11:53:05 -0600201 }
202 file.close();
203 }
204 else
205 {
206 // If not able to read, OCC may be offline
207 log<level::DEBUG>(
208 fmt::format("Status::readOccState: open failed (errno={})",
209 open_errno)
210 .c_str());
211 lastState = 0;
212 }
213}
214
Chris Cain78e86012021-03-04 16:15:31 -0600215#ifdef POWER10
216// Check if Hypervisor target is PowerVM
217bool Status::isPowerVM()
218{
219 using namespace open_power::occ::powermode;
220 namespace Hyper = sdbusplus::com::ibm::Host::server;
221 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
222 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
223 constexpr auto HYPE_PROP = "Target";
224
225 bool powerVmTarget = false;
226
227 // This will throw exception on failure
228 auto& bus = utils::getBus();
229 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
230 auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
231 "org.freedesktop.DBus.Properties", "Get");
232 method.append(HYPE_INTERFACE, HYPE_PROP);
233 auto reply = bus.call(method);
234
235 std::variant<std::string> hyperEntryValue;
236 reply.read(hyperEntryValue);
237 auto propVal = std::get<std::string>(hyperEntryValue);
238 if (Hyper::Target::convertHypervisorFromString(propVal) ==
239 Hyper::Target::Hypervisor::PowerVM)
240 {
241 powerVmTarget = true;
242 }
243
244 log<level::DEBUG>(
245 fmt::format("Status::isPowerVM returning {}", powerVmTarget).c_str());
246
247 return powerVmTarget;
248}
249
250// Get the requested power mode
251SysPwrMode Status::getMode()
252{
253 using namespace open_power::occ::powermode;
254 SysPwrMode pmode = SysPwrMode::NO_CHANGE;
255
256 // This will throw exception on failure
257 auto& bus = utils::getBus();
258 auto service = utils::getService(PMODE_PATH, PMODE_INTERFACE);
259 auto method = bus.new_method_call(service.c_str(), PMODE_PATH,
260 "org.freedesktop.DBus.Properties", "Get");
261 method.append(PMODE_INTERFACE, POWER_MODE_PROP);
262 auto reply = bus.call(method);
263
264 std::variant<std::string> stateEntryValue;
265 reply.read(stateEntryValue);
266 auto propVal = std::get<std::string>(stateEntryValue);
267 pmode = powermode::convertStringToMode(propVal);
268
269 log<level::DEBUG>(
270 fmt::format("Status::getMode returning {}", pmode).c_str());
271
272 return pmode;
273}
274
275// Special processing that needs to happen once the OCCs change to ACTIVE state
276void Status::occsWentActive()
277{
278 CmdStatus status = CmdStatus::SUCCESS;
279
280 status = sendModeChange();
281 if (status != CmdStatus::SUCCESS)
282 {
283 log<level::ERR>(fmt::format("Status::occsWentActive: OCC mode "
284 "change failed with status {}",
285 status)
286 .c_str());
287 }
288
289 status = sendIpsData();
290 if (status != CmdStatus::SUCCESS)
291 {
292 log<level::ERR>(
293 fmt::format(
294 "Status::occsWentActive: Sending Idle Power Save Config data"
295 " failed with status {}",
296 status)
297 .c_str());
298 }
299}
300
301// Send mode change request to the master OCC
302CmdStatus Status::sendModeChange()
303{
304 CmdStatus status = CmdStatus::FAILURE;
305
306 if (!device.master())
307 {
308 log<level::ERR>(
309 fmt::format("Status::sendModeChange: MODE CHANGE does not "
310 "get sent to slave OCC{}",
311 instance)
312 .c_str());
313 return status;
314 }
315 if (!isPowerVM())
316 {
317 // Mode change is only supported on PowerVM systems
318 log<level::DEBUG>("Status::sendModeChange: MODE CHANGE does not "
319 "get sent on non-PowerVM systems");
320 return CmdStatus::SUCCESS;
321 }
322
323 const SysPwrMode newMode = getMode();
324
325 if (VALID_POWER_MODE_SETTING(newMode))
326 {
327 std::vector<std::uint8_t> cmd, rsp;
328 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
329 cmd.push_back(0x00); // Data Length (2 bytes)
330 cmd.push_back(0x06);
331 cmd.push_back(0x30); // Data (Version)
332 cmd.push_back(uint8_t(OccState::NO_CHANGE));
333 cmd.push_back(uint8_t(newMode));
334 cmd.push_back(0x00); // Mode Data (Freq Point)
335 cmd.push_back(0x00); //
336 cmd.push_back(0x00); // reserved
337 log<level::INFO>(fmt::format("Status::sendModeChange: SET_MODE({}) "
338 "command to OCC{} ({} bytes)",
339 newMode, instance, cmd.size())
340 .c_str());
341 status = occCmd.send(cmd, rsp);
342 if (status == CmdStatus::SUCCESS)
343 {
344 if (rsp.size() == 5)
345 {
346 if (RspStatus::SUCCESS == RspStatus(rsp[2]))
347 {
348 log<level::DEBUG>("Status::sendModeChange: - Mode change "
349 "completed successfully");
350 }
351 else
352 {
353 log<level::ERR>(
354 fmt::format("Status::sendModeChange: SET MODE "
355 "failed with status 0x{:02X}",
356 rsp[2])
357 .c_str());
358 }
359 }
360 else
361 {
362 log<level::ERR>(
363 "Status::sendModeChange: INVALID SET MODE response");
364 dump_hex(rsp);
365 }
366 }
367 else
368 {
369 if (status == CmdStatus::OPEN_FAILURE)
370 {
371 log<level::WARNING>(
372 "Status::sendModeChange: OCC not active yet");
373 }
374 else
375 {
376 log<level::ERR>("Status::sendModeChange: SET_MODE FAILED!");
377 }
378 }
379 }
380 else
381 {
382 log<level::ERR>(
383 fmt::format(
384 "Status::sendModeChange: Unable to set power mode to {}",
385 newMode)
386 .c_str());
387 }
388
389 return status;
390}
391
392// Send Idle Power Saver config data to the master OCC
393CmdStatus Status::sendIpsData()
394{
395 CmdStatus status = CmdStatus::FAILURE;
396
397 if (!device.master())
398 {
399 log<level::ERR>(
400 fmt::format("Status::sendIpsData: SET_CFG_DATA[IPS] does not "
401 "get sent to slave OCC{}",
402 instance)
403 .c_str());
404 return status;
405 }
406 if (!isPowerVM())
407 {
408 // Idle Power Saver data is only supported on PowerVM systems
409 log<level::DEBUG>("Status::sendIpsData: SET_CFG_DATA[IPS] does not "
410 "get sent on non-PowerVM systems");
411 return CmdStatus::SUCCESS;
412 }
413
414 std::vector<std::uint8_t> cmd, rsp;
415 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
416 cmd.push_back(0x00); // Data Length (2 bytes)
417 cmd.push_back(0x09);
418 // Data:
419 cmd.push_back(0x11); // Config Format: IPS Settings
420 cmd.push_back(0x00); // Version
421 cmd.push_back(0x01); // IPS Enable: enabled
422 cmd.push_back(0x00); // Enter Delay Time (240s)
423 cmd.push_back(0xF0); //
424 cmd.push_back(0x08); // Enter Utilization (8%)
425 cmd.push_back(0x00); // Exit Delay Time (10s)
426 cmd.push_back(0x0A); //
427 cmd.push_back(0x0C); // Exit Utilization (12%)
428 log<level::INFO>(fmt::format("Status::sendIpsData: SET_CFG_DATA[IPS] "
429 "command to OCC{} ({} bytes)",
430 instance, cmd.size())
431 .c_str());
432 status = occCmd.send(cmd, rsp);
433 if (status == CmdStatus::SUCCESS)
434 {
435 if (rsp.size() == 5)
436 {
437 if (RspStatus::SUCCESS == RspStatus(rsp[2]))
438 {
439 log<level::DEBUG>("Status::sendIpsData: - SET_CFG_DATA[IPS] "
440 "completed successfully");
441 }
442 else
443 {
444 log<level::ERR>(
445 fmt::format("Status::sendIpsData: SET_CFG_DATA[IPS] "
446 "failed with status 0x{:02X}",
447 rsp[2])
448 .c_str());
449 }
450 }
451 else
452 {
453 log<level::ERR>(
454 "Status::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
455 dump_hex(rsp);
456 }
457 }
458 else
459 {
460 if (status == CmdStatus::OPEN_FAILURE)
461 {
462 log<level::WARNING>("Status::sendIpsData: OCC not active yet");
463 }
464 else
465 {
466 log<level::ERR>("Status::sendIpsData: SET_CFG_DATA[IPS] FAILED!");
467 }
468 }
469
470 return status;
471}
472
473#endif // POWER10
474
Vishwanatha Subbanna307d80b2017-06-28 15:56:09 +0530475} // namespace occ
476} // namespace open_power