blob: a78897aa3722b32c9bf361c050cd59682de442dc [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;
Brandon Kim3def3c82022-09-13 05:29:20 +000047 static constexpr uint32_t testQueueSize = 0x200;
Brandon Kimfcbc3db2022-06-06 21:26:18 -070048 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
Brandon Kim3def3c82022-09-13 05:29:20 +000070 uint16_t bigQueueSize = 0x201;
Brandon Kim26660e92022-06-15 16:58:44 -070071 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(),
Brandon Kim3def3c82022-09-13 05:29:20 +000077 "[initialize] Proposed queue size '513' is bigger than the BMC's allocated MMIO region of '512'");
Brandon Kim26660e92022-06-15 16:58:44 -070078 throw;
79 },
80 std::runtime_error);
81 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070082
83 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
84 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +000085 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070086 // Return a smaller write than the intended testRegionSize to test the error
87 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +000088 .WillOnce(Return(testQueueSize - 1));
Brandon Kimfcbc3db2022-06-06 21:26:18 -070089 EXPECT_THROW(
90 try {
91 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
92 testUeRegionSize, testMagicNumber);
93 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +000094 EXPECT_STREQ(e.what(), "[initialize] Only erased '511'");
Brandon Kimfcbc3db2022-06-06 21:26:18 -070095 throw;
96 },
97 std::runtime_error);
Brandon Kim60cab572022-06-15 14:20:05 -070098 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070099
100 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
101 .WillOnce(Return(testRegionSize));
102 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000103 .WillOnce(Return(testQueueSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700104 // Return a smaller write than the intended initializationHeader to test the
105 // error
106 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
107 EXPECT_THROW(
108 try {
109 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
110 testUeRegionSize, testMagicNumber);
111 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700112 EXPECT_STREQ(e.what(),
113 "[initialize] Only wrote '0' bytes of the header");
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700114 throw;
115 },
116 std::runtime_error);
Brandon Kim60cab572022-06-15 14:20:05 -0700117 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700118}
119
120TEST_F(BufferTest, BufferInitializePass)
121{
122 InSequence s;
123 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
124 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +0000125 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700126 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000127 .WillOnce(Return(testQueueSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700128
129 uint8_t* testInitializationHeaderPtr =
130 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700131 EXPECT_CALL(*dataInterfaceMockPtr,
132 write(0, ElementsAreArray(testInitializationHeaderPtr,
Brandon Kim17ee1a92022-06-07 21:04:08 -0700133 bufferHeaderSize)))
134 .WillOnce(Return(bufferHeaderSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700135 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
136 testQueueSize, testUeRegionSize,
137 testMagicNumber));
Brandon Kim60cab572022-06-15 14:20:05 -0700138 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700139}
140
Brandon Kim17ee1a92022-06-07 21:04:08 -0700141TEST_F(BufferTest, BufferHeaderReadFail)
142{
143 std::vector<std::uint8_t> testBytesRead{};
144 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
145 .WillOnce(Return(testBytesRead));
146 EXPECT_THROW(
147 try {
148 bufferImpl->readBufferHeader();
149 } catch (const std::runtime_error& e) {
150 EXPECT_STREQ(e.what(),
151 "Buffer header read only read '0', expected '48'");
152 throw;
153 },
154 std::runtime_error);
155}
156
157TEST_F(BufferTest, BufferHeaderReadPass)
158{
159 uint8_t* testInitializationHeaderPtr =
160 reinterpret_cast<uint8_t*>(&testInitializationHeader);
161 std::vector<uint8_t> testInitializationHeaderVector(
162 testInitializationHeaderPtr,
163 testInitializationHeaderPtr + bufferHeaderSize);
164
165 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
166 .WillOnce(Return(testInitializationHeaderVector));
167 EXPECT_NO_THROW(bufferImpl->readBufferHeader());
168 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
169}
170
Brandon Kimcf0b9752022-06-15 10:32:21 -0700171TEST_F(BufferTest, BufferUpdateReadPtrFail)
172{
173 // Return write size that is not 2 which is sizeof(little_uint16_t)
174 constexpr size_t wrongWriteSize = 1;
175 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
176 .WillOnce(Return(wrongWriteSize));
177 EXPECT_THROW(
178 try {
179 bufferImpl->updateReadPtr(0);
180 } catch (const std::runtime_error& e) {
181 EXPECT_STREQ(
182 e.what(),
Brandon Kim271d2312022-09-02 16:34:55 +0000183 "[updateReadPtr] Wrote '1' bytes, instead of expected '3'");
Brandon Kimcf0b9752022-06-15 10:32:21 -0700184 throw;
185 },
186 std::runtime_error);
187}
188
189TEST_F(BufferTest, BufferUpdateReadPtrPass)
190{
Brandon Kim271d2312022-09-02 16:34:55 +0000191 constexpr size_t expectedWriteSize = 3;
192 constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
193 // Check that we truncate the highest 24bits
Brandon Kimcf0b9752022-06-15 10:32:21 -0700194 const uint32_t testNewReadPtr = 0x99881234;
Brandon Kim271d2312022-09-02 16:34:55 +0000195 const std::vector<uint8_t> expectedReadPtr{0x34, 0x12, 0x88};
Brandon Kimcf0b9752022-06-15 10:32:21 -0700196
197 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
198 ElementsAreArray(expectedReadPtr)))
199 .WillOnce(Return(expectedWriteSize));
200 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
Brandon Kimc49284b2022-06-17 09:55:26 -0700201
202 auto cachedHeader = bufferImpl->getCachedBufferHeader();
Brandon Kim271d2312022-09-02 16:34:55 +0000203 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr),
204 0x881234);
Brandon Kimc49284b2022-06-17 09:55:26 -0700205}
206
207TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
208{
209 // Return write size that is not 4 which is sizeof(little_uint32_t)
210 constexpr size_t wrongWriteSize = 1;
211 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
212 .WillOnce(Return(wrongWriteSize));
213 EXPECT_THROW(
214 try {
215 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
216 } catch (const std::runtime_error& e) {
217 EXPECT_STREQ(
218 e.what(),
219 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
220 throw;
221 },
222 std::runtime_error);
223}
224
225TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
226{
227 constexpr size_t expectedWriteSize = 4;
Brandon Kim271d2312022-09-02 16:34:55 +0000228 constexpr uint8_t expectedBmcReadPtrOffset = 0x1d;
Brandon Kimc49284b2022-06-17 09:55:26 -0700229 const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
230
231 EXPECT_CALL(*dataInterfaceMockPtr,
232 write(expectedBmcReadPtrOffset,
233 ElementsAreArray(expectedNewBmcFlagsVector)))
234 .WillOnce(Return(expectedWriteSize));
235 EXPECT_NO_THROW(
236 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
237
238 auto cachedHeader = bufferImpl->getCachedBufferHeader();
239 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
240 static_cast<uint32_t>(BmcFlags::ready));
Brandon Kimcf0b9752022-06-15 10:32:21 -0700241}
242
Brandon Kim9836cfa2022-06-15 11:21:11 -0700243class BufferWraparoundReadTest : public BufferTest
244{
245 protected:
246 BufferWraparoundReadTest()
247 {
Brandon Kim4662b1b2022-06-16 21:38:02 -0700248 initializeFuncMock();
249 }
250 void initializeFuncMock()
251 {
Brandon Kim9836cfa2022-06-15 11:21:11 -0700252 // Initialize the memory and the cachedBufferHeader
253 InSequence s;
254 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
255 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +0000256 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700257 EXPECT_CALL(*dataInterfaceMockPtr,
258 write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000259 .WillOnce(Return(testQueueSize));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700260
Brandon Kim4662b1b2022-06-16 21:38:02 -0700261 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700262 .WillOnce(Return(bufferHeaderSize));
263 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
264 testQueueSize, testUeRegionSize,
265 testMagicNumber));
266 }
Brandon Kim271d2312022-09-02 16:34:55 +0000267 static constexpr size_t expectedWriteSize = 3;
268 static constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700269 static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700270
Brandon Kim3def3c82022-09-13 05:29:20 +0000271 static constexpr size_t testMaxOffset =
272 testQueueSize - testUeRegionSize - sizeof(struct CircularBufferHeader);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700273 uint8_t* testInitializationHeaderPtr =
274 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700275};
276
Brandon Kim3def3c82022-09-13 05:29:20 +0000277TEST_F(BufferWraparoundReadTest, GetMaxOffsetTest)
278{
279 EXPECT_EQ(bufferImpl->getMaxOffset(), testMaxOffset);
280}
281
Brandon Kim35d43352022-06-16 11:13:36 -0700282TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700283{
284 InSequence s;
Brandon Kim3def3c82022-09-13 05:29:20 +0000285 size_t tooBigOffset = testMaxOffset + 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 Kim3def3c82022-09-13 05:29:20 +0000292 "[wraparoundRead] relativeOffset '385' was bigger than maxOffset '384'");
Brandon Kim35d43352022-06-16 11:13:36 -0700293 throw;
294 },
295 std::runtime_error);
296
Brandon Kim3def3c82022-09-13 05:29:20 +0000297 size_t tooBigLength = testMaxOffset + 1;
Brandon Kim35d43352022-06-16 11:13:36 -0700298 EXPECT_THROW(
299 try {
300 bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
301 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +0000302 EXPECT_STREQ(e.what(), "[wraparoundRead] length '385' was bigger "
303 "than maxOffset '384'");
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{
Brandon Kim271d2312022-09-02 16:34:55 +0000347 static_cast<uint8_t>(testOffset + testLength), 0x0, 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700348 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 Kim3def3c82022-09-13 05:29:20 +0000366 size_t testOffset = testMaxOffset - (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 Kim3def3c82022-09-13 05:29:20 +0000397 size_t testOffset = testMaxOffset - (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 Kim271d2312022-09-02 16:34:55 +0000412 static_cast<uint8_t>(testBytesLeft), 0x0, 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;
Brandon Kim3def3c82022-09-13 05:29:20 +0000433 size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700434
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
Brandon Kim271d2312022-09-02 16:34:55 +0000443 const std::vector<uint8_t> expectedReadPtr{0x0, 0x0, 0x0};
Brandon Kim4662b1b2022-06-16 21:38:02 -0700444 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;
Brandon Kim3def3c82022-09-13 05:29:20 +0000473 const uint32_t queueSizeToQueueEnd = testMaxOffset - relativeOffset;
Brandon Kim35d43352022-06-16 11:13:36 -0700474
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 Kim613ba532022-09-12 20:55:21 +0000505 size_t testOffset = 0x0;
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 Kim613ba532022-09-12 20:55:21 +0000517 EXPECT_EQ(bufferImpl->readEntryHeader(), 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(
Brandon Kim613ba532022-09-12 20:55:21 +0000537 try { bufferImpl->readEntry(); } catch (const std::runtime_error& e) {
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700538 // Calculation: testChecksum (0x21) XOR (0x22) = 3
Brandon Kim40ce08e2022-06-15 16:58:44 -0700539 EXPECT_STREQ(e.what(),
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700540 "[readEntry] Checksum was '3', expected '0'");
Brandon Kim40ce08e2022-06-15 16:58:44 -0700541 throw;
542 },
543 std::runtime_error);
544}
545
Brandon Kim613ba532022-09-12 20:55:21 +0000546TEST_F(BufferEntryTest, ReadEntryPassWraparound)
Brandon Kim40ce08e2022-06-15 16:58:44 -0700547{
548 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700549 // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
550 // (20 times) = 0 therefore leave the checksum as is
Brandon Kim35d43352022-06-16 11:13:36 -0700551 std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700552 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
553 std::vector<uint8_t> testEntryHeaderVector(
554 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim613ba532022-09-12 20:55:21 +0000555 // Set testOffset so that we can test the wraparound here at the header and
556 // update the readPtr
Brandon Kim3def3c82022-09-13 05:29:20 +0000557 testOffset = testMaxOffset - 1;
Brandon Kim613ba532022-09-12 20:55:21 +0000558 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
559 .WillOnce(Return(expectedWriteSize));
560 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
561
Brandon Kim35d43352022-06-16 11:13:36 -0700562 wraparoundReadMock(testOffset, testEntryHeaderVector);
563 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700564
565 EntryPair testedEntryPair;
Brandon Kim613ba532022-09-12 20:55:21 +0000566 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
Brandon Kim40ce08e2022-06-15 16:58:44 -0700567 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
568 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
Brandon Kim35d43352022-06-16 11:13:36 -0700569 struct CircularBufferHeader cachedBufferHeader =
570 bufferImpl->getCachedBufferHeader();
571 // The bmcReadPtr should have been updated to reflect the wraparound
572 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
Brandon Kim613ba532022-09-12 20:55:21 +0000573 entryHeaderSize + testEntrySize - 1);
574
575 // Set testOffset so that we can test the wraparound here as well on our
576 // second read for the entry (by 1 byte)
Brandon Kim3def3c82022-09-13 05:29:20 +0000577 testOffset = testMaxOffset - entryHeaderSize - 1;
Brandon Kim613ba532022-09-12 20:55:21 +0000578 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
579 .WillOnce(Return(expectedWriteSize));
580 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
581
582 wraparoundReadMock(testOffset, testEntryHeaderVector);
583 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
584
585 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
586 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
587 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
588 cachedBufferHeader = bufferImpl->getCachedBufferHeader();
589 // The bmcReadPtr should have been updated to reflect the wraparound
590 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
Brandon Kim35d43352022-06-16 11:13:36 -0700591 testEntrySize - 1);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700592}
593
Brandon Kim4662b1b2022-06-16 21:38:02 -0700594class BufferReadErrorLogsTest : public BufferEntryTest
595{
596 protected:
597 BufferReadErrorLogsTest() = default;
598
599 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
600 size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
601};
602
603TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
604{
605 InSequence s;
606 // Set the biosWritePtr too big
607 testInitializationHeader.biosWritePtr =
Brandon Kim3def3c82022-09-13 05:29:20 +0000608 boost::endian::native_to_little((testMaxOffset + 1));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700609 initializeFuncMock();
610
611 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
612 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
613 testInitializationHeaderPtr +
614 bufferHeaderSize)));
615 EXPECT_THROW(
616 try {
617 bufferImpl->readErrorLogs();
618 } catch (const std::runtime_error& e) {
619 EXPECT_STREQ(e.what(),
Brandon Kim3def3c82022-09-13 05:29:20 +0000620 "[readErrorLogs] currentBiosWritePtr was '385' "
621 "which was bigger than maxOffset '384'");
Brandon Kim4662b1b2022-06-16 21:38:02 -0700622 throw;
623 },
624 std::runtime_error);
625
626 // Reset the biosWritePtr and set the bmcReadPtr too big
627 testInitializationHeader.biosWritePtr = 0;
628 initializeFuncMock();
629 testInitializationHeader.bmcReadPtr =
Brandon Kim3def3c82022-09-13 05:29:20 +0000630 boost::endian::native_to_little((testMaxOffset + 1));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700631 initializeFuncMock();
632
633 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
634 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
635 testInitializationHeaderPtr +
636 bufferHeaderSize)));
637 EXPECT_THROW(
638 try {
639 bufferImpl->readErrorLogs();
640 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +0000641 EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '385' "
642 "which was bigger than maxOffset '384'");
Brandon Kim4662b1b2022-06-16 21:38:02 -0700643 throw;
644 },
645 std::runtime_error);
646}
647
648TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
649{
650 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
651 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
652 testInitializationHeaderPtr +
653 bufferHeaderSize)));
654 EXPECT_NO_THROW(bufferImpl->readErrorLogs());
655}
656
657TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
658{
659 InSequence s;
660 // Set the biosWritePtr to 1 entryHeader + entry size
661 testInitializationHeader.biosWritePtr =
662 boost::endian::native_to_little((entryAndHeaderSize));
663 initializeFuncMock();
664 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
665 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
666 testInitializationHeaderPtr +
667 bufferHeaderSize)));
668 std::vector<uint8_t> testEntryHeaderVector(
669 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
670 std::vector<uint8_t> testEntryVector(testEntrySize);
671 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
672 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
673
674 std::vector<EntryPair> entryPairs;
675 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
676
677 // Check that we only read one entryPair and that the content is correct
Brandon Kim1a3dc602022-06-17 11:34:33 -0700678 EXPECT_EQ(entryPairs.size(), 1U);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700679 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
680 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
681}
682
683TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
684{
685 InSequence s;
686 // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
Brandon Kim3def3c82022-09-13 05:29:20 +0000687 uint32_t entryAndHeaderSizeAwayFromEnd = testMaxOffset - entryAndHeaderSize;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700688 testInitializationHeader.bmcReadPtr =
689 boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
690 // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
691 testInitializationHeader.biosWritePtr = entryAndHeaderSize;
692 initializeFuncMock();
693 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
694 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
695 testInitializationHeaderPtr +
696 bufferHeaderSize)));
697
698 std::vector<uint8_t> testEntryHeaderVector(
699 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
700 std::vector<uint8_t> testEntryVector(testEntrySize);
701 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
702 testEntryHeaderVector);
703 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
704 entryHeaderSize,
705 testEntryVector);
706 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
707 testEntryHeaderVector);
708 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
709 entryHeaderSize,
710 testEntryVector);
711
712 std::vector<EntryPair> entryPairs;
713 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
714
715 // Check that we only read one entryPair and that the content is correct
716 EXPECT_EQ(entryPairs.size(), 2);
717 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
718 EXPECT_EQ(entryPairs[1].first, testEntryHeader);
719 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
720 EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
721}
722
723TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
724{
725 InSequence s;
726 testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
727 // Make the biosWritePtr intentially 1 smaller than expected
728 testInitializationHeader.biosWritePtr =
729 boost::endian::native_to_little(entryAndHeaderSize - 1);
730 initializeFuncMock();
731 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
732 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
733 testInitializationHeaderPtr +
734 bufferHeaderSize)));
735
736 std::vector<uint8_t> testEntryHeaderVector(
737 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
738 std::vector<uint8_t> testEntryVector(testEntrySize);
739 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
740 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
741
742 EXPECT_THROW(
743 try {
744 bufferImpl->readErrorLogs();
745 } catch (const std::runtime_error& e) {
746 EXPECT_STREQ(
747 e.what(),
748 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
749 "are not identical after reading through all the logs");
750 throw;
751 },
752 std::runtime_error);
753}
754
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700755} // namespace
756} // namespace bios_bmc_smm_error_logger