blob: 712c8015943cf0847a50ab150fefe11fa7451b03 [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));
Brandon Kimc49284b2022-06-17 09:55:26 -0700206
207 auto cachedHeader = bufferImpl->getCachedBufferHeader();
208 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr), 0x1234);
209}
210
211TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
212{
213 // Return write size that is not 4 which is sizeof(little_uint32_t)
214 constexpr size_t wrongWriteSize = 1;
215 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
216 .WillOnce(Return(wrongWriteSize));
217 EXPECT_THROW(
218 try {
219 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
220 } catch (const std::runtime_error& e) {
221 EXPECT_STREQ(
222 e.what(),
223 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
224 throw;
225 },
226 std::runtime_error);
227}
228
229TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
230{
231 constexpr size_t expectedWriteSize = 4;
232 constexpr uint8_t expectedBmcReadPtrOffset = 0x1c;
233 const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
234
235 EXPECT_CALL(*dataInterfaceMockPtr,
236 write(expectedBmcReadPtrOffset,
237 ElementsAreArray(expectedNewBmcFlagsVector)))
238 .WillOnce(Return(expectedWriteSize));
239 EXPECT_NO_THROW(
240 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
241
242 auto cachedHeader = bufferImpl->getCachedBufferHeader();
243 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
244 static_cast<uint32_t>(BmcFlags::ready));
Brandon Kimcf0b9752022-06-15 10:32:21 -0700245}
246
Brandon Kim9836cfa2022-06-15 11:21:11 -0700247class BufferWraparoundReadTest : public BufferTest
248{
249 protected:
250 BufferWraparoundReadTest()
251 {
Brandon Kim4662b1b2022-06-16 21:38:02 -0700252 initializeFuncMock();
253 }
254 void initializeFuncMock()
255 {
Brandon Kim9836cfa2022-06-15 11:21:11 -0700256 // Initialize the memory and the cachedBufferHeader
257 InSequence s;
258 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
259 .WillOnce(Return(testRegionSize));
Brandon Kim26660e92022-06-15 16:58:44 -0700260 size_t testProposedBufferSize = sizeof(struct CircularBufferHeader) +
261 testUeRegionSize + testQueueSize;
262 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700263 EXPECT_CALL(*dataInterfaceMockPtr,
264 write(0, ElementsAreArray(emptyArray)))
Brandon Kim26660e92022-06-15 16:58:44 -0700265 .WillOnce(Return(testProposedBufferSize));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700266
Brandon Kim4662b1b2022-06-16 21:38:02 -0700267 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700268 .WillOnce(Return(bufferHeaderSize));
269 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
270 testQueueSize, testUeRegionSize,
271 testMagicNumber));
272 }
273 static constexpr size_t expectedWriteSize = 2;
274 static constexpr uint8_t expectedBmcReadPtrOffset = 0x20;
275 static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700276
277 uint8_t* testInitializationHeaderPtr =
278 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700279};
280
Brandon Kim35d43352022-06-16 11:13:36 -0700281TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700282{
283 InSequence s;
Brandon Kim35d43352022-06-16 11:13:36 -0700284
285 size_t tooBigOffset = testQueueSize + 1;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700286 EXPECT_THROW(
287 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700288 bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700289 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700290 EXPECT_STREQ(
291 e.what(),
Brandon Kim35d43352022-06-16 11:13:36 -0700292 "[wraparoundRead] relativeOffset '257' was bigger than queueSize '256'");
293 throw;
294 },
295 std::runtime_error);
296
297 size_t tooBigLength = testQueueSize + 1;
298 EXPECT_THROW(
299 try {
300 bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
301 } catch (const std::runtime_error& e) {
302 EXPECT_STREQ(e.what(), "[wraparoundRead] length '257' was bigger "
303 "than queueSize '256'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700304 throw;
305 },
306 std::runtime_error);
307}
308
Brandon Kim35d43352022-06-16 11:13:36 -0700309TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700310{
311 InSequence s;
Brandon Kim35d43352022-06-16 11:13:36 -0700312 size_t testLength = 0x10;
313 size_t testOffset = 0x20;
314
315 // Fail the first read
316 std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
317 EXPECT_CALL(*dataInterfaceMockPtr,
318 read(testOffset + expectedqueueOffset, testLength))
319 .WillOnce(Return(shortTestBytesRead));
320
Brandon Kim9836cfa2022-06-15 11:21:11 -0700321 EXPECT_THROW(
322 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700323 bufferImpl->wraparoundRead(testOffset, testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700324 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700325 EXPECT_STREQ(e.what(),
326 "[wraparoundRead] Read '15' which was not the "
327 "requested length of '16'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700328 throw;
329 },
330 std::runtime_error);
331}
332
333TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
334{
335 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700336 size_t testLength = 0x10;
Brandon Kim35d43352022-06-16 11:13:36 -0700337 size_t testOffset = 0x20;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700338
339 // Successfully read all the requested length without a wrap around
340 std::vector<std::uint8_t> testBytesRead(testLength);
Brandon Kim35d43352022-06-16 11:13:36 -0700341 EXPECT_CALL(*dataInterfaceMockPtr,
342 read(testOffset + expectedqueueOffset, testLength))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700343 .WillOnce(Return(testBytesRead));
344
345 // Call to updateReadPtr is triggered
346 const std::vector<uint8_t> expectedReadPtr{
347 static_cast<uint8_t>(testOffset + testLength), 0x0};
348 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
349 ElementsAreArray(expectedReadPtr)))
350 .WillOnce(Return(expectedWriteSize));
351
352 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
353 ElementsAreArray(testBytesRead));
Brandon Kim35d43352022-06-16 11:13:36 -0700354 struct CircularBufferHeader cachedBufferHeader =
355 bufferImpl->getCachedBufferHeader();
356 // The bmcReadPtr should have been updated
357 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
358 testOffset + testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700359}
360
361TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
362{
363 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700364 size_t testBytesLeft = 3;
365 size_t testLength = 0x10;
Brandon Kim26660e92022-06-15 16:58:44 -0700366 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700367
Brandon Kim35d43352022-06-16 11:13:36 -0700368 // Read until the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700369 std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
Brandon Kim35d43352022-06-16 11:13:36 -0700370 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
371 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700372 .WillOnce(Return(testBytesReadShort));
373
374 // Read 1 byte short after wraparound
375 std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
376 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
377 .WillOnce(Return(testBytesLeftReadShort));
378
379 EXPECT_THROW(
380 try {
381 bufferImpl->wraparoundRead(testOffset, testLength);
382 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700383 EXPECT_STREQ(
384 e.what(),
385 "[wraparoundRead] Buffer wrapped around but read '2' which was "
386 "not the requested lenght of '3'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700387 throw;
388 },
389 std::runtime_error);
390}
391
392TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
393{
394 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700395 size_t testBytesLeft = 3;
396 size_t testLength = 0x10;
Brandon Kim26660e92022-06-15 16:58:44 -0700397 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700398
Brandon Kim35d43352022-06-16 11:13:36 -0700399 // Read to the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700400 std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
401 9, 8, 7, 6, 5, 4};
Brandon Kim35d43352022-06-16 11:13:36 -0700402 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
403 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700404 .WillOnce(Return(testBytesReadFirst));
405
406 std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
407 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
408 .WillOnce(Return(testBytesReadSecond));
409
410 // Call to updateReadPtr is triggered
411 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim26660e92022-06-15 16:58:44 -0700412 static_cast<uint8_t>(testBytesLeft), 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700413 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
414 ElementsAreArray(expectedReadPtr)))
415 .WillOnce(Return(expectedWriteSize));
416
417 std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
418 8, 7, 6, 5, 4, 3, 2, 1};
419 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
420 ElementsAreArray(expectedBytes));
Brandon Kim35d43352022-06-16 11:13:36 -0700421 struct CircularBufferHeader cachedBufferHeader =
422 bufferImpl->getCachedBufferHeader();
423 // The bmcReadPtr should have been updated to reflect the wraparound
424 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
425 testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700426}
427
Brandon Kim4662b1b2022-06-16 21:38:02 -0700428TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
429{
430 InSequence s;
431 size_t testBytesLeft = 0;
432 size_t testLength = 4;
433 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
434
435 // Read to the very end of the queue
436 std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
437 EXPECT_CALL(*dataInterfaceMockPtr,
438 read(testOffset + expectedqueueOffset, testLength))
439 .WillOnce(Return(testBytes));
440
441 // Call to updateReadPtr is triggered, since we read to the very end of the
442 // buffer, update the readPtr up around to 0
443 const std::vector<uint8_t> expectedReadPtr{0x0, 0x0};
444 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
445 ElementsAreArray(expectedReadPtr)))
446 .WillOnce(Return(expectedWriteSize));
447
448 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
449 ElementsAreArray(testBytes));
450 struct CircularBufferHeader cachedBufferHeader =
451 bufferImpl->getCachedBufferHeader();
452 // The bmcReadPtr should have been updated to reflect the wraparound
453 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
454 0);
455}
456
Brandon Kim40ce08e2022-06-15 16:58:44 -0700457class BufferEntryTest : public BufferWraparoundReadTest
Brandon Kim7bac2d62022-06-07 21:37:51 -0700458{
459 protected:
Brandon Kim40ce08e2022-06-15 16:58:44 -0700460 BufferEntryTest()
Brandon Kim7bac2d62022-06-07 21:37:51 -0700461 {
462 testEntryHeader.sequenceId = testSequenceId;
463 testEntryHeader.entrySize = testEntrySize;
464 testEntryHeader.checksum = testChecksum;
465 testEntryHeader.rdeCommandType = testRdeCommandType;
466 }
Brandon Kim40ce08e2022-06-15 16:58:44 -0700467 ~BufferEntryTest() override = default;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700468
Brandon Kim35d43352022-06-16 11:13:36 -0700469 void wraparoundReadMock(const uint32_t relativeOffset,
470 std::span<std::uint8_t> expetedBytesOutput)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700471 {
Brandon Kim35d43352022-06-16 11:13:36 -0700472 InSequence s;
473 const uint32_t queueSizeToQueueEnd = testQueueSize - relativeOffset;
474
475 // This will wrap, split the read mocks in 2
476 if (expetedBytesOutput.size() > queueSizeToQueueEnd)
477 {
478 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
479 .WillOnce(Return(std::vector<std::uint8_t>(
480 expetedBytesOutput.begin(),
481 expetedBytesOutput.begin() + queueSizeToQueueEnd)));
482 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
483 .WillOnce(Return(std::vector<std::uint8_t>(
484 expetedBytesOutput.begin() + queueSizeToQueueEnd,
485 expetedBytesOutput.end())));
486 }
487 else
488 {
489 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
490 .WillOnce(Return(std::vector<std::uint8_t>(
491 expetedBytesOutput.begin(), expetedBytesOutput.end())));
492 }
Brandon Kim7bac2d62022-06-07 21:37:51 -0700493
494 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
495 .WillOnce(Return(expectedWriteSize));
496 }
497
498 static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
499 static constexpr uint16_t testSequenceId = 0;
500 static constexpr uint16_t testEntrySize = 0x20;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700501 static constexpr uint8_t testRdeCommandType = 0x01;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700502 // Calculated checksum for the header
503 static constexpr uint8_t testChecksum =
504 (testSequenceId ^ testEntrySize ^ testRdeCommandType);
Brandon Kim35d43352022-06-16 11:13:36 -0700505 size_t testOffset = 0x20;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700506
507 struct QueueEntryHeader testEntryHeader
508 {};
509};
510
Brandon Kim40ce08e2022-06-15 16:58:44 -0700511TEST_F(BufferEntryTest, ReadEntryHeaderPass)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700512{
513 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
514 std::vector<uint8_t> testEntryHeaderVector(
515 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700516 wraparoundReadMock(testOffset, testEntryHeaderVector);
Brandon Kim7bac2d62022-06-07 21:37:51 -0700517 EXPECT_EQ(bufferImpl->readEntryHeader(testOffset), testEntryHeader);
Brandon Kim35d43352022-06-16 11:13:36 -0700518 // Check the bmcReadPtr
519 struct CircularBufferHeader cachedBufferHeader =
520 bufferImpl->getCachedBufferHeader();
521 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
522 testOffset + testEntryHeaderVector.size());
Brandon Kim7bac2d62022-06-07 21:37:51 -0700523}
524
Brandon Kim40ce08e2022-06-15 16:58:44 -0700525TEST_F(BufferEntryTest, ReadEntryChecksumFail)
526{
527 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700528 std::vector<uint8_t> testEntryVector(testEntrySize);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700529 // Offset the checksum by 1
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700530 testEntryHeader.checksum += 1;
Brandon Kim40ce08e2022-06-15 16:58:44 -0700531 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
532 std::vector<uint8_t> testEntryHeaderVector(
533 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700534 wraparoundReadMock(testOffset, testEntryHeaderVector);
535 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700536 EXPECT_THROW(
537 try {
538 bufferImpl->readEntry(testOffset);
539 } catch (const std::runtime_error& e) {
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700540 // Calculation: testChecksum (0x21) XOR (0x22) = 3
Brandon Kim40ce08e2022-06-15 16:58:44 -0700541 EXPECT_STREQ(e.what(),
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700542 "[readEntry] Checksum was '3', expected '0'");
Brandon Kim40ce08e2022-06-15 16:58:44 -0700543 throw;
544 },
545 std::runtime_error);
546}
547
548TEST_F(BufferEntryTest, ReadEntryPass)
549{
550 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700551 // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
552 // (20 times) = 0 therefore leave the checksum as is
Brandon Kim35d43352022-06-16 11:13:36 -0700553 std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700554 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
555 std::vector<uint8_t> testEntryHeaderVector(
556 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700557 // Set testOffset so that we can test the wraparound here as well on our
558 // second read for the entry (by 1 byte)
559 testOffset = testQueueSize - entryHeaderSize - 1;
560 wraparoundReadMock(testOffset, testEntryHeaderVector);
561 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700562
563 EntryPair testedEntryPair;
564 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry(testOffset));
565 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
566 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
Brandon Kim35d43352022-06-16 11:13:36 -0700567 struct CircularBufferHeader cachedBufferHeader =
568 bufferImpl->getCachedBufferHeader();
569 // The bmcReadPtr should have been updated to reflect the wraparound
570 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
571 testEntrySize - 1);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700572}
573
Brandon Kim4662b1b2022-06-16 21:38:02 -0700574class BufferReadErrorLogsTest : public BufferEntryTest
575{
576 protected:
577 BufferReadErrorLogsTest() = default;
578
579 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
580 size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
581};
582
583TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
584{
585 InSequence s;
586 // Set the biosWritePtr too big
587 testInitializationHeader.biosWritePtr =
588 boost::endian::native_to_little((testQueueSize + 1));
589 initializeFuncMock();
590
591 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
592 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
593 testInitializationHeaderPtr +
594 bufferHeaderSize)));
595 EXPECT_THROW(
596 try {
597 bufferImpl->readErrorLogs();
598 } catch (const std::runtime_error& e) {
599 EXPECT_STREQ(e.what(),
600 "[readErrorLogs] currentBiosWritePtr was '257' "
601 "which was bigger than queueSize '256'");
602 throw;
603 },
604 std::runtime_error);
605
606 // Reset the biosWritePtr and set the bmcReadPtr too big
607 testInitializationHeader.biosWritePtr = 0;
608 initializeFuncMock();
609 testInitializationHeader.bmcReadPtr =
610 boost::endian::native_to_little((testQueueSize + 1));
611 initializeFuncMock();
612
613 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
614 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
615 testInitializationHeaderPtr +
616 bufferHeaderSize)));
617 EXPECT_THROW(
618 try {
619 bufferImpl->readErrorLogs();
620 } catch (const std::runtime_error& e) {
621 EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '257' "
622 "which was bigger than queueSize '256'");
623 throw;
624 },
625 std::runtime_error);
626}
627
628TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
629{
630 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
631 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
632 testInitializationHeaderPtr +
633 bufferHeaderSize)));
634 EXPECT_NO_THROW(bufferImpl->readErrorLogs());
635}
636
637TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
638{
639 InSequence s;
640 // Set the biosWritePtr to 1 entryHeader + entry size
641 testInitializationHeader.biosWritePtr =
642 boost::endian::native_to_little((entryAndHeaderSize));
643 initializeFuncMock();
644 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
645 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
646 testInitializationHeaderPtr +
647 bufferHeaderSize)));
648 std::vector<uint8_t> testEntryHeaderVector(
649 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
650 std::vector<uint8_t> testEntryVector(testEntrySize);
651 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
652 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
653
654 std::vector<EntryPair> entryPairs;
655 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
656
657 // Check that we only read one entryPair and that the content is correct
Brandon Kim1a3dc602022-06-17 11:34:33 -0700658 EXPECT_EQ(entryPairs.size(), 1U);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700659 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
660 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
661}
662
663TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
664{
665 InSequence s;
666 // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
667 uint32_t entryAndHeaderSizeAwayFromEnd = testQueueSize - entryAndHeaderSize;
668 testInitializationHeader.bmcReadPtr =
669 boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
670 // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
671 testInitializationHeader.biosWritePtr = entryAndHeaderSize;
672 initializeFuncMock();
673 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
674 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
675 testInitializationHeaderPtr +
676 bufferHeaderSize)));
677
678 std::vector<uint8_t> testEntryHeaderVector(
679 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
680 std::vector<uint8_t> testEntryVector(testEntrySize);
681 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
682 testEntryHeaderVector);
683 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
684 entryHeaderSize,
685 testEntryVector);
686 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
687 testEntryHeaderVector);
688 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
689 entryHeaderSize,
690 testEntryVector);
691
692 std::vector<EntryPair> entryPairs;
693 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
694
695 // Check that we only read one entryPair and that the content is correct
696 EXPECT_EQ(entryPairs.size(), 2);
697 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
698 EXPECT_EQ(entryPairs[1].first, testEntryHeader);
699 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
700 EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
701}
702
703TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
704{
705 InSequence s;
706 testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
707 // Make the biosWritePtr intentially 1 smaller than expected
708 testInitializationHeader.biosWritePtr =
709 boost::endian::native_to_little(entryAndHeaderSize - 1);
710 initializeFuncMock();
711 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
712 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
713 testInitializationHeaderPtr +
714 bufferHeaderSize)));
715
716 std::vector<uint8_t> testEntryHeaderVector(
717 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
718 std::vector<uint8_t> testEntryVector(testEntrySize);
719 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
720 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
721
722 EXPECT_THROW(
723 try {
724 bufferImpl->readErrorLogs();
725 } catch (const std::runtime_error& e) {
726 EXPECT_STREQ(
727 e.what(),
728 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
729 "are not identical after reading through all the logs");
730 throw;
731 },
732 std::runtime_error);
733}
734
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700735} // namespace
736} // namespace bios_bmc_smm_error_logger