blob: 9042f676c71ead27540f2b8f1f57e5f685320f7c [file] [log] [blame]
Brandon Kimfcbc3db2022-06-06 21:26:18 -07001#include "buffer.hpp"
2#include "data_interface_mock.hpp"
3
Brandon Kim17ee1a92022-06-07 21:04:08 -07004#include <boost/endian/arithmetic.hpp>
5#include <boost/endian/conversion.hpp>
6
7#include <algorithm>
Brandon Kimfcbc3db2022-06-06 21:26:18 -07008#include <array>
9#include <cstdint>
10#include <memory>
11
12#include <gmock/gmock.h>
13#include <gtest/gtest.h>
14
15namespace bios_bmc_smm_error_logger
16{
17namespace
18{
19
20using ::testing::_;
21using ::testing::ElementsAreArray;
22using ::testing::InSequence;
23using ::testing::Return;
24
25class 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;
Brandon Kim17ee1a92022-06-07 21:04:08 -070036 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 });
Brandon Kimfcbc3db2022-06-06 21:26:18 -070041 }
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};
Brandon Kim17ee1a92022-06-07 21:04:08 -070051 static constexpr size_t bufferHeaderSize =
52 sizeof(struct CircularBufferHeader);
53
Brandon Kimfcbc3db2022-06-06 21:26:18 -070054 struct CircularBufferHeader testInitializationHeader
55 {};
56
57 std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
58 DataInterfaceMock* dataInterfaceMockPtr;
Brandon Kimfcbc3db2022-06-06 21:26:18 -070059 std::unique_ptr<BufferImpl> bufferImpl;
60};
61
62TEST_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);
Brandon Kim60cab572022-06-15 14:20:05 -070081 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070082
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);
Brandon Kim60cab572022-06-15 14:20:05 -0700101 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700102}
103
104TEST_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);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700115 EXPECT_CALL(*dataInterfaceMockPtr,
116 write(0, ElementsAreArray(testInitializationHeaderPtr,
Brandon Kim17ee1a92022-06-07 21:04:08 -0700117 bufferHeaderSize)))
118 .WillOnce(Return(bufferHeaderSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700119 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
120 testQueueSize, testUeRegionSize,
121 testMagicNumber));
Brandon Kim60cab572022-06-15 14:20:05 -0700122 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700123}
124
Brandon Kim17ee1a92022-06-07 21:04:08 -0700125TEST_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
141TEST_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
Brandon Kimcf0b9752022-06-15 10:32:21 -0700155TEST_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
173TEST_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
Brandon Kim9836cfa2022-06-15 11:21:11 -0700187class BufferWraparoundReadTest : public BufferTest
188{
189 protected:
190 BufferWraparoundReadTest()
191 {
192 // Initialize the memory and the cachedBufferHeader
193 InSequence s;
194 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
195 .WillOnce(Return(testRegionSize));
196 const std::vector<uint8_t> emptyArray(testRegionSize, 0);
197 EXPECT_CALL(*dataInterfaceMockPtr,
198 write(0, ElementsAreArray(emptyArray)))
199 .WillOnce(Return(testRegionSize));
200
201 uint8_t* testInitializationHeaderPtr =
202 reinterpret_cast<uint8_t*>(&testInitializationHeader);
203 EXPECT_CALL(*dataInterfaceMockPtr,
204 write(0, ElementsAreArray(testInitializationHeaderPtr,
205 bufferHeaderSize)))
206 .WillOnce(Return(bufferHeaderSize));
207 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
208 testQueueSize, testUeRegionSize,
209 testMagicNumber));
210 }
211 static constexpr size_t expectedWriteSize = 2;
212 static constexpr uint8_t expectedBmcReadPtrOffset = 0x20;
213 static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
214};
215
216TEST_F(BufferWraparoundReadTest, TooBigReadFail)
217{
218 InSequence s;
219 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
220 .WillOnce(Return(testRegionSize));
221 size_t tooBigLength = testRegionSize - expectedqueueOffset + 1;
222 EXPECT_THROW(
223 try {
224 bufferImpl->wraparoundRead(/* offset */ 0, tooBigLength);
225 } catch (const std::runtime_error& e) {
226 EXPECT_STREQ(e.what(),
227 "[wraparoundRead] queueOffset '128' + length '385' + "
228 "additionalBoundaryCheck '0' + was "
229 "bigger than memoryRegionSize '512'");
230 throw;
231 },
232 std::runtime_error);
233}
234
235TEST_F(BufferWraparoundReadTest, TooBigReadWithAdditionalBoundaryCheckFail)
236{
237 InSequence s;
238 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
239 .WillOnce(Return(testRegionSize));
240 // Use additionalBoundaryCheck to still go over the memoryRegionSize by 1
241 size_t additionalBoundaryCheck = 10;
242 size_t tooBigLength =
243 testRegionSize - expectedqueueOffset - additionalBoundaryCheck + 1;
244 EXPECT_THROW(
245 try {
246 bufferImpl->wraparoundRead(/* offset */ 0, tooBigLength,
247 additionalBoundaryCheck);
248 } catch (const std::runtime_error& e) {
249 EXPECT_STREQ(e.what(),
250 "[wraparoundRead] queueOffset '128' + length '375' + "
251 "additionalBoundaryCheck '10' + was "
252 "bigger than memoryRegionSize '512'");
253 throw;
254 },
255 std::runtime_error);
256}
257
258TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
259{
260 InSequence s;
261 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
262 .WillOnce(Return(testRegionSize));
263 size_t testLength = 0x10;
264 size_t testOffset = 0x50;
265
266 // Successfully read all the requested length without a wrap around
267 std::vector<std::uint8_t> testBytesRead(testLength);
268 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset, testLength))
269 .WillOnce(Return(testBytesRead));
270
271 // Call to updateReadPtr is triggered
272 const std::vector<uint8_t> expectedReadPtr{
273 static_cast<uint8_t>(testOffset + testLength), 0x0};
274 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
275 ElementsAreArray(expectedReadPtr)))
276 .WillOnce(Return(expectedWriteSize));
277
278 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
279 ElementsAreArray(testBytesRead));
280}
281
282TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
283{
284 InSequence s;
285 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
286 .WillOnce(Return(testRegionSize));
287 size_t testBytesLeft = 3;
288 size_t testLength = 0x10;
289 size_t testOffset = testRegionSize - (testLength - testBytesLeft);
290
291 // Read 3 bytes short
292 std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
293 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset, testLength))
294 .WillOnce(Return(testBytesReadShort));
295
296 // Read 1 byte short after wraparound
297 std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
298 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
299 .WillOnce(Return(testBytesLeftReadShort));
300
301 EXPECT_THROW(
302 try {
303 bufferImpl->wraparoundRead(testOffset, testLength);
304 } catch (const std::runtime_error& e) {
305 EXPECT_STREQ(e.what(),
306 "[wraparoundRead] Buffer wrapped around but was not "
307 "able to read all of the requested info. "
308 "Bytes remaining to read '1' of '16'");
309 throw;
310 },
311 std::runtime_error);
312}
313
314TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
315{
316 InSequence s;
317 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
318 .WillOnce(Return(testRegionSize));
319 size_t testBytesLeft = 3;
320 size_t testLength = 0x10;
321 size_t testOffset = testRegionSize - (testLength - testBytesLeft);
322
323 // Read 3 bytes short
324 std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
325 9, 8, 7, 6, 5, 4};
326 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset, testLength))
327 .WillOnce(Return(testBytesReadFirst));
328
329 std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
330 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
331 .WillOnce(Return(testBytesReadSecond));
332
333 // Call to updateReadPtr is triggered
334 const std::vector<uint8_t> expectedReadPtr{
335 static_cast<uint8_t>(expectedqueueOffset + testBytesLeft), 0x0};
336 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
337 ElementsAreArray(expectedReadPtr)))
338 .WillOnce(Return(expectedWriteSize));
339
340 std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
341 8, 7, 6, 5, 4, 3, 2, 1};
342 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
343 ElementsAreArray(expectedBytes));
344}
345
Brandon Kim7bac2d62022-06-07 21:37:51 -0700346class BufferEntryHeaderTest : public BufferWraparoundReadTest
347{
348 protected:
349 BufferEntryHeaderTest()
350 {
351 testEntryHeader.sequenceId = testSequenceId;
352 testEntryHeader.entrySize = testEntrySize;
353 testEntryHeader.checksum = testChecksum;
354 testEntryHeader.rdeCommandType = testRdeCommandType;
355 }
356 ~BufferEntryHeaderTest() override = default;
357
358 void wraparoundReadMock(std::span<std::uint8_t> expetedBytesOutput)
359 {
360 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
361 .WillOnce(Return(testRegionSize));
362 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
363 .WillOnce(Return(std::vector<std::uint8_t>(
364 expetedBytesOutput.begin(), expetedBytesOutput.end())));
365
366 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
367 .WillOnce(Return(expectedWriteSize));
368 }
369
370 static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
371 static constexpr uint16_t testSequenceId = 0;
372 static constexpr uint16_t testEntrySize = 0x20;
373 static constexpr uint8_t testChecksum = 1;
374 static constexpr uint8_t testRdeCommandType = 0x01;
375 size_t testOffset = 0x50;
376
377 struct QueueEntryHeader testEntryHeader
378 {};
379};
380
381TEST_F(BufferEntryHeaderTest, ReadEntryHeaderPass)
382{
383 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
384 std::vector<uint8_t> testEntryHeaderVector(
385 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
386 wraparoundReadMock(testEntryHeaderVector);
387 EXPECT_EQ(bufferImpl->readEntryHeader(testOffset), testEntryHeader);
388}
389
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700390} // namespace
391} // namespace bios_bmc_smm_error_logger