xref: /openbmc/phosphor-bmc-code-mgmt/test/common/software/software_update.cpp (revision c1b36628298d7799677680e70d455f85acf83650)
1 
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 
21 PHOSPHOR_LOG2_USING;
22 
23 using namespace phosphor::software;
24 using namespace phosphor::software::example_device;
25 
26 using SoftwareActivationProgress =
27     sdbusplus::aserver::xyz::openbmc_project::software::ActivationProgress<
28         Software>;
29 
30 sdbusplus::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 
96 static 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 
137 void 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 
149 TEST(SoftwareUpdate, TestSoftwareUpdateSuccess)
150 {
151     const int fd =
152         makeUpdateFd(exampleCompatibleHardware, exampleVendorIANA, false);
153 
154     testcaseSoftwareUpdateCommon(fd, true);
155 }
156 
157 TEST(SoftwareUpdate, TestSoftwareUpdateFailureWrongCompatible)
158 {
159     const int fd = makeUpdateFd("not_compatible", exampleVendorIANA, false);
160 
161     testcaseSoftwareUpdateCommon(fd, false);
162 }
163 
164 TEST(SoftwareUpdate, TestSoftwareUpdateFailureWrongVendorIANA)
165 {
166     const int fd = makeUpdateFd(exampleCompatibleHardware, 0x03289, false);
167 
168     testcaseSoftwareUpdateCommon(fd, false);
169 }
170 
171 TEST(SoftwareUpdate, TestSoftwareUpdateFailureCorruptedPackage)
172 {
173     const int fd =
174         makeUpdateFd(exampleCompatibleHardware, exampleVendorIANA, true);
175 
176     testcaseSoftwareUpdateCommon(fd, false);
177 }
178