blob: 675be7905b41a56b6e2574a2bb5daf7f70fbdcfb [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 {
Patrick Williams1a643562024-08-16 15:22:05 -040039 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
Patrick Williamsc20d1212024-12-18 11:22:36 -050054 struct CircularBufferHeader testInitializationHeader{};
Brandon Kimfcbc3db2022-06-06 21:26:18 -070055
56 std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
57 DataInterfaceMock* dataInterfaceMockPtr;
Brandon Kimfcbc3db2022-06-06 21:26:18 -070058 std::unique_ptr<BufferImpl> bufferImpl;
59};
60
61TEST_F(BufferTest, BufferInitializeEraseFail)
62{
63 InSequence s;
Brandon Kim26660e92022-06-15 16:58:44 -070064 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
65 .WillOnce(Return(testRegionSize));
66 EXPECT_THROW(
67 try {
68 // Test too big of a proposed buffer compared to the memori size
Brandon Kim3def3c82022-09-13 05:29:20 +000069 uint16_t bigQueueSize = 0x201;
Brandon Kim26660e92022-06-15 16:58:44 -070070 uint16_t bigUeRegionSize = 0x50;
71 bufferImpl->initialize(testBmcInterfaceVersion, bigQueueSize,
72 bigUeRegionSize, testMagicNumber);
73 } catch (const std::runtime_error& e) {
74 EXPECT_STREQ(
75 e.what(),
Brandon Kim3def3c82022-09-13 05:29:20 +000076 "[initialize] Proposed queue size '513' is bigger than the BMC's allocated MMIO region of '512'");
Brandon Kim26660e92022-06-15 16:58:44 -070077 throw;
78 },
79 std::runtime_error);
80 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070081
82 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
83 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +000084 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070085 // Return a smaller write than the intended testRegionSize to test the error
86 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +000087 .WillOnce(Return(testQueueSize - 1));
Brandon Kimfcbc3db2022-06-06 21:26:18 -070088 EXPECT_THROW(
89 try {
90 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
91 testUeRegionSize, testMagicNumber);
92 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +000093 EXPECT_STREQ(e.what(), "[initialize] Only erased '511'");
Brandon Kimfcbc3db2022-06-06 21:26:18 -070094 throw;
95 },
96 std::runtime_error);
Brandon Kim60cab572022-06-15 14:20:05 -070097 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070098
99 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
100 .WillOnce(Return(testRegionSize));
101 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000102 .WillOnce(Return(testQueueSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700103 // Return a smaller write than the intended initializationHeader to test the
104 // error
105 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
106 EXPECT_THROW(
107 try {
108 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
109 testUeRegionSize, testMagicNumber);
110 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700111 EXPECT_STREQ(e.what(),
112 "[initialize] Only wrote '0' bytes of the header");
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700113 throw;
114 },
115 std::runtime_error);
Brandon Kim60cab572022-06-15 14:20:05 -0700116 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700117}
118
119TEST_F(BufferTest, BufferInitializePass)
120{
121 InSequence s;
122 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
123 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +0000124 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700125 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000126 .WillOnce(Return(testQueueSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700127
128 uint8_t* testInitializationHeaderPtr =
129 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700130 EXPECT_CALL(*dataInterfaceMockPtr,
131 write(0, ElementsAreArray(testInitializationHeaderPtr,
Brandon Kim17ee1a92022-06-07 21:04:08 -0700132 bufferHeaderSize)))
133 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400134 EXPECT_NO_THROW(
135 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
136 testUeRegionSize, testMagicNumber));
Brandon Kim60cab572022-06-15 14:20:05 -0700137 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700138}
139
Brandon Kim17ee1a92022-06-07 21:04:08 -0700140TEST_F(BufferTest, BufferHeaderReadFail)
141{
142 std::vector<std::uint8_t> testBytesRead{};
143 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
144 .WillOnce(Return(testBytesRead));
145 EXPECT_THROW(
146 try {
147 bufferImpl->readBufferHeader();
148 } catch (const std::runtime_error& e) {
149 EXPECT_STREQ(e.what(),
150 "Buffer header read only read '0', expected '48'");
151 throw;
152 },
153 std::runtime_error);
154}
155
156TEST_F(BufferTest, BufferHeaderReadPass)
157{
158 uint8_t* testInitializationHeaderPtr =
159 reinterpret_cast<uint8_t*>(&testInitializationHeader);
160 std::vector<uint8_t> testInitializationHeaderVector(
161 testInitializationHeaderPtr,
162 testInitializationHeaderPtr + bufferHeaderSize);
163
164 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
165 .WillOnce(Return(testInitializationHeaderVector));
166 EXPECT_NO_THROW(bufferImpl->readBufferHeader());
167 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
168}
169
Brandon Kimcf0b9752022-06-15 10:32:21 -0700170TEST_F(BufferTest, BufferUpdateReadPtrFail)
171{
172 // Return write size that is not 2 which is sizeof(little_uint16_t)
173 constexpr size_t wrongWriteSize = 1;
174 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
175 .WillOnce(Return(wrongWriteSize));
176 EXPECT_THROW(
177 try {
178 bufferImpl->updateReadPtr(0);
179 } catch (const std::runtime_error& e) {
180 EXPECT_STREQ(
181 e.what(),
Brandon Kim271d2312022-09-02 16:34:55 +0000182 "[updateReadPtr] Wrote '1' bytes, instead of expected '3'");
Brandon Kimcf0b9752022-06-15 10:32:21 -0700183 throw;
184 },
185 std::runtime_error);
186}
187
188TEST_F(BufferTest, BufferUpdateReadPtrPass)
189{
Brandon Kim271d2312022-09-02 16:34:55 +0000190 constexpr size_t expectedWriteSize = 3;
191 constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
192 // Check that we truncate the highest 24bits
Brandon Kimcf0b9752022-06-15 10:32:21 -0700193 const uint32_t testNewReadPtr = 0x99881234;
Brandon Kim271d2312022-09-02 16:34:55 +0000194 const std::vector<uint8_t> expectedReadPtr{0x34, 0x12, 0x88};
Brandon Kimcf0b9752022-06-15 10:32:21 -0700195
196 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
197 ElementsAreArray(expectedReadPtr)))
198 .WillOnce(Return(expectedWriteSize));
199 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
Brandon Kimc49284b2022-06-17 09:55:26 -0700200
201 auto cachedHeader = bufferImpl->getCachedBufferHeader();
Brandon Kim271d2312022-09-02 16:34:55 +0000202 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr),
203 0x881234);
Brandon Kimc49284b2022-06-17 09:55:26 -0700204}
205
206TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
207{
208 // Return write size that is not 4 which is sizeof(little_uint32_t)
209 constexpr size_t wrongWriteSize = 1;
210 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
211 .WillOnce(Return(wrongWriteSize));
212 EXPECT_THROW(
213 try {
214 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
215 } catch (const std::runtime_error& e) {
216 EXPECT_STREQ(
217 e.what(),
218 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
219 throw;
220 },
221 std::runtime_error);
222}
223
224TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
225{
226 constexpr size_t expectedWriteSize = 4;
Brandon Kim271d2312022-09-02 16:34:55 +0000227 constexpr uint8_t expectedBmcReadPtrOffset = 0x1d;
Brandon Kimc49284b2022-06-17 09:55:26 -0700228 const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
229
230 EXPECT_CALL(*dataInterfaceMockPtr,
231 write(expectedBmcReadPtrOffset,
232 ElementsAreArray(expectedNewBmcFlagsVector)))
233 .WillOnce(Return(expectedWriteSize));
234 EXPECT_NO_THROW(
235 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
236
237 auto cachedHeader = bufferImpl->getCachedBufferHeader();
238 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
239 static_cast<uint32_t>(BmcFlags::ready));
Brandon Kimcf0b9752022-06-15 10:32:21 -0700240}
241
Brandon Kim82ab8322023-08-17 00:50:18 +0000242TEST_F(BufferTest, GetMaxOffsetQueueSizeFail)
243{
244 InSequence s;
245 static constexpr size_t wrongQueueSize = testQueueSize - 1;
246 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
247 .WillOnce(Return(testRegionSize));
248 const std::vector<uint8_t> emptyArray(wrongQueueSize, 0);
249 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
250 .WillOnce(Return(wrongQueueSize));
251
252 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
253 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400254 EXPECT_NO_THROW(
255 bufferImpl->initialize(testBmcInterfaceVersion, wrongQueueSize,
256 testUeRegionSize, testMagicNumber));
Brandon Kim82ab8322023-08-17 00:50:18 +0000257 EXPECT_THROW(
258 try {
259 bufferImpl->getMaxOffset();
260 } catch (const std::runtime_error& e) {
261 EXPECT_STREQ(e.what(),
262 "[getMaxOffset] runtime queueSize '511' did not match "
263 "compile-time queueSize '512'. This indicates that the"
264 " buffer was corrupted");
265 throw;
266 },
267 std::runtime_error);
268}
269
270TEST_F(BufferTest, GetMaxOffsetUeRegionSizeFail)
271{
272 InSequence s;
273 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
274 .WillOnce(Return(testRegionSize));
275 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
276 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
277 .WillOnce(Return(testQueueSize));
278
279 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
280 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400281 EXPECT_NO_THROW(
282 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
283 testUeRegionSize + 1, testMagicNumber));
Brandon Kim82ab8322023-08-17 00:50:18 +0000284 EXPECT_THROW(
285 try {
286 bufferImpl->getMaxOffset();
287 } catch (const std::runtime_error& e) {
288 EXPECT_STREQ(
289 e.what(),
290 "[getMaxOffset] runtime ueRegionSize '81' did not match "
291 "compile-time ueRegionSize '80'. This indicates that the"
292 " buffer was corrupted");
293 throw;
294 },
295 std::runtime_error);
296}
297
298TEST_F(BufferTest, GetOffsetUeRegionSizeFail)
299{
300 InSequence s;
301 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
302 .WillOnce(Return(testRegionSize));
303 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
304 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
305 .WillOnce(Return(testQueueSize));
306
307 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
308 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400309 EXPECT_NO_THROW(
310 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
311 testUeRegionSize - 1, testMagicNumber));
Brandon Kim82ab8322023-08-17 00:50:18 +0000312 EXPECT_THROW(
313 try {
314 bufferImpl->getQueueOffset();
315 } catch (const std::runtime_error& e) {
316 EXPECT_STREQ(
317 e.what(),
318 "[getQueueOffset] runtime ueRegionSize '79' did not match "
319 "compile-time ueRegionSize '80'. This indicates that the"
320 " buffer was corrupted");
321 throw;
322 },
323 std::runtime_error);
324}
325
Brandon Kimaec99862025-06-08 22:41:40 +0000326TEST_F(BufferTest, ReadUeLog_NoUeRegionConfigured)
327{
328 struct CircularBufferHeader header = testInitializationHeader;
329 header.ueRegionSize =
330 boost::endian::native_to_little<uint16_t>(0); // No UE region
331
332 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
333 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
334 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
335 .WillOnce(Return(headerBytes));
336
337 auto result = bufferImpl->readUeLogFromReservedRegion();
338 EXPECT_TRUE(result.empty());
339}
340
341TEST_F(BufferTest, ReadUeLog_NotPresentDueToFlags)
342{
343 struct CircularBufferHeader header = testInitializationHeader;
344 header.ueRegionSize = boost::endian::native_to_little<uint16_t>(0x20);
345 // Flags are the same, so no new UE log
346 header.biosFlags = boost::endian::native_to_little<uint32_t>(
347 static_cast<uint32_t>((BufferFlags::ueSwitch)));
348 header.bmcFlags = boost::endian::native_to_little<uint32_t>(
349 static_cast<uint32_t>(BufferFlags::ueSwitch));
350
351 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
352 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
353 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
354 .WillOnce(Return(headerBytes));
355
356 auto result = bufferImpl->readUeLogFromReservedRegion();
357 EXPECT_TRUE(result.empty());
358}
359
360TEST_F(BufferTest, ReadUeLog_PresentAndSuccessfullyRead)
361{
362 struct CircularBufferHeader header = testInitializationHeader;
363 uint16_t ueSize = 0x20;
364 header.ueRegionSize = boost::endian::native_to_little(ueSize);
365 header.biosFlags = boost::endian::native_to_little<uint32_t>(
366 static_cast<uint32_t>(BufferFlags::ueSwitch));
367 header.bmcFlags =
368 boost::endian::native_to_little<uint32_t>(0); // BIOS set, BMC not yet
369
370 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
371 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
372 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
373 .WillOnce(Return(headerBytes));
374
375 size_t ueRegionOffset = bufferHeaderSize;
376 std::vector<uint8_t> ueData(ueSize, 0xAA);
377 EXPECT_CALL(*dataInterfaceMockPtr, read(ueRegionOffset, ueSize))
378 .WillOnce(Return(ueData));
379
380 auto result = bufferImpl->readUeLogFromReservedRegion();
381 ASSERT_FALSE(result.empty());
382 EXPECT_THAT(result, ElementsAreArray(ueData));
383
384 // The initial bmcFlags (0) should remain unchanged in the cache
385 struct CircularBufferHeader cachedHeaderAfterRead =
386 bufferImpl->getCachedBufferHeader();
387 EXPECT_EQ(boost::endian::little_to_native(cachedHeaderAfterRead.bmcFlags),
388 0);
389}
390
391TEST_F(BufferTest, ReadUeLog_PresentButReadFails)
392{
393 struct CircularBufferHeader header = testInitializationHeader;
394 uint16_t ueSize = 0x20;
395 header.ueRegionSize = boost::endian::native_to_little(ueSize);
396 header.biosFlags = boost::endian::native_to_little<uint32_t>(
397 static_cast<uint32_t>(BufferFlags::ueSwitch));
398 header.bmcFlags = boost::endian::native_to_little<uint32_t>(0);
399
400 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
401 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
402 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
403 .WillOnce(Return(headerBytes));
404
405 size_t ueRegionOffset = bufferHeaderSize;
406 std::vector<uint8_t> shortUeData(ueSize - 1, 0xAA); // Short read
407 EXPECT_CALL(*dataInterfaceMockPtr, read(ueRegionOffset, ueSize))
408 .WillOnce(Return(shortUeData));
409
410 // Expect an exception due to short read, which is treated as corruption for
411 // UE log
412 EXPECT_THROW(
413 try {
414 bufferImpl->readUeLogFromReservedRegion();
415 } catch (const std::runtime_error& e) {
416 EXPECT_THAT(e.what(),
417 ::testing::HasSubstr("Failed to read full UE log"));
418 throw;
419 },
420 std::runtime_error);
421}
422
Brandon Kim9836cfa2022-06-15 11:21:11 -0700423class BufferWraparoundReadTest : public BufferTest
424{
425 protected:
426 BufferWraparoundReadTest()
427 {
Brandon Kim4662b1b2022-06-16 21:38:02 -0700428 initializeFuncMock();
429 }
430 void initializeFuncMock()
431 {
Brandon Kim9836cfa2022-06-15 11:21:11 -0700432 // Initialize the memory and the cachedBufferHeader
433 InSequence s;
434 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
435 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +0000436 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700437 EXPECT_CALL(*dataInterfaceMockPtr,
438 write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000439 .WillOnce(Return(testQueueSize));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700440
Brandon Kim4662b1b2022-06-16 21:38:02 -0700441 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700442 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400443 EXPECT_NO_THROW(
444 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
445 testUeRegionSize, testMagicNumber));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700446 }
Brandon Kim271d2312022-09-02 16:34:55 +0000447 static constexpr size_t expectedWriteSize = 3;
448 static constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700449 static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700450
Patrick Williams1a643562024-08-16 15:22:05 -0400451 static constexpr size_t testMaxOffset =
452 testQueueSize - testUeRegionSize - sizeof(struct CircularBufferHeader);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700453 uint8_t* testInitializationHeaderPtr =
454 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700455};
456
Brandon Kim82ab8322023-08-17 00:50:18 +0000457TEST_F(BufferWraparoundReadTest, GetMaxOffsetPassTest)
Brandon Kim3def3c82022-09-13 05:29:20 +0000458{
459 EXPECT_EQ(bufferImpl->getMaxOffset(), testMaxOffset);
460}
461
Brandon Kim82ab8322023-08-17 00:50:18 +0000462TEST_F(BufferWraparoundReadTest, GetQueueOffsetPassTest)
463{
464 EXPECT_EQ(bufferImpl->getQueueOffset(),
465 bufferHeaderSize + testUeRegionSize);
466}
467
Brandon Kim35d43352022-06-16 11:13:36 -0700468TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700469{
470 InSequence s;
Brandon Kim3def3c82022-09-13 05:29:20 +0000471 size_t tooBigOffset = testMaxOffset + 1;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700472 EXPECT_THROW(
473 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700474 bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700475 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700476 EXPECT_STREQ(
477 e.what(),
Brandon Kim3def3c82022-09-13 05:29:20 +0000478 "[wraparoundRead] relativeOffset '385' was bigger than maxOffset '384'");
Brandon Kim35d43352022-06-16 11:13:36 -0700479 throw;
480 },
481 std::runtime_error);
482
Brandon Kim3def3c82022-09-13 05:29:20 +0000483 size_t tooBigLength = testMaxOffset + 1;
Brandon Kim35d43352022-06-16 11:13:36 -0700484 EXPECT_THROW(
485 try {
486 bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
487 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +0000488 EXPECT_STREQ(e.what(), "[wraparoundRead] length '385' was bigger "
489 "than maxOffset '384'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700490 throw;
491 },
492 std::runtime_error);
493}
494
Brandon Kim35d43352022-06-16 11:13:36 -0700495TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700496{
497 InSequence s;
Brandon Kim35d43352022-06-16 11:13:36 -0700498 size_t testLength = 0x10;
499 size_t testOffset = 0x20;
500
501 // Fail the first read
502 std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
503 EXPECT_CALL(*dataInterfaceMockPtr,
504 read(testOffset + expectedqueueOffset, testLength))
505 .WillOnce(Return(shortTestBytesRead));
506
Brandon Kim9836cfa2022-06-15 11:21:11 -0700507 EXPECT_THROW(
508 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700509 bufferImpl->wraparoundRead(testOffset, testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700510 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700511 EXPECT_STREQ(e.what(),
512 "[wraparoundRead] Read '15' which was not the "
513 "requested length of '16'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700514 throw;
515 },
516 std::runtime_error);
517}
518
519TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
520{
521 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700522 size_t testLength = 0x10;
Brandon Kim35d43352022-06-16 11:13:36 -0700523 size_t testOffset = 0x20;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700524
525 // Successfully read all the requested length without a wrap around
526 std::vector<std::uint8_t> testBytesRead(testLength);
Brandon Kim35d43352022-06-16 11:13:36 -0700527 EXPECT_CALL(*dataInterfaceMockPtr,
528 read(testOffset + expectedqueueOffset, testLength))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700529 .WillOnce(Return(testBytesRead));
530
531 // Call to updateReadPtr is triggered
532 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim271d2312022-09-02 16:34:55 +0000533 static_cast<uint8_t>(testOffset + testLength), 0x0, 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700534 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
535 ElementsAreArray(expectedReadPtr)))
536 .WillOnce(Return(expectedWriteSize));
537
538 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
539 ElementsAreArray(testBytesRead));
Brandon Kim35d43352022-06-16 11:13:36 -0700540 struct CircularBufferHeader cachedBufferHeader =
541 bufferImpl->getCachedBufferHeader();
542 // The bmcReadPtr should have been updated
543 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
544 testOffset + testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700545}
546
547TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
548{
549 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700550 size_t testBytesLeft = 3;
551 size_t testLength = 0x10;
Brandon Kim3def3c82022-09-13 05:29:20 +0000552 size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700553
Brandon Kim35d43352022-06-16 11:13:36 -0700554 // Read until the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700555 std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
Brandon Kim35d43352022-06-16 11:13:36 -0700556 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
557 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700558 .WillOnce(Return(testBytesReadShort));
559
560 // Read 1 byte short after wraparound
561 std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
562 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
563 .WillOnce(Return(testBytesLeftReadShort));
564
565 EXPECT_THROW(
566 try {
567 bufferImpl->wraparoundRead(testOffset, testLength);
568 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700569 EXPECT_STREQ(
570 e.what(),
571 "[wraparoundRead] Buffer wrapped around but read '2' which was "
572 "not the requested lenght of '3'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700573 throw;
574 },
575 std::runtime_error);
576}
577
578TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
579{
580 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700581 size_t testBytesLeft = 3;
582 size_t testLength = 0x10;
Brandon Kim3def3c82022-09-13 05:29:20 +0000583 size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700584
Brandon Kim35d43352022-06-16 11:13:36 -0700585 // Read to the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700586 std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
587 9, 8, 7, 6, 5, 4};
Brandon Kim35d43352022-06-16 11:13:36 -0700588 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
589 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700590 .WillOnce(Return(testBytesReadFirst));
591
592 std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
593 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
594 .WillOnce(Return(testBytesReadSecond));
595
596 // Call to updateReadPtr is triggered
597 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim271d2312022-09-02 16:34:55 +0000598 static_cast<uint8_t>(testBytesLeft), 0x0, 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700599 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
600 ElementsAreArray(expectedReadPtr)))
601 .WillOnce(Return(expectedWriteSize));
602
603 std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
604 8, 7, 6, 5, 4, 3, 2, 1};
605 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
606 ElementsAreArray(expectedBytes));
Brandon Kim35d43352022-06-16 11:13:36 -0700607 struct CircularBufferHeader cachedBufferHeader =
608 bufferImpl->getCachedBufferHeader();
609 // The bmcReadPtr should have been updated to reflect the wraparound
610 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
611 testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700612}
613
Brandon Kim4662b1b2022-06-16 21:38:02 -0700614TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
615{
616 InSequence s;
617 size_t testBytesLeft = 0;
618 size_t testLength = 4;
Brandon Kim3def3c82022-09-13 05:29:20 +0000619 size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700620
621 // Read to the very end of the queue
622 std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
623 EXPECT_CALL(*dataInterfaceMockPtr,
624 read(testOffset + expectedqueueOffset, testLength))
625 .WillOnce(Return(testBytes));
626
627 // Call to updateReadPtr is triggered, since we read to the very end of the
628 // buffer, update the readPtr up around to 0
Brandon Kim271d2312022-09-02 16:34:55 +0000629 const std::vector<uint8_t> expectedReadPtr{0x0, 0x0, 0x0};
Brandon Kim4662b1b2022-06-16 21:38:02 -0700630 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
631 ElementsAreArray(expectedReadPtr)))
632 .WillOnce(Return(expectedWriteSize));
633
634 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
635 ElementsAreArray(testBytes));
636 struct CircularBufferHeader cachedBufferHeader =
637 bufferImpl->getCachedBufferHeader();
638 // The bmcReadPtr should have been updated to reflect the wraparound
639 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
640 0);
641}
642
Brandon Kim40ce08e2022-06-15 16:58:44 -0700643class BufferEntryTest : public BufferWraparoundReadTest
Brandon Kim7bac2d62022-06-07 21:37:51 -0700644{
645 protected:
Brandon Kim40ce08e2022-06-15 16:58:44 -0700646 BufferEntryTest()
Brandon Kim7bac2d62022-06-07 21:37:51 -0700647 {
648 testEntryHeader.sequenceId = testSequenceId;
649 testEntryHeader.entrySize = testEntrySize;
650 testEntryHeader.checksum = testChecksum;
651 testEntryHeader.rdeCommandType = testRdeCommandType;
652 }
Brandon Kim40ce08e2022-06-15 16:58:44 -0700653 ~BufferEntryTest() override = default;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700654
Brandon Kim35d43352022-06-16 11:13:36 -0700655 void wraparoundReadMock(const uint32_t relativeOffset,
656 std::span<std::uint8_t> expetedBytesOutput)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700657 {
Brandon Kim35d43352022-06-16 11:13:36 -0700658 InSequence s;
Brandon Kim3def3c82022-09-13 05:29:20 +0000659 const uint32_t queueSizeToQueueEnd = testMaxOffset - relativeOffset;
Brandon Kim35d43352022-06-16 11:13:36 -0700660
661 // This will wrap, split the read mocks in 2
662 if (expetedBytesOutput.size() > queueSizeToQueueEnd)
663 {
664 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
665 .WillOnce(Return(std::vector<std::uint8_t>(
666 expetedBytesOutput.begin(),
667 expetedBytesOutput.begin() + queueSizeToQueueEnd)));
668 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
669 .WillOnce(Return(std::vector<std::uint8_t>(
670 expetedBytesOutput.begin() + queueSizeToQueueEnd,
671 expetedBytesOutput.end())));
672 }
673 else
674 {
675 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
676 .WillOnce(Return(std::vector<std::uint8_t>(
677 expetedBytesOutput.begin(), expetedBytesOutput.end())));
678 }
Brandon Kim7bac2d62022-06-07 21:37:51 -0700679
680 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
681 .WillOnce(Return(expectedWriteSize));
682 }
683
684 static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
685 static constexpr uint16_t testSequenceId = 0;
686 static constexpr uint16_t testEntrySize = 0x20;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700687 static constexpr uint8_t testRdeCommandType = 0x01;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700688 // Calculated checksum for the header
689 static constexpr uint8_t testChecksum =
690 (testSequenceId ^ testEntrySize ^ testRdeCommandType);
Brandon Kim613ba532022-09-12 20:55:21 +0000691 size_t testOffset = 0x0;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700692
Patrick Williamsc20d1212024-12-18 11:22:36 -0500693 struct QueueEntryHeader testEntryHeader{};
Brandon Kim7bac2d62022-06-07 21:37:51 -0700694};
695
Brandon Kim40ce08e2022-06-15 16:58:44 -0700696TEST_F(BufferEntryTest, ReadEntryHeaderPass)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700697{
698 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
699 std::vector<uint8_t> testEntryHeaderVector(
700 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700701 wraparoundReadMock(testOffset, testEntryHeaderVector);
Brandon Kim613ba532022-09-12 20:55:21 +0000702 EXPECT_EQ(bufferImpl->readEntryHeader(), testEntryHeader);
Brandon Kim35d43352022-06-16 11:13:36 -0700703 // Check the bmcReadPtr
704 struct CircularBufferHeader cachedBufferHeader =
705 bufferImpl->getCachedBufferHeader();
706 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
707 testOffset + testEntryHeaderVector.size());
Brandon Kim7bac2d62022-06-07 21:37:51 -0700708}
709
Brandon Kim40ce08e2022-06-15 16:58:44 -0700710TEST_F(BufferEntryTest, ReadEntryChecksumFail)
711{
712 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700713 std::vector<uint8_t> testEntryVector(testEntrySize);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700714 // Offset the checksum by 1
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700715 testEntryHeader.checksum += 1;
Brandon Kim40ce08e2022-06-15 16:58:44 -0700716 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
717 std::vector<uint8_t> testEntryHeaderVector(
718 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700719 wraparoundReadMock(testOffset, testEntryHeaderVector);
720 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700721 EXPECT_THROW(
Brandon Kim613ba532022-09-12 20:55:21 +0000722 try { bufferImpl->readEntry(); } catch (const std::runtime_error& e) {
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700723 // Calculation: testChecksum (0x21) XOR (0x22) = 3
Brandon Kim40ce08e2022-06-15 16:58:44 -0700724 EXPECT_STREQ(e.what(),
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700725 "[readEntry] Checksum was '3', expected '0'");
Brandon Kim40ce08e2022-06-15 16:58:44 -0700726 throw;
727 },
728 std::runtime_error);
729}
730
Brandon Kim613ba532022-09-12 20:55:21 +0000731TEST_F(BufferEntryTest, ReadEntryPassWraparound)
Brandon Kim40ce08e2022-06-15 16:58:44 -0700732{
733 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700734 // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
735 // (20 times) = 0 therefore leave the checksum as is
Brandon Kim35d43352022-06-16 11:13:36 -0700736 std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700737 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
738 std::vector<uint8_t> testEntryHeaderVector(
739 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim613ba532022-09-12 20:55:21 +0000740 // Set testOffset so that we can test the wraparound here at the header and
741 // update the readPtr
Brandon Kim3def3c82022-09-13 05:29:20 +0000742 testOffset = testMaxOffset - 1;
Brandon Kim613ba532022-09-12 20:55:21 +0000743 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
744 .WillOnce(Return(expectedWriteSize));
745 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
746
Brandon Kim35d43352022-06-16 11:13:36 -0700747 wraparoundReadMock(testOffset, testEntryHeaderVector);
748 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700749
750 EntryPair testedEntryPair;
Brandon Kim613ba532022-09-12 20:55:21 +0000751 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
Brandon Kim40ce08e2022-06-15 16:58:44 -0700752 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
753 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
Brandon Kim35d43352022-06-16 11:13:36 -0700754 struct CircularBufferHeader cachedBufferHeader =
755 bufferImpl->getCachedBufferHeader();
756 // The bmcReadPtr should have been updated to reflect the wraparound
757 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
Brandon Kim613ba532022-09-12 20:55:21 +0000758 entryHeaderSize + testEntrySize - 1);
759
760 // Set testOffset so that we can test the wraparound here as well on our
761 // second read for the entry (by 1 byte)
Brandon Kim3def3c82022-09-13 05:29:20 +0000762 testOffset = testMaxOffset - entryHeaderSize - 1;
Brandon Kim613ba532022-09-12 20:55:21 +0000763 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
764 .WillOnce(Return(expectedWriteSize));
765 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
766
767 wraparoundReadMock(testOffset, testEntryHeaderVector);
768 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
769
770 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
771 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
772 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
773 cachedBufferHeader = bufferImpl->getCachedBufferHeader();
774 // The bmcReadPtr should have been updated to reflect the wraparound
775 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
Brandon Kim35d43352022-06-16 11:13:36 -0700776 testEntrySize - 1);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700777}
778
Brandon Kim4662b1b2022-06-16 21:38:02 -0700779class BufferReadErrorLogsTest : public BufferEntryTest
780{
781 protected:
782 BufferReadErrorLogsTest() = default;
783
784 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
785 size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
786};
787
788TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
789{
790 InSequence s;
791 // Set the biosWritePtr too big
792 testInitializationHeader.biosWritePtr =
Brandon Kim3def3c82022-09-13 05:29:20 +0000793 boost::endian::native_to_little((testMaxOffset + 1));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700794 initializeFuncMock();
795
796 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400797 .WillOnce(Return(std::vector<uint8_t>(
798 testInitializationHeaderPtr,
799 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700800 EXPECT_THROW(
801 try {
802 bufferImpl->readErrorLogs();
803 } catch (const std::runtime_error& e) {
804 EXPECT_STREQ(e.what(),
Brandon Kim3def3c82022-09-13 05:29:20 +0000805 "[readErrorLogs] currentBiosWritePtr was '385' "
806 "which was bigger than maxOffset '384'");
Brandon Kim4662b1b2022-06-16 21:38:02 -0700807 throw;
808 },
809 std::runtime_error);
810
811 // Reset the biosWritePtr and set the bmcReadPtr too big
812 testInitializationHeader.biosWritePtr = 0;
813 initializeFuncMock();
814 testInitializationHeader.bmcReadPtr =
Brandon Kim3def3c82022-09-13 05:29:20 +0000815 boost::endian::native_to_little((testMaxOffset + 1));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700816 initializeFuncMock();
817
818 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400819 .WillOnce(Return(std::vector<uint8_t>(
820 testInitializationHeaderPtr,
821 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700822 EXPECT_THROW(
823 try {
824 bufferImpl->readErrorLogs();
825 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +0000826 EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '385' "
827 "which was bigger than maxOffset '384'");
Brandon Kim4662b1b2022-06-16 21:38:02 -0700828 throw;
829 },
830 std::runtime_error);
831}
832
833TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
834{
835 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400836 .WillOnce(Return(std::vector<uint8_t>(
837 testInitializationHeaderPtr,
838 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700839 EXPECT_NO_THROW(bufferImpl->readErrorLogs());
840}
841
842TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
843{
844 InSequence s;
845 // Set the biosWritePtr to 1 entryHeader + entry size
846 testInitializationHeader.biosWritePtr =
847 boost::endian::native_to_little((entryAndHeaderSize));
848 initializeFuncMock();
849 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400850 .WillOnce(Return(std::vector<uint8_t>(
851 testInitializationHeaderPtr,
852 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700853 std::vector<uint8_t> testEntryHeaderVector(
854 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
855 std::vector<uint8_t> testEntryVector(testEntrySize);
856 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
857 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
858
859 std::vector<EntryPair> entryPairs;
860 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
861
862 // Check that we only read one entryPair and that the content is correct
Brandon Kim1a3dc602022-06-17 11:34:33 -0700863 EXPECT_EQ(entryPairs.size(), 1U);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700864 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
865 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
866}
867
868TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
869{
870 InSequence s;
871 // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
Brandon Kim3def3c82022-09-13 05:29:20 +0000872 uint32_t entryAndHeaderSizeAwayFromEnd = testMaxOffset - entryAndHeaderSize;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700873 testInitializationHeader.bmcReadPtr =
874 boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
875 // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
876 testInitializationHeader.biosWritePtr = entryAndHeaderSize;
877 initializeFuncMock();
878 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400879 .WillOnce(Return(std::vector<uint8_t>(
880 testInitializationHeaderPtr,
881 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700882
883 std::vector<uint8_t> testEntryHeaderVector(
884 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
885 std::vector<uint8_t> testEntryVector(testEntrySize);
886 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
887 testEntryHeaderVector);
888 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
889 entryHeaderSize,
890 testEntryVector);
891 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
892 testEntryHeaderVector);
893 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
894 entryHeaderSize,
895 testEntryVector);
896
897 std::vector<EntryPair> entryPairs;
898 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
899
900 // Check that we only read one entryPair and that the content is correct
901 EXPECT_EQ(entryPairs.size(), 2);
902 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
903 EXPECT_EQ(entryPairs[1].first, testEntryHeader);
904 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
905 EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
906}
907
908TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
909{
910 InSequence s;
911 testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
912 // Make the biosWritePtr intentially 1 smaller than expected
913 testInitializationHeader.biosWritePtr =
914 boost::endian::native_to_little(entryAndHeaderSize - 1);
915 initializeFuncMock();
916 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400917 .WillOnce(Return(std::vector<uint8_t>(
918 testInitializationHeaderPtr,
919 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700920
921 std::vector<uint8_t> testEntryHeaderVector(
922 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
923 std::vector<uint8_t> testEntryVector(testEntrySize);
924 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
925 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
926
927 EXPECT_THROW(
928 try {
929 bufferImpl->readErrorLogs();
930 } catch (const std::runtime_error& e) {
931 EXPECT_STREQ(
932 e.what(),
933 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
934 "are not identical after reading through all the logs");
935 throw;
936 },
937 std::runtime_error);
938}
939
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700940} // namespace
941} // namespace bios_bmc_smm_error_logger