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 
82     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
83         .WillOnce(Return(testRegionSize));
84     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
85         .WillOnce(Return(testRegionSize));
86     // Return a smaller write than the intended initializationHeader to test the
87     // error
88     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
89     EXPECT_THROW(
90         try {
91             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
92                                    testUeRegionSize, testMagicNumber);
93         } catch (const std::runtime_error& e) {
94             EXPECT_STREQ(
95                 e.what(),
96                 "Buffer initialization buffer header write only wrote '0'");
97             throw;
98         },
99         std::runtime_error);
100 }
101 
102 TEST_F(BufferTest, BufferInitializePass)
103 {
104     InSequence s;
105     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
106         .WillOnce(Return(testRegionSize));
107     const std::vector<uint8_t> emptyArray(testRegionSize, 0);
108     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
109         .WillOnce(Return(testRegionSize));
110 
111     uint8_t* testInitializationHeaderPtr =
112         reinterpret_cast<uint8_t*>(&testInitializationHeader);
113     EXPECT_CALL(*dataInterfaceMockPtr,
114                 write(0, ElementsAreArray(testInitializationHeaderPtr,
115                                           bufferHeaderSize)))
116         .WillOnce(Return(bufferHeaderSize));
117     EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
118                                            testQueueSize, testUeRegionSize,
119                                            testMagicNumber));
120 }
121 
122 TEST_F(BufferTest, BufferHeaderReadFail)
123 {
124     std::vector<std::uint8_t> testBytesRead{};
125     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
126         .WillOnce(Return(testBytesRead));
127     EXPECT_THROW(
128         try {
129             bufferImpl->readBufferHeader();
130         } catch (const std::runtime_error& e) {
131             EXPECT_STREQ(e.what(),
132                          "Buffer header read only read '0', expected '48'");
133             throw;
134         },
135         std::runtime_error);
136 }
137 
138 TEST_F(BufferTest, BufferHeaderReadPass)
139 {
140     uint8_t* testInitializationHeaderPtr =
141         reinterpret_cast<uint8_t*>(&testInitializationHeader);
142     std::vector<uint8_t> testInitializationHeaderVector(
143         testInitializationHeaderPtr,
144         testInitializationHeaderPtr + bufferHeaderSize);
145 
146     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
147         .WillOnce(Return(testInitializationHeaderVector));
148     EXPECT_NO_THROW(bufferImpl->readBufferHeader());
149     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
150 }
151 
152 } // namespace
153 } // namespace bios_bmc_smm_error_logger
154