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: 29 DeviceTest() : 30 exampleUpdater(ctx, true, "vUnknown"), 31 device(exampleUpdater.getDevice()) 32 {} 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 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 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 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 146 TEST_F(DeviceTest, TestDeviceStartUpdateImmediateSuccess) 147 { 148 ctx.spawn(testDeviceStartUpdateCommon(ctx, device, 149 RequestedApplyTimes::Immediate)); 150 ctx.run(); 151 } 152 153 TEST_F(DeviceTest, TestDeviceStartUpdateOnResetSuccess) 154 { 155 ctx.spawn( 156 testDeviceStartUpdateCommon(ctx, device, RequestedApplyTimes::OnReset)); 157 ctx.run(); 158 } 159 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 183 TEST_F(DeviceTest, TestDeviceStartUpdateInvalidFD) 184 { 185 ctx.spawn(testDeviceStartUpdateInvalidFD(ctx, device)); 186 ctx.run(); 187 } 188 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 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