xref: /openbmc/phosphor-bmc-code-mgmt/bios/spi_device.cpp (revision a2eb951f7384c2b4fa494f90e78295f615c12a56)
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"
7*a2eb951fSKevin Tung #include "common/include/utils.hpp"
8f2c95a08SAlexander Hansen 
9f2c95a08SAlexander Hansen #include <gpiod.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;
getSPIDevAddr(uint64_t spiControllerIndex)27f2c95a08SAlexander Hansen 
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 }
SPIDevice(sdbusplus::async::context & ctx,uint64_t spiControllerIndex,uint64_t spiDeviceIndex,bool dryRun,const std::vector<std::string> & gpioLinesIn,const std::vector<uint64_t> & gpioValuesIn,SoftwareConfig & config,SoftwareManager * parent,enum FlashLayout layout,enum FlashTool tool,const std::string & versionDirPath)58dfb60670SAlexander Hansen 
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,
62f2c95a08SAlexander Hansen                      const std::vector<uint64_t>& 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 }
updateDevice(const uint8_t * image,size_t image_size)90f2c95a08SAlexander Hansen 
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";
bindSPIFlash()133f2c95a08SAlexander Hansen 
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 }
unbindSPIFlash()173f2c95a08SAlexander Hansen 
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 }
isSPIControllerBound()190f2c95a08SAlexander Hansen 
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 }
isSPIFlashBound()197f2c95a08SAlexander Hansen 
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 }
requestMuxGPIOs(const std::vector<std::string> & gpioLines,const std::vector<int> & gpioValues,bool inverted)206ac4fdd05SAlexander Hansen 
207f2c95a08SAlexander Hansen static std::unique_ptr<::gpiod::line_bulk> requestMuxGPIOs(
208f2c95a08SAlexander Hansen     const std::vector<std::string>& gpioLines,
209f2c95a08SAlexander Hansen     const std::vector<int>& gpioValues, bool inverted)
210f2c95a08SAlexander Hansen {
211f2c95a08SAlexander Hansen     std::vector<::gpiod::line> lines;
212f2c95a08SAlexander Hansen 
213f2c95a08SAlexander Hansen     for (const std::string& lineName : gpioLines)
214f2c95a08SAlexander Hansen     {
215f2c95a08SAlexander Hansen         const ::gpiod::line line = ::gpiod::find_line(lineName);
216f2c95a08SAlexander Hansen 
217f2c95a08SAlexander Hansen         if (line.is_used())
218f2c95a08SAlexander Hansen         {
219f2c95a08SAlexander Hansen             error("gpio line {LINE} was still used", "LINE", lineName);
220f2c95a08SAlexander Hansen             return nullptr;
221f2c95a08SAlexander Hansen         }
222f2c95a08SAlexander Hansen 
223f2c95a08SAlexander Hansen         lines.push_back(line);
224f2c95a08SAlexander Hansen     }
225f2c95a08SAlexander Hansen 
226f2c95a08SAlexander Hansen     ::gpiod::line_request config{"", ::gpiod::line_request::DIRECTION_OUTPUT,
227f2c95a08SAlexander Hansen                                  0};
228f2c95a08SAlexander Hansen 
229db0e88ccSAlexander Hansen     debug("requesting gpios for mux");
230f2c95a08SAlexander Hansen 
231f2c95a08SAlexander Hansen     auto lineBulk = std::make_unique<::gpiod::line_bulk>(lines);
232f2c95a08SAlexander Hansen 
233f2c95a08SAlexander Hansen     if (inverted)
234f2c95a08SAlexander Hansen     {
235f2c95a08SAlexander Hansen         std::vector<int> valuesInverted;
236f2c95a08SAlexander Hansen         valuesInverted.reserve(gpioValues.size());
237f2c95a08SAlexander Hansen 
238f2c95a08SAlexander Hansen         for (int value : gpioValues)
239f2c95a08SAlexander Hansen         {
240f2c95a08SAlexander Hansen             valuesInverted.push_back(value ? 0 : 1);
241f2c95a08SAlexander Hansen         }
242f2c95a08SAlexander Hansen 
243f2c95a08SAlexander Hansen         lineBulk->request(config, valuesInverted);
244f2c95a08SAlexander Hansen     }
245f2c95a08SAlexander Hansen     else
246f2c95a08SAlexander Hansen     {
247f2c95a08SAlexander Hansen         lineBulk->request(config, gpioValues);
248f2c95a08SAlexander Hansen     }
249f2c95a08SAlexander Hansen 
250f2c95a08SAlexander Hansen     return lineBulk;
251f2c95a08SAlexander Hansen }
writeSPIFlash(const uint8_t * image,size_t image_size)252f2c95a08SAlexander Hansen 
253f2c95a08SAlexander Hansen sdbusplus::async::task<bool> SPIDevice::writeSPIFlash(const uint8_t* image,
254f2c95a08SAlexander Hansen                                                       size_t image_size)
255f2c95a08SAlexander Hansen {
256f2c95a08SAlexander Hansen     debug("[gpio] requesting gpios to mux SPI to BMC");
257f2c95a08SAlexander Hansen 
258f2c95a08SAlexander Hansen     std::unique_ptr<::gpiod::line_bulk> lineBulk =
259f2c95a08SAlexander Hansen         requestMuxGPIOs(gpioLines, gpioValues, false);
260f2c95a08SAlexander Hansen 
261f2c95a08SAlexander Hansen     if (!lineBulk)
262f2c95a08SAlexander Hansen     {
263f2c95a08SAlexander Hansen         co_return false;
264f2c95a08SAlexander Hansen     }
265f2c95a08SAlexander Hansen 
266f2c95a08SAlexander Hansen     bool success = co_await SPIDevice::bindSPIFlash();
267f2c95a08SAlexander Hansen     if (success)
268f2c95a08SAlexander Hansen     {
269f2c95a08SAlexander Hansen         if (dryRun)
270f2c95a08SAlexander Hansen         {
271f2c95a08SAlexander Hansen             info("dry run, NOT writing to the chip");
272f2c95a08SAlexander Hansen         }
273f2c95a08SAlexander Hansen         else
274f2c95a08SAlexander Hansen         {
275f2c95a08SAlexander Hansen             if (tool == flashToolFlashrom)
276f2c95a08SAlexander Hansen             {
277*a2eb951fSKevin Tung                 success = co_await SPIDevice::writeSPIFlashWithFlashrom(
278*a2eb951fSKevin Tung                     image, image_size);
279*a2eb951fSKevin Tung                 if (!success)
280f2c95a08SAlexander Hansen                 {
281f2c95a08SAlexander Hansen                     error(
282*a2eb951fSKevin Tung                         "Error writing to SPI flash {CONTROLLERINDEX}:{DEVICEINDEX}",
283f2c95a08SAlexander Hansen                         "CONTROLLERINDEX", spiControllerIndex, "DEVICEINDEX",
284*a2eb951fSKevin Tung                         spiDeviceIndex);
285f2c95a08SAlexander Hansen                 }
286f2c95a08SAlexander Hansen             }
2875db0c6beSAlexander Hansen             else if (tool == flashToolFlashcp)
2885db0c6beSAlexander Hansen             {
2895db0c6beSAlexander Hansen                 success = co_await SPIDevice::writeSPIFlashWithFlashcp(
2905db0c6beSAlexander Hansen                     image, image_size);
2915db0c6beSAlexander Hansen             }
292f2c95a08SAlexander Hansen             else
293f2c95a08SAlexander Hansen             {
294f2c95a08SAlexander Hansen                 success =
295f2c95a08SAlexander Hansen                     co_await SPIDevice::writeSPIFlashDefault(image, image_size);
296f2c95a08SAlexander Hansen             }
297f2c95a08SAlexander Hansen         }
298f2c95a08SAlexander Hansen 
299f2c95a08SAlexander Hansen         success = success && co_await SPIDevice::unbindSPIFlash();
300f2c95a08SAlexander Hansen     }
301f2c95a08SAlexander Hansen 
302f2c95a08SAlexander Hansen     lineBulk->release();
303f2c95a08SAlexander Hansen 
304f2c95a08SAlexander Hansen     // switch bios flash back to host via mux / GPIO
305f2c95a08SAlexander Hansen     // (not assume there is a pull to the default value)
306f2c95a08SAlexander Hansen     debug("[gpio] requesting gpios to mux SPI to Host");
307f2c95a08SAlexander Hansen 
308f2c95a08SAlexander Hansen     lineBulk = requestMuxGPIOs(gpioLines, gpioValues, true);
309f2c95a08SAlexander Hansen 
310f2c95a08SAlexander Hansen     if (!lineBulk)
311f2c95a08SAlexander Hansen     {
312f2c95a08SAlexander Hansen         co_return success;
313f2c95a08SAlexander Hansen     }
314f2c95a08SAlexander Hansen 
315f2c95a08SAlexander Hansen     lineBulk->release();
316f2c95a08SAlexander Hansen 
317f2c95a08SAlexander Hansen     co_return success;
318f2c95a08SAlexander Hansen }
319f2c95a08SAlexander Hansen 
320*a2eb951fSKevin Tung sdbusplus::async::task<bool> SPIDevice::writeSPIFlashWithFlashrom(
asyncSystem(sdbusplus::async::context & ctx,const std::string & cmd)321f2c95a08SAlexander Hansen     const uint8_t* image, size_t image_size) const
322f2c95a08SAlexander Hansen {
323f2c95a08SAlexander Hansen     // randomize the name to enable parallel updates
324f2c95a08SAlexander Hansen     const std::string path = "/tmp/spi-device-image-" +
325f2c95a08SAlexander Hansen                              std::to_string(Software::getRandomId()) + ".bin";
326f2c95a08SAlexander Hansen 
327f2c95a08SAlexander Hansen     int fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
328f2c95a08SAlexander Hansen     if (fd < 0)
329f2c95a08SAlexander Hansen     {
330f2c95a08SAlexander Hansen         error("Failed to open file: {PATH}", "PATH", path);
331f2c95a08SAlexander Hansen         co_return 1;
332f2c95a08SAlexander Hansen     }
333f2c95a08SAlexander Hansen 
334f2c95a08SAlexander Hansen     const ssize_t bytesWritten = write(fd, image, image_size);
335f2c95a08SAlexander Hansen 
336f2c95a08SAlexander Hansen     close(fd);
337f2c95a08SAlexander Hansen 
338f2c95a08SAlexander Hansen     setUpdateProgress(30);
339f2c95a08SAlexander Hansen 
340f2c95a08SAlexander Hansen     if (bytesWritten < 0 || static_cast<size_t>(bytesWritten) != image_size)
341f2c95a08SAlexander Hansen     {
342f2c95a08SAlexander Hansen         error("Failed to write image to file");
343f2c95a08SAlexander Hansen         co_return 1;
344f2c95a08SAlexander Hansen     }
345f2c95a08SAlexander Hansen 
346f2c95a08SAlexander Hansen     debug("wrote {SIZE} bytes to {PATH}", "SIZE", bytesWritten, "PATH", path);
347f2c95a08SAlexander Hansen 
348f2c95a08SAlexander Hansen     auto devPath = getMTDDevicePath();
349f2c95a08SAlexander Hansen 
350f2c95a08SAlexander Hansen     if (!devPath.has_value())
351f2c95a08SAlexander Hansen     {
352f2c95a08SAlexander Hansen         co_return 1;
353f2c95a08SAlexander Hansen     }
354f2c95a08SAlexander Hansen 
3557fbe7d82SAlexander Hansen     size_t devNum = 0;
3567fbe7d82SAlexander Hansen 
3577fbe7d82SAlexander Hansen     try
3587fbe7d82SAlexander Hansen     {
3597fbe7d82SAlexander Hansen         devNum = std::stoi(devPath.value().substr(8));
3607fbe7d82SAlexander Hansen     }
3617fbe7d82SAlexander Hansen     catch (std::exception& e)
3627fbe7d82SAlexander Hansen     {
3637fbe7d82SAlexander Hansen         error("could not parse mtd device number from {STR}: {ERROR}", "STR",
3647fbe7d82SAlexander Hansen               devPath.value(), "ERROR", e);
3657fbe7d82SAlexander Hansen         co_return 1;
3667fbe7d82SAlexander Hansen     }
3677fbe7d82SAlexander Hansen 
3687fbe7d82SAlexander Hansen     std::string cmd = "flashrom -p linux_mtd:dev=" + std::to_string(devNum);
369f2c95a08SAlexander Hansen 
370f2c95a08SAlexander Hansen     if (layout == flashLayoutFlat)
371f2c95a08SAlexander Hansen     {
372f2c95a08SAlexander Hansen         cmd += " -w " + path;
373f2c95a08SAlexander Hansen     }
374f2c95a08SAlexander Hansen     else
375f2c95a08SAlexander Hansen     {
376f2c95a08SAlexander Hansen         error("unsupported flash layout");
377f2c95a08SAlexander Hansen 
378f2c95a08SAlexander Hansen         co_return 1;
379f2c95a08SAlexander Hansen     }
380f2c95a08SAlexander Hansen 
381f2c95a08SAlexander Hansen     debug("[flashrom] running {CMD}", "CMD", cmd);
382f2c95a08SAlexander Hansen 
383*a2eb951fSKevin Tung     auto success = co_await asyncSystem(ctx, cmd);
384f2c95a08SAlexander Hansen 
385f2c95a08SAlexander Hansen     std::filesystem::remove(path);
386f2c95a08SAlexander Hansen 
387*a2eb951fSKevin Tung     co_return success;
388f2c95a08SAlexander Hansen }
389f2c95a08SAlexander Hansen 
3905db0c6beSAlexander Hansen sdbusplus::async::task<bool> SPIDevice::writeSPIFlashWithFlashcp(
3915db0c6beSAlexander Hansen     const uint8_t* image, size_t image_size) const
3925db0c6beSAlexander Hansen {
3935db0c6beSAlexander Hansen     // randomize the name to enable parallel updates
3945db0c6beSAlexander Hansen     const std::string path = "/tmp/spi-device-image-" +
3955db0c6beSAlexander Hansen                              std::to_string(Software::getRandomId()) + ".bin";
3965db0c6beSAlexander Hansen 
3975db0c6beSAlexander Hansen     int fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
3985db0c6beSAlexander Hansen     if (fd < 0)
3995db0c6beSAlexander Hansen     {
4005db0c6beSAlexander Hansen         error("Failed to open file: {PATH}", "PATH", path);
4015db0c6beSAlexander Hansen         co_return 1;
4025db0c6beSAlexander Hansen     }
4035db0c6beSAlexander Hansen 
4045db0c6beSAlexander Hansen     const ssize_t bytesWritten = write(fd, image, image_size);
4055db0c6beSAlexander Hansen 
4065db0c6beSAlexander Hansen     close(fd);
4075db0c6beSAlexander Hansen 
4085db0c6beSAlexander Hansen     setUpdateProgress(30);
4095db0c6beSAlexander Hansen 
4105db0c6beSAlexander Hansen     if (bytesWritten < 0 || static_cast<size_t>(bytesWritten) != image_size)
4115db0c6beSAlexander Hansen     {
4125db0c6beSAlexander Hansen         error("Failed to write image to file");
4135db0c6beSAlexander Hansen         co_return 1;
4145db0c6beSAlexander Hansen     }
4155db0c6beSAlexander Hansen 
4165db0c6beSAlexander Hansen     debug("wrote {SIZE} bytes to {PATH}", "SIZE", bytesWritten, "PATH", path);
4175db0c6beSAlexander Hansen 
4185db0c6beSAlexander Hansen     auto devPath = getMTDDevicePath();
4195db0c6beSAlexander Hansen 
4205db0c6beSAlexander Hansen     if (!devPath.has_value())
4215db0c6beSAlexander Hansen     {
4225db0c6beSAlexander Hansen         co_return 1;
4235db0c6beSAlexander Hansen     }
4245db0c6beSAlexander Hansen 
4255db0c6beSAlexander Hansen     std::string cmd = std::format("flashcp -v {} {}", path, devPath.value());
4265db0c6beSAlexander Hansen 
4275db0c6beSAlexander Hansen     debug("running {CMD}", "CMD", cmd);
4285db0c6beSAlexander Hansen 
429*a2eb951fSKevin Tung     auto success = co_await asyncSystem(ctx, cmd);
4305db0c6beSAlexander Hansen 
4315db0c6beSAlexander Hansen     std::filesystem::remove(path);
4325db0c6beSAlexander Hansen 
433*a2eb951fSKevin Tung     co_return success;
writeSPIFlashWithFlashcp(const uint8_t * image,size_t image_size) const4345db0c6beSAlexander Hansen }
4355db0c6beSAlexander Hansen 
436f2c95a08SAlexander Hansen sdbusplus::async::task<bool> SPIDevice::writeSPIFlashDefault(
437f2c95a08SAlexander Hansen     const uint8_t* image, size_t image_size)
438f2c95a08SAlexander Hansen {
439f2c95a08SAlexander Hansen     auto devPath = getMTDDevicePath();
440f2c95a08SAlexander Hansen 
441f2c95a08SAlexander Hansen     if (!devPath.has_value())
442f2c95a08SAlexander Hansen     {
443f2c95a08SAlexander Hansen         co_return false;
444f2c95a08SAlexander Hansen     }
445f2c95a08SAlexander Hansen 
446f2c95a08SAlexander Hansen     int fd = open(devPath.value().c_str(), O_WRONLY);
447f2c95a08SAlexander Hansen     if (fd < 0)
448f2c95a08SAlexander Hansen     {
449f2c95a08SAlexander Hansen         error("Failed to open device: {PATH}", "PATH", devPath.value());
450f2c95a08SAlexander Hansen         co_return false;
451f2c95a08SAlexander Hansen     }
452f2c95a08SAlexander Hansen 
453f2c95a08SAlexander Hansen     // Write the image in chunks to avoid blocking for too long.
454f2c95a08SAlexander Hansen     // Also, to provide meaningful progress updates.
455f2c95a08SAlexander Hansen 
456f2c95a08SAlexander Hansen     const size_t chunk = static_cast<size_t>(1024 * 1024);
457f2c95a08SAlexander Hansen     ssize_t bytesWritten = 0;
458f2c95a08SAlexander Hansen 
459f2c95a08SAlexander Hansen     const int progressStart = 30;
460f2c95a08SAlexander Hansen     const int progressEnd = 90;
461f2c95a08SAlexander Hansen 
462f2c95a08SAlexander Hansen     for (size_t offset = 0; offset < image_size; offset += chunk)
463f2c95a08SAlexander Hansen     {
464f2c95a08SAlexander Hansen         const ssize_t written =
465f2c95a08SAlexander Hansen             write(fd, image + offset, std::min(chunk, image_size - offset));
466f2c95a08SAlexander Hansen 
467f2c95a08SAlexander Hansen         if (written < 0)
468f2c95a08SAlexander Hansen         {
469f2c95a08SAlexander Hansen             error("Failed to write to device");
470f2c95a08SAlexander Hansen             co_return false;
471f2c95a08SAlexander Hansen         }
472f2c95a08SAlexander Hansen 
473f2c95a08SAlexander Hansen         bytesWritten += written;
474f2c95a08SAlexander Hansen 
475f2c95a08SAlexander Hansen         setUpdateProgress(
476f2c95a08SAlexander Hansen             progressStart + int((progressEnd - progressStart) *
477f2c95a08SAlexander Hansen                                 (double(offset) / double(image_size))));
478f2c95a08SAlexander Hansen     }
479f2c95a08SAlexander Hansen 
480f2c95a08SAlexander Hansen     close(fd);
481f2c95a08SAlexander Hansen 
482f2c95a08SAlexander Hansen     if (static_cast<size_t>(bytesWritten) != image_size)
483f2c95a08SAlexander Hansen     {
484f2c95a08SAlexander Hansen         error("Incomplete write to device");
485f2c95a08SAlexander Hansen         co_return false;
486f2c95a08SAlexander Hansen     }
487f2c95a08SAlexander Hansen 
488f2c95a08SAlexander Hansen     debug("Successfully wrote {NBYTES} bytes to {PATH}", "NBYTES", bytesWritten,
489f2c95a08SAlexander Hansen           "PATH", devPath.value());
490f2c95a08SAlexander Hansen 
491f2c95a08SAlexander Hansen     co_return true;
492f2c95a08SAlexander Hansen }
493f2c95a08SAlexander Hansen 
494f2c95a08SAlexander Hansen std::string SPIDevice::getVersion()
495f2c95a08SAlexander Hansen {
496f2c95a08SAlexander Hansen     std::string version{};
497f2c95a08SAlexander Hansen     try
498f2c95a08SAlexander Hansen     {
499f2c95a08SAlexander Hansen         std::ifstream config(biosVersionPath);
500f2c95a08SAlexander Hansen 
501f2c95a08SAlexander Hansen         config >> version;
502f2c95a08SAlexander Hansen     }
503f2c95a08SAlexander Hansen     catch (std::exception& e)
504f2c95a08SAlexander Hansen     {
505f2c95a08SAlexander Hansen         error("Failed to get version with {ERROR}", "ERROR", e.what());
506f2c95a08SAlexander Hansen         version = versionUnknown;
507f2c95a08SAlexander Hansen     }
508f2c95a08SAlexander Hansen 
509f2c95a08SAlexander Hansen     if (version.empty())
510f2c95a08SAlexander Hansen     {
511f2c95a08SAlexander Hansen         version = versionUnknown;
512f2c95a08SAlexander Hansen     }
513f2c95a08SAlexander Hansen 
514f2c95a08SAlexander Hansen     return version;
515f2c95a08SAlexander Hansen }
516f2c95a08SAlexander Hansen 
517f2c95a08SAlexander Hansen auto SPIDevice::processUpdate(std::string versionFileName)
518f2c95a08SAlexander Hansen     -> sdbusplus::async::task<>
519f2c95a08SAlexander Hansen {
520f2c95a08SAlexander Hansen     if (biosVersionFilename != versionFileName)
521f2c95a08SAlexander Hansen     {
522f2c95a08SAlexander Hansen         error(
523f2c95a08SAlexander Hansen             "Update config file name '{NAME}' (!= '{EXPECTED}') is not expected",
524f2c95a08SAlexander Hansen             "NAME", versionFileName, "EXPECTED", biosVersionFilename);
525f2c95a08SAlexander Hansen         co_return;
526f2c95a08SAlexander Hansen     }
527f2c95a08SAlexander Hansen 
528f2c95a08SAlexander Hansen     if (softwareCurrent)
529f2c95a08SAlexander Hansen     {
530f2c95a08SAlexander Hansen         softwareCurrent->setVersion(getVersion());
531f2c95a08SAlexander Hansen     }
532f2c95a08SAlexander Hansen 
533f2c95a08SAlexander Hansen     co_return;
534f2c95a08SAlexander Hansen }
535f2c95a08SAlexander Hansen 
536f2c95a08SAlexander Hansen std::optional<std::string> SPIDevice::getMTDDevicePath() const
537f2c95a08SAlexander Hansen {
getVersion()538f2c95a08SAlexander Hansen     const std::string spiPath =
539f2c95a08SAlexander Hansen         "/sys/class/spi_master/spi" + std::to_string(spiControllerIndex) +
540f2c95a08SAlexander Hansen         "/spi" + std::to_string(spiControllerIndex) + "." +
541f2c95a08SAlexander Hansen         std::to_string(spiDeviceIndex) + "/mtd/";
542f2c95a08SAlexander Hansen 
543f2c95a08SAlexander Hansen     if (!std::filesystem::exists(spiPath))
544f2c95a08SAlexander Hansen     {
545f2c95a08SAlexander Hansen         error("Error: SPI path not found: {PATH}", "PATH", spiPath);
546f2c95a08SAlexander Hansen         return "";
547f2c95a08SAlexander Hansen     }
548f2c95a08SAlexander Hansen 
549f2c95a08SAlexander Hansen     for (const auto& entry : std::filesystem::directory_iterator(spiPath))
550f2c95a08SAlexander Hansen     {
551f2c95a08SAlexander Hansen         const std::string mtdName = entry.path().filename().string();
552f2c95a08SAlexander Hansen 
553f2c95a08SAlexander Hansen         if (mtdName.starts_with("mtd") && !mtdName.ends_with("ro"))
554f2c95a08SAlexander Hansen         {
555f2c95a08SAlexander Hansen             return "/dev/" + mtdName;
556f2c95a08SAlexander Hansen         }
557f2c95a08SAlexander Hansen     }
558f2c95a08SAlexander Hansen 
559f2c95a08SAlexander Hansen     error("Error: No MTD device found for spi {CONTROLLERINDEX}.{DEVICEINDEX}",
560f2c95a08SAlexander Hansen           "CONTROLLERINDEX", spiControllerIndex, "DEVICEINDEX", spiDeviceIndex);
processUpdate(std::string versionFileName)561f2c95a08SAlexander Hansen 
562f2c95a08SAlexander Hansen     return std::nullopt;
563f2c95a08SAlexander Hansen }
564