1 #include "occ_status.hpp" 2 3 #include "occ_sensor.hpp" 4 #include "powermode.hpp" 5 #include "utils.hpp" 6 7 #include <fmt/core.h> 8 9 #ifdef POWER10 10 #include <com/ibm/Host/Target/server.hpp> 11 #endif 12 #include <phosphor-logging/log.hpp> 13 14 namespace open_power 15 { 16 namespace occ 17 { 18 19 using namespace phosphor::logging; 20 21 // Handles updates to occActive property 22 bool Status::occActive(bool value) 23 { 24 if (value != this->occActive()) 25 { 26 log<level::INFO>(fmt::format("Status::occActive OCC{} changed to {}", 27 instance, value) 28 .c_str()); 29 if (value) 30 { 31 // Bind the device 32 device.bind(); 33 34 // Start watching for errors 35 addErrorWatch(); 36 37 // Reset last OCC state 38 lastState = 0; 39 40 // Call into Manager to let know that we have bound 41 if (this->callBack) 42 { 43 this->callBack(value); 44 } 45 } 46 else 47 { 48 // Call into Manager to let know that we will unbind. 49 if (this->callBack) 50 { 51 this->callBack(value); 52 } 53 54 // Stop watching for errors 55 removeErrorWatch(); 56 57 // Do the unbind. 58 device.unBind(); 59 } 60 } 61 else if (value && !device.bound()) 62 { 63 // Existing error watch is on a dead file descriptor. 64 removeErrorWatch(); 65 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 78 addErrorWatch(); 79 } 80 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 } 91 return Base::Status::occActive(value); 92 } 93 94 // Callback handler when a device error is reported. 95 void Status::deviceErrorHandler(bool error) 96 { 97 // Make sure we have an error 98 if (error) 99 { 100 // This would deem OCC inactive 101 this->occActive(false); 102 103 // Reset the OCC 104 this->resetOCC(); 105 } 106 } 107 108 // Sends message to host control command handler to reset OCC 109 void Status::resetOCC() 110 { 111 log<level::INFO>( 112 fmt::format(">>Status::resetOCC() - requesting reset for OCC{}", 113 instance) 114 .c_str()); 115 #ifdef PLDM 116 if (resetCallBack) 117 { 118 this->resetCallBack(instance); 119 } 120 #else 121 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 125 auto service = utils::getService(CONTROL_HOST_PATH, CONTROL_HOST_INTF); 126 127 auto& bus = utils::getBus(); 128 auto method = bus.new_method_call(service.c_str(), CONTROL_HOST_PATH, 129 CONTROL_HOST_INTF, "Execute"); 130 // OCC Reset control command 131 method.append(convertForMessage(Control::Host::Command::OCCReset).c_str()); 132 133 // OCC Sensor ID for callout reasons 134 method.append(std::variant<uint8_t>(std::get<0>(sensorMap.at(instance)))); 135 bus.call_noreply(method); 136 return; 137 #endif 138 } 139 140 // Handler called by Host control command handler to convey the 141 // status of the executed command 142 void Status::hostControlEvent(sdbusplus::message::message& msg) 143 { 144 std::string cmdCompleted{}; 145 std::string cmdStatus{}; 146 147 msg.read(cmdCompleted, cmdStatus); 148 149 log<level::DEBUG>("Host control signal values", 150 entry("COMMAND=%s", cmdCompleted.c_str()), 151 entry("STATUS=%s", cmdStatus.c_str())); 152 153 if (Control::Host::convertResultFromString(cmdStatus) != 154 Control::Host::Result::Success) 155 { 156 if (Control::Host::convertCommandFromString(cmdCompleted) == 157 Control::Host::Command::OCCReset) 158 { 159 // Must be a Timeout. Log an Error trace 160 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)))); 163 } 164 } 165 return; 166 } 167 168 void 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; 193 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 201 } 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 215 #ifdef POWER10 216 // Check if Hypervisor target is PowerVM 217 bool 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 251 SysPwrMode 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 276 void 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 302 CmdStatus 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 393 CmdStatus 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(0x00); // IPS Enable: disabled 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 475 } // namespace occ 476 } // namespace open_power 477