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>( 284 fmt::format( 285 "Status::occsWentActive: OCC mode change failed with status {}", 286 status) 287 .c_str()); 288 } 289 290 status = sendIpsData(); 291 if (status != CmdStatus::SUCCESS) 292 { 293 log<level::ERR>( 294 fmt::format( 295 "Status::occsWentActive: Sending Idle Power Save Config data 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( 310 "Status::sendModeChange: MODE CHANGE does not 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>( 319 "Status::sendModeChange: MODE CHANGE does not 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>( 338 fmt::format( 339 "Status::sendModeChange: SET_MODE({}) command to OCC{} ({} bytes)", 340 newMode, instance, cmd.size()) 341 .c_str()); 342 status = occCmd.send(cmd, rsp); 343 if (status == CmdStatus::SUCCESS) 344 { 345 if (rsp.size() == 5) 346 { 347 if (RspStatus::SUCCESS == RspStatus(rsp[2])) 348 { 349 log<level::DEBUG>( 350 "Status::sendModeChange: - Mode change completed successfully"); 351 } 352 else 353 { 354 log<level::ERR>( 355 fmt::format( 356 "Status::sendModeChange: SET MODE failed with status 0x{:02X}", 357 rsp[2]) 358 .c_str()); 359 } 360 } 361 else 362 { 363 log<level::ERR>( 364 "Status::sendModeChange: INVALID SET MODE response"); 365 dump_hex(rsp); 366 } 367 } 368 else 369 { 370 if (status == CmdStatus::OPEN_FAILURE) 371 { 372 log<level::WARNING>( 373 "Status::sendModeChange: OCC not active yet"); 374 } 375 else 376 { 377 log<level::ERR>("Status::sendModeChange: SET_MODE FAILED!"); 378 } 379 } 380 } 381 else 382 { 383 log<level::ERR>( 384 fmt::format( 385 "Status::sendModeChange: Unable to set power mode to {}", 386 newMode) 387 .c_str()); 388 } 389 390 return status; 391 } 392 393 // Send Idle Power Saver config data to the master OCC 394 CmdStatus Status::sendIpsData() 395 { 396 CmdStatus status = CmdStatus::FAILURE; 397 398 if (!device.master()) 399 { 400 log<level::ERR>( 401 fmt::format( 402 "Status::sendIpsData: SET_CFG_DATA[IPS] does not get sent to slave OCC{}", 403 instance) 404 .c_str()); 405 return status; 406 } 407 if (!isPowerVM()) 408 { 409 // Idle Power Saver data is only supported on PowerVM systems 410 log<level::DEBUG>( 411 "Status::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems"); 412 return CmdStatus::SUCCESS; 413 } 414 415 std::vector<std::uint8_t> cmd, rsp; 416 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA)); 417 cmd.push_back(0x00); // Data Length (2 bytes) 418 cmd.push_back(0x09); 419 // Data: 420 cmd.push_back(0x11); // Config Format: IPS Settings 421 cmd.push_back(0x00); // Version 422 cmd.push_back(0x00); // IPS Enable: disabled 423 cmd.push_back(0x00); // Enter Delay Time (240s) 424 cmd.push_back(0xF0); // 425 cmd.push_back(0x08); // Enter Utilization (8%) 426 cmd.push_back(0x00); // Exit Delay Time (10s) 427 cmd.push_back(0x0A); // 428 cmd.push_back(0x0C); // Exit Utilization (12%) 429 log<level::INFO>( 430 fmt::format( 431 "Status::sendIpsData: SET_CFG_DATA[IPS] command to OCC{} ({} bytes)", 432 instance, cmd.size()) 433 .c_str()); 434 status = occCmd.send(cmd, rsp); 435 if (status == CmdStatus::SUCCESS) 436 { 437 if (rsp.size() == 5) 438 { 439 if (RspStatus::SUCCESS == RspStatus(rsp[2])) 440 { 441 log<level::DEBUG>( 442 "Status::sendIpsData: - SET_CFG_DATA[IPS] completed successfully"); 443 } 444 else 445 { 446 log<level::ERR>( 447 fmt::format( 448 "Status::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}", 449 rsp[2]) 450 .c_str()); 451 } 452 } 453 else 454 { 455 log<level::ERR>( 456 "Status::sendIpsData: INVALID SET_CFG_DATA[IPS] response"); 457 dump_hex(rsp); 458 } 459 } 460 else 461 { 462 if (status == CmdStatus::OPEN_FAILURE) 463 { 464 log<level::WARNING>("Status::sendIpsData: OCC not active yet"); 465 } 466 else 467 { 468 log<level::ERR>("Status::sendIpsData: SET_CFG_DATA[IPS] FAILED!"); 469 } 470 } 471 472 return status; 473 } 474 475 #endif // POWER10 476 477 } // namespace occ 478 } // namespace open_power 479