1 /* The goal of these tests is to verify that once a host-client has started the
2  * process with one blob bundle, they cannot pivot to upload data to another.
3  *
4  * This prevent someone from starting to upload a BMC firmware, and then midway
5  * through start uploading a BIOS image.
6  */
7 #include "firmware_handler.hpp"
8 #include "flags.hpp"
9 #include "image_mock.hpp"
10 #include "status.hpp"
11 #include "triggerable_mock.hpp"
12 #include "util.hpp"
13 
14 #include <memory>
15 #include <string>
16 #include <unordered_map>
17 #include <vector>
18 
19 #include <gtest/gtest.h>
20 
21 namespace ipmi_flash
22 {
23 namespace
24 {
25 
26 using ::testing::Return;
27 
28 class IpmiOnlyTwoFirmwaresTest : public ::testing::Test
29 {
30   protected:
31     void SetUp() override
32     {
33         std::unique_ptr<ImageHandlerInterface> image =
34             std::make_unique<ImageHandlerMock>();
35         hashImageMock = reinterpret_cast<ImageHandlerMock*>(image.get());
36         blobs.emplace_back(hashBlobId, std::move(image));
37 
38         image = std::make_unique<ImageHandlerMock>();
39         staticImageMock = reinterpret_cast<ImageHandlerMock*>(image.get());
40         blobs.emplace_back(staticLayoutBlobId, std::move(image));
41 
42         image = std::make_unique<ImageHandlerMock>();
43         biosImageMock = reinterpret_cast<ImageHandlerMock*>(image.get());
44         blobs.emplace_back(biosBlobId, std::move(image));
45 
46         std::unique_ptr<TriggerableActionInterface> bmcPrepareMock =
47             std::make_unique<TriggerMock>();
48         bmcPrepareMockPtr =
49             reinterpret_cast<TriggerMock*>(bmcPrepareMock.get());
50 
51         std::unique_ptr<TriggerableActionInterface> bmcVerifyMock =
52             std::make_unique<TriggerMock>();
53         bmcVerifyMockPtr = reinterpret_cast<TriggerMock*>(bmcVerifyMock.get());
54 
55         std::unique_ptr<TriggerableActionInterface> bmcUpdateMock =
56             std::make_unique<TriggerMock>();
57         bmcUpdateMockPtr = reinterpret_cast<TriggerMock*>(bmcUpdateMock.get());
58 
59         std::unique_ptr<TriggerableActionInterface> biosPrepareMock =
60             std::make_unique<TriggerMock>();
61         biosPrepareMockPtr =
62             reinterpret_cast<TriggerMock*>(biosPrepareMock.get());
63 
64         std::unique_ptr<TriggerableActionInterface> biosVerifyMock =
65             std::make_unique<TriggerMock>();
66         biosVerifyMockPtr =
67             reinterpret_cast<TriggerMock*>(biosVerifyMock.get());
68 
69         std::unique_ptr<TriggerableActionInterface> biosUpdateMock =
70             std::make_unique<TriggerMock>();
71         biosUpdateMockPtr =
72             reinterpret_cast<TriggerMock*>(biosUpdateMock.get());
73 
74         ActionMap packs;
75 
76         std::unique_ptr<ActionPack> bmcPack = std::make_unique<ActionPack>();
77         bmcPack->preparation = std::move(bmcPrepareMock);
78         bmcPack->verification = std::move(bmcVerifyMock);
79         bmcPack->update = std::move(bmcUpdateMock);
80 
81         std::unique_ptr<ActionPack> biosPack = std::make_unique<ActionPack>();
82         biosPack->preparation = std::move(biosPrepareMock);
83         biosPack->verification = std::move(biosVerifyMock);
84         biosPack->update = std::move(biosUpdateMock);
85 
86         packs[staticLayoutBlobId] = std::move(bmcPack);
87         packs[biosBlobId] = std::move(biosPack);
88 
89         std::vector<DataHandlerPack> data;
90         data.emplace_back(FirmwareFlags::UpdateFlags::ipmi, nullptr);
91 
92         handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(
93             std::move(blobs), std::move(data), std::move(packs));
94     }
95 
96     void expectedState(FirmwareBlobHandler::UpdateState state)
97     {
98         auto realHandler = dynamic_cast<FirmwareBlobHandler*>(handler.get());
99         EXPECT_EQ(state, realHandler->getCurrentState());
100     }
101 
102     ImageHandlerMock *hashImageMock, *staticImageMock, *biosImageMock;
103 
104     std::vector<HandlerPack> blobs;
105 
106     std::unique_ptr<blobs::GenericBlobInterface> handler;
107 
108     TriggerMock *bmcPrepareMockPtr, *bmcVerifyMockPtr, *bmcUpdateMockPtr;
109     TriggerMock *biosPrepareMockPtr, *biosVerifyMockPtr, *biosUpdateMockPtr;
110 
111     std::uint16_t session = 1;
112     std::uint16_t flags =
113         blobs::OpenFlags::write | FirmwareFlags::UpdateFlags::ipmi;
114 };
115 
116 TEST_F(IpmiOnlyTwoFirmwaresTest, OpeningBiosAfterBlobFails)
117 {
118     /* You can only have one file open at a time, and the first firmware file
119      * you open locks it down
120      */
121     EXPECT_CALL(*staticImageMock, open(staticLayoutBlobId, std::ios::out))
122         .WillOnce(Return(true));
123     EXPECT_CALL(*bmcPrepareMockPtr, trigger()).WillOnce(Return(true));
124 
125     EXPECT_TRUE(handler->open(session, flags, staticLayoutBlobId));
126     expectedState(FirmwareBlobHandler::UpdateState::uploadInProgress);
127 
128     EXPECT_CALL(*staticImageMock, close()).WillOnce(Return());
129     handler->close(session);
130 
131     expectedState(FirmwareBlobHandler::UpdateState::verificationPending);
132 
133     EXPECT_CALL(*biosImageMock, open(biosBlobId, std::ios::out)).Times(0);
134     EXPECT_FALSE(handler->open(session, flags, biosBlobId));
135 }
136 
137 TEST_F(IpmiOnlyTwoFirmwaresTest, OpeningHashBeforeBiosSucceeds)
138 {
139     /* Opening the hash blob does nothing special in this regard. */
140     EXPECT_CALL(*hashImageMock, open(hashBlobId, std::ios::out))
141         .WillOnce(Return(true));
142     EXPECT_TRUE(handler->open(session, flags, hashBlobId));
143 
144     expectedState(FirmwareBlobHandler::UpdateState::uploadInProgress);
145 
146     EXPECT_CALL(*hashImageMock, close()).WillOnce(Return());
147     handler->close(session);
148 
149     expectedState(FirmwareBlobHandler::UpdateState::verificationPending);
150     ASSERT_FALSE(handler->canHandleBlob(verifyBlobId));
151 
152     EXPECT_CALL(*biosImageMock, open(biosBlobId, std::ios::out))
153         .WillOnce(Return(true));
154     EXPECT_TRUE(handler->open(session, flags, biosBlobId));
155 
156     expectedState(FirmwareBlobHandler::UpdateState::uploadInProgress);
157 
158     EXPECT_CALL(*biosImageMock, close()).WillOnce(Return());
159     handler->close(session);
160 }
161 
162 } // namespace
163 } // namespace ipmi_flash
164