| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 1 | #include "spi_device.hpp" | 
 | 2 |  | 
 | 3 | #include "common/include/NotifyWatch.hpp" | 
 | 4 | #include "common/include/device.hpp" | 
 | 5 | #include "common/include/host_power.hpp" | 
 | 6 | #include "common/include/software_manager.hpp" | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 7 | #include "common/include/utils.hpp" | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 8 |  | 
 | 9 | #include <gpiod.hpp> | 
 | 10 | #include <phosphor-logging/lg2.hpp> | 
 | 11 | #include <sdbusplus/async.hpp> | 
 | 12 | #include <sdbusplus/async/context.hpp> | 
 | 13 | #include <xyz/openbmc_project/Association/Definitions/server.hpp> | 
 | 14 | #include <xyz/openbmc_project/ObjectMapper/client.hpp> | 
 | 15 | #include <xyz/openbmc_project/State/Host/client.hpp> | 
 | 16 |  | 
 | 17 | #include <cstddef> | 
 | 18 | #include <fstream> | 
 | 19 | #include <random> | 
 | 20 |  | 
 | 21 | PHOSPHOR_LOG2_USING; | 
 | 22 |  | 
 | 23 | using namespace std::literals; | 
 | 24 | using namespace phosphor::software; | 
 | 25 | using namespace phosphor::software::manager; | 
 | 26 | using namespace phosphor::software::host_power; | 
 | 27 |  | 
| Alexander Hansen | dfb6067 | 2025-05-07 14:22:45 +0200 | [diff] [blame] | 28 | static std::optional<std::string> getSPIDevAddr(uint64_t spiControllerIndex) | 
 | 29 | { | 
 | 30 |     const fs::path spi_path = | 
 | 31 |         "/sys/class/spi_master/spi" + std::to_string(spiControllerIndex); | 
 | 32 |  | 
 | 33 |     if (!fs::exists(spi_path)) | 
 | 34 |     { | 
 | 35 |         error("SPI controller index not found at {PATH}", "PATH", | 
 | 36 |               spi_path.string()); | 
 | 37 |         return std::nullopt; | 
 | 38 |     } | 
 | 39 |  | 
 | 40 |     fs::path target = fs::read_symlink(spi_path); | 
 | 41 |  | 
 | 42 |     // The path looks like | 
 | 43 |     // ../../devices/platform/ahb/1e630000.spi/spi_master/spi1 We want to | 
 | 44 |     // extract e.g. '1e630000.spi' | 
 | 45 |  | 
 | 46 |     for (const auto& part : target) | 
 | 47 |     { | 
 | 48 |         std::string part_str = part.string(); | 
 | 49 |         if (part_str.find(".spi") != std::string::npos) | 
 | 50 |         { | 
 | 51 |             debug("Found SPI Address {ADDR}", "ADDR", part_str); | 
 | 52 |             return part_str; | 
 | 53 |         } | 
 | 54 |     } | 
 | 55 |  | 
 | 56 |     return std::nullopt; | 
 | 57 | } | 
 | 58 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 59 | SPIDevice::SPIDevice(sdbusplus::async::context& ctx, | 
 | 60 |                      uint64_t spiControllerIndex, uint64_t spiDeviceIndex, | 
 | 61 |                      bool dryRun, const std::vector<std::string>& gpioLinesIn, | 
 | 62 |                      const std::vector<uint64_t>& gpioValuesIn, | 
 | 63 |                      SoftwareConfig& config, SoftwareManager* parent, | 
 | 64 |                      enum FlashLayout layout, enum FlashTool tool, | 
 | 65 |                      const std::string& versionDirPath) : | 
 | 66 |     Device(ctx, config, parent, | 
 | 67 |            {RequestedApplyTimes::Immediate, RequestedApplyTimes::OnReset}), | 
 | 68 |     NotifyWatchIntf(ctx, versionDirPath), dryRun(dryRun), | 
 | 69 |     gpioLines(gpioLinesIn), | 
 | 70 |     gpioValues(gpioValuesIn.begin(), gpioValuesIn.end()), | 
 | 71 |     spiControllerIndex(spiControllerIndex), spiDeviceIndex(spiDeviceIndex), | 
 | 72 |     layout(layout), tool(tool) | 
 | 73 | { | 
| Alexander Hansen | dfb6067 | 2025-05-07 14:22:45 +0200 | [diff] [blame] | 74 |     auto optAddr = getSPIDevAddr(spiControllerIndex); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 75 |  | 
| Alexander Hansen | dfb6067 | 2025-05-07 14:22:45 +0200 | [diff] [blame] | 76 |     if (!optAddr.has_value()) | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 77 |     { | 
| Alexander Hansen | dfb6067 | 2025-05-07 14:22:45 +0200 | [diff] [blame] | 78 |         throw std::invalid_argument("SPI controller index not found"); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 79 |     } | 
 | 80 |  | 
| Alexander Hansen | dfb6067 | 2025-05-07 14:22:45 +0200 | [diff] [blame] | 81 |     spiDev = optAddr.value(); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 82 |  | 
 | 83 |     ctx.spawn(readNotifyAsync()); | 
 | 84 |  | 
 | 85 |     debug( | 
 | 86 |         "SPI Device {NAME} at {CONTROLLERINDEX}:{DEVICEINDEX} initialized successfully", | 
 | 87 |         "NAME", config.configName, "CONTROLLERINDEX", spiControllerIndex, | 
 | 88 |         "DEVICEINDEX", spiDeviceIndex); | 
 | 89 | } | 
 | 90 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 91 | sdbusplus::async::task<bool> SPIDevice::updateDevice(const uint8_t* image, | 
 | 92 |                                                      size_t image_size) | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 93 | { | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 94 |     // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch) | 
 | 95 |     auto prevPowerstate = co_await HostPower::getState(ctx); | 
 | 96 |  | 
 | 97 |     if (prevPowerstate != stateOn && prevPowerstate != stateOff) | 
 | 98 |     { | 
 | 99 |         co_return false; | 
 | 100 |     } | 
 | 101 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 102 |     bool success = co_await HostPower::setState(ctx, stateOff); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 103 |     if (!success) | 
 | 104 |     { | 
 | 105 |         error("error changing host power state"); | 
 | 106 |         co_return false; | 
 | 107 |     } | 
 | 108 |     setUpdateProgress(10); | 
 | 109 |  | 
 | 110 |     success = co_await writeSPIFlash(image, image_size); | 
 | 111 |  | 
 | 112 |     if (success) | 
 | 113 |     { | 
 | 114 |         setUpdateProgress(100); | 
 | 115 |     } | 
 | 116 |  | 
 | 117 |     // restore the previous powerstate | 
 | 118 |     const bool powerstate_restore = | 
 | 119 |         co_await HostPower::setState(ctx, prevPowerstate); | 
 | 120 |     if (!powerstate_restore) | 
 | 121 |     { | 
 | 122 |         error("error changing host power state"); | 
 | 123 |         co_return false; | 
 | 124 |     } | 
 | 125 |  | 
 | 126 |     // return value here is only describing if we successfully wrote to the | 
 | 127 |     // SPI flash. Restoring powerstate can still fail. | 
 | 128 |     co_return success; | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 129 | } | 
 | 130 |  | 
 | 131 | const std::string spiAspeedSMCPath = "/sys/bus/platform/drivers/spi-aspeed-smc"; | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 132 | const std::string spiNorPath = "/sys/bus/spi/drivers/spi-nor"; | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 133 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 134 | sdbusplus::async::task<bool> SPIDevice::bindSPIFlash() | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 135 | { | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 136 |     if (!SPIDevice::isSPIControllerBound()) | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 137 |     { | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 138 |         debug("binding flash to SMC"); | 
 | 139 |         std::ofstream ofbind(spiAspeedSMCPath + "/bind", std::ofstream::out); | 
 | 140 |         ofbind << spiDev; | 
 | 141 |         ofbind.close(); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 142 |     } | 
 | 143 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 144 |     const int driverBindSleepDuration = 2; | 
 | 145 |  | 
 | 146 |     co_await sdbusplus::async::sleep_for( | 
 | 147 |         ctx, std::chrono::seconds(driverBindSleepDuration)); | 
 | 148 |  | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 149 |     if (!isSPIControllerBound()) | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 150 |     { | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 151 |         error("failed to bind spi controller"); | 
 | 152 |         co_return false; | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 153 |     } | 
 | 154 |  | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 155 |     const std::string name = | 
 | 156 |         std::format("spi{}.{}", spiControllerIndex, spiDeviceIndex); | 
 | 157 |  | 
 | 158 |     std::ofstream ofbindSPINor(spiNorPath + "/bind", std::ofstream::out); | 
 | 159 |     ofbindSPINor << name; | 
 | 160 |     ofbindSPINor.close(); | 
 | 161 |  | 
 | 162 |     co_await sdbusplus::async::sleep_for( | 
 | 163 |         ctx, std::chrono::seconds(driverBindSleepDuration)); | 
 | 164 |  | 
 | 165 |     if (!isSPIFlashBound()) | 
 | 166 |     { | 
 | 167 |         error("failed to bind spi flash (spi-nor driver)"); | 
 | 168 |         co_return false; | 
 | 169 |     } | 
 | 170 |  | 
 | 171 |     co_return true; | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 172 | } | 
 | 173 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 174 | sdbusplus::async::task<bool> SPIDevice::unbindSPIFlash() | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 175 | { | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 176 |     debug("unbinding flash"); | 
 | 177 |  | 
 | 178 |     const std::string name = | 
 | 179 |         std::format("spi{}.{}", spiControllerIndex, spiDeviceIndex); | 
 | 180 |  | 
 | 181 |     std::ofstream ofunbind(spiNorPath + "/unbind", std::ofstream::out); | 
 | 182 |     ofunbind << name; | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 183 |     ofunbind.close(); | 
 | 184 |  | 
 | 185 |     // wait for kernel | 
 | 186 |     co_await sdbusplus::async::sleep_for(ctx, std::chrono::seconds(2)); | 
 | 187 |  | 
 | 188 |     co_return !isSPIFlashBound(); | 
 | 189 | } | 
 | 190 |  | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 191 | bool SPIDevice::isSPIControllerBound() | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 192 | { | 
 | 193 |     std::string path = spiAspeedSMCPath + "/" + spiDev; | 
 | 194 |  | 
 | 195 |     return std::filesystem::exists(path); | 
 | 196 | } | 
 | 197 |  | 
| Alexander Hansen | ac4fdd0 | 2025-05-20 12:54:50 +0200 | [diff] [blame] | 198 | bool SPIDevice::isSPIFlashBound() | 
 | 199 | { | 
 | 200 |     const std::string name = | 
 | 201 |         std::format("spi{}.{}", spiControllerIndex, spiDeviceIndex); | 
 | 202 |     std::string path = spiNorPath + "/" + name; | 
 | 203 |  | 
 | 204 |     return std::filesystem::exists(path); | 
 | 205 | } | 
 | 206 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 207 | static std::unique_ptr<::gpiod::line_bulk> requestMuxGPIOs( | 
 | 208 |     const std::vector<std::string>& gpioLines, | 
 | 209 |     const std::vector<int>& gpioValues, bool inverted) | 
 | 210 | { | 
 | 211 |     std::vector<::gpiod::line> lines; | 
 | 212 |  | 
 | 213 |     for (const std::string& lineName : gpioLines) | 
 | 214 |     { | 
 | 215 |         const ::gpiod::line line = ::gpiod::find_line(lineName); | 
 | 216 |  | 
 | 217 |         if (line.is_used()) | 
 | 218 |         { | 
 | 219 |             error("gpio line {LINE} was still used", "LINE", lineName); | 
 | 220 |             return nullptr; | 
 | 221 |         } | 
 | 222 |  | 
 | 223 |         lines.push_back(line); | 
 | 224 |     } | 
 | 225 |  | 
 | 226 |     ::gpiod::line_request config{"", ::gpiod::line_request::DIRECTION_OUTPUT, | 
 | 227 |                                  0}; | 
 | 228 |  | 
| Alexander Hansen | db0e88c | 2025-05-28 13:48:40 +0200 | [diff] [blame] | 229 |     debug("requesting gpios for mux"); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 230 |  | 
 | 231 |     auto lineBulk = std::make_unique<::gpiod::line_bulk>(lines); | 
 | 232 |  | 
 | 233 |     if (inverted) | 
 | 234 |     { | 
 | 235 |         std::vector<int> valuesInverted; | 
 | 236 |         valuesInverted.reserve(gpioValues.size()); | 
 | 237 |  | 
 | 238 |         for (int value : gpioValues) | 
 | 239 |         { | 
 | 240 |             valuesInverted.push_back(value ? 0 : 1); | 
 | 241 |         } | 
 | 242 |  | 
 | 243 |         lineBulk->request(config, valuesInverted); | 
 | 244 |     } | 
 | 245 |     else | 
 | 246 |     { | 
 | 247 |         lineBulk->request(config, gpioValues); | 
 | 248 |     } | 
 | 249 |  | 
 | 250 |     return lineBulk; | 
 | 251 | } | 
 | 252 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 253 | sdbusplus::async::task<bool> SPIDevice::writeSPIFlash(const uint8_t* image, | 
 | 254 |                                                       size_t image_size) | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 255 | { | 
 | 256 |     debug("[gpio] requesting gpios to mux SPI to BMC"); | 
 | 257 |  | 
 | 258 |     std::unique_ptr<::gpiod::line_bulk> lineBulk = | 
 | 259 |         requestMuxGPIOs(gpioLines, gpioValues, false); | 
 | 260 |  | 
 | 261 |     if (!lineBulk) | 
 | 262 |     { | 
 | 263 |         co_return false; | 
 | 264 |     } | 
 | 265 |  | 
 | 266 |     bool success = co_await SPIDevice::bindSPIFlash(); | 
 | 267 |     if (success) | 
 | 268 |     { | 
 | 269 |         if (dryRun) | 
 | 270 |         { | 
 | 271 |             info("dry run, NOT writing to the chip"); | 
 | 272 |         } | 
 | 273 |         else | 
 | 274 |         { | 
 | 275 |             if (tool == flashToolFlashrom) | 
 | 276 |             { | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 277 |                 success = co_await SPIDevice::writeSPIFlashWithFlashrom( | 
 | 278 |                     image, image_size); | 
 | 279 |                 if (!success) | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 280 |                 { | 
 | 281 |                     error( | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 282 |                         "Error writing to SPI flash {CONTROLLERINDEX}:{DEVICEINDEX}", | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 283 |                         "CONTROLLERINDEX", spiControllerIndex, "DEVICEINDEX", | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 284 |                         spiDeviceIndex); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 285 |                 } | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 286 |             } | 
| Alexander Hansen | 5db0c6b | 2025-05-30 11:21:30 +0200 | [diff] [blame] | 287 |             else if (tool == flashToolFlashcp) | 
 | 288 |             { | 
 | 289 |                 success = co_await SPIDevice::writeSPIFlashWithFlashcp( | 
 | 290 |                     image, image_size); | 
 | 291 |             } | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 292 |             else | 
 | 293 |             { | 
 | 294 |                 success = | 
 | 295 |                     co_await SPIDevice::writeSPIFlashDefault(image, image_size); | 
 | 296 |             } | 
 | 297 |         } | 
 | 298 |  | 
 | 299 |         success = success && co_await SPIDevice::unbindSPIFlash(); | 
 | 300 |     } | 
 | 301 |  | 
 | 302 |     lineBulk->release(); | 
 | 303 |  | 
 | 304 |     // switch bios flash back to host via mux / GPIO | 
 | 305 |     // (not assume there is a pull to the default value) | 
 | 306 |     debug("[gpio] requesting gpios to mux SPI to Host"); | 
 | 307 |  | 
 | 308 |     lineBulk = requestMuxGPIOs(gpioLines, gpioValues, true); | 
 | 309 |  | 
 | 310 |     if (!lineBulk) | 
 | 311 |     { | 
 | 312 |         co_return success; | 
 | 313 |     } | 
 | 314 |  | 
 | 315 |     lineBulk->release(); | 
 | 316 |  | 
 | 317 |     co_return success; | 
 | 318 | } | 
 | 319 |  | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 320 | sdbusplus::async::task<bool> SPIDevice::writeSPIFlashWithFlashrom( | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 321 |     const uint8_t* image, size_t image_size) const | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 322 | { | 
 | 323 |     // randomize the name to enable parallel updates | 
 | 324 |     const std::string path = "/tmp/spi-device-image-" + | 
 | 325 |                              std::to_string(Software::getRandomId()) + ".bin"; | 
 | 326 |  | 
 | 327 |     int fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); | 
 | 328 |     if (fd < 0) | 
 | 329 |     { | 
 | 330 |         error("Failed to open file: {PATH}", "PATH", path); | 
 | 331 |         co_return 1; | 
 | 332 |     } | 
 | 333 |  | 
 | 334 |     const ssize_t bytesWritten = write(fd, image, image_size); | 
 | 335 |  | 
 | 336 |     close(fd); | 
 | 337 |  | 
 | 338 |     setUpdateProgress(30); | 
 | 339 |  | 
 | 340 |     if (bytesWritten < 0 || static_cast<size_t>(bytesWritten) != image_size) | 
 | 341 |     { | 
 | 342 |         error("Failed to write image to file"); | 
 | 343 |         co_return 1; | 
 | 344 |     } | 
 | 345 |  | 
 | 346 |     debug("wrote {SIZE} bytes to {PATH}", "SIZE", bytesWritten, "PATH", path); | 
 | 347 |  | 
 | 348 |     auto devPath = getMTDDevicePath(); | 
 | 349 |  | 
 | 350 |     if (!devPath.has_value()) | 
 | 351 |     { | 
 | 352 |         co_return 1; | 
 | 353 |     } | 
 | 354 |  | 
| Alexander Hansen | 7fbe7d8 | 2025-05-28 17:04:23 +0200 | [diff] [blame] | 355 |     size_t devNum = 0; | 
 | 356 |  | 
 | 357 |     try | 
 | 358 |     { | 
 | 359 |         devNum = std::stoi(devPath.value().substr(8)); | 
 | 360 |     } | 
 | 361 |     catch (std::exception& e) | 
 | 362 |     { | 
 | 363 |         error("could not parse mtd device number from {STR}: {ERROR}", "STR", | 
 | 364 |               devPath.value(), "ERROR", e); | 
 | 365 |         co_return 1; | 
 | 366 |     } | 
 | 367 |  | 
 | 368 |     std::string cmd = "flashrom -p linux_mtd:dev=" + std::to_string(devNum); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 369 |  | 
 | 370 |     if (layout == flashLayoutFlat) | 
 | 371 |     { | 
 | 372 |         cmd += " -w " + path; | 
 | 373 |     } | 
 | 374 |     else | 
 | 375 |     { | 
 | 376 |         error("unsupported flash layout"); | 
 | 377 |  | 
 | 378 |         co_return 1; | 
 | 379 |     } | 
 | 380 |  | 
 | 381 |     debug("[flashrom] running {CMD}", "CMD", cmd); | 
 | 382 |  | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 383 |     auto success = co_await asyncSystem(ctx, cmd); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 384 |  | 
 | 385 |     std::filesystem::remove(path); | 
 | 386 |  | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 387 |     co_return success; | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 388 | } | 
 | 389 |  | 
| Alexander Hansen | 5db0c6b | 2025-05-30 11:21:30 +0200 | [diff] [blame] | 390 | sdbusplus::async::task<bool> SPIDevice::writeSPIFlashWithFlashcp( | 
 | 391 |     const uint8_t* image, size_t image_size) const | 
| Alexander Hansen | 5db0c6b | 2025-05-30 11:21:30 +0200 | [diff] [blame] | 392 | { | 
 | 393 |     // randomize the name to enable parallel updates | 
 | 394 |     const std::string path = "/tmp/spi-device-image-" + | 
 | 395 |                              std::to_string(Software::getRandomId()) + ".bin"; | 
 | 396 |  | 
 | 397 |     int fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); | 
 | 398 |     if (fd < 0) | 
 | 399 |     { | 
 | 400 |         error("Failed to open file: {PATH}", "PATH", path); | 
 | 401 |         co_return 1; | 
 | 402 |     } | 
 | 403 |  | 
 | 404 |     const ssize_t bytesWritten = write(fd, image, image_size); | 
 | 405 |  | 
 | 406 |     close(fd); | 
 | 407 |  | 
 | 408 |     setUpdateProgress(30); | 
 | 409 |  | 
 | 410 |     if (bytesWritten < 0 || static_cast<size_t>(bytesWritten) != image_size) | 
 | 411 |     { | 
 | 412 |         error("Failed to write image to file"); | 
 | 413 |         co_return 1; | 
 | 414 |     } | 
 | 415 |  | 
 | 416 |     debug("wrote {SIZE} bytes to {PATH}", "SIZE", bytesWritten, "PATH", path); | 
 | 417 |  | 
 | 418 |     auto devPath = getMTDDevicePath(); | 
 | 419 |  | 
 | 420 |     if (!devPath.has_value()) | 
 | 421 |     { | 
 | 422 |         co_return 1; | 
 | 423 |     } | 
 | 424 |  | 
 | 425 |     std::string cmd = std::format("flashcp -v {} {}", path, devPath.value()); | 
 | 426 |  | 
 | 427 |     debug("running {CMD}", "CMD", cmd); | 
 | 428 |  | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 429 |     auto success = co_await asyncSystem(ctx, cmd); | 
| Alexander Hansen | 5db0c6b | 2025-05-30 11:21:30 +0200 | [diff] [blame] | 430 |  | 
 | 431 |     std::filesystem::remove(path); | 
 | 432 |  | 
| Kevin Tung | a2eb951 | 2025-05-05 18:28:56 +0800 | [diff] [blame] | 433 |     co_return success; | 
| Alexander Hansen | 5db0c6b | 2025-05-30 11:21:30 +0200 | [diff] [blame] | 434 | } | 
 | 435 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 436 | sdbusplus::async::task<bool> SPIDevice::writeSPIFlashDefault( | 
 | 437 |     const uint8_t* image, size_t image_size) | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 438 | { | 
 | 439 |     auto devPath = getMTDDevicePath(); | 
 | 440 |  | 
 | 441 |     if (!devPath.has_value()) | 
 | 442 |     { | 
 | 443 |         co_return false; | 
 | 444 |     } | 
 | 445 |  | 
 | 446 |     int fd = open(devPath.value().c_str(), O_WRONLY); | 
 | 447 |     if (fd < 0) | 
 | 448 |     { | 
 | 449 |         error("Failed to open device: {PATH}", "PATH", devPath.value()); | 
 | 450 |         co_return false; | 
 | 451 |     } | 
 | 452 |  | 
 | 453 |     // Write the image in chunks to avoid blocking for too long. | 
 | 454 |     // Also, to provide meaningful progress updates. | 
 | 455 |  | 
 | 456 |     const size_t chunk = static_cast<size_t>(1024 * 1024); | 
 | 457 |     ssize_t bytesWritten = 0; | 
 | 458 |  | 
 | 459 |     const int progressStart = 30; | 
 | 460 |     const int progressEnd = 90; | 
 | 461 |  | 
 | 462 |     for (size_t offset = 0; offset < image_size; offset += chunk) | 
 | 463 |     { | 
 | 464 |         const ssize_t written = | 
 | 465 |             write(fd, image + offset, std::min(chunk, image_size - offset)); | 
 | 466 |  | 
 | 467 |         if (written < 0) | 
 | 468 |         { | 
 | 469 |             error("Failed to write to device"); | 
 | 470 |             co_return false; | 
 | 471 |         } | 
 | 472 |  | 
 | 473 |         bytesWritten += written; | 
 | 474 |  | 
 | 475 |         setUpdateProgress( | 
 | 476 |             progressStart + int((progressEnd - progressStart) * | 
 | 477 |                                 (double(offset) / double(image_size)))); | 
 | 478 |     } | 
 | 479 |  | 
 | 480 |     close(fd); | 
 | 481 |  | 
 | 482 |     if (static_cast<size_t>(bytesWritten) != image_size) | 
 | 483 |     { | 
 | 484 |         error("Incomplete write to device"); | 
 | 485 |         co_return false; | 
 | 486 |     } | 
 | 487 |  | 
 | 488 |     debug("Successfully wrote {NBYTES} bytes to {PATH}", "NBYTES", bytesWritten, | 
 | 489 |           "PATH", devPath.value()); | 
 | 490 |  | 
 | 491 |     co_return true; | 
 | 492 | } | 
 | 493 |  | 
 | 494 | std::string SPIDevice::getVersion() | 
 | 495 | { | 
 | 496 |     std::string version{}; | 
 | 497 |     try | 
 | 498 |     { | 
 | 499 |         std::ifstream config(biosVersionPath); | 
 | 500 |  | 
 | 501 |         config >> version; | 
 | 502 |     } | 
 | 503 |     catch (std::exception& e) | 
 | 504 |     { | 
 | 505 |         error("Failed to get version with {ERROR}", "ERROR", e.what()); | 
 | 506 |         version = versionUnknown; | 
 | 507 |     } | 
 | 508 |  | 
 | 509 |     if (version.empty()) | 
 | 510 |     { | 
 | 511 |         version = versionUnknown; | 
 | 512 |     } | 
 | 513 |  | 
 | 514 |     return version; | 
 | 515 | } | 
 | 516 |  | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 517 | auto SPIDevice::processUpdate(std::string versionFileName) | 
 | 518 |     -> sdbusplus::async::task<> | 
 | 519 | { | 
 | 520 |     if (biosVersionFilename != versionFileName) | 
 | 521 |     { | 
 | 522 |         error( | 
 | 523 |             "Update config file name '{NAME}' (!= '{EXPECTED}') is not expected", | 
 | 524 |             "NAME", versionFileName, "EXPECTED", biosVersionFilename); | 
 | 525 |         co_return; | 
 | 526 |     } | 
 | 527 |  | 
 | 528 |     if (softwareCurrent) | 
 | 529 |     { | 
| Daniel Hsu | 2e168db | 2025-09-08 14:06:48 +0800 | [diff] [blame] | 530 |         softwareCurrent->setVersion(getVersion(), | 
 | 531 |                                     SoftwareVersion::VersionPurpose::Host); | 
| Alexander Hansen | f2c95a0 | 2024-11-26 11:16:44 +0100 | [diff] [blame] | 532 |     } | 
 | 533 |  | 
 | 534 |     co_return; | 
 | 535 | } | 
 | 536 |  | 
 | 537 | std::optional<std::string> SPIDevice::getMTDDevicePath() const | 
 | 538 | { | 
 | 539 |     const std::string spiPath = | 
 | 540 |         "/sys/class/spi_master/spi" + std::to_string(spiControllerIndex) + | 
 | 541 |         "/spi" + std::to_string(spiControllerIndex) + "." + | 
 | 542 |         std::to_string(spiDeviceIndex) + "/mtd/"; | 
 | 543 |  | 
 | 544 |     if (!std::filesystem::exists(spiPath)) | 
 | 545 |     { | 
 | 546 |         error("Error: SPI path not found: {PATH}", "PATH", spiPath); | 
 | 547 |         return ""; | 
 | 548 |     } | 
 | 549 |  | 
 | 550 |     for (const auto& entry : std::filesystem::directory_iterator(spiPath)) | 
 | 551 |     { | 
 | 552 |         const std::string mtdName = entry.path().filename().string(); | 
 | 553 |  | 
 | 554 |         if (mtdName.starts_with("mtd") && !mtdName.ends_with("ro")) | 
 | 555 |         { | 
 | 556 |             return "/dev/" + mtdName; | 
 | 557 |         } | 
 | 558 |     } | 
 | 559 |  | 
 | 560 |     error("Error: No MTD device found for spi {CONTROLLERINDEX}.{DEVICEINDEX}", | 
 | 561 |           "CONTROLLERINDEX", spiControllerIndex, "DEVICEINDEX", spiDeviceIndex); | 
 | 562 |  | 
 | 563 |     return std::nullopt; | 
 | 564 | } |