blob: 0fff5ee69b4cba67e520fd027eb3d68302e88de4 [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 Kim271d2312022-09-02 16:34:55 +000047 static constexpr uint32_t testQueueSize = 0x100;
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
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(),
Brandon Kim271d2312022-09-02 16:34:55 +0000188 "[updateReadPtr] Wrote '1' bytes, instead of expected '3'");
Brandon Kimcf0b9752022-06-15 10:32:21 -0700189 throw;
190 },
191 std::runtime_error);
192}
193
194TEST_F(BufferTest, BufferUpdateReadPtrPass)
195{
Brandon Kim271d2312022-09-02 16:34:55 +0000196 constexpr size_t expectedWriteSize = 3;
197 constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
198 // Check that we truncate the highest 24bits
Brandon Kimcf0b9752022-06-15 10:32:21 -0700199 const uint32_t testNewReadPtr = 0x99881234;
Brandon Kim271d2312022-09-02 16:34:55 +0000200 const std::vector<uint8_t> expectedReadPtr{0x34, 0x12, 0x88};
Brandon Kimcf0b9752022-06-15 10:32:21 -0700201
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();
Brandon Kim271d2312022-09-02 16:34:55 +0000208 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr),
209 0x881234);
Brandon Kimc49284b2022-06-17 09:55:26 -0700210}
211
212TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
213{
214 // Return write size that is not 4 which is sizeof(little_uint32_t)
215 constexpr size_t wrongWriteSize = 1;
216 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
217 .WillOnce(Return(wrongWriteSize));
218 EXPECT_THROW(
219 try {
220 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
221 } catch (const std::runtime_error& e) {
222 EXPECT_STREQ(
223 e.what(),
224 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
225 throw;
226 },
227 std::runtime_error);
228}
229
230TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
231{
232 constexpr size_t expectedWriteSize = 4;
Brandon Kim271d2312022-09-02 16:34:55 +0000233 constexpr uint8_t expectedBmcReadPtrOffset = 0x1d;
Brandon Kimc49284b2022-06-17 09:55:26 -0700234 const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
235
236 EXPECT_CALL(*dataInterfaceMockPtr,
237 write(expectedBmcReadPtrOffset,
238 ElementsAreArray(expectedNewBmcFlagsVector)))
239 .WillOnce(Return(expectedWriteSize));
240 EXPECT_NO_THROW(
241 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
242
243 auto cachedHeader = bufferImpl->getCachedBufferHeader();
244 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
245 static_cast<uint32_t>(BmcFlags::ready));
Brandon Kimcf0b9752022-06-15 10:32:21 -0700246}
247
Brandon Kim9836cfa2022-06-15 11:21:11 -0700248class BufferWraparoundReadTest : public BufferTest
249{
250 protected:
251 BufferWraparoundReadTest()
252 {
Brandon Kim4662b1b2022-06-16 21:38:02 -0700253 initializeFuncMock();
254 }
255 void initializeFuncMock()
256 {
Brandon Kim9836cfa2022-06-15 11:21:11 -0700257 // Initialize the memory and the cachedBufferHeader
258 InSequence s;
259 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
260 .WillOnce(Return(testRegionSize));
Brandon Kim26660e92022-06-15 16:58:44 -0700261 size_t testProposedBufferSize = sizeof(struct CircularBufferHeader) +
262 testUeRegionSize + testQueueSize;
263 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700264 EXPECT_CALL(*dataInterfaceMockPtr,
265 write(0, ElementsAreArray(emptyArray)))
Brandon Kim26660e92022-06-15 16:58:44 -0700266 .WillOnce(Return(testProposedBufferSize));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700267
Brandon Kim4662b1b2022-06-16 21:38:02 -0700268 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700269 .WillOnce(Return(bufferHeaderSize));
270 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
271 testQueueSize, testUeRegionSize,
272 testMagicNumber));
273 }
Brandon Kim271d2312022-09-02 16:34:55 +0000274 static constexpr size_t expectedWriteSize = 3;
275 static constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700276 static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700277
278 uint8_t* testInitializationHeaderPtr =
279 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700280};
281
Brandon Kim35d43352022-06-16 11:13:36 -0700282TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700283{
284 InSequence s;
Brandon Kim35d43352022-06-16 11:13:36 -0700285
286 size_t tooBigOffset = testQueueSize + 1;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700287 EXPECT_THROW(
288 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700289 bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700290 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700291 EXPECT_STREQ(
292 e.what(),
Brandon Kim35d43352022-06-16 11:13:36 -0700293 "[wraparoundRead] relativeOffset '257' was bigger than queueSize '256'");
294 throw;
295 },
296 std::runtime_error);
297
298 size_t tooBigLength = testQueueSize + 1;
299 EXPECT_THROW(
300 try {
301 bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
302 } catch (const std::runtime_error& e) {
303 EXPECT_STREQ(e.what(), "[wraparoundRead] length '257' was bigger "
304 "than queueSize '256'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700305 throw;
306 },
307 std::runtime_error);
308}
309
Brandon Kim35d43352022-06-16 11:13:36 -0700310TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700311{
312 InSequence s;
Brandon Kim35d43352022-06-16 11:13:36 -0700313 size_t testLength = 0x10;
314 size_t testOffset = 0x20;
315
316 // Fail the first read
317 std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
318 EXPECT_CALL(*dataInterfaceMockPtr,
319 read(testOffset + expectedqueueOffset, testLength))
320 .WillOnce(Return(shortTestBytesRead));
321
Brandon Kim9836cfa2022-06-15 11:21:11 -0700322 EXPECT_THROW(
323 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700324 bufferImpl->wraparoundRead(testOffset, testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700325 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700326 EXPECT_STREQ(e.what(),
327 "[wraparoundRead] Read '15' which was not the "
328 "requested length of '16'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700329 throw;
330 },
331 std::runtime_error);
332}
333
334TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
335{
336 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700337 size_t testLength = 0x10;
Brandon Kim35d43352022-06-16 11:13:36 -0700338 size_t testOffset = 0x20;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700339
340 // Successfully read all the requested length without a wrap around
341 std::vector<std::uint8_t> testBytesRead(testLength);
Brandon Kim35d43352022-06-16 11:13:36 -0700342 EXPECT_CALL(*dataInterfaceMockPtr,
343 read(testOffset + expectedqueueOffset, testLength))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700344 .WillOnce(Return(testBytesRead));
345
346 // Call to updateReadPtr is triggered
347 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim271d2312022-09-02 16:34:55 +0000348 static_cast<uint8_t>(testOffset + testLength), 0x0, 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700349 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
350 ElementsAreArray(expectedReadPtr)))
351 .WillOnce(Return(expectedWriteSize));
352
353 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
354 ElementsAreArray(testBytesRead));
Brandon Kim35d43352022-06-16 11:13:36 -0700355 struct CircularBufferHeader cachedBufferHeader =
356 bufferImpl->getCachedBufferHeader();
357 // The bmcReadPtr should have been updated
358 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
359 testOffset + testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700360}
361
362TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
363{
364 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700365 size_t testBytesLeft = 3;
366 size_t testLength = 0x10;
Brandon Kim26660e92022-06-15 16:58:44 -0700367 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700368
Brandon Kim35d43352022-06-16 11:13:36 -0700369 // Read until the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700370 std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
Brandon Kim35d43352022-06-16 11:13:36 -0700371 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
372 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700373 .WillOnce(Return(testBytesReadShort));
374
375 // Read 1 byte short after wraparound
376 std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
377 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
378 .WillOnce(Return(testBytesLeftReadShort));
379
380 EXPECT_THROW(
381 try {
382 bufferImpl->wraparoundRead(testOffset, testLength);
383 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700384 EXPECT_STREQ(
385 e.what(),
386 "[wraparoundRead] Buffer wrapped around but read '2' which was "
387 "not the requested lenght of '3'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700388 throw;
389 },
390 std::runtime_error);
391}
392
393TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
394{
395 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700396 size_t testBytesLeft = 3;
397 size_t testLength = 0x10;
Brandon Kim26660e92022-06-15 16:58:44 -0700398 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700399
Brandon Kim35d43352022-06-16 11:13:36 -0700400 // Read to the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700401 std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
402 9, 8, 7, 6, 5, 4};
Brandon Kim35d43352022-06-16 11:13:36 -0700403 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
404 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700405 .WillOnce(Return(testBytesReadFirst));
406
407 std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
408 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
409 .WillOnce(Return(testBytesReadSecond));
410
411 // Call to updateReadPtr is triggered
412 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim271d2312022-09-02 16:34:55 +0000413 static_cast<uint8_t>(testBytesLeft), 0x0, 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700414 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
415 ElementsAreArray(expectedReadPtr)))
416 .WillOnce(Return(expectedWriteSize));
417
418 std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
419 8, 7, 6, 5, 4, 3, 2, 1};
420 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
421 ElementsAreArray(expectedBytes));
Brandon Kim35d43352022-06-16 11:13:36 -0700422 struct CircularBufferHeader cachedBufferHeader =
423 bufferImpl->getCachedBufferHeader();
424 // The bmcReadPtr should have been updated to reflect the wraparound
425 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
426 testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700427}
428
Brandon Kim4662b1b2022-06-16 21:38:02 -0700429TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
430{
431 InSequence s;
432 size_t testBytesLeft = 0;
433 size_t testLength = 4;
434 size_t testOffset = testQueueSize - (testLength - testBytesLeft);
435
436 // Read to the very end of the queue
437 std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
438 EXPECT_CALL(*dataInterfaceMockPtr,
439 read(testOffset + expectedqueueOffset, testLength))
440 .WillOnce(Return(testBytes));
441
442 // Call to updateReadPtr is triggered, since we read to the very end of the
443 // buffer, update the readPtr up around to 0
Brandon Kim271d2312022-09-02 16:34:55 +0000444 const std::vector<uint8_t> expectedReadPtr{0x0, 0x0, 0x0};
Brandon Kim4662b1b2022-06-16 21:38:02 -0700445 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
446 ElementsAreArray(expectedReadPtr)))
447 .WillOnce(Return(expectedWriteSize));
448
449 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
450 ElementsAreArray(testBytes));
451 struct CircularBufferHeader cachedBufferHeader =
452 bufferImpl->getCachedBufferHeader();
453 // The bmcReadPtr should have been updated to reflect the wraparound
454 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
455 0);
456}
457
Brandon Kim40ce08e2022-06-15 16:58:44 -0700458class BufferEntryTest : public BufferWraparoundReadTest
Brandon Kim7bac2d62022-06-07 21:37:51 -0700459{
460 protected:
Brandon Kim40ce08e2022-06-15 16:58:44 -0700461 BufferEntryTest()
Brandon Kim7bac2d62022-06-07 21:37:51 -0700462 {
463 testEntryHeader.sequenceId = testSequenceId;
464 testEntryHeader.entrySize = testEntrySize;
465 testEntryHeader.checksum = testChecksum;
466 testEntryHeader.rdeCommandType = testRdeCommandType;
467 }
Brandon Kim40ce08e2022-06-15 16:58:44 -0700468 ~BufferEntryTest() override = default;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700469
Brandon Kim35d43352022-06-16 11:13:36 -0700470 void wraparoundReadMock(const uint32_t relativeOffset,
471 std::span<std::uint8_t> expetedBytesOutput)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700472 {
Brandon Kim35d43352022-06-16 11:13:36 -0700473 InSequence s;
474 const uint32_t queueSizeToQueueEnd = testQueueSize - relativeOffset;
475
476 // This will wrap, split the read mocks in 2
477 if (expetedBytesOutput.size() > queueSizeToQueueEnd)
478 {
479 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
480 .WillOnce(Return(std::vector<std::uint8_t>(
481 expetedBytesOutput.begin(),
482 expetedBytesOutput.begin() + queueSizeToQueueEnd)));
483 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
484 .WillOnce(Return(std::vector<std::uint8_t>(
485 expetedBytesOutput.begin() + queueSizeToQueueEnd,
486 expetedBytesOutput.end())));
487 }
488 else
489 {
490 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
491 .WillOnce(Return(std::vector<std::uint8_t>(
492 expetedBytesOutput.begin(), expetedBytesOutput.end())));
493 }
Brandon Kim7bac2d62022-06-07 21:37:51 -0700494
495 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
496 .WillOnce(Return(expectedWriteSize));
497 }
498
499 static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
500 static constexpr uint16_t testSequenceId = 0;
501 static constexpr uint16_t testEntrySize = 0x20;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700502 static constexpr uint8_t testRdeCommandType = 0x01;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700503 // Calculated checksum for the header
504 static constexpr uint8_t testChecksum =
505 (testSequenceId ^ testEntrySize ^ testRdeCommandType);
Brandon Kim613ba532022-09-12 20:55:21 +0000506 size_t testOffset = 0x0;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700507
508 struct QueueEntryHeader testEntryHeader
509 {};
510};
511
Brandon Kim40ce08e2022-06-15 16:58:44 -0700512TEST_F(BufferEntryTest, ReadEntryHeaderPass)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700513{
514 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
515 std::vector<uint8_t> testEntryHeaderVector(
516 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700517 wraparoundReadMock(testOffset, testEntryHeaderVector);
Brandon Kim613ba532022-09-12 20:55:21 +0000518 EXPECT_EQ(bufferImpl->readEntryHeader(), testEntryHeader);
Brandon Kim35d43352022-06-16 11:13:36 -0700519 // Check the bmcReadPtr
520 struct CircularBufferHeader cachedBufferHeader =
521 bufferImpl->getCachedBufferHeader();
522 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
523 testOffset + testEntryHeaderVector.size());
Brandon Kim7bac2d62022-06-07 21:37:51 -0700524}
525
Brandon Kim40ce08e2022-06-15 16:58:44 -0700526TEST_F(BufferEntryTest, ReadEntryChecksumFail)
527{
528 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700529 std::vector<uint8_t> testEntryVector(testEntrySize);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700530 // Offset the checksum by 1
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700531 testEntryHeader.checksum += 1;
Brandon Kim40ce08e2022-06-15 16:58:44 -0700532 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
533 std::vector<uint8_t> testEntryHeaderVector(
534 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700535 wraparoundReadMock(testOffset, testEntryHeaderVector);
536 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700537 EXPECT_THROW(
Brandon Kim613ba532022-09-12 20:55:21 +0000538 try { bufferImpl->readEntry(); } catch (const std::runtime_error& e) {
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700539 // Calculation: testChecksum (0x21) XOR (0x22) = 3
Brandon Kim40ce08e2022-06-15 16:58:44 -0700540 EXPECT_STREQ(e.what(),
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700541 "[readEntry] Checksum was '3', expected '0'");
Brandon Kim40ce08e2022-06-15 16:58:44 -0700542 throw;
543 },
544 std::runtime_error);
545}
546
Brandon Kim613ba532022-09-12 20:55:21 +0000547TEST_F(BufferEntryTest, ReadEntryPassWraparound)
Brandon Kim40ce08e2022-06-15 16:58:44 -0700548{
549 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700550 // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
551 // (20 times) = 0 therefore leave the checksum as is
Brandon Kim35d43352022-06-16 11:13:36 -0700552 std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700553 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
554 std::vector<uint8_t> testEntryHeaderVector(
555 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim613ba532022-09-12 20:55:21 +0000556 // Set testOffset so that we can test the wraparound here at the header and
557 // update the readPtr
558 testOffset = testQueueSize - 1;
559 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
560 .WillOnce(Return(expectedWriteSize));
561 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
562
Brandon Kim35d43352022-06-16 11:13:36 -0700563 wraparoundReadMock(testOffset, testEntryHeaderVector);
564 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700565
566 EntryPair testedEntryPair;
Brandon Kim613ba532022-09-12 20:55:21 +0000567 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
Brandon Kim40ce08e2022-06-15 16:58:44 -0700568 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
569 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
Brandon Kim35d43352022-06-16 11:13:36 -0700570 struct CircularBufferHeader cachedBufferHeader =
571 bufferImpl->getCachedBufferHeader();
572 // The bmcReadPtr should have been updated to reflect the wraparound
573 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
Brandon Kim613ba532022-09-12 20:55:21 +0000574 entryHeaderSize + testEntrySize - 1);
575
576 // Set testOffset so that we can test the wraparound here as well on our
577 // second read for the entry (by 1 byte)
578 testOffset = testQueueSize - entryHeaderSize - 1;
579 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
580 .WillOnce(Return(expectedWriteSize));
581 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
582
583 wraparoundReadMock(testOffset, testEntryHeaderVector);
584 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
585
586 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
587 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
588 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
589 cachedBufferHeader = bufferImpl->getCachedBufferHeader();
590 // The bmcReadPtr should have been updated to reflect the wraparound
591 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
Brandon Kim35d43352022-06-16 11:13:36 -0700592 testEntrySize - 1);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700593}
594
Brandon Kim4662b1b2022-06-16 21:38:02 -0700595class BufferReadErrorLogsTest : public BufferEntryTest
596{
597 protected:
598 BufferReadErrorLogsTest() = default;
599
600 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
601 size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
602};
603
604TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
605{
606 InSequence s;
607 // Set the biosWritePtr too big
608 testInitializationHeader.biosWritePtr =
609 boost::endian::native_to_little((testQueueSize + 1));
610 initializeFuncMock();
611
612 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
613 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
614 testInitializationHeaderPtr +
615 bufferHeaderSize)));
616 EXPECT_THROW(
617 try {
618 bufferImpl->readErrorLogs();
619 } catch (const std::runtime_error& e) {
620 EXPECT_STREQ(e.what(),
621 "[readErrorLogs] currentBiosWritePtr was '257' "
622 "which was bigger than queueSize '256'");
623 throw;
624 },
625 std::runtime_error);
626
627 // Reset the biosWritePtr and set the bmcReadPtr too big
628 testInitializationHeader.biosWritePtr = 0;
629 initializeFuncMock();
630 testInitializationHeader.bmcReadPtr =
631 boost::endian::native_to_little((testQueueSize + 1));
632 initializeFuncMock();
633
634 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
635 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
636 testInitializationHeaderPtr +
637 bufferHeaderSize)));
638 EXPECT_THROW(
639 try {
640 bufferImpl->readErrorLogs();
641 } catch (const std::runtime_error& e) {
642 EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '257' "
643 "which was bigger than queueSize '256'");
644 throw;
645 },
646 std::runtime_error);
647}
648
649TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
650{
651 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
652 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
653 testInitializationHeaderPtr +
654 bufferHeaderSize)));
655 EXPECT_NO_THROW(bufferImpl->readErrorLogs());
656}
657
658TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
659{
660 InSequence s;
661 // Set the biosWritePtr to 1 entryHeader + entry size
662 testInitializationHeader.biosWritePtr =
663 boost::endian::native_to_little((entryAndHeaderSize));
664 initializeFuncMock();
665 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
666 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
667 testInitializationHeaderPtr +
668 bufferHeaderSize)));
669 std::vector<uint8_t> testEntryHeaderVector(
670 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
671 std::vector<uint8_t> testEntryVector(testEntrySize);
672 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
673 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
674
675 std::vector<EntryPair> entryPairs;
676 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
677
678 // Check that we only read one entryPair and that the content is correct
Brandon Kim1a3dc602022-06-17 11:34:33 -0700679 EXPECT_EQ(entryPairs.size(), 1U);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700680 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
681 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
682}
683
684TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
685{
686 InSequence s;
687 // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
688 uint32_t entryAndHeaderSizeAwayFromEnd = testQueueSize - entryAndHeaderSize;
689 testInitializationHeader.bmcReadPtr =
690 boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
691 // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
692 testInitializationHeader.biosWritePtr = entryAndHeaderSize;
693 initializeFuncMock();
694 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
695 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
696 testInitializationHeaderPtr +
697 bufferHeaderSize)));
698
699 std::vector<uint8_t> testEntryHeaderVector(
700 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
701 std::vector<uint8_t> testEntryVector(testEntrySize);
702 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
703 testEntryHeaderVector);
704 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
705 entryHeaderSize,
706 testEntryVector);
707 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
708 testEntryHeaderVector);
709 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
710 entryHeaderSize,
711 testEntryVector);
712
713 std::vector<EntryPair> entryPairs;
714 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
715
716 // Check that we only read one entryPair and that the content is correct
717 EXPECT_EQ(entryPairs.size(), 2);
718 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
719 EXPECT_EQ(entryPairs[1].first, testEntryHeader);
720 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
721 EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
722}
723
724TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
725{
726 InSequence s;
727 testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
728 // Make the biosWritePtr intentially 1 smaller than expected
729 testInitializationHeader.biosWritePtr =
730 boost::endian::native_to_little(entryAndHeaderSize - 1);
731 initializeFuncMock();
732 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
733 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
734 testInitializationHeaderPtr +
735 bufferHeaderSize)));
736
737 std::vector<uint8_t> testEntryHeaderVector(
738 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
739 std::vector<uint8_t> testEntryVector(testEntrySize);
740 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
741 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
742
743 EXPECT_THROW(
744 try {
745 bufferImpl->readErrorLogs();
746 } catch (const std::runtime_error& e) {
747 EXPECT_STREQ(
748 e.what(),
749 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
750 "are not identical after reading through all the logs");
751 throw;
752 },
753 std::runtime_error);
754}
755
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700756} // namespace
757} // namespace bios_bmc_smm_error_logger