1 /** 2 * Copyright © 2025 IBM 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 #pragma once 17 18 #include "chassis_status_monitor.hpp" 19 #include "power_interface.hpp" 20 #include "power_sequencer_device.hpp" 21 #include "services.hpp" 22 23 #include <stddef.h> // for size_t 24 25 #include <format> 26 #include <memory> 27 #include <optional> 28 #include <stdexcept> 29 #include <string> 30 #include <tuple> 31 #include <utility> 32 #include <vector> 33 34 namespace phosphor::power::sequencer 35 { 36 37 using ChassisStatusMonitorOptions = 38 phosphor::power::util::ChassisStatusMonitorOptions; 39 using PowerState = PowerInterface::PowerState; 40 using PowerGood = PowerInterface::PowerGood; 41 42 /** 43 * @class Chassis 44 * 45 * A chassis within the system. 46 * 47 * Chassis are typically a physical enclosure that contains system components 48 * such as CPUs, fans, power supplies, and PCIe cards. A chassis can be 49 * stand-alone, such as a tower or desktop. A chassis can also be designed to be 50 * mounted in an equipment rack. 51 */ 52 class Chassis 53 { 54 public: 55 Chassis() = delete; 56 Chassis(const Chassis&) = delete; 57 Chassis(Chassis&&) = delete; 58 Chassis& operator=(const Chassis&) = delete; 59 Chassis& operator=(Chassis&&) = delete; 60 ~Chassis() = default; 61 62 /** 63 * Constructor. 64 * 65 * @param number Chassis number within the system. Must be >= 1. 66 * @param inventoryPath D-Bus inventory path of the chassis 67 * @param powerSequencers Power sequencer devices within the chassis 68 * @param monitorOptions Types of chassis status monitoring to perform. 69 */ Chassis(size_t number,const std::string & inventoryPath,std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers,const ChassisStatusMonitorOptions & monitorOptions)70 explicit Chassis( 71 size_t number, const std::string& inventoryPath, 72 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers, 73 const ChassisStatusMonitorOptions& monitorOptions) : 74 number{number}, inventoryPath{inventoryPath}, 75 powerSequencers{std::move(powerSequencers)}, 76 monitorOptions{monitorOptions} 77 { 78 // Disable monitoring for D-Bus properties owned by this application 79 this->monitorOptions.isPowerStateMonitored = false; 80 this->monitorOptions.isPowerGoodMonitored = false; 81 } 82 83 /** 84 * Returns the chassis number within the system. 85 * 86 * @return chassis number 87 */ getNumber() const88 size_t getNumber() const 89 { 90 return number; 91 } 92 93 /** 94 * Returns the D-Bus inventory path of the chassis. 95 * 96 * @return inventory path 97 */ getInventoryPath() const98 const std::string& getInventoryPath() const 99 { 100 return inventoryPath; 101 } 102 103 /** 104 * Returns the power sequencer devices within the chassis. 105 * 106 * @return power sequencer devices 107 */ 108 const std::vector<std::unique_ptr<PowerSequencerDevice>>& getPowerSequencers() const109 getPowerSequencers() const 110 { 111 return powerSequencers; 112 } 113 114 /** 115 * Returns the types of chassis status monitoring to perform. 116 * 117 * @return chassis status monitoring options 118 */ getMonitorOptions() const119 const ChassisStatusMonitorOptions& getMonitorOptions() const 120 { 121 return monitorOptions; 122 } 123 124 /** 125 * Initializes chassis monitoring. 126 * 127 * Creates a ChassisStatusMonitor object based on the monitoring options 128 * specified in the constructor. 129 * 130 * This method must be called before any methods that return or check the 131 * chassis status. 132 * 133 * Normally this method is only called once. However, it can be called 134 * multiple times if required, such as for automated testing. 135 * 136 * @param services System services like hardware presence and the journal 137 */ initializeMonitoring(Services & services)138 void initializeMonitoring(Services& services) 139 { 140 // Note: replaces/deletes any previous monitor object 141 statusMonitor = services.createChassisStatusMonitor( 142 number, inventoryPath, monitorOptions); 143 } 144 145 /** 146 * Returns the ChassisStatusMonitor object that is monitoring D-Bus 147 * properties for the chassis. 148 * 149 * Throws an exception if chassis monitoring has not been initialized. 150 * 151 * @return reference to ChassisStatusMonitor object 152 */ getStatusMonitor()153 ChassisStatusMonitor& getStatusMonitor() 154 { 155 verifyMonitoringInitialized(); 156 return *statusMonitor; 157 } 158 159 /** 160 * Returns whether the chassis is present. 161 * 162 * Throws an exception if: 163 * - Chassis monitoring has not been initialized 164 * - D-Bus property value could not be obtained 165 * 166 * @return true is chassis is present, false otherwise 167 */ isPresent()168 bool isPresent() 169 { 170 verifyMonitoringInitialized(); 171 return statusMonitor->isPresent(); 172 } 173 174 /** 175 * Returns whether the chassis is available. 176 * 177 * If the D-Bus Available property is false, it means that communication to 178 * the chassis is not possible. For example, the chassis does not have any 179 * input power or communication cables to the BMC are disconnected. 180 * 181 * Throws an exception if: 182 * - Chassis monitoring has not been initialized 183 * - D-Bus property value could not be obtained 184 * 185 * @return true is chassis is available, false otherwise 186 */ isAvailable()187 bool isAvailable() 188 { 189 verifyMonitoringInitialized(); 190 return statusMonitor->isAvailable(); 191 } 192 193 /** 194 * Returns whether the chassis is enabled. 195 * 196 * If the D-Bus Enabled property is false, it means that the chassis has 197 * been put in hardware isolation (guarded). 198 * 199 * Throws an exception if: 200 * - Chassis monitoring has not been initialized 201 * - D-Bus property value could not be obtained 202 * 203 * @return true is chassis is enabled, false otherwise 204 */ isEnabled()205 bool isEnabled() 206 { 207 verifyMonitoringInitialized(); 208 return statusMonitor->isEnabled(); 209 } 210 211 /** 212 * Returns whether the chassis input power status is good. 213 * 214 * Throws an exception if: 215 * - Chassis monitoring has not been initialized 216 * - D-Bus property value could not be obtained 217 * 218 * @return true if chassis input power is good, false otherwise 219 */ isInputPowerGood()220 bool isInputPowerGood() 221 { 222 verifyMonitoringInitialized(); 223 return statusMonitor->isInputPowerGood(); 224 } 225 226 /** 227 * Returns whether the power supplies power status is good. 228 * 229 * Throws an exception if: 230 * - Chassis monitoring has not been initialized 231 * - D-Bus property value could not be obtained 232 * 233 * @return true if power supplies power is good, false otherwise 234 */ isPowerSuppliesPowerGood()235 bool isPowerSuppliesPowerGood() 236 { 237 verifyMonitoringInitialized(); 238 return statusMonitor->isPowerSuppliesPowerGood(); 239 } 240 241 /** 242 * Returns the last requested chassis power state. 243 * 244 * The initial power state is obtained by the monitor() method. That method 245 * must be called before calling getPowerState(). 246 * 247 * Throws an exception if the power state could not be obtained. 248 * 249 * @return last requested power state 250 */ getPowerState()251 PowerState getPowerState() 252 { 253 if (!powerState) 254 { 255 throw std::runtime_error{std::format( 256 "Power state could not be obtained for chassis {}", number)}; 257 } 258 return *powerState; 259 } 260 261 /** 262 * Returns whether the chassis can be set to the specified power state. 263 * 264 * Determined based on the current chassis status. For example, the chassis 265 * cannot be powered on if it is not present. 266 * 267 * Throws an exception if chassis monitoring has not been initialized. 268 * 269 * @param newPowerState New chassis power state 270 * @return If the state can be set, returns true and an empty string. If the 271 * state cannot be set, returns false and a string containing the 272 * reason. 273 */ 274 std::tuple<bool, std::string> canSetPowerState(PowerState newPowerState); 275 276 /** 277 * Sets the requested chassis power state. 278 * 279 * Powers the chassis on or off based on the specified state. 280 * 281 * Throws an exception if one of the following occurs: 282 * - Chassis monitoring has not been initialized. 283 * - Chassis D-Bus status cannot be obtained. 284 * - State change is not possible based on the chassis status. 285 * - Error occurs powering on the power sequencer devices. 286 * 287 * @param newPowerState New chassis power state 288 * @param services System services like hardware presence and the journal 289 */ 290 void setPowerState(PowerState newPowerState, Services& services); 291 292 /** 293 * Returns the chassis power good value. 294 * 295 * The power good value is read by the monitor() method. That method must be 296 * called before calling getPowerGood(). 297 * 298 * Throws an exception if the power good value could not be obtained. 299 * 300 * @return chassis power good 301 */ getPowerGood()302 PowerGood getPowerGood() 303 { 304 if (!powerGood) 305 { 306 throw std::runtime_error{std::format( 307 "Power good could not be obtained for chassis {}", number)}; 308 } 309 return *powerGood; 310 } 311 312 /** 313 * Monitors the status of the chassis. 314 * 315 * Sets the chassis power good value by reading the power good value from 316 * each power sequencer device. 317 * 318 * Reacts to any changes to chassis D-Bus properties. 319 * 320 * This method must be called periodically (such as once per second) to 321 * ensure the chassis power state and power good values are correct. 322 * 323 * Throws an exception if one of the following occurs: 324 * - Chassis monitoring has not been initialized. 325 * - Chassis D-Bus status cannot be obtained. 326 * 327 * @param services System services like hardware presence and the journal 328 */ 329 void monitor(Services& services); 330 331 /** 332 * Closes all power sequencer devices that are open. 333 * 334 * Does not throw exceptions. This method may be called because a chassis is 335 * no longer present or no longer has input power. In those scenarios 336 * closing the device may fail. However, closing the devices is still 337 * necessary in order to clean up resources like file handles. 338 */ 339 void closeDevices(); 340 341 private: 342 /** 343 * Verifies that chassis monitoring has been initialized and a 344 * ChassisStatusMonitor object has been created. 345 * 346 * Throws an exception if monitoring has not been initialized. 347 */ verifyMonitoringInitialized()348 void verifyMonitoringInitialized() 349 { 350 if (!statusMonitor) 351 { 352 throw std::runtime_error{std::format( 353 "Monitoring not initialized for chassis {}", number)}; 354 } 355 } 356 357 /** 358 * Opens the specified power sequencer device if it is not already open. 359 * 360 * Throws an exception if an error occurs. 361 * 362 * @param device power sequencer device 363 * @param services System services like hardware presence and the journal 364 */ openDeviceIfNeeded(PowerSequencerDevice & device,Services & services)365 void openDeviceIfNeeded(PowerSequencerDevice& device, Services& services) 366 { 367 if (!device.isOpen()) 368 { 369 device.open(services); 370 } 371 } 372 373 /** 374 * Reads the power good value from all power sequencer devices. 375 * 376 * Determines the combined power good value for the entire chassis. 377 * 378 * @param services System services like hardware presence and the journal 379 */ 380 void readPowerGood(Services& services); 381 382 /** 383 * Sets the initial power state value if it currently has no value. 384 * 385 * This is necessary when the application first starts or when a previously 386 * unavailable chassis becomes available. 387 * 388 * The initial power state value is based on the current power good value. 389 * We assume that the last requested power state matches the power good 390 * value. For example, if the chassis power good is on, then we assume the 391 * last requested chassis power state was on. 392 * 393 * The power state value will be set explicitly next time the chassis is 394 * powered on or off by setPowerState(). 395 */ 396 void setInitialPowerStateIfNeeded(); 397 398 /** 399 * Powers on all the power sequencer devices in the chassis. 400 * 401 * Throws an exception if an error occurs. 402 * 403 * @param services System services like hardware presence and the journal 404 */ 405 void powerOn(Services& services); 406 407 /** 408 * Powers off all the power sequencer devices in the chassis. 409 * 410 * Throws an exception if an error occurs. 411 * 412 * @param services System services like hardware presence and the journal 413 */ 414 void powerOff(Services& services); 415 416 /** 417 * Chassis number within the system. 418 * 419 * Chassis numbers start at 1 because chassis 0 represents the entire 420 * system. 421 */ 422 size_t number; 423 424 /** 425 * D-Bus inventory path of the chassis. 426 */ 427 std::string inventoryPath{}; 428 429 /** 430 * Power sequencer devices within the chassis. 431 */ 432 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers{}; 433 434 /** 435 * Types of chassis status monitoring to perform. 436 */ 437 ChassisStatusMonitorOptions monitorOptions{}; 438 439 /** 440 * Monitors the chassis status using D-Bus properties. 441 */ 442 std::unique_ptr<ChassisStatusMonitor> statusMonitor{}; 443 444 /** 445 * Last requested chassis power state. 446 */ 447 std::optional<PowerState> powerState{}; 448 449 /** 450 * Chassis power good. 451 */ 452 std::optional<PowerGood> powerGood{}; 453 }; 454 455 } // namespace phosphor::power::sequencer 456