blob: 577e3221ca17255554d5ea766274c8848eec742b [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 {
Brandon Kim4662b1b2022-06-16 21:38:02 -0700213 initializeFuncMock();
214 }
215 void initializeFuncMock()
216 {
Brandon Kim9836cfa2022-06-15 11:21:11 -0700217 // Initialize the memory and the cachedBufferHeader
218 InSequence s;
219 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
220 .WillOnce(Return(testRegionSize));
Brandon Kim26660e92022-06-15 16:58:44 -0700221 size_t testProposedBufferSize = sizeof(struct CircularBufferHeader) +
222 testUeRegionSize + testQueueSize;
223 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700224 EXPECT_CALL(*dataInterfaceMockPtr,
225 write(0, ElementsAreArray(emptyArray)))
Brandon Kim26660e92022-06-15 16:58:44 -0700226 .WillOnce(Return(testProposedBufferSize));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700227
Brandon Kim4662b1b2022-06-16 21:38:02 -0700228 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700229 .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;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700237
238 uint8_t* testInitializationHeaderPtr =
239 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700240};
241
Brandon Kim35d43352022-06-16 11:13:36 -0700242TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700243{
244 InSequence s;
Brandon Kim35d43352022-06-16 11:13:36 -0700245
246 size_t tooBigOffset = testQueueSize + 1;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700247 EXPECT_THROW(
248 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700249 bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700250 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700251 EXPECT_STREQ(
252 e.what(),
Brandon Kim35d43352022-06-16 11:13:36 -0700253 "[wraparoundRead] relativeOffset '257' was bigger than queueSize '256'");
254 throw;
255 },
256 std::runtime_error);
257
258 size_t tooBigLength = testQueueSize + 1;
259 EXPECT_THROW(
260 try {
261 bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
262 } catch (const std::runtime_error& e) {
263 EXPECT_STREQ(e.what(), "[wraparoundRead] length '257' was bigger "
264 "than queueSize '256'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700265 throw;
266 },
267 std::runtime_error);
268}
269
Brandon Kim35d43352022-06-16 11:13:36 -0700270TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700271{
272 InSequence s;
Brandon Kim35d43352022-06-16 11:13:36 -0700273 size_t testLength = 0x10;
274 size_t testOffset = 0x20;
275
276 // Fail the first read
277 std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
278 EXPECT_CALL(*dataInterfaceMockPtr,
279 read(testOffset + expectedqueueOffset, testLength))
280 .WillOnce(Return(shortTestBytesRead));
281
Brandon Kim9836cfa2022-06-15 11:21:11 -0700282 EXPECT_THROW(
283 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700284 bufferImpl->wraparoundRead(testOffset, testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700285 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700286 EXPECT_STREQ(e.what(),
287 "[wraparoundRead] Read '15' which was not the "
288 "requested length of '16'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700289 throw;
290 },
291 std::runtime_error);
292}
293
294TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
295{
296 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700297 size_t testLength = 0x10;
Brandon Kim35d43352022-06-16 11:13:36 -0700298 size_t testOffset = 0x20;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700299
300 // Successfully read all the requested length without a wrap around
301 std::vector<std::uint8_t> testBytesRead(testLength);
Brandon Kim35d43352022-06-16 11:13:36 -0700302 EXPECT_CALL(*dataInterfaceMockPtr,
303 read(testOffset + expectedqueueOffset, testLength))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700304 .WillOnce(Return(testBytesRead));
305
306 // Call to updateReadPtr is triggered
307 const std::vector<uint8_t> expectedReadPtr{
308 static_cast<uint8_t>(testOffset + testLength), 0x0};
309 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
310 ElementsAreArray(expectedReadPtr)))
311 .WillOnce(Return(expectedWriteSize));
312
313 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
314 ElementsAreArray(testBytesRead));
Brandon Kim35d43352022-06-16 11:13:36 -0700315 struct CircularBufferHeader cachedBufferHeader =
316 bufferImpl->getCachedBufferHeader();
317 // The bmcReadPtr should have been updated
318 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
319 testOffset + testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700320}
321
322TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
323{
324 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700325 size_t testBytesLeft = 3;
326 size_t testLength = 0x10;
Brandon Kim26660e92022-06-15 16:58:44 -0700327 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700328
Brandon Kim35d43352022-06-16 11:13:36 -0700329 // Read until the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700330 std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
Brandon Kim35d43352022-06-16 11:13:36 -0700331 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
332 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700333 .WillOnce(Return(testBytesReadShort));
334
335 // Read 1 byte short after wraparound
336 std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
337 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
338 .WillOnce(Return(testBytesLeftReadShort));
339
340 EXPECT_THROW(
341 try {
342 bufferImpl->wraparoundRead(testOffset, testLength);
343 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700344 EXPECT_STREQ(
345 e.what(),
346 "[wraparoundRead] Buffer wrapped around but read '2' which was "
347 "not the requested lenght of '3'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700348 throw;
349 },
350 std::runtime_error);
351}
352
353TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
354{
355 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700356 size_t testBytesLeft = 3;
357 size_t testLength = 0x10;
Brandon Kim26660e92022-06-15 16:58:44 -0700358 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700359
Brandon Kim35d43352022-06-16 11:13:36 -0700360 // Read to the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700361 std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
362 9, 8, 7, 6, 5, 4};
Brandon Kim35d43352022-06-16 11:13:36 -0700363 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
364 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700365 .WillOnce(Return(testBytesReadFirst));
366
367 std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
368 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
369 .WillOnce(Return(testBytesReadSecond));
370
371 // Call to updateReadPtr is triggered
372 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim26660e92022-06-15 16:58:44 -0700373 static_cast<uint8_t>(testBytesLeft), 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700374 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
375 ElementsAreArray(expectedReadPtr)))
376 .WillOnce(Return(expectedWriteSize));
377
378 std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
379 8, 7, 6, 5, 4, 3, 2, 1};
380 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
381 ElementsAreArray(expectedBytes));
Brandon Kim35d43352022-06-16 11:13:36 -0700382 struct CircularBufferHeader cachedBufferHeader =
383 bufferImpl->getCachedBufferHeader();
384 // The bmcReadPtr should have been updated to reflect the wraparound
385 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
386 testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700387}
388
Brandon Kim4662b1b2022-06-16 21:38:02 -0700389TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
390{
391 InSequence s;
392 size_t testBytesLeft = 0;
393 size_t testLength = 4;
394 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
395
396 // Read to the very end of the queue
397 std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
398 EXPECT_CALL(*dataInterfaceMockPtr,
399 read(testOffset + expectedqueueOffset, testLength))
400 .WillOnce(Return(testBytes));
401
402 // Call to updateReadPtr is triggered, since we read to the very end of the
403 // buffer, update the readPtr up around to 0
404 const std::vector<uint8_t> expectedReadPtr{0x0, 0x0};
405 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
406 ElementsAreArray(expectedReadPtr)))
407 .WillOnce(Return(expectedWriteSize));
408
409 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
410 ElementsAreArray(testBytes));
411 struct CircularBufferHeader cachedBufferHeader =
412 bufferImpl->getCachedBufferHeader();
413 // The bmcReadPtr should have been updated to reflect the wraparound
414 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
415 0);
416}
417
Brandon Kim40ce08e2022-06-15 16:58:44 -0700418class BufferEntryTest : public BufferWraparoundReadTest
Brandon Kim7bac2d62022-06-07 21:37:51 -0700419{
420 protected:
Brandon Kim40ce08e2022-06-15 16:58:44 -0700421 BufferEntryTest()
Brandon Kim7bac2d62022-06-07 21:37:51 -0700422 {
423 testEntryHeader.sequenceId = testSequenceId;
424 testEntryHeader.entrySize = testEntrySize;
425 testEntryHeader.checksum = testChecksum;
426 testEntryHeader.rdeCommandType = testRdeCommandType;
427 }
Brandon Kim40ce08e2022-06-15 16:58:44 -0700428 ~BufferEntryTest() override = default;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700429
Brandon Kim35d43352022-06-16 11:13:36 -0700430 void wraparoundReadMock(const uint32_t relativeOffset,
431 std::span<std::uint8_t> expetedBytesOutput)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700432 {
Brandon Kim35d43352022-06-16 11:13:36 -0700433 InSequence s;
434 const uint32_t queueSizeToQueueEnd = testQueueSize - relativeOffset;
435
436 // This will wrap, split the read mocks in 2
437 if (expetedBytesOutput.size() > queueSizeToQueueEnd)
438 {
439 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
440 .WillOnce(Return(std::vector<std::uint8_t>(
441 expetedBytesOutput.begin(),
442 expetedBytesOutput.begin() + queueSizeToQueueEnd)));
443 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
444 .WillOnce(Return(std::vector<std::uint8_t>(
445 expetedBytesOutput.begin() + queueSizeToQueueEnd,
446 expetedBytesOutput.end())));
447 }
448 else
449 {
450 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
451 .WillOnce(Return(std::vector<std::uint8_t>(
452 expetedBytesOutput.begin(), expetedBytesOutput.end())));
453 }
Brandon Kim7bac2d62022-06-07 21:37:51 -0700454
455 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
456 .WillOnce(Return(expectedWriteSize));
457 }
458
459 static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
460 static constexpr uint16_t testSequenceId = 0;
461 static constexpr uint16_t testEntrySize = 0x20;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700462 static constexpr uint8_t testRdeCommandType = 0x01;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700463 // Calculated checksum for the header
464 static constexpr uint8_t testChecksum =
465 (testSequenceId ^ testEntrySize ^ testRdeCommandType);
Brandon Kim35d43352022-06-16 11:13:36 -0700466 size_t testOffset = 0x20;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700467
468 struct QueueEntryHeader testEntryHeader
469 {};
470};
471
Brandon Kim40ce08e2022-06-15 16:58:44 -0700472TEST_F(BufferEntryTest, ReadEntryHeaderPass)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700473{
474 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
475 std::vector<uint8_t> testEntryHeaderVector(
476 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700477 wraparoundReadMock(testOffset, testEntryHeaderVector);
Brandon Kim7bac2d62022-06-07 21:37:51 -0700478 EXPECT_EQ(bufferImpl->readEntryHeader(testOffset), testEntryHeader);
Brandon Kim35d43352022-06-16 11:13:36 -0700479 // Check the bmcReadPtr
480 struct CircularBufferHeader cachedBufferHeader =
481 bufferImpl->getCachedBufferHeader();
482 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
483 testOffset + testEntryHeaderVector.size());
Brandon Kim7bac2d62022-06-07 21:37:51 -0700484}
485
Brandon Kim40ce08e2022-06-15 16:58:44 -0700486TEST_F(BufferEntryTest, ReadEntryChecksumFail)
487{
488 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700489 std::vector<uint8_t> testEntryVector(testEntrySize);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700490 // Offset the checksum by 1
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700491 testEntryHeader.checksum += 1;
Brandon Kim40ce08e2022-06-15 16:58:44 -0700492 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
493 std::vector<uint8_t> testEntryHeaderVector(
494 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700495 wraparoundReadMock(testOffset, testEntryHeaderVector);
496 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700497 EXPECT_THROW(
498 try {
499 bufferImpl->readEntry(testOffset);
500 } catch (const std::runtime_error& e) {
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700501 // Calculation: testChecksum (0x21) XOR (0x22) = 3
Brandon Kim40ce08e2022-06-15 16:58:44 -0700502 EXPECT_STREQ(e.what(),
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700503 "[readEntry] Checksum was '3', expected '0'");
Brandon Kim40ce08e2022-06-15 16:58:44 -0700504 throw;
505 },
506 std::runtime_error);
507}
508
509TEST_F(BufferEntryTest, ReadEntryPass)
510{
511 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700512 // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
513 // (20 times) = 0 therefore leave the checksum as is
Brandon Kim35d43352022-06-16 11:13:36 -0700514 std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700515 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
516 std::vector<uint8_t> testEntryHeaderVector(
517 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700518 // Set testOffset so that we can test the wraparound here as well on our
519 // second read for the entry (by 1 byte)
520 testOffset = testQueueSize - entryHeaderSize - 1;
521 wraparoundReadMock(testOffset, testEntryHeaderVector);
522 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700523
524 EntryPair testedEntryPair;
525 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry(testOffset));
526 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
527 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
Brandon Kim35d43352022-06-16 11:13:36 -0700528 struct CircularBufferHeader cachedBufferHeader =
529 bufferImpl->getCachedBufferHeader();
530 // The bmcReadPtr should have been updated to reflect the wraparound
531 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
532 testEntrySize - 1);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700533}
534
Brandon Kim4662b1b2022-06-16 21:38:02 -0700535class BufferReadErrorLogsTest : public BufferEntryTest
536{
537 protected:
538 BufferReadErrorLogsTest() = default;
539
540 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
541 size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
542};
543
544TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
545{
546 InSequence s;
547 // Set the biosWritePtr too big
548 testInitializationHeader.biosWritePtr =
549 boost::endian::native_to_little((testQueueSize + 1));
550 initializeFuncMock();
551
552 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
553 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
554 testInitializationHeaderPtr +
555 bufferHeaderSize)));
556 EXPECT_THROW(
557 try {
558 bufferImpl->readErrorLogs();
559 } catch (const std::runtime_error& e) {
560 EXPECT_STREQ(e.what(),
561 "[readErrorLogs] currentBiosWritePtr was '257' "
562 "which was bigger than queueSize '256'");
563 throw;
564 },
565 std::runtime_error);
566
567 // Reset the biosWritePtr and set the bmcReadPtr too big
568 testInitializationHeader.biosWritePtr = 0;
569 initializeFuncMock();
570 testInitializationHeader.bmcReadPtr =
571 boost::endian::native_to_little((testQueueSize + 1));
572 initializeFuncMock();
573
574 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
575 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
576 testInitializationHeaderPtr +
577 bufferHeaderSize)));
578 EXPECT_THROW(
579 try {
580 bufferImpl->readErrorLogs();
581 } catch (const std::runtime_error& e) {
582 EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '257' "
583 "which was bigger than queueSize '256'");
584 throw;
585 },
586 std::runtime_error);
587}
588
589TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
590{
591 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
592 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
593 testInitializationHeaderPtr +
594 bufferHeaderSize)));
595 EXPECT_NO_THROW(bufferImpl->readErrorLogs());
596}
597
598TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
599{
600 InSequence s;
601 // Set the biosWritePtr to 1 entryHeader + entry size
602 testInitializationHeader.biosWritePtr =
603 boost::endian::native_to_little((entryAndHeaderSize));
604 initializeFuncMock();
605 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
606 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
607 testInitializationHeaderPtr +
608 bufferHeaderSize)));
609 std::vector<uint8_t> testEntryHeaderVector(
610 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
611 std::vector<uint8_t> testEntryVector(testEntrySize);
612 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
613 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
614
615 std::vector<EntryPair> entryPairs;
616 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
617
618 // Check that we only read one entryPair and that the content is correct
619 EXPECT_EQ(entryPairs.size(), 1);
620 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
621 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
622}
623
624TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
625{
626 InSequence s;
627 // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
628 uint32_t entryAndHeaderSizeAwayFromEnd = testQueueSize - entryAndHeaderSize;
629 testInitializationHeader.bmcReadPtr =
630 boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
631 // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
632 testInitializationHeader.biosWritePtr = entryAndHeaderSize;
633 initializeFuncMock();
634 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
635 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
636 testInitializationHeaderPtr +
637 bufferHeaderSize)));
638
639 std::vector<uint8_t> testEntryHeaderVector(
640 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
641 std::vector<uint8_t> testEntryVector(testEntrySize);
642 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
643 testEntryHeaderVector);
644 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
645 entryHeaderSize,
646 testEntryVector);
647 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
648 testEntryHeaderVector);
649 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
650 entryHeaderSize,
651 testEntryVector);
652
653 std::vector<EntryPair> entryPairs;
654 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
655
656 // Check that we only read one entryPair and that the content is correct
657 EXPECT_EQ(entryPairs.size(), 2);
658 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
659 EXPECT_EQ(entryPairs[1].first, testEntryHeader);
660 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
661 EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
662}
663
664TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
665{
666 InSequence s;
667 testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
668 // Make the biosWritePtr intentially 1 smaller than expected
669 testInitializationHeader.biosWritePtr =
670 boost::endian::native_to_little(entryAndHeaderSize - 1);
671 initializeFuncMock();
672 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
673 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
674 testInitializationHeaderPtr +
675 bufferHeaderSize)));
676
677 std::vector<uint8_t> testEntryHeaderVector(
678 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
679 std::vector<uint8_t> testEntryVector(testEntrySize);
680 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
681 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
682
683 EXPECT_THROW(
684 try {
685 bufferImpl->readErrorLogs();
686 } catch (const std::runtime_error& e) {
687 EXPECT_STREQ(
688 e.what(),
689 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
690 "are not identical after reading through all the logs");
691 throw;
692 },
693 std::runtime_error);
694}
695
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700696} // namespace
697} // namespace bios_bmc_smm_error_logger