Lei YU | d19df25 | 2019-10-25 17:31:52 +0800 | [diff] [blame] | 1 | /** |
| 2 | * Copyright © 2019 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 | |
Lei YU | 7c2fbbb | 2019-11-06 14:56:02 +0800 | [diff] [blame] | 18 | #include "i2c_interface.hpp" |
| 19 | |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 20 | #include <sdbusplus/bus.hpp> |
Brandon Wyman | d1bc4ce | 2019-12-13 14:20:34 -0600 | [diff] [blame] | 21 | |
| 22 | #include <filesystem> |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 23 | #include <memory> |
Lei YU | d19df25 | 2019-10-25 17:31:52 +0800 | [diff] [blame] | 24 | #include <string> |
| 25 | |
Lei YU | 7c2fbbb | 2019-11-06 14:56:02 +0800 | [diff] [blame] | 26 | class TestUpdater; |
| 27 | |
Lei YU | d19df25 | 2019-10-25 17:31:52 +0800 | [diff] [blame] | 28 | namespace updater |
| 29 | { |
| 30 | |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 31 | namespace fs = std::filesystem; |
| 32 | |
Faisal Awada | 57fb664 | 2025-02-21 16:52:54 -0600 | [diff] [blame] | 33 | constexpr auto FW_UPDATE_FAILED_MSG = |
| 34 | "xyz.openbmc_project.Power.PowerSupply.Error.FirmwareUpdateFailed"; |
| 35 | constexpr auto PSU_FW_FILE_ISSUE_MSG = |
| 36 | "xyz.openbmc_project.Power.PowerSupply.Error.FirmwareIssue"; |
| 37 | constexpr auto FW_UPDATE_SUCCESS_MSG = |
| 38 | "xyz.openbmc_project.Power.PowerSupply.Info.FirmwareUpdateSuccessful"; |
| 39 | |
| 40 | constexpr auto ERROR_SEVERITY = "xyz.openbmc_project.Logging.Entry.Level.Error"; |
| 41 | constexpr auto INFORMATIONAL_SEVERITY = |
| 42 | "xyz.openbmc_project.Logging.Entry.Level.Informational"; |
| 43 | |
Lei YU | d19df25 | 2019-10-25 17:31:52 +0800 | [diff] [blame] | 44 | /** |
| 45 | * Update PSU firmware |
| 46 | * |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 47 | * @param[in] bus - The sdbusplus DBus bus connection |
Lei YU | d19df25 | 2019-10-25 17:31:52 +0800 | [diff] [blame] | 48 | * @param[in] psuInventoryPath - The inventory path of the PSU |
| 49 | * @param[in] imageDir - The directory containing the PSU image |
| 50 | * |
| 51 | * @return true if successful, otherwise false |
| 52 | */ |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 53 | bool update(sdbusplus::bus_t& bus, const std::string& psuInventoryPath, |
| 54 | const std::string& imageDir); |
Lei YU | d19df25 | 2019-10-25 17:31:52 +0800 | [diff] [blame] | 55 | |
Faisal Awada | fe5b5c6 | 2025-03-22 10:50:01 -0500 | [diff] [blame] | 56 | /** |
| 57 | * Validate number of present PSUs vs number of required PSUs for this system, |
| 58 | * and validate all PSUs have same model before proceeding to Update PSU |
| 59 | * firmware |
| 60 | * |
| 61 | * @param[in] bus - The sdbusplus DBus bus connection |
| 62 | * @param[in] psuInventoryPath - The inventory path of the PSU |
| 63 | * @param[in] imageDir - The directory containing the PSU image |
| 64 | * |
| 65 | * @return true if successful, otherwise false |
| 66 | */ |
| 67 | bool validateAndUpdate(sdbusplus::bus_t& bus, |
| 68 | const std::string& psuInventoryPath, |
| 69 | const std::string& imageDir); |
| 70 | |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 71 | class Updater |
| 72 | { |
| 73 | public: |
Lei YU | 7c2fbbb | 2019-11-06 14:56:02 +0800 | [diff] [blame] | 74 | friend TestUpdater; |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 75 | Updater() = delete; |
| 76 | Updater(const Updater&) = delete; |
| 77 | Updater& operator=(const Updater&) = delete; |
| 78 | Updater(Updater&&) = default; |
| 79 | Updater& operator=(Updater&&) = default; |
| 80 | |
| 81 | /** |
| 82 | * @brief Constructor |
| 83 | * |
| 84 | * @param psuInventoryPath - The PSU inventory path |
| 85 | * @param devPath - The PSU device path |
| 86 | * @param imageDir - The update image directory |
| 87 | */ |
| 88 | Updater(const std::string& psuInventoryPath, const std::string& devPath, |
| 89 | const std::string& imageDir); |
| 90 | |
| 91 | /** @brief Destructor */ |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 92 | virtual ~Updater() = default; |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 93 | |
| 94 | /** @brief Bind or unbind the driver |
| 95 | * |
| 96 | * @param doBind - indicate if it's going to bind or unbind the driver |
| 97 | */ |
| 98 | void bindUnbind(bool doBind); |
| 99 | |
| 100 | /** @brief Set the PSU inventory present property |
| 101 | * |
| 102 | * @param present - The present state to set |
| 103 | */ |
| 104 | void setPresent(bool present); |
| 105 | |
Lei YU | 575ed13 | 2019-10-29 17:22:16 +0800 | [diff] [blame] | 106 | /** @brief Check if it's ready to update the PSU |
| 107 | * |
| 108 | * @return true if it's ready, otherwise false |
| 109 | */ |
| 110 | bool isReadyToUpdate(); |
| 111 | |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 112 | /** @brief Do the PSU update |
| 113 | * |
| 114 | * @return 0 if success, otherwise non-zero |
| 115 | */ |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 116 | virtual int doUpdate(); |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 117 | |
Lei YU | 7c2fbbb | 2019-11-06 14:56:02 +0800 | [diff] [blame] | 118 | /** @brief Create I2C device |
| 119 | * |
| 120 | * Creates the I2C device based on the device name. |
| 121 | * e.g. It opens busId 3, address 0x68 for "3-0068" |
| 122 | */ |
| 123 | void createI2CDevice(); |
| 124 | |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 125 | protected: |
| 126 | /** @brief Accessor for PSU inventory path */ |
| 127 | const std::string& getPsuInventoryPath() const |
| 128 | { |
| 129 | return psuInventoryPath; |
| 130 | } |
| 131 | |
| 132 | /** @brief Accessor for device path */ |
| 133 | const std::string& getDevPath() const |
| 134 | { |
| 135 | return devPath; |
| 136 | } |
| 137 | |
| 138 | /** @brief Accessor for device name */ |
| 139 | const std::string& getDevName() const |
| 140 | { |
| 141 | return devName; |
| 142 | } |
| 143 | |
| 144 | /** @brief Accessor for image directory */ |
| 145 | const std::string& getImageDir() const |
| 146 | { |
| 147 | return imageDir; |
| 148 | } |
| 149 | |
| 150 | /** @brief I2C interface accessor */ |
| 151 | i2c::I2CInterface* getI2C() |
| 152 | { |
| 153 | return i2c.get(); |
| 154 | } |
| 155 | |
Faisal Awada | 57fb664 | 2025-02-21 16:52:54 -0600 | [diff] [blame] | 156 | /** |
| 157 | * @brief Creates a serviceable Predictive Event Log, |
| 158 | * |
| 159 | * This method generates an event log with the given error name, severity, |
| 160 | * and additional data. It interacts with the OpenBMC logging service to |
| 161 | * record faults. |
| 162 | * |
| 163 | * @param[in] errorName The name of the error to log. |
| 164 | * @param[in] severity The severity level of the error. |
| 165 | * @param[in] additionalData Additional key-value pairs containing details |
| 166 | * about the error. |
| 167 | */ |
| 168 | void createServiceableEventLog( |
| 169 | const std::string& errorName, const std::string& severity, |
| 170 | std::map<std::string, std::string>& additionalData); |
| 171 | |
| 172 | /** |
| 173 | * @brief Retrieves additional data related to I2C communication. |
| 174 | * |
| 175 | * This method collects and returns I2C bus information, including the |
| 176 | * bus ID, address, and error number, which are used for reporting |
| 177 | * Predictive Error Log. |
| 178 | * |
| 179 | * @return A map containing I2C-related key-value pairs. |
| 180 | */ |
| 181 | std::map<std::string, std::string> getI2CAdditionalData(); |
| 182 | |
| 183 | /** |
| 184 | * @brief Call out an I2C-related Predictive Error Log. |
| 185 | * |
| 186 | * This method creates a serviceable event log related to I2C failures. |
| 187 | * It collects additional data about the I2C communication and logs the |
| 188 | * failure with appropriate severity. |
| 189 | * |
| 190 | * @param[in] extraAdditionalData Additional key-value pairs specific to |
| 191 | * the error context. |
| 192 | * @param[in] exceptionString A string describing the exception that |
| 193 | * triggered the error. |
| 194 | * @param[in] errorCode Exception error code. |
| 195 | */ |
| 196 | void callOutI2CEventLog( |
| 197 | std::map<std::string, std::string> extraAdditionalData, |
| 198 | const std::string& exceptionString = "", const int errorCode = 0); |
| 199 | |
| 200 | /** |
| 201 | * @brief Call out a PSU-related Predictive Error Log. |
| 202 | * |
| 203 | * This method logs a failure related to PSU firmware updates and additional |
| 204 | * diagnostics data to the event log. |
| 205 | * |
| 206 | * @param[in] extraAdditionalData Additional key-value pairs specific to |
| 207 | * the PSU-related error. |
| 208 | */ |
| 209 | void callOutPsuEventLog( |
| 210 | std::map<std::string, std::string> extraAdditionalData); |
| 211 | |
| 212 | /** |
| 213 | * @brief Call out a software-related Predictive Error Log. |
| 214 | * |
| 215 | * This method logs a failure related to PSU firmware file issues or other |
| 216 | * software-related errors. It merges any additional error-specific data |
| 217 | * before logging the event. |
| 218 | * |
| 219 | * @param[in] extraAdditionalData Additional key-value pairs specific to |
| 220 | * the software-related error. |
| 221 | */ |
| 222 | void callOutSWEventLog( |
| 223 | std::map<std::string, std::string> extraAdditionalData); |
| 224 | |
| 225 | /** |
| 226 | * @brief Accessor to set logEventLog to true |
| 227 | * |
| 228 | */ |
| 229 | void enableEventLogging() |
| 230 | { |
| 231 | eventLogState = true; |
| 232 | } |
| 233 | |
| 234 | /** |
| 235 | * @brief Accessor to set eventLogState to false |
| 236 | * |
| 237 | */ |
| 238 | void disableEventLogging() |
| 239 | { |
| 240 | eventLogState = false; |
| 241 | } |
| 242 | |
| 243 | /** |
| 244 | * @brief Accessor eventLogState status (enable true, disable false) |
| 245 | * |
| 246 | * @return true or false |
| 247 | */ |
| 248 | bool isEventLogEnabled() |
| 249 | { |
| 250 | return eventLogState; |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * @brief Accessor to disable eventLoggedThisSession |
| 255 | * |
| 256 | */ |
| 257 | void enableEventLoggedThisSession() |
| 258 | { |
| 259 | eventLoggedThisSession = true; |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * @brief Accessor to retieve eventLoggedThisSession status |
| 264 | * |
| 265 | * @return true or false |
| 266 | */ |
| 267 | bool isEventLoggedThisSession() |
| 268 | { |
| 269 | return eventLoggedThisSession; |
| 270 | } |
| 271 | |
| 272 | /** |
| 273 | * @brief Call out successful PSU firmware update. |
| 274 | * |
| 275 | */ |
| 276 | void callOutGoodEventLog(); |
| 277 | |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 278 | private: |
| 279 | /** @brief The sdbusplus DBus bus connection */ |
Patrick Williams | 7354ce6 | 2022-07-22 19:26:56 -0500 | [diff] [blame] | 280 | sdbusplus::bus_t bus; |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 281 | |
| 282 | /** @brief The PSU inventory path */ |
| 283 | std::string psuInventoryPath; |
| 284 | |
| 285 | /** @brief The PSU device path |
| 286 | * |
| 287 | * Usually it is a device in i2c subsystem, e.g. |
| 288 | * /sys/bus/i2c/devices/3-0068 |
| 289 | */ |
| 290 | std::string devPath; |
| 291 | |
| 292 | /** @brief The PSU device name |
| 293 | * |
| 294 | * Usually it is a i2c device name, e.g. |
| 295 | * 3-0068 |
| 296 | */ |
| 297 | std::string devName; |
| 298 | |
| 299 | /** @brief The PSU image directory */ |
| 300 | std::string imageDir; |
| 301 | |
| 302 | /** @brief The PSU device driver's path |
| 303 | * |
| 304 | * Usually it is the PSU driver, e.g. |
| 305 | * /sys/bus/i2c/drivers/ibm-cffps |
| 306 | */ |
| 307 | fs::path driverPath; |
Lei YU | 7c2fbbb | 2019-11-06 14:56:02 +0800 | [diff] [blame] | 308 | |
| 309 | /** @brief The i2c device interface */ |
| 310 | std::unique_ptr<i2c::I2CInterface> i2c; |
Faisal Awada | 57fb664 | 2025-02-21 16:52:54 -0600 | [diff] [blame] | 311 | |
| 312 | /** @brief Event Log flag */ |
| 313 | bool eventLogState = false; |
| 314 | |
| 315 | /** @brief Event logged this session flag, this is to make sure no other |
| 316 | * event log can be logged |
| 317 | */ |
| 318 | bool eventLoggedThisSession = false; |
Lei YU | 9ab6d75 | 2019-10-28 17:03:20 +0800 | [diff] [blame] | 319 | }; |
| 320 | |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 321 | namespace internal |
| 322 | { |
| 323 | |
| 324 | /** |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 325 | * @brief Factory function to create an Updater instance based on PSU model |
| 326 | * number |
| 327 | * |
| 328 | * @param[in] model - PSU model number |
| 329 | * @param[in] psuInventoryPath - PSU inventory path |
| 330 | * @param[in] devPath - Device path |
| 331 | * @param[in] imageDir - Image directory |
| 332 | * |
| 333 | * return pointer class based on the device PSU model. |
| 334 | */ |
| 335 | std::unique_ptr<updater::Updater> getClassInstance( |
| 336 | const std::string& model, const std::string& psuInventoryPath, |
| 337 | const std::string& devPath, const std::string& imageDir); |
| 338 | |
| 339 | /** |
| 340 | * @brief Retrieve the firmware filename path in the specified directory |
| 341 | * |
| 342 | * @param[in] directory - Path to FS directory |
| 343 | * |
| 344 | * @retun filename or null |
| 345 | */ |
| 346 | const std::string getFWFilenamePath(const std::string& directory); |
| 347 | |
| 348 | /** |
| 349 | * @brief Calculate CRC-8 for a data vector |
| 350 | * |
| 351 | * @param[in] data - Firmware data block |
| 352 | * |
| 353 | * @return CRC8 |
| 354 | */ |
| 355 | uint8_t calculateCRC8(const std::vector<uint8_t>& data); |
| 356 | |
| 357 | /** |
| 358 | * @brief Delay execution in milliseconds |
| 359 | * |
| 360 | * @param[in] milliseconds - Time in milliseconds |
| 361 | */ |
| 362 | void delay(const int& milliseconds); |
| 363 | |
| 364 | /** |
| 365 | * @brief Convert a big-endian value to little-endian |
| 366 | * |
| 367 | * @param[in] bigEndianValue - Uint 32 bit value |
| 368 | * |
| 369 | * @return vector of little endians. |
| 370 | */ |
| 371 | std::vector<uint8_t> bigEndianToLittleEndian(const uint32_t bigEndianValue); |
| 372 | |
| 373 | /** |
| 374 | * @brief Validate the existence and size of a firmware file. |
| 375 | * |
| 376 | * @param[in] fileName - Firmware file name |
| 377 | * |
| 378 | * @return true for success or false |
| 379 | */ |
| 380 | bool validateFWFile(const std::string& fileName); |
| 381 | |
| 382 | /** |
| 383 | * @brief Open a firmware file for reading in binary mode. |
| 384 | * |
| 385 | * @param[in] fileName - Firmware file name |
| 386 | * |
| 387 | * @return pointer to firmware file stream |
| 388 | */ |
| 389 | std::unique_ptr<std::ifstream> openFirmwareFile(const std::string& fileName); |
| 390 | |
| 391 | /** |
| 392 | * @brief Read firmware bytes from file. |
| 393 | * |
| 394 | * @param[in] inputFile - Input file stream |
| 395 | * @param[in] numberOfBytesToRead - Number of bytes to read from firmware file. |
| 396 | * |
| 397 | * @return vector of data read |
| 398 | */ |
| 399 | std::vector<uint8_t> readFirmwareBytes(std::ifstream& inputFile, |
| 400 | const size_t numberOfBytesToRead); |
| 401 | |
Faisal Awada | ec61bbd | 2024-11-04 08:46:20 -0600 | [diff] [blame] | 402 | } // namespace internal |
Lei YU | d19df25 | 2019-10-25 17:31:52 +0800 | [diff] [blame] | 403 | } // namespace updater |