1 #include "buffer.hpp"
2 #include "data_interface_mock.hpp"
3 
4 #include <boost/endian/arithmetic.hpp>
5 #include <boost/endian/conversion.hpp>
6 
7 #include <algorithm>
8 #include <array>
9 #include <cstdint>
10 #include <memory>
11 
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14 
15 namespace bios_bmc_smm_error_logger
16 {
17 namespace
18 {
19 
20 using ::testing::_;
21 using ::testing::ElementsAreArray;
22 using ::testing::InSequence;
23 using ::testing::Return;
24 
25 class BufferTest : public ::testing::Test
26 {
27   protected:
28     BufferTest() :
29         dataInterfaceMock(std::make_unique<DataInterfaceMock>()),
30         dataInterfaceMockPtr(dataInterfaceMock.get())
31     {
32         bufferImpl = std::make_unique<BufferImpl>(std::move(dataInterfaceMock));
33         testInitializationHeader.bmcInterfaceVersion = testBmcInterfaceVersion;
34         testInitializationHeader.queueSize = testQueueSize;
35         testInitializationHeader.ueRegionSize = testUeRegionSize;
36         std::transform(testMagicNumber.begin(), testMagicNumber.end(),
37                        testInitializationHeader.magicNumber.begin(),
38                        [](uint32_t number) -> little_uint32_t {
39                            return boost::endian::native_to_little(number);
40                        });
41     }
42     ~BufferTest() override = default;
43 
44     // CircularBufferHeader size is 0x30, ensure the test region is bigger
45     static constexpr size_t testRegionSize = 0x200;
46     static constexpr uint32_t testBmcInterfaceVersion = 123;
47     static constexpr uint16_t testQueueSize = 0x100;
48     static constexpr uint16_t testUeRegionSize = 0x50;
49     static constexpr std::array<uint32_t, 4> testMagicNumber = {
50         0x12345678, 0x22345678, 0x32345678, 0x42345678};
51     static constexpr size_t bufferHeaderSize =
52         sizeof(struct CircularBufferHeader);
53 
54     struct CircularBufferHeader testInitializationHeader
55     {};
56 
57     std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
58     DataInterfaceMock* dataInterfaceMockPtr;
59     std::unique_ptr<BufferImpl> bufferImpl;
60 };
61 
62 TEST_F(BufferTest, BufferInitializeEraseFail)
63 {
64     InSequence s;
65 
66     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
67         .WillOnce(Return(testRegionSize));
68     const std::vector<uint8_t> emptyArray(testRegionSize, 0);
69     // Return a smaller write than the intended testRegionSize to test the error
70     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
71         .WillOnce(Return(testRegionSize - 1));
72     EXPECT_THROW(
73         try {
74             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
75                                    testUeRegionSize, testMagicNumber);
76         } catch (const std::runtime_error& e) {
77             EXPECT_STREQ(e.what(), "Buffer initialization only erased '511'");
78             throw;
79         },
80         std::runtime_error);
81     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
82 
83     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
84         .WillOnce(Return(testRegionSize));
85     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
86         .WillOnce(Return(testRegionSize));
87     // Return a smaller write than the intended initializationHeader to test the
88     // error
89     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
90     EXPECT_THROW(
91         try {
92             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
93                                    testUeRegionSize, testMagicNumber);
94         } catch (const std::runtime_error& e) {
95             EXPECT_STREQ(
96                 e.what(),
97                 "Buffer initialization buffer header write only wrote '0'");
98             throw;
99         },
100         std::runtime_error);
101     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
102 }
103 
104 TEST_F(BufferTest, BufferInitializePass)
105 {
106     InSequence s;
107     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
108         .WillOnce(Return(testRegionSize));
109     const std::vector<uint8_t> emptyArray(testRegionSize, 0);
110     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
111         .WillOnce(Return(testRegionSize));
112 
113     uint8_t* testInitializationHeaderPtr =
114         reinterpret_cast<uint8_t*>(&testInitializationHeader);
115     EXPECT_CALL(*dataInterfaceMockPtr,
116                 write(0, ElementsAreArray(testInitializationHeaderPtr,
117                                           bufferHeaderSize)))
118         .WillOnce(Return(bufferHeaderSize));
119     EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
120                                            testQueueSize, testUeRegionSize,
121                                            testMagicNumber));
122     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
123 }
124 
125 TEST_F(BufferTest, BufferHeaderReadFail)
126 {
127     std::vector<std::uint8_t> testBytesRead{};
128     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
129         .WillOnce(Return(testBytesRead));
130     EXPECT_THROW(
131         try {
132             bufferImpl->readBufferHeader();
133         } catch (const std::runtime_error& e) {
134             EXPECT_STREQ(e.what(),
135                          "Buffer header read only read '0', expected '48'");
136             throw;
137         },
138         std::runtime_error);
139 }
140 
141 TEST_F(BufferTest, BufferHeaderReadPass)
142 {
143     uint8_t* testInitializationHeaderPtr =
144         reinterpret_cast<uint8_t*>(&testInitializationHeader);
145     std::vector<uint8_t> testInitializationHeaderVector(
146         testInitializationHeaderPtr,
147         testInitializationHeaderPtr + bufferHeaderSize);
148 
149     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
150         .WillOnce(Return(testInitializationHeaderVector));
151     EXPECT_NO_THROW(bufferImpl->readBufferHeader());
152     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
153 }
154 
155 TEST_F(BufferTest, BufferUpdateReadPtrFail)
156 {
157     // Return write size that is not 2 which is sizeof(little_uint16_t)
158     constexpr size_t wrongWriteSize = 1;
159     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
160         .WillOnce(Return(wrongWriteSize));
161     EXPECT_THROW(
162         try {
163             bufferImpl->updateReadPtr(0);
164         } catch (const std::runtime_error& e) {
165             EXPECT_STREQ(
166                 e.what(),
167                 "[updateReadPtr] Wrote '1' bytes, instead of expected '2'");
168             throw;
169         },
170         std::runtime_error);
171 }
172 
173 TEST_F(BufferTest, BufferUpdateReadPtrPass)
174 {
175     constexpr size_t expectedWriteSize = 2;
176     constexpr uint8_t expectedBmcReadPtrOffset = 0x20;
177     // Check that we truncate the highest 16bits
178     const uint32_t testNewReadPtr = 0x99881234;
179     const std::vector<uint8_t> expectedReadPtr{0x34, 0x12};
180 
181     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
182                                              ElementsAreArray(expectedReadPtr)))
183         .WillOnce(Return(expectedWriteSize));
184     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
185 }
186 
187 } // namespace
188 } // namespace bios_bmc_smm_error_logger
189