xref: /openbmc/phosphor-bmc-code-mgmt/test/common/device/device.cpp (revision 3a31f0ac322abb081603e04e3eb2e665e8814673)
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