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