1 #include "../exampledevice/example_device.hpp"
2 #include "test/create_package/create_pldm_fw_package.hpp"
3
4 #include <sys/mman.h>
5
6 #include <phosphor-logging/lg2.hpp>
7 #include <sdbusplus/asio/connection.hpp>
8 #include <sdbusplus/asio/object_server.hpp>
9 #include <sdbusplus/async.hpp>
10 #include <sdbusplus/server.hpp>
11 #include <xyz/openbmc_project/Association/Definitions/server.hpp>
12 #include <xyz/openbmc_project/Software/Update/server.hpp>
13
14 #include <memory>
15
16 #include <gtest/gtest.h>
17
18 PHOSPHOR_LOG2_USING;
19
20 using namespace phosphor::software;
21 using namespace phosphor::software::example_device;
22 using SoftwareActivationProgress =
23 sdbusplus::aserver::xyz::openbmc_project::software::ActivationProgress<
24 Software>;
25
26 class DeviceTest : public testing::Test
27 {
28 protected:
DeviceTest()29 DeviceTest() :
30 exampleUpdater(ctx, true, "vUnknown"),
31 device(exampleUpdater.getDevice())
32 {}
~DeviceTest()33 ~DeviceTest() noexcept override {}
34
35 sdbusplus::async::context ctx;
36 ExampleCodeUpdater exampleUpdater;
37 std::unique_ptr<ExampleDevice>& device;
38
39 public:
40 DeviceTest(const DeviceTest&) = delete;
41 DeviceTest(DeviceTest&&) = delete;
42 DeviceTest& operator=(const DeviceTest&) = delete;
43 DeviceTest& operator=(DeviceTest&&) = delete;
44
45 // @returns memfd
46 // @returns -1 on failure
47 static int createTestPkgMemfd();
48 };
49
createTestPkgMemfd()50 int DeviceTest::createTestPkgMemfd()
51 {
52 uint8_t component_image[] = {0x12, 0x34, 0x83, 0x21};
53
54 size_t sizeOut;
55 std::unique_ptr<uint8_t[]> buf = create_pldm_package_buffer(
56 component_image, sizeof(component_image),
57 std::optional<uint32_t>(exampleVendorIANA),
58 std::optional<std::string>(exampleCompatibleHardware), sizeOut);
59
60 const int fd = memfd_create("test_memfd", 0);
61
62 EXPECT_TRUE(fd >= 0);
63
64 debug("create fd {FD}", "FD", fd);
65
66 if (write(fd, (void*)buf.get(), sizeOut) == -1)
67 {
68 std::cerr << "Failed to write to memfd: " << strerror(errno)
69 << std::endl;
70 close(fd);
71 EXPECT_TRUE(false);
72 return -1;
73 }
74
75 if (lseek(fd, 0, SEEK_SET) != 0)
76 {
77 error("could not seek to the beginning of the file");
78 close(fd);
79 EXPECT_TRUE(false);
80 return -1;
81 }
82
83 return fd;
84 }
85
TEST_F(DeviceTest,TestDeviceConstructor)86 TEST_F(DeviceTest, TestDeviceConstructor)
87 {
88 EXPECT_TRUE(device->getEMConfigType().starts_with("Nop"));
89
90 // the software version is initialized
91 EXPECT_NE(device->softwareCurrent, nullptr);
92
93 // there is no pending update
94 EXPECT_EQ(device->softwarePending, nullptr);
95 }
96
testDeviceStartUpdateCommon(sdbusplus::async::context & ctx,std::unique_ptr<ExampleDevice> & device,RequestedApplyTimes applyTime)97 sdbusplus::async::task<> testDeviceStartUpdateCommon(
98 sdbusplus::async::context& ctx, std::unique_ptr<ExampleDevice>& device,
99 RequestedApplyTimes applyTime)
100 {
101 const Software* oldSoftware = device->softwareCurrent.get();
102
103 const int fd = DeviceTest::createTestPkgMemfd();
104
105 EXPECT_TRUE(fd >= 0);
106
107 if (fd < 0)
108 {
109 co_return;
110 }
111
112 std::unique_ptr<Software> softwareUpdate =
113 std::make_unique<Software>(ctx, *device);
114
115 const Software* newSoftware = softwareUpdate.get();
116
117 co_await device->startUpdateAsync(fd, applyTime, std::move(softwareUpdate));
118
119 EXPECT_TRUE(device->deviceSpecificUpdateFunctionCalled);
120
121 if (applyTime == RequestedApplyTimes::Immediate)
122 {
123 EXPECT_NE(device->softwareCurrent.get(), oldSoftware);
124 EXPECT_EQ(device->softwareCurrent.get(), newSoftware);
125
126 EXPECT_FALSE(device->softwarePending);
127 }
128
129 if (applyTime == RequestedApplyTimes::OnReset)
130 {
131 // assert that the old software is still the running version,
132 // since the apply time is 'OnReset'
133 EXPECT_EQ(device->softwareCurrent.get(), oldSoftware);
134
135 // assert that the updated software is present
136 EXPECT_EQ(device->softwarePending.get(), newSoftware);
137 }
138
139 close(fd);
140
141 ctx.request_stop();
142
143 co_return;
144 }
145
TEST_F(DeviceTest,TestDeviceStartUpdateImmediateSuccess)146 TEST_F(DeviceTest, TestDeviceStartUpdateImmediateSuccess)
147 {
148 ctx.spawn(testDeviceStartUpdateCommon(ctx, device,
149 RequestedApplyTimes::Immediate));
150 ctx.run();
151 }
152
TEST_F(DeviceTest,TestDeviceStartUpdateOnResetSuccess)153 TEST_F(DeviceTest, TestDeviceStartUpdateOnResetSuccess)
154 {
155 ctx.spawn(
156 testDeviceStartUpdateCommon(ctx, device, RequestedApplyTimes::OnReset));
157 ctx.run();
158 }
159
testDeviceStartUpdateInvalidFD(sdbusplus::async::context & ctx,std::unique_ptr<ExampleDevice> & device)160 sdbusplus::async::task<> testDeviceStartUpdateInvalidFD(
161 sdbusplus::async::context& ctx, std::unique_ptr<ExampleDevice>& device)
162 {
163 std::unique_ptr<SoftwareActivationProgress> activationProgress =
164 std::make_unique<SoftwareActivationProgress>(ctx, "/");
165
166 sdbusplus::message::unix_fd image;
167 image.fd = -1;
168
169 std::unique_ptr<Software> softwareUpdate =
170 std::make_unique<Software>(ctx, *device);
171
172 co_await device->startUpdateAsync(image, RequestedApplyTimes::Immediate,
173 std::move(softwareUpdate));
174
175 // assert the bad file descriptor was caught and we did not proceed
176 EXPECT_FALSE(device->deviceSpecificUpdateFunctionCalled);
177
178 ctx.request_stop();
179
180 co_return;
181 }
182
TEST_F(DeviceTest,TestDeviceStartUpdateInvalidFD)183 TEST_F(DeviceTest, TestDeviceStartUpdateInvalidFD)
184 {
185 ctx.spawn(testDeviceStartUpdateInvalidFD(ctx, device));
186 ctx.run();
187 }
188
testDeviceSpecificUpdateFunction(sdbusplus::async::context & ctx,std::unique_ptr<ExampleDevice> & device)189 sdbusplus::async::task<> testDeviceSpecificUpdateFunction(
190 sdbusplus::async::context& ctx, std::unique_ptr<ExampleDevice>& device)
191 {
192 uint8_t buffer[10];
193 size_t buffer_size = 10;
194
195 auto previousVersion = device->softwareCurrent->swid;
196
197 bool success =
198 co_await device->updateDevice((const uint8_t*)buffer, buffer_size);
199
200 EXPECT_TRUE(success);
201
202 EXPECT_NE(device->softwareCurrent, nullptr);
203 EXPECT_EQ(device->softwarePending, nullptr);
204
205 ctx.request_stop();
206
207 co_return;
208 }
209
TEST_F(DeviceTest,TestDeviceSpecificUpdateFunction)210 TEST_F(DeviceTest, TestDeviceSpecificUpdateFunction)
211 {
212 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
213 ctx.spawn(testDeviceSpecificUpdateFunction(ctx, device));
214 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
215 ctx.run();
216 }
217