blob: 8649c27eadeeaad959508468cf0b58128a466f0e [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;
Brandon Kim26660e92022-06-15 16:58:44 -070065 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
66 .WillOnce(Return(testRegionSize));
67 EXPECT_THROW(
68 try {
69 // Test too big of a proposed buffer compared to the memori size
70 uint16_t bigQueueSize = 0x181;
71 uint16_t bigUeRegionSize = 0x50;
72 bufferImpl->initialize(testBmcInterfaceVersion, bigQueueSize,
73 bigUeRegionSize, testMagicNumber);
74 } catch (const std::runtime_error& e) {
75 EXPECT_STREQ(
76 e.what(),
77 "[initialize] Proposed region size '513' "
78 "is bigger than the BMC's allocated MMIO region of '512'");
79 throw;
80 },
81 std::runtime_error);
82 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070083
84 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
85 .WillOnce(Return(testRegionSize));
Brandon Kim26660e92022-06-15 16:58:44 -070086 size_t testProposedBufferSize =
87 sizeof(struct CircularBufferHeader) + testUeRegionSize + testQueueSize;
88 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070089 // Return a smaller write than the intended testRegionSize to test the error
90 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim26660e92022-06-15 16:58:44 -070091 .WillOnce(Return(testProposedBufferSize - 1));
Brandon Kimfcbc3db2022-06-06 21:26:18 -070092 EXPECT_THROW(
93 try {
94 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
95 testUeRegionSize, testMagicNumber);
96 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -070097 EXPECT_STREQ(e.what(), "[initialize] Only erased '383'");
Brandon Kimfcbc3db2022-06-06 21:26:18 -070098 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 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
104 .WillOnce(Return(testRegionSize));
105 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim26660e92022-06-15 16:58:44 -0700106 .WillOnce(Return(testProposedBufferSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700107 // Return a smaller write than the intended initializationHeader to test the
108 // error
109 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
110 EXPECT_THROW(
111 try {
112 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
113 testUeRegionSize, testMagicNumber);
114 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700115 EXPECT_STREQ(e.what(),
116 "[initialize] Only wrote '0' bytes of the header");
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700117 throw;
118 },
119 std::runtime_error);
Brandon Kim60cab572022-06-15 14:20:05 -0700120 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700121}
122
123TEST_F(BufferTest, BufferInitializePass)
124{
125 InSequence s;
126 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
127 .WillOnce(Return(testRegionSize));
Brandon Kim26660e92022-06-15 16:58:44 -0700128 size_t testProposedBufferSize =
129 sizeof(struct CircularBufferHeader) + testUeRegionSize + testQueueSize;
130 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700131 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim26660e92022-06-15 16:58:44 -0700132 .WillOnce(Return(testProposedBufferSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700133
134 uint8_t* testInitializationHeaderPtr =
135 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700136 EXPECT_CALL(*dataInterfaceMockPtr,
137 write(0, ElementsAreArray(testInitializationHeaderPtr,
Brandon Kim17ee1a92022-06-07 21:04:08 -0700138 bufferHeaderSize)))
139 .WillOnce(Return(bufferHeaderSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700140 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
141 testQueueSize, testUeRegionSize,
142 testMagicNumber));
Brandon Kim60cab572022-06-15 14:20:05 -0700143 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700144}
145
Brandon Kim17ee1a92022-06-07 21:04:08 -0700146TEST_F(BufferTest, BufferHeaderReadFail)
147{
148 std::vector<std::uint8_t> testBytesRead{};
149 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
150 .WillOnce(Return(testBytesRead));
151 EXPECT_THROW(
152 try {
153 bufferImpl->readBufferHeader();
154 } catch (const std::runtime_error& e) {
155 EXPECT_STREQ(e.what(),
156 "Buffer header read only read '0', expected '48'");
157 throw;
158 },
159 std::runtime_error);
160}
161
162TEST_F(BufferTest, BufferHeaderReadPass)
163{
164 uint8_t* testInitializationHeaderPtr =
165 reinterpret_cast<uint8_t*>(&testInitializationHeader);
166 std::vector<uint8_t> testInitializationHeaderVector(
167 testInitializationHeaderPtr,
168 testInitializationHeaderPtr + bufferHeaderSize);
169
170 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
171 .WillOnce(Return(testInitializationHeaderVector));
172 EXPECT_NO_THROW(bufferImpl->readBufferHeader());
173 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
174}
175
Brandon Kimcf0b9752022-06-15 10:32:21 -0700176TEST_F(BufferTest, BufferUpdateReadPtrFail)
177{
178 // Return write size that is not 2 which is sizeof(little_uint16_t)
179 constexpr size_t wrongWriteSize = 1;
180 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
181 .WillOnce(Return(wrongWriteSize));
182 EXPECT_THROW(
183 try {
184 bufferImpl->updateReadPtr(0);
185 } catch (const std::runtime_error& e) {
186 EXPECT_STREQ(
187 e.what(),
188 "[updateReadPtr] Wrote '1' bytes, instead of expected '2'");
189 throw;
190 },
191 std::runtime_error);
192}
193
194TEST_F(BufferTest, BufferUpdateReadPtrPass)
195{
196 constexpr size_t expectedWriteSize = 2;
197 constexpr uint8_t expectedBmcReadPtrOffset = 0x20;
198 // Check that we truncate the highest 16bits
199 const uint32_t testNewReadPtr = 0x99881234;
200 const std::vector<uint8_t> expectedReadPtr{0x34, 0x12};
201
202 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
203 ElementsAreArray(expectedReadPtr)))
204 .WillOnce(Return(expectedWriteSize));
205 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
206}
207
Brandon Kim9836cfa2022-06-15 11:21:11 -0700208class BufferWraparoundReadTest : public BufferTest
209{
210 protected:
211 BufferWraparoundReadTest()
212 {
213 // Initialize the memory and the cachedBufferHeader
214 InSequence s;
215 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
216 .WillOnce(Return(testRegionSize));
Brandon Kim26660e92022-06-15 16:58:44 -0700217 size_t testProposedBufferSize = sizeof(struct CircularBufferHeader) +
218 testUeRegionSize + testQueueSize;
219 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700220 EXPECT_CALL(*dataInterfaceMockPtr,
221 write(0, ElementsAreArray(emptyArray)))
Brandon Kim26660e92022-06-15 16:58:44 -0700222 .WillOnce(Return(testProposedBufferSize));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700223
224 uint8_t* testInitializationHeaderPtr =
225 reinterpret_cast<uint8_t*>(&testInitializationHeader);
226 EXPECT_CALL(*dataInterfaceMockPtr,
227 write(0, ElementsAreArray(testInitializationHeaderPtr,
228 bufferHeaderSize)))
229 .WillOnce(Return(bufferHeaderSize));
230 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
231 testQueueSize, testUeRegionSize,
232 testMagicNumber));
233 }
234 static constexpr size_t expectedWriteSize = 2;
235 static constexpr uint8_t expectedBmcReadPtrOffset = 0x20;
236 static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
237};
238
239TEST_F(BufferWraparoundReadTest, TooBigReadFail)
240{
241 InSequence s;
Brandon Kim26660e92022-06-15 16:58:44 -0700242 size_t tooBigLength = testQueueSize + 1;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700243 EXPECT_THROW(
244 try {
245 bufferImpl->wraparoundRead(/* offset */ 0, tooBigLength);
246 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700247 EXPECT_STREQ(
248 e.what(),
249 "[wraparoundRead] length '257' + additionalBoundaryCheck '0' "
250 "was bigger than queueSize '256'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700251 throw;
252 },
253 std::runtime_error);
254}
255
256TEST_F(BufferWraparoundReadTest, TooBigReadWithAdditionalBoundaryCheckFail)
257{
258 InSequence s;
Brandon Kim26660e92022-06-15 16:58:44 -0700259 // Use additionalBoundaryCheck to still go over the queueSize by 1
Brandon Kim9836cfa2022-06-15 11:21:11 -0700260 size_t additionalBoundaryCheck = 10;
Brandon Kim26660e92022-06-15 16:58:44 -0700261 size_t tooBigLength = testQueueSize - additionalBoundaryCheck + 1;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700262 EXPECT_THROW(
263 try {
264 bufferImpl->wraparoundRead(/* offset */ 0, tooBigLength,
265 additionalBoundaryCheck);
266 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700267 EXPECT_STREQ(
268 e.what(),
269 "[wraparoundRead] length '247' + additionalBoundaryCheck '10' "
270 "was bigger than queueSize '256'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700271 throw;
272 },
273 std::runtime_error);
274}
275
276TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
277{
278 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700279 size_t testLength = 0x10;
280 size_t testOffset = 0x50;
281
282 // Successfully read all the requested length without a wrap around
283 std::vector<std::uint8_t> testBytesRead(testLength);
284 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset, testLength))
285 .WillOnce(Return(testBytesRead));
286
287 // Call to updateReadPtr is triggered
288 const std::vector<uint8_t> expectedReadPtr{
289 static_cast<uint8_t>(testOffset + testLength), 0x0};
290 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
291 ElementsAreArray(expectedReadPtr)))
292 .WillOnce(Return(expectedWriteSize));
293
294 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
295 ElementsAreArray(testBytesRead));
296}
297
298TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
299{
300 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700301 size_t testBytesLeft = 3;
302 size_t testLength = 0x10;
Brandon Kim26660e92022-06-15 16:58:44 -0700303 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700304
305 // Read 3 bytes short
306 std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
307 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset, testLength))
308 .WillOnce(Return(testBytesReadShort));
309
310 // Read 1 byte short after wraparound
311 std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
312 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
313 .WillOnce(Return(testBytesLeftReadShort));
314
315 EXPECT_THROW(
316 try {
317 bufferImpl->wraparoundRead(testOffset, testLength);
318 } catch (const std::runtime_error& e) {
319 EXPECT_STREQ(e.what(),
320 "[wraparoundRead] Buffer wrapped around but was not "
321 "able to read all of the requested info. "
322 "Bytes remaining to read '1' of '16'");
323 throw;
324 },
325 std::runtime_error);
326}
327
328TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
329{
330 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700331 size_t testBytesLeft = 3;
332 size_t testLength = 0x10;
Brandon Kim26660e92022-06-15 16:58:44 -0700333 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700334
335 // Read 3 bytes short
336 std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
337 9, 8, 7, 6, 5, 4};
338 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset, testLength))
339 .WillOnce(Return(testBytesReadFirst));
340
341 std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
342 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
343 .WillOnce(Return(testBytesReadSecond));
344
345 // Call to updateReadPtr is triggered
346 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim26660e92022-06-15 16:58:44 -0700347 static_cast<uint8_t>(testBytesLeft), 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700348 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
349 ElementsAreArray(expectedReadPtr)))
350 .WillOnce(Return(expectedWriteSize));
351
352 std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
353 8, 7, 6, 5, 4, 3, 2, 1};
354 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
355 ElementsAreArray(expectedBytes));
356}
357
Brandon Kim40ce08e2022-06-15 16:58:44 -0700358class BufferEntryTest : public BufferWraparoundReadTest
Brandon Kim7bac2d62022-06-07 21:37:51 -0700359{
360 protected:
Brandon Kim40ce08e2022-06-15 16:58:44 -0700361 BufferEntryTest()
Brandon Kim7bac2d62022-06-07 21:37:51 -0700362 {
363 testEntryHeader.sequenceId = testSequenceId;
364 testEntryHeader.entrySize = testEntrySize;
365 testEntryHeader.checksum = testChecksum;
366 testEntryHeader.rdeCommandType = testRdeCommandType;
367 }
Brandon Kim40ce08e2022-06-15 16:58:44 -0700368 ~BufferEntryTest() override = default;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700369
370 void wraparoundReadMock(std::span<std::uint8_t> expetedBytesOutput)
371 {
Brandon Kim7bac2d62022-06-07 21:37:51 -0700372 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
373 .WillOnce(Return(std::vector<std::uint8_t>(
374 expetedBytesOutput.begin(), expetedBytesOutput.end())));
375
376 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
377 .WillOnce(Return(expectedWriteSize));
378 }
379
380 static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
381 static constexpr uint16_t testSequenceId = 0;
382 static constexpr uint16_t testEntrySize = 0x20;
Brandon Kim40ce08e2022-06-15 16:58:44 -0700383 // Calculated checksum for the header (0x100 - 0 - 0x20 - 0x01) & 0xff
384 static constexpr uint8_t testChecksum = 0xdf;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700385 static constexpr uint8_t testRdeCommandType = 0x01;
386 size_t testOffset = 0x50;
387
388 struct QueueEntryHeader testEntryHeader
389 {};
390};
391
Brandon Kim40ce08e2022-06-15 16:58:44 -0700392TEST_F(BufferEntryTest, ReadEntryHeaderPass)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700393{
394 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
395 std::vector<uint8_t> testEntryHeaderVector(
396 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
397 wraparoundReadMock(testEntryHeaderVector);
398 EXPECT_EQ(bufferImpl->readEntryHeader(testOffset), testEntryHeader);
399}
400
Brandon Kim40ce08e2022-06-15 16:58:44 -0700401TEST_F(BufferEntryTest, ReadEntryChecksumFail)
402{
403 InSequence s;
404 // We expect this will bump checksum up by "testEntrySize" = 0x20
405 std::vector<uint8_t> testEntryVector(testEntrySize, 1);
406 // Offset the checksum by 1
407 testEntryHeader.checksum -= (0x20 - 1);
408 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
409 std::vector<uint8_t> testEntryHeaderVector(
410 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
411 wraparoundReadMock(testEntryHeaderVector);
412
413 wraparoundReadMock(testEntryVector);
414 EXPECT_THROW(
415 try {
416 bufferImpl->readEntry(testOffset);
417 } catch (const std::runtime_error& e) {
418 EXPECT_STREQ(e.what(),
419 "[readEntry] Checksum was '1', expected '0'");
420 throw;
421 },
422 std::runtime_error);
423}
424
425TEST_F(BufferEntryTest, ReadEntryPass)
426{
427 InSequence s;
428 // We expect this will bump checksum up by "testEntrySize" = 0x40
429 std::vector<uint8_t> testEntryVector(testEntrySize, 2);
430 testEntryHeader.checksum -= (0x40);
431 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
432 std::vector<uint8_t> testEntryHeaderVector(
433 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
434 wraparoundReadMock(testEntryHeaderVector);
435 wraparoundReadMock(testEntryVector);
436
437 EntryPair testedEntryPair;
438 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry(testOffset));
439 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
440 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
441}
442
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700443} // namespace
444} // namespace bios_bmc_smm_error_logger