xref: /openbmc/phosphor-bmc-code-mgmt/bios/spi_device.cpp (revision e634411ba7c22d18ae01a0b03ce4f7b881c38fcc)
1f2c95a08SAlexander Hansen #include "spi_device.hpp"
2f2c95a08SAlexander Hansen 
3f2c95a08SAlexander Hansen #include "common/include/NotifyWatch.hpp"
4f2c95a08SAlexander Hansen #include "common/include/device.hpp"
5f2c95a08SAlexander Hansen #include "common/include/host_power.hpp"
6f2c95a08SAlexander Hansen #include "common/include/software_manager.hpp"
7a2eb951fSKevin Tung #include "common/include/utils.hpp"
8f2c95a08SAlexander Hansen 
9*e634411bSHenry Wu #include <gpio_controller.hpp>
10f2c95a08SAlexander Hansen #include <phosphor-logging/lg2.hpp>
11f2c95a08SAlexander Hansen #include <sdbusplus/async.hpp>
12f2c95a08SAlexander Hansen #include <sdbusplus/async/context.hpp>
13f2c95a08SAlexander Hansen #include <xyz/openbmc_project/Association/Definitions/server.hpp>
14f2c95a08SAlexander Hansen #include <xyz/openbmc_project/ObjectMapper/client.hpp>
15f2c95a08SAlexander Hansen #include <xyz/openbmc_project/State/Host/client.hpp>
16f2c95a08SAlexander Hansen 
17f2c95a08SAlexander Hansen #include <cstddef>
18f2c95a08SAlexander Hansen #include <fstream>
19f2c95a08SAlexander Hansen #include <random>
20f2c95a08SAlexander Hansen 
21f2c95a08SAlexander Hansen PHOSPHOR_LOG2_USING;
22f2c95a08SAlexander Hansen 
23f2c95a08SAlexander Hansen using namespace std::literals;
24f2c95a08SAlexander Hansen using namespace phosphor::software;
25f2c95a08SAlexander Hansen using namespace phosphor::software::manager;
26f2c95a08SAlexander Hansen using namespace phosphor::software::host_power;
27f2c95a08SAlexander Hansen 
getSPIDevAddr(uint64_t spiControllerIndex)28dfb60670SAlexander Hansen static std::optional<std::string> getSPIDevAddr(uint64_t spiControllerIndex)
29dfb60670SAlexander Hansen {
30dfb60670SAlexander Hansen     const fs::path spi_path =
31dfb60670SAlexander Hansen         "/sys/class/spi_master/spi" + std::to_string(spiControllerIndex);
32dfb60670SAlexander Hansen 
33dfb60670SAlexander Hansen     if (!fs::exists(spi_path))
34dfb60670SAlexander Hansen     {
35dfb60670SAlexander Hansen         error("SPI controller index not found at {PATH}", "PATH",
36dfb60670SAlexander Hansen               spi_path.string());
37dfb60670SAlexander Hansen         return std::nullopt;
38dfb60670SAlexander Hansen     }
39dfb60670SAlexander Hansen 
40dfb60670SAlexander Hansen     fs::path target = fs::read_symlink(spi_path);
41dfb60670SAlexander Hansen 
42dfb60670SAlexander Hansen     // The path looks like
43dfb60670SAlexander Hansen     // ../../devices/platform/ahb/1e630000.spi/spi_master/spi1 We want to
44dfb60670SAlexander Hansen     // extract e.g. '1e630000.spi'
45dfb60670SAlexander Hansen 
46dfb60670SAlexander Hansen     for (const auto& part : target)
47dfb60670SAlexander Hansen     {
48dfb60670SAlexander Hansen         std::string part_str = part.string();
49dfb60670SAlexander Hansen         if (part_str.find(".spi") != std::string::npos)
50dfb60670SAlexander Hansen         {
51dfb60670SAlexander Hansen             debug("Found SPI Address {ADDR}", "ADDR", part_str);
52dfb60670SAlexander Hansen             return part_str;
53dfb60670SAlexander Hansen         }
54dfb60670SAlexander Hansen     }
55dfb60670SAlexander Hansen 
56dfb60670SAlexander Hansen     return std::nullopt;
57dfb60670SAlexander Hansen }
58dfb60670SAlexander Hansen 
SPIDevice(sdbusplus::async::context & ctx,uint64_t spiControllerIndex,uint64_t spiDeviceIndex,bool dryRun,const std::vector<std::string> & gpioLinesIn,const std::vector<bool> & gpioValuesIn,SoftwareConfig & config,SoftwareManager * parent,enum FlashLayout layout,enum FlashTool tool,const std::string & versionDirPath)59f2c95a08SAlexander Hansen SPIDevice::SPIDevice(sdbusplus::async::context& ctx,
60f2c95a08SAlexander Hansen                      uint64_t spiControllerIndex, uint64_t spiDeviceIndex,
61f2c95a08SAlexander Hansen                      bool dryRun, const std::vector<std::string>& gpioLinesIn,
62*e634411bSHenry Wu                      const std::vector<bool>& gpioValuesIn,
63f2c95a08SAlexander Hansen                      SoftwareConfig& config, SoftwareManager* parent,
64f2c95a08SAlexander Hansen                      enum FlashLayout layout, enum FlashTool tool,
65f2c95a08SAlexander Hansen                      const std::string& versionDirPath) :
66f2c95a08SAlexander Hansen     Device(ctx, config, parent,
67f2c95a08SAlexander Hansen            {RequestedApplyTimes::Immediate, RequestedApplyTimes::OnReset}),
68f2c95a08SAlexander Hansen     NotifyWatchIntf(ctx, versionDirPath), dryRun(dryRun),
69f2c95a08SAlexander Hansen     gpioLines(gpioLinesIn),
70f2c95a08SAlexander Hansen     gpioValues(gpioValuesIn.begin(), gpioValuesIn.end()),
71f2c95a08SAlexander Hansen     spiControllerIndex(spiControllerIndex), spiDeviceIndex(spiDeviceIndex),
72f2c95a08SAlexander Hansen     layout(layout), tool(tool)
73f2c95a08SAlexander Hansen {
74dfb60670SAlexander Hansen     auto optAddr = getSPIDevAddr(spiControllerIndex);
75f2c95a08SAlexander Hansen 
76dfb60670SAlexander Hansen     if (!optAddr.has_value())
77f2c95a08SAlexander Hansen     {
78dfb60670SAlexander Hansen         throw std::invalid_argument("SPI controller index not found");
79f2c95a08SAlexander Hansen     }
80f2c95a08SAlexander Hansen 
81dfb60670SAlexander Hansen     spiDev = optAddr.value();
82f2c95a08SAlexander Hansen 
83f2c95a08SAlexander Hansen     ctx.spawn(readNotifyAsync());
84f2c95a08SAlexander Hansen 
85f2c95a08SAlexander Hansen     debug(
86f2c95a08SAlexander Hansen         "SPI Device {NAME} at {CONTROLLERINDEX}:{DEVICEINDEX} initialized successfully",
87f2c95a08SAlexander Hansen         "NAME", config.configName, "CONTROLLERINDEX", spiControllerIndex,
88f2c95a08SAlexander Hansen         "DEVICEINDEX", spiDeviceIndex);
89f2c95a08SAlexander Hansen }
90f2c95a08SAlexander Hansen 
updateDevice(const uint8_t * image,size_t image_size)91f2c95a08SAlexander Hansen sdbusplus::async::task<bool> SPIDevice::updateDevice(const uint8_t* image,
92f2c95a08SAlexander Hansen                                                      size_t image_size)
93f2c95a08SAlexander Hansen {
94f2c95a08SAlexander Hansen     // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch)
95f2c95a08SAlexander Hansen     auto prevPowerstate = co_await HostPower::getState(ctx);
96f2c95a08SAlexander Hansen 
97f2c95a08SAlexander Hansen     if (prevPowerstate != stateOn && prevPowerstate != stateOff)
98f2c95a08SAlexander Hansen     {
99f2c95a08SAlexander Hansen         co_return false;
100f2c95a08SAlexander Hansen     }
101f2c95a08SAlexander Hansen 
102f2c95a08SAlexander Hansen     bool success = co_await HostPower::setState(ctx, stateOff);
103f2c95a08SAlexander Hansen     if (!success)
104f2c95a08SAlexander Hansen     {
105f2c95a08SAlexander Hansen         error("error changing host power state");
106f2c95a08SAlexander Hansen         co_return false;
107f2c95a08SAlexander Hansen     }
108f2c95a08SAlexander Hansen     setUpdateProgress(10);
109f2c95a08SAlexander Hansen 
110f2c95a08SAlexander Hansen     success = co_await writeSPIFlash(image, image_size);
111f2c95a08SAlexander Hansen 
112f2c95a08SAlexander Hansen     if (success)
113f2c95a08SAlexander Hansen     {
114f2c95a08SAlexander Hansen         setUpdateProgress(100);
115f2c95a08SAlexander Hansen     }
116f2c95a08SAlexander Hansen 
117f2c95a08SAlexander Hansen     // restore the previous powerstate
118f2c95a08SAlexander Hansen     const bool powerstate_restore =
119f2c95a08SAlexander Hansen         co_await HostPower::setState(ctx, prevPowerstate);
120f2c95a08SAlexander Hansen     if (!powerstate_restore)
121f2c95a08SAlexander Hansen     {
122f2c95a08SAlexander Hansen         error("error changing host power state");
123f2c95a08SAlexander Hansen         co_return false;
124f2c95a08SAlexander Hansen     }
125f2c95a08SAlexander Hansen 
126f2c95a08SAlexander Hansen     // return value here is only describing if we successfully wrote to the
127f2c95a08SAlexander Hansen     // SPI flash. Restoring powerstate can still fail.
128f2c95a08SAlexander Hansen     co_return success;
129f2c95a08SAlexander Hansen }
130f2c95a08SAlexander Hansen 
131f2c95a08SAlexander Hansen const std::string spiAspeedSMCPath = "/sys/bus/platform/drivers/spi-aspeed-smc";
132ac4fdd05SAlexander Hansen const std::string spiNorPath = "/sys/bus/spi/drivers/spi-nor";
133f2c95a08SAlexander Hansen 
bindSPIFlash()134f2c95a08SAlexander Hansen sdbusplus::async::task<bool> SPIDevice::bindSPIFlash()
135f2c95a08SAlexander Hansen {
136ac4fdd05SAlexander Hansen     if (!SPIDevice::isSPIControllerBound())
137ac4fdd05SAlexander Hansen     {
138f2c95a08SAlexander Hansen         debug("binding flash to SMC");
139f2c95a08SAlexander Hansen         std::ofstream ofbind(spiAspeedSMCPath + "/bind", std::ofstream::out);
140f2c95a08SAlexander Hansen         ofbind << spiDev;
141f2c95a08SAlexander Hansen         ofbind.close();
142ac4fdd05SAlexander Hansen     }
143f2c95a08SAlexander Hansen 
144f2c95a08SAlexander Hansen     const int driverBindSleepDuration = 2;
145f2c95a08SAlexander Hansen 
146f2c95a08SAlexander Hansen     co_await sdbusplus::async::sleep_for(
147f2c95a08SAlexander Hansen         ctx, std::chrono::seconds(driverBindSleepDuration));
148f2c95a08SAlexander Hansen 
149ac4fdd05SAlexander Hansen     if (!isSPIControllerBound())
150f2c95a08SAlexander Hansen     {
151ac4fdd05SAlexander Hansen         error("failed to bind spi controller");
152ac4fdd05SAlexander Hansen         co_return false;
153f2c95a08SAlexander Hansen     }
154f2c95a08SAlexander Hansen 
155ac4fdd05SAlexander Hansen     const std::string name =
156ac4fdd05SAlexander Hansen         std::format("spi{}.{}", spiControllerIndex, spiDeviceIndex);
157ac4fdd05SAlexander Hansen 
158ac4fdd05SAlexander Hansen     std::ofstream ofbindSPINor(spiNorPath + "/bind", std::ofstream::out);
159ac4fdd05SAlexander Hansen     ofbindSPINor << name;
160ac4fdd05SAlexander Hansen     ofbindSPINor.close();
161ac4fdd05SAlexander Hansen 
162ac4fdd05SAlexander Hansen     co_await sdbusplus::async::sleep_for(
163ac4fdd05SAlexander Hansen         ctx, std::chrono::seconds(driverBindSleepDuration));
164ac4fdd05SAlexander Hansen 
165ac4fdd05SAlexander Hansen     if (!isSPIFlashBound())
166ac4fdd05SAlexander Hansen     {
167ac4fdd05SAlexander Hansen         error("failed to bind spi flash (spi-nor driver)");
168ac4fdd05SAlexander Hansen         co_return false;
169ac4fdd05SAlexander Hansen     }
170ac4fdd05SAlexander Hansen 
171ac4fdd05SAlexander Hansen     co_return true;
172f2c95a08SAlexander Hansen }
173f2c95a08SAlexander Hansen 
unbindSPIFlash()174f2c95a08SAlexander Hansen sdbusplus::async::task<bool> SPIDevice::unbindSPIFlash()
175f2c95a08SAlexander Hansen {
176ac4fdd05SAlexander Hansen     debug("unbinding flash");
177ac4fdd05SAlexander Hansen 
178ac4fdd05SAlexander Hansen     const std::string name =
179ac4fdd05SAlexander Hansen         std::format("spi{}.{}", spiControllerIndex, spiDeviceIndex);
180ac4fdd05SAlexander Hansen 
181ac4fdd05SAlexander Hansen     std::ofstream ofunbind(spiNorPath + "/unbind", std::ofstream::out);
182ac4fdd05SAlexander Hansen     ofunbind << name;
183f2c95a08SAlexander Hansen     ofunbind.close();
184f2c95a08SAlexander Hansen 
185f2c95a08SAlexander Hansen     // wait for kernel
186f2c95a08SAlexander Hansen     co_await sdbusplus::async::sleep_for(ctx, std::chrono::seconds(2));
187f2c95a08SAlexander Hansen 
188f2c95a08SAlexander Hansen     co_return !isSPIFlashBound();
189f2c95a08SAlexander Hansen }
190f2c95a08SAlexander Hansen 
isSPIControllerBound()191ac4fdd05SAlexander Hansen bool SPIDevice::isSPIControllerBound()
192f2c95a08SAlexander Hansen {
193f2c95a08SAlexander Hansen     std::string path = spiAspeedSMCPath + "/" + spiDev;
194f2c95a08SAlexander Hansen 
195f2c95a08SAlexander Hansen     return std::filesystem::exists(path);
196f2c95a08SAlexander Hansen }
197f2c95a08SAlexander Hansen 
isSPIFlashBound()198ac4fdd05SAlexander Hansen bool SPIDevice::isSPIFlashBound()
199ac4fdd05SAlexander Hansen {
200ac4fdd05SAlexander Hansen     const std::string name =
201ac4fdd05SAlexander Hansen         std::format("spi{}.{}", spiControllerIndex, spiDeviceIndex);
202ac4fdd05SAlexander Hansen     std::string path = spiNorPath + "/" + name;
203ac4fdd05SAlexander Hansen 
204ac4fdd05SAlexander Hansen     return std::filesystem::exists(path);
205ac4fdd05SAlexander Hansen }
206ac4fdd05SAlexander Hansen 
writeSPIFlash(const uint8_t * image,size_t image_size)207f2c95a08SAlexander Hansen sdbusplus::async::task<bool> SPIDevice::writeSPIFlash(const uint8_t* image,
208f2c95a08SAlexander Hansen                                                       size_t image_size)
209f2c95a08SAlexander Hansen {
210f2c95a08SAlexander Hansen     debug("[gpio] requesting gpios to mux SPI to BMC");
211f2c95a08SAlexander Hansen 
212*e634411bSHenry Wu     GPIOGroup muxGPIO(gpioLines, gpioValues);
213*e634411bSHenry Wu     std::optional<ScopedBmcMux> guard;
214*e634411bSHenry Wu     if (!gpioLines.empty())
215f2c95a08SAlexander Hansen     {
216*e634411bSHenry Wu         try
217*e634411bSHenry Wu         {
218*e634411bSHenry Wu             guard.emplace(muxGPIO);
219*e634411bSHenry Wu         }
220*e634411bSHenry Wu         catch (const std::exception& e)
221*e634411bSHenry Wu         {
222*e634411bSHenry Wu             error("Failed to mux GPIOs to BMC: {ERROR}", "ERROR", e.what());
223*e634411bSHenry Wu             co_return false;
224*e634411bSHenry Wu         }
225*e634411bSHenry Wu     }
226*e634411bSHenry Wu     else
227*e634411bSHenry Wu     {
228*e634411bSHenry Wu         error("No GPIO lines configured for SPI muxing");
229f2c95a08SAlexander Hansen         co_return false;
230f2c95a08SAlexander Hansen     }
231f2c95a08SAlexander Hansen 
232f2c95a08SAlexander Hansen     bool success = co_await SPIDevice::bindSPIFlash();
233f2c95a08SAlexander Hansen     if (success)
234f2c95a08SAlexander Hansen     {
235f2c95a08SAlexander Hansen         if (dryRun)
236f2c95a08SAlexander Hansen         {
237f2c95a08SAlexander Hansen             info("dry run, NOT writing to the chip");
238f2c95a08SAlexander Hansen         }
239f2c95a08SAlexander Hansen         else
240f2c95a08SAlexander Hansen         {
241f2c95a08SAlexander Hansen             if (tool == flashToolFlashrom)
242f2c95a08SAlexander Hansen             {
243a2eb951fSKevin Tung                 success = co_await SPIDevice::writeSPIFlashWithFlashrom(
244a2eb951fSKevin Tung                     image, image_size);
245a2eb951fSKevin Tung                 if (!success)
246f2c95a08SAlexander Hansen                 {
247f2c95a08SAlexander Hansen                     error(
248a2eb951fSKevin Tung                         "Error writing to SPI flash {CONTROLLERINDEX}:{DEVICEINDEX}",
249f2c95a08SAlexander Hansen                         "CONTROLLERINDEX", spiControllerIndex, "DEVICEINDEX",
250a2eb951fSKevin Tung                         spiDeviceIndex);
251f2c95a08SAlexander Hansen                 }
252f2c95a08SAlexander Hansen             }
2535db0c6beSAlexander Hansen             else if (tool == flashToolFlashcp)
2545db0c6beSAlexander Hansen             {
2555db0c6beSAlexander Hansen                 success = co_await SPIDevice::writeSPIFlashWithFlashcp(
2565db0c6beSAlexander Hansen                     image, image_size);
2575db0c6beSAlexander Hansen             }
258f2c95a08SAlexander Hansen             else
259f2c95a08SAlexander Hansen             {
260f2c95a08SAlexander Hansen                 success =
261f2c95a08SAlexander Hansen                     co_await SPIDevice::writeSPIFlashDefault(image, image_size);
262f2c95a08SAlexander Hansen             }
263f2c95a08SAlexander Hansen         }
264f2c95a08SAlexander Hansen 
265f2c95a08SAlexander Hansen         success = success && co_await SPIDevice::unbindSPIFlash();
266f2c95a08SAlexander Hansen     }
267f2c95a08SAlexander Hansen 
268*e634411bSHenry Wu     lg2::info("Successfully updated SPI flash");
269f2c95a08SAlexander Hansen     co_return success;
270f2c95a08SAlexander Hansen }
271f2c95a08SAlexander Hansen 
writeSPIFlashWithFlashrom(const uint8_t * image,size_t image_size) const272a2eb951fSKevin Tung sdbusplus::async::task<bool> SPIDevice::writeSPIFlashWithFlashrom(
273f2c95a08SAlexander Hansen     const uint8_t* image, size_t image_size) const
274f2c95a08SAlexander Hansen {
275f2c95a08SAlexander Hansen     // randomize the name to enable parallel updates
276f2c95a08SAlexander Hansen     const std::string path = "/tmp/spi-device-image-" +
277f2c95a08SAlexander Hansen                              std::to_string(Software::getRandomId()) + ".bin";
278f2c95a08SAlexander Hansen 
279f2c95a08SAlexander Hansen     int fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
280f2c95a08SAlexander Hansen     if (fd < 0)
281f2c95a08SAlexander Hansen     {
282f2c95a08SAlexander Hansen         error("Failed to open file: {PATH}", "PATH", path);
283f2c95a08SAlexander Hansen         co_return 1;
284f2c95a08SAlexander Hansen     }
285f2c95a08SAlexander Hansen 
286f2c95a08SAlexander Hansen     const ssize_t bytesWritten = write(fd, image, image_size);
287f2c95a08SAlexander Hansen 
288f2c95a08SAlexander Hansen     close(fd);
289f2c95a08SAlexander Hansen 
290f2c95a08SAlexander Hansen     setUpdateProgress(30);
291f2c95a08SAlexander Hansen 
292f2c95a08SAlexander Hansen     if (bytesWritten < 0 || static_cast<size_t>(bytesWritten) != image_size)
293f2c95a08SAlexander Hansen     {
294f2c95a08SAlexander Hansen         error("Failed to write image to file");
295f2c95a08SAlexander Hansen         co_return 1;
296f2c95a08SAlexander Hansen     }
297f2c95a08SAlexander Hansen 
298f2c95a08SAlexander Hansen     debug("wrote {SIZE} bytes to {PATH}", "SIZE", bytesWritten, "PATH", path);
299f2c95a08SAlexander Hansen 
300f2c95a08SAlexander Hansen     auto devPath = getMTDDevicePath();
301f2c95a08SAlexander Hansen 
302f2c95a08SAlexander Hansen     if (!devPath.has_value())
303f2c95a08SAlexander Hansen     {
304f2c95a08SAlexander Hansen         co_return 1;
305f2c95a08SAlexander Hansen     }
306f2c95a08SAlexander Hansen 
3077fbe7d82SAlexander Hansen     size_t devNum = 0;
3087fbe7d82SAlexander Hansen 
3097fbe7d82SAlexander Hansen     try
3107fbe7d82SAlexander Hansen     {
3117fbe7d82SAlexander Hansen         devNum = std::stoi(devPath.value().substr(8));
3127fbe7d82SAlexander Hansen     }
3137fbe7d82SAlexander Hansen     catch (std::exception& e)
3147fbe7d82SAlexander Hansen     {
3157fbe7d82SAlexander Hansen         error("could not parse mtd device number from {STR}: {ERROR}", "STR",
3167fbe7d82SAlexander Hansen               devPath.value(), "ERROR", e);
3177fbe7d82SAlexander Hansen         co_return 1;
3187fbe7d82SAlexander Hansen     }
3197fbe7d82SAlexander Hansen 
3207fbe7d82SAlexander Hansen     std::string cmd = "flashrom -p linux_mtd:dev=" + std::to_string(devNum);
321f2c95a08SAlexander Hansen 
322f2c95a08SAlexander Hansen     if (layout == flashLayoutFlat)
323f2c95a08SAlexander Hansen     {
324f2c95a08SAlexander Hansen         cmd += " -w " + path;
325f2c95a08SAlexander Hansen     }
326f2c95a08SAlexander Hansen     else
327f2c95a08SAlexander Hansen     {
328f2c95a08SAlexander Hansen         error("unsupported flash layout");
329f2c95a08SAlexander Hansen 
330f2c95a08SAlexander Hansen         co_return 1;
331f2c95a08SAlexander Hansen     }
332f2c95a08SAlexander Hansen 
333f2c95a08SAlexander Hansen     debug("[flashrom] running {CMD}", "CMD", cmd);
334f2c95a08SAlexander Hansen 
335a2eb951fSKevin Tung     auto success = co_await asyncSystem(ctx, cmd);
336f2c95a08SAlexander Hansen 
337f2c95a08SAlexander Hansen     std::filesystem::remove(path);
338f2c95a08SAlexander Hansen 
339a2eb951fSKevin Tung     co_return success;
340f2c95a08SAlexander Hansen }
341f2c95a08SAlexander Hansen 
writeSPIFlashWithFlashcp(const uint8_t * image,size_t image_size) const3425db0c6beSAlexander Hansen sdbusplus::async::task<bool> SPIDevice::writeSPIFlashWithFlashcp(
3435db0c6beSAlexander Hansen     const uint8_t* image, size_t image_size) const
3445db0c6beSAlexander Hansen {
3455db0c6beSAlexander Hansen     // randomize the name to enable parallel updates
3465db0c6beSAlexander Hansen     const std::string path = "/tmp/spi-device-image-" +
3475db0c6beSAlexander Hansen                              std::to_string(Software::getRandomId()) + ".bin";
3485db0c6beSAlexander Hansen 
3495db0c6beSAlexander Hansen     int fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
3505db0c6beSAlexander Hansen     if (fd < 0)
3515db0c6beSAlexander Hansen     {
3525db0c6beSAlexander Hansen         error("Failed to open file: {PATH}", "PATH", path);
3535db0c6beSAlexander Hansen         co_return 1;
3545db0c6beSAlexander Hansen     }
3555db0c6beSAlexander Hansen 
3565db0c6beSAlexander Hansen     const ssize_t bytesWritten = write(fd, image, image_size);
3575db0c6beSAlexander Hansen 
3585db0c6beSAlexander Hansen     close(fd);
3595db0c6beSAlexander Hansen 
3605db0c6beSAlexander Hansen     setUpdateProgress(30);
3615db0c6beSAlexander Hansen 
3625db0c6beSAlexander Hansen     if (bytesWritten < 0 || static_cast<size_t>(bytesWritten) != image_size)
3635db0c6beSAlexander Hansen     {
3645db0c6beSAlexander Hansen         error("Failed to write image to file");
3655db0c6beSAlexander Hansen         co_return 1;
3665db0c6beSAlexander Hansen     }
3675db0c6beSAlexander Hansen 
3685db0c6beSAlexander Hansen     debug("wrote {SIZE} bytes to {PATH}", "SIZE", bytesWritten, "PATH", path);
3695db0c6beSAlexander Hansen 
3705db0c6beSAlexander Hansen     auto devPath = getMTDDevicePath();
3715db0c6beSAlexander Hansen 
3725db0c6beSAlexander Hansen     if (!devPath.has_value())
3735db0c6beSAlexander Hansen     {
3745db0c6beSAlexander Hansen         co_return 1;
3755db0c6beSAlexander Hansen     }
3765db0c6beSAlexander Hansen 
3775db0c6beSAlexander Hansen     std::string cmd = std::format("flashcp -v {} {}", path, devPath.value());
3785db0c6beSAlexander Hansen 
3795db0c6beSAlexander Hansen     debug("running {CMD}", "CMD", cmd);
3805db0c6beSAlexander Hansen 
381a2eb951fSKevin Tung     auto success = co_await asyncSystem(ctx, cmd);
3825db0c6beSAlexander Hansen 
3835db0c6beSAlexander Hansen     std::filesystem::remove(path);
3845db0c6beSAlexander Hansen 
385a2eb951fSKevin Tung     co_return success;
3865db0c6beSAlexander Hansen }
3875db0c6beSAlexander Hansen 
writeSPIFlashDefault(const uint8_t * image,size_t image_size)388f2c95a08SAlexander Hansen sdbusplus::async::task<bool> SPIDevice::writeSPIFlashDefault(
389f2c95a08SAlexander Hansen     const uint8_t* image, size_t image_size)
390f2c95a08SAlexander Hansen {
391f2c95a08SAlexander Hansen     auto devPath = getMTDDevicePath();
392f2c95a08SAlexander Hansen 
393f2c95a08SAlexander Hansen     if (!devPath.has_value())
394f2c95a08SAlexander Hansen     {
395f2c95a08SAlexander Hansen         co_return false;
396f2c95a08SAlexander Hansen     }
397f2c95a08SAlexander Hansen 
398f2c95a08SAlexander Hansen     int fd = open(devPath.value().c_str(), O_WRONLY);
399f2c95a08SAlexander Hansen     if (fd < 0)
400f2c95a08SAlexander Hansen     {
401f2c95a08SAlexander Hansen         error("Failed to open device: {PATH}", "PATH", devPath.value());
402f2c95a08SAlexander Hansen         co_return false;
403f2c95a08SAlexander Hansen     }
404f2c95a08SAlexander Hansen 
405f2c95a08SAlexander Hansen     // Write the image in chunks to avoid blocking for too long.
406f2c95a08SAlexander Hansen     // Also, to provide meaningful progress updates.
407f2c95a08SAlexander Hansen 
408f2c95a08SAlexander Hansen     const size_t chunk = static_cast<size_t>(1024 * 1024);
409f2c95a08SAlexander Hansen     ssize_t bytesWritten = 0;
410f2c95a08SAlexander Hansen 
411f2c95a08SAlexander Hansen     const int progressStart = 30;
412f2c95a08SAlexander Hansen     const int progressEnd = 90;
413f2c95a08SAlexander Hansen 
414f2c95a08SAlexander Hansen     for (size_t offset = 0; offset < image_size; offset += chunk)
415f2c95a08SAlexander Hansen     {
416f2c95a08SAlexander Hansen         const ssize_t written =
417f2c95a08SAlexander Hansen             write(fd, image + offset, std::min(chunk, image_size - offset));
418f2c95a08SAlexander Hansen 
419f2c95a08SAlexander Hansen         if (written < 0)
420f2c95a08SAlexander Hansen         {
421f2c95a08SAlexander Hansen             error("Failed to write to device");
422f2c95a08SAlexander Hansen             co_return false;
423f2c95a08SAlexander Hansen         }
424f2c95a08SAlexander Hansen 
425f2c95a08SAlexander Hansen         bytesWritten += written;
426f2c95a08SAlexander Hansen 
427f2c95a08SAlexander Hansen         setUpdateProgress(
428f2c95a08SAlexander Hansen             progressStart + int((progressEnd - progressStart) *
429f2c95a08SAlexander Hansen                                 (double(offset) / double(image_size))));
430f2c95a08SAlexander Hansen     }
431f2c95a08SAlexander Hansen 
432f2c95a08SAlexander Hansen     close(fd);
433f2c95a08SAlexander Hansen 
434f2c95a08SAlexander Hansen     if (static_cast<size_t>(bytesWritten) != image_size)
435f2c95a08SAlexander Hansen     {
436f2c95a08SAlexander Hansen         error("Incomplete write to device");
437f2c95a08SAlexander Hansen         co_return false;
438f2c95a08SAlexander Hansen     }
439f2c95a08SAlexander Hansen 
440f2c95a08SAlexander Hansen     debug("Successfully wrote {NBYTES} bytes to {PATH}", "NBYTES", bytesWritten,
441f2c95a08SAlexander Hansen           "PATH", devPath.value());
442f2c95a08SAlexander Hansen 
443f2c95a08SAlexander Hansen     co_return true;
444f2c95a08SAlexander Hansen }
445f2c95a08SAlexander Hansen 
getVersion()446f2c95a08SAlexander Hansen std::string SPIDevice::getVersion()
447f2c95a08SAlexander Hansen {
448f2c95a08SAlexander Hansen     std::string version{};
449f2c95a08SAlexander Hansen     try
450f2c95a08SAlexander Hansen     {
451f2c95a08SAlexander Hansen         std::ifstream config(biosVersionPath);
452f2c95a08SAlexander Hansen 
453f2c95a08SAlexander Hansen         config >> version;
454f2c95a08SAlexander Hansen     }
455f2c95a08SAlexander Hansen     catch (std::exception& e)
456f2c95a08SAlexander Hansen     {
457f2c95a08SAlexander Hansen         error("Failed to get version with {ERROR}", "ERROR", e.what());
458f2c95a08SAlexander Hansen         version = versionUnknown;
459f2c95a08SAlexander Hansen     }
460f2c95a08SAlexander Hansen 
461f2c95a08SAlexander Hansen     if (version.empty())
462f2c95a08SAlexander Hansen     {
463f2c95a08SAlexander Hansen         version = versionUnknown;
464f2c95a08SAlexander Hansen     }
465f2c95a08SAlexander Hansen 
466f2c95a08SAlexander Hansen     return version;
467f2c95a08SAlexander Hansen }
468f2c95a08SAlexander Hansen 
processUpdate(std::string versionFileName)469f2c95a08SAlexander Hansen auto SPIDevice::processUpdate(std::string versionFileName)
470f2c95a08SAlexander Hansen     -> sdbusplus::async::task<>
471f2c95a08SAlexander Hansen {
472f2c95a08SAlexander Hansen     if (biosVersionFilename != versionFileName)
473f2c95a08SAlexander Hansen     {
474f2c95a08SAlexander Hansen         error(
475f2c95a08SAlexander Hansen             "Update config file name '{NAME}' (!= '{EXPECTED}') is not expected",
476f2c95a08SAlexander Hansen             "NAME", versionFileName, "EXPECTED", biosVersionFilename);
477f2c95a08SAlexander Hansen         co_return;
478f2c95a08SAlexander Hansen     }
479f2c95a08SAlexander Hansen 
480f2c95a08SAlexander Hansen     if (softwareCurrent)
481f2c95a08SAlexander Hansen     {
4822e168dbaSDaniel Hsu         softwareCurrent->setVersion(getVersion(),
4832e168dbaSDaniel Hsu                                     SoftwareVersion::VersionPurpose::Host);
484f2c95a08SAlexander Hansen     }
485f2c95a08SAlexander Hansen 
486f2c95a08SAlexander Hansen     co_return;
487f2c95a08SAlexander Hansen }
488f2c95a08SAlexander Hansen 
getMTDDevicePath() const489f2c95a08SAlexander Hansen std::optional<std::string> SPIDevice::getMTDDevicePath() const
490f2c95a08SAlexander Hansen {
491f2c95a08SAlexander Hansen     const std::string spiPath =
492f2c95a08SAlexander Hansen         "/sys/class/spi_master/spi" + std::to_string(spiControllerIndex) +
493f2c95a08SAlexander Hansen         "/spi" + std::to_string(spiControllerIndex) + "." +
494f2c95a08SAlexander Hansen         std::to_string(spiDeviceIndex) + "/mtd/";
495f2c95a08SAlexander Hansen 
496f2c95a08SAlexander Hansen     if (!std::filesystem::exists(spiPath))
497f2c95a08SAlexander Hansen     {
498f2c95a08SAlexander Hansen         error("Error: SPI path not found: {PATH}", "PATH", spiPath);
499f2c95a08SAlexander Hansen         return "";
500f2c95a08SAlexander Hansen     }
501f2c95a08SAlexander Hansen 
502f2c95a08SAlexander Hansen     for (const auto& entry : std::filesystem::directory_iterator(spiPath))
503f2c95a08SAlexander Hansen     {
504f2c95a08SAlexander Hansen         const std::string mtdName = entry.path().filename().string();
505f2c95a08SAlexander Hansen 
506f2c95a08SAlexander Hansen         if (mtdName.starts_with("mtd") && !mtdName.ends_with("ro"))
507f2c95a08SAlexander Hansen         {
508f2c95a08SAlexander Hansen             return "/dev/" + mtdName;
509f2c95a08SAlexander Hansen         }
510f2c95a08SAlexander Hansen     }
511f2c95a08SAlexander Hansen 
512f2c95a08SAlexander Hansen     error("Error: No MTD device found for spi {CONTROLLERINDEX}.{DEVICEINDEX}",
513f2c95a08SAlexander Hansen           "CONTROLLERINDEX", spiControllerIndex, "DEVICEINDEX", spiDeviceIndex);
514f2c95a08SAlexander Hansen 
515f2c95a08SAlexander Hansen     return std::nullopt;
516f2c95a08SAlexander Hansen }
517