blob: 9c29fbb79814830740c7edfdd4fb0cfe3ffe8959 [file] [log] [blame]
Alexander Hansenf5346672025-11-03 12:53:59 +01001
2#include "../exampledevice/example_device.hpp"
3#include "test/create_package/create_pldm_fw_package.hpp"
4
5#include <fcntl.h>
6#include <inttypes.h>
7#include <sys/mman.h>
8#include <unistd.h>
9
10#include <phosphor-logging/lg2.hpp>
11#include <sdbusplus/async/context.hpp>
12#include <xyz/openbmc_project/Association/Definitions/client.hpp>
13#include <xyz/openbmc_project/Software/Update/client.hpp>
14#include <xyz/openbmc_project/Software/Version/client.hpp>
15
16#include <cstdlib>
17#include <cstring>
18
19#include <gtest/gtest.h>
20
21PHOSPHOR_LOG2_USING;
22
23using namespace phosphor::software;
24using namespace phosphor::software::example_device;
25
26using SoftwareActivationProgress =
27 sdbusplus::aserver::xyz::openbmc_project::software::ActivationProgress<
28 Software>;
29
30sdbusplus::async::task<> testSoftwareUpdateCommon(
31 sdbusplus::async::context& ctx, int fd, bool expectNewVersion)
32{
33 ExampleCodeUpdater exampleUpdater(ctx, true, "v12.345");
34
35 auto& device = exampleUpdater.getDevice();
36
37 device->softwareCurrent->enableUpdate({RequestedApplyTimes::Immediate});
38
39 std::string objPathCurrentSoftware =
40 reinterpret_cast<ExampleSoftware*>(device->softwareCurrent.get())
41 ->objectPath;
42
43 auto busName = exampleUpdater.getBusName();
44
45 // go via dbus to call the dbus method to start the update
46 auto client =
47 sdbusplus::client::xyz::openbmc_project::software::Update<>(ctx)
48 .service(busName)
49 .path(objPathCurrentSoftware);
50
51 sdbusplus::message::object_path objPathNewSoftware =
52 co_await client.start_update(fd, RequestedApplyTimes::Immediate);
53
54 EXPECT_NE(objPathNewSoftware, objPathCurrentSoftware);
55
56 auto clientNewVersion =
57 sdbusplus::client::xyz::openbmc_project::software::Version<>(ctx)
58 .service(busName)
59 .path(objPathNewSoftware.str);
60
61 // call the client for new version to appear within timeout
62 std::string newVersion;
63 ssize_t timeout = 500;
64 while (timeout > 0)
65 {
66 co_await sdbusplus::async::sleep_for(ctx,
67 std::chrono::milliseconds(50));
68 try
69 {
70 debug("Test: querying new version");
71 newVersion = co_await clientNewVersion.version();
72 break;
73 }
74 catch (std::exception& _)
75 {
76 timeout -= 50;
77 }
78 }
79
80 EXPECT_EQ(timeout > 0, expectNewVersion);
81
82 // assert that update function was called
83 EXPECT_EQ(device->deviceSpecificUpdateFunctionCalled, expectNewVersion);
84
85 if (expectNewVersion)
86 {
87 EXPECT_EQ(newVersion, exampleVersion);
88 EXPECT_EQ(device->softwareCurrent->swid, objPathNewSoftware.filename());
89 }
90
91 ctx.request_stop();
92
93 co_return;
94}
95
96static int makeUpdateFd(const std::string& compatible,
97 const uint32_t vendorIANA, bool corrupted)
98{
99 int fd = memfd_create("test_memfd", 0);
100 EXPECT_GE(fd, 0);
101
102 if (fd < 0)
103 {
104 return fd;
105 }
106
107 debug("create fd {FD}", "FD", fd);
108
109 uint8_t component_image[] = {0x12, 0x34, 0x83, 0x21};
110
111 size_t size_out = 0;
112 std::unique_ptr<uint8_t[]> buf = create_pldm_package_buffer(
113 component_image, sizeof(component_image),
114 std::optional<uint32_t>(vendorIANA),
115 std::optional<std::string>(compatible), size_out);
116
117 if (corrupted)
118 {
119 buf[3] = 0x8;
120 buf[9] = 0x3;
121 }
122
123 ssize_t bytes_written = write(fd, (void*)buf.get(), size_out);
124 EXPECT_NE(bytes_written, -1)
125 << "Failed to write to memfd: " << strerror(errno);
126 if (bytes_written == -1)
127 {
128 close(fd);
129 return -1;
130 }
131
132 EXPECT_EQ(lseek(fd, 0, SEEK_SET), 0)
133 << "could not seek to the beginning of the file";
134 return fd;
135}
136
137void testcaseSoftwareUpdateCommon(const int fd, bool expectSuccess)
138{
139 ASSERT_GE(fd, 0);
140
141 sdbusplus::async::context ctx;
142
143 ctx.spawn(testSoftwareUpdateCommon(ctx, fd, expectSuccess));
144
145 ctx.run();
146 close(fd);
147}
148
149TEST(SoftwareUpdate, TestSoftwareUpdateSuccess)
150{
151 const int fd =
152 makeUpdateFd(exampleCompatibleHardware, exampleVendorIANA, false);
153
154 testcaseSoftwareUpdateCommon(fd, true);
155}
156
157TEST(SoftwareUpdate, TestSoftwareUpdateFailureWrongCompatible)
158{
159 const int fd = makeUpdateFd("not_compatible", exampleVendorIANA, false);
160
161 testcaseSoftwareUpdateCommon(fd, false);
162}
163
164TEST(SoftwareUpdate, TestSoftwareUpdateFailureWrongVendorIANA)
165{
166 const int fd = makeUpdateFd(exampleCompatibleHardware, 0x03289, false);
167
168 testcaseSoftwareUpdateCommon(fd, false);
169}
170
171TEST(SoftwareUpdate, TestSoftwareUpdateFailureCorruptedPackage)
172{
173 const int fd =
174 makeUpdateFd(exampleCompatibleHardware, exampleVendorIANA, true);
175
176 testcaseSoftwareUpdateCommon(fd, false);
177}