blob: c6cec94e13c802085a7b19fd0803bd4082a72701 [file] [log] [blame]
Brandon Kim82ab8322023-08-17 00:50:18 +00001#include "config.h"
2
Brandon Kimfcbc3db2022-06-06 21:26:18 -07003#include "buffer.hpp"
4
5#include "pci_handler.hpp"
6
Brandon Kim17ee1a92022-06-07 21:04:08 -07007#include <boost/endian/arithmetic.hpp>
8#include <boost/endian/conversion.hpp>
9
10#include <algorithm>
Brandon Kimfcbc3db2022-06-06 21:26:18 -070011#include <array>
Brandon Kimcf0b9752022-06-15 10:32:21 -070012#include <cstddef>
Brandon Kimfcbc3db2022-06-06 21:26:18 -070013#include <cstdint>
Patrick Williams5de90612024-02-13 21:31:53 -060014#include <format>
Brandon Kimfcbc3db2022-06-06 21:26:18 -070015#include <memory>
Brandon Kim40ce08e2022-06-15 16:58:44 -070016#include <numeric>
Brandon Kimfcbc3db2022-06-06 21:26:18 -070017#include <span>
18#include <vector>
19
20namespace bios_bmc_smm_error_logger
21{
22
23BufferImpl::BufferImpl(std::unique_ptr<DataInterface> dataInterface) :
24 dataInterface(std::move(dataInterface)){};
25
26void BufferImpl::initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize,
27 uint16_t ueRegionSize,
28 const std::array<uint32_t, 4>& magicNumber)
29{
Brandon Kimfcbc3db2022-06-06 21:26:18 -070030 const size_t memoryRegionSize = dataInterface->getMemoryRegionSize();
Brandon Kim3def3c82022-09-13 05:29:20 +000031 if (queueSize > memoryRegionSize)
Brandon Kim26660e92022-06-15 16:58:44 -070032 {
Patrick Williams5de90612024-02-13 21:31:53 -060033 throw std::runtime_error(std::format(
Brandon Kim3def3c82022-09-13 05:29:20 +000034 "[initialize] Proposed queue size '{}' is bigger than the "
Brandon Kim26660e92022-06-15 16:58:44 -070035 "BMC's allocated MMIO region of '{}'",
Brandon Kim3def3c82022-09-13 05:29:20 +000036 queueSize, memoryRegionSize));
Brandon Kim26660e92022-06-15 16:58:44 -070037 }
38
39 // Initialize the whole buffer with 0x00
Brandon Kim3def3c82022-09-13 05:29:20 +000040 const std::vector<uint8_t> emptyVector(queueSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070041 size_t byteWritten = dataInterface->write(0, emptyVector);
Brandon Kim3def3c82022-09-13 05:29:20 +000042 if (byteWritten != queueSize)
Brandon Kimfcbc3db2022-06-06 21:26:18 -070043 {
44 throw std::runtime_error(
Patrick Williams5de90612024-02-13 21:31:53 -060045 std::format("[initialize] Only erased '{}'", byteWritten));
Brandon Kimfcbc3db2022-06-06 21:26:18 -070046 }
47
48 // Create an initial buffer header and write to it
49 struct CircularBufferHeader initializationHeader = {};
Brandon Kim17ee1a92022-06-07 21:04:08 -070050 initializationHeader.bmcInterfaceVersion =
51 boost::endian::native_to_little(bmcInterfaceVersion);
52 initializationHeader.queueSize = boost::endian::native_to_little(queueSize);
53 initializationHeader.ueRegionSize =
54 boost::endian::native_to_little(ueRegionSize);
55 std::transform(magicNumber.begin(), magicNumber.end(),
56 initializationHeader.magicNumber.begin(),
57 [](uint32_t number) -> little_uint32_t {
Patrick Williams589c1752023-10-20 11:20:01 -050058 return boost::endian::native_to_little(number);
59 });
Brandon Kimfcbc3db2022-06-06 21:26:18 -070060
61 uint8_t* initializationHeaderPtr =
62 reinterpret_cast<uint8_t*>(&initializationHeader);
63 size_t initializationHeaderSize = sizeof(initializationHeader);
64 byteWritten = dataInterface->write(
65 0, std::span<const uint8_t>(initializationHeaderPtr,
66 initializationHeaderPtr +
67 initializationHeaderSize));
68 if (byteWritten != initializationHeaderSize)
69 {
Patrick Williams5de90612024-02-13 21:31:53 -060070 throw std::runtime_error(std::format(
Brandon Kim26660e92022-06-15 16:58:44 -070071 "[initialize] Only wrote '{}' bytes of the header", byteWritten));
Brandon Kimfcbc3db2022-06-06 21:26:18 -070072 }
Brandon Kim60cab572022-06-15 14:20:05 -070073 cachedBufferHeader = initializationHeader;
Brandon Kimfcbc3db2022-06-06 21:26:18 -070074}
75
Brandon Kim17ee1a92022-06-07 21:04:08 -070076void BufferImpl::readBufferHeader()
77{
78 size_t headerSize = sizeof(struct CircularBufferHeader);
Patrick Williamsb8125762023-05-10 07:51:26 -050079 std::vector<uint8_t> bytesRead = dataInterface->read(/*offset=*/0,
80 headerSize);
Brandon Kim17ee1a92022-06-07 21:04:08 -070081
82 if (bytesRead.size() != headerSize)
83 {
84 throw std::runtime_error(
Patrick Williams5de90612024-02-13 21:31:53 -060085 std::format("Buffer header read only read '{}', expected '{}'",
Brandon Kim17ee1a92022-06-07 21:04:08 -070086 bytesRead.size(), headerSize));
87 }
88
89 cachedBufferHeader =
Brandon Kim60cab572022-06-15 14:20:05 -070090 *reinterpret_cast<struct CircularBufferHeader*>(bytesRead.data());
Brandon Kim17ee1a92022-06-07 21:04:08 -070091};
92
93struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const
94{
95 return cachedBufferHeader;
96}
97
Brandon Kimcf0b9752022-06-15 10:32:21 -070098void BufferImpl::updateReadPtr(const uint32_t newReadPtr)
99{
Patrick Williamsb8125762023-05-10 07:51:26 -0500100 constexpr uint8_t bmcReadPtrOffset = offsetof(struct CircularBufferHeader,
101 bmcReadPtr);
Brandon Kimcf0b9752022-06-15 10:32:21 -0700102
Brandon Kim271d2312022-09-02 16:34:55 +0000103 little_uint24_t truncatedReadPtr =
104 boost::endian::native_to_little(newReadPtr & 0xffffff);
Brandon Kimcf0b9752022-06-15 10:32:21 -0700105 uint8_t* truncatedReadPtrPtr =
106 reinterpret_cast<uint8_t*>(&truncatedReadPtr);
107
108 size_t writtenSize = dataInterface->write(
109 bmcReadPtrOffset, std::span<const uint8_t>{
110 truncatedReadPtrPtr,
Brandon Kim271d2312022-09-02 16:34:55 +0000111 truncatedReadPtrPtr + sizeof(truncatedReadPtr)});
112 if (writtenSize != sizeof(truncatedReadPtr))
Brandon Kimcf0b9752022-06-15 10:32:21 -0700113 {
Patrick Williams5de90612024-02-13 21:31:53 -0600114 throw std::runtime_error(std::format(
Brandon Kimcf0b9752022-06-15 10:32:21 -0700115 "[updateReadPtr] Wrote '{}' bytes, instead of expected '{}'",
Brandon Kim271d2312022-09-02 16:34:55 +0000116 writtenSize, sizeof(truncatedReadPtr)));
Brandon Kimcf0b9752022-06-15 10:32:21 -0700117 }
118 cachedBufferHeader.bmcReadPtr = truncatedReadPtr;
119}
120
Brandon Kimc49284b2022-06-17 09:55:26 -0700121void BufferImpl::updateBmcFlags(const uint32_t newBmcFlag)
122{
Patrick Williamsb8125762023-05-10 07:51:26 -0500123 constexpr uint8_t bmcFlagsPtrOffset = offsetof(struct CircularBufferHeader,
124 bmcFlags);
Brandon Kimc49284b2022-06-17 09:55:26 -0700125
126 little_uint32_t littleNewBmcFlag =
127 boost::endian::native_to_little(newBmcFlag);
128 uint8_t* littleNewBmcFlagPtr =
129 reinterpret_cast<uint8_t*>(&littleNewBmcFlag);
130
131 size_t writtenSize = dataInterface->write(
132 bmcFlagsPtrOffset, std::span<const uint8_t>{
133 littleNewBmcFlagPtr,
134 littleNewBmcFlagPtr + sizeof(little_uint32_t)});
135 if (writtenSize != sizeof(little_uint32_t))
136 {
Patrick Williams5de90612024-02-13 21:31:53 -0600137 throw std::runtime_error(std::format(
Brandon Kimc49284b2022-06-17 09:55:26 -0700138 "[updateBmcFlags] Wrote '{}' bytes, instead of expected '{}'",
139 writtenSize, sizeof(little_uint32_t)));
140 }
141 cachedBufferHeader.bmcFlags = littleNewBmcFlag;
142}
143
Brandon Kim35d43352022-06-16 11:13:36 -0700144std::vector<uint8_t> BufferImpl::wraparoundRead(const uint32_t relativeOffset,
145 const uint32_t length)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700146{
Brandon Kim3def3c82022-09-13 05:29:20 +0000147 const size_t maxOffset = getMaxOffset();
Brandon Kim9836cfa2022-06-15 11:21:11 -0700148
Brandon Kim3def3c82022-09-13 05:29:20 +0000149 if (relativeOffset > maxOffset)
Brandon Kim35d43352022-06-16 11:13:36 -0700150 {
151 throw std::runtime_error(
Patrick Williams5de90612024-02-13 21:31:53 -0600152 std::format("[wraparoundRead] relativeOffset '{}' was bigger "
Brandon Kim3def3c82022-09-13 05:29:20 +0000153 "than maxOffset '{}'",
154 relativeOffset, maxOffset));
Brandon Kim35d43352022-06-16 11:13:36 -0700155 }
Brandon Kim3def3c82022-09-13 05:29:20 +0000156 if (length > maxOffset)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700157 {
Patrick Williams5de90612024-02-13 21:31:53 -0600158 throw std::runtime_error(std::format(
Brandon Kim3def3c82022-09-13 05:29:20 +0000159 "[wraparoundRead] length '{}' was bigger than maxOffset '{}'",
160 length, maxOffset));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700161 }
162
Brandon Kim35d43352022-06-16 11:13:36 -0700163 // Do a calculation to see if the read will wraparound
164 const size_t queueOffset = getQueueOffset();
Brandon Kim3def3c82022-09-13 05:29:20 +0000165 const size_t writableSpace = maxOffset - relativeOffset;
Brandon Kim35d43352022-06-16 11:13:36 -0700166 size_t numWraparoundBytesToRead = 0;
Brandon Kim3def3c82022-09-13 05:29:20 +0000167 if (length > writableSpace)
Brandon Kim35d43352022-06-16 11:13:36 -0700168 {
169 // This means we will wrap, count the bytes that are left to read
Brandon Kim3def3c82022-09-13 05:29:20 +0000170 numWraparoundBytesToRead = length - writableSpace;
Brandon Kim35d43352022-06-16 11:13:36 -0700171 }
172 const size_t numBytesToReadTillQueueEnd = length - numWraparoundBytesToRead;
173
174 std::vector<uint8_t> bytesRead = dataInterface->read(
175 queueOffset + relativeOffset, numBytesToReadTillQueueEnd);
176 if (bytesRead.size() != numBytesToReadTillQueueEnd)
177 {
178 throw std::runtime_error(
Patrick Williams5de90612024-02-13 21:31:53 -0600179 std::format("[wraparoundRead] Read '{}' which was not "
Brandon Kim35d43352022-06-16 11:13:36 -0700180 "the requested length of '{}'",
181 bytesRead.size(), numBytesToReadTillQueueEnd));
182 }
183 size_t updatedReadPtr = relativeOffset + numBytesToReadTillQueueEnd;
Brandon Kim3def3c82022-09-13 05:29:20 +0000184 if (updatedReadPtr == maxOffset)
Brandon Kim4662b1b2022-06-16 21:38:02 -0700185 {
186 // If we read all the way up to the end of the queue, we need to
187 // manually wrap the updateReadPtr around to 0
188 updatedReadPtr = 0;
189 }
Brandon Kim9836cfa2022-06-15 11:21:11 -0700190
191 // If there are any more bytes to be read beyond the buffer, wrap around and
192 // read from the beginning of the buffer (offset by the queueOffset)
Brandon Kim35d43352022-06-16 11:13:36 -0700193 if (numWraparoundBytesToRead > 0)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700194 {
195 std::vector<uint8_t> wrappedBytesRead =
Brandon Kim35d43352022-06-16 11:13:36 -0700196 dataInterface->read(queueOffset, numWraparoundBytesToRead);
197 if (numWraparoundBytesToRead != wrappedBytesRead.size())
Brandon Kim9836cfa2022-06-15 11:21:11 -0700198 {
Patrick Williams5de90612024-02-13 21:31:53 -0600199 throw std::runtime_error(std::format(
Brandon Kim35d43352022-06-16 11:13:36 -0700200 "[wraparoundRead] Buffer wrapped around but read '{}' which "
201 "was not the requested lenght of '{}'",
202 wrappedBytesRead.size(), numWraparoundBytesToRead));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700203 }
204 bytesRead.insert(bytesRead.end(), wrappedBytesRead.begin(),
205 wrappedBytesRead.end());
Brandon Kim35d43352022-06-16 11:13:36 -0700206 updatedReadPtr = numWraparoundBytesToRead;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700207 }
Brandon Kim35d43352022-06-16 11:13:36 -0700208 updateReadPtr(updatedReadPtr);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700209
210 return bytesRead;
211}
212
Brandon Kim613ba532022-09-12 20:55:21 +0000213struct QueueEntryHeader BufferImpl::readEntryHeader()
Brandon Kim7bac2d62022-06-07 21:37:51 -0700214{
215 size_t headerSize = sizeof(struct QueueEntryHeader);
216 // wraparonudRead will throw if it did not read all the bytes, let it
217 // propagate up the stack
Brandon Kim613ba532022-09-12 20:55:21 +0000218 std::vector<uint8_t> bytesRead = wraparoundRead(
219 boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
220 headerSize);
Brandon Kim7bac2d62022-06-07 21:37:51 -0700221
222 return *reinterpret_cast<struct QueueEntryHeader*>(bytesRead.data());
223}
224
Brandon Kim613ba532022-09-12 20:55:21 +0000225EntryPair BufferImpl::readEntry()
Brandon Kim40ce08e2022-06-15 16:58:44 -0700226{
Brandon Kim613ba532022-09-12 20:55:21 +0000227 struct QueueEntryHeader entryHeader = readEntryHeader();
Brandon Kim35d43352022-06-16 11:13:36 -0700228 size_t entrySize = boost::endian::little_to_native(entryHeader.entrySize);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700229
230 // wraparonudRead may throw if entrySize was bigger than the buffer or if it
Brandon Kim26660e92022-06-15 16:58:44 -0700231 // was not able to read all the bytes, let it propagate up the stack
Brandon Kim35d43352022-06-16 11:13:36 -0700232 std::vector<uint8_t> entry = wraparoundRead(
Brandon Kim613ba532022-09-12 20:55:21 +0000233 boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
234 entrySize);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700235
236 // Calculate the checksum
237 uint8_t* entryHeaderPtr = reinterpret_cast<uint8_t*>(&entryHeader);
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700238 uint8_t checksum =
239 std::accumulate(entryHeaderPtr,
240 entryHeaderPtr + sizeof(struct QueueEntryHeader), 0,
241 std::bit_xor<void>()) ^
242 std::accumulate(entry.begin(), entry.end(), 0, std::bit_xor<void>());
243
Brandon Kim40ce08e2022-06-15 16:58:44 -0700244 if (checksum != 0)
245 {
Patrick Williams5de90612024-02-13 21:31:53 -0600246 throw std::runtime_error(std::format(
Brandon Kim40ce08e2022-06-15 16:58:44 -0700247 "[readEntry] Checksum was '{}', expected '0'", checksum));
248 }
249
250 return {entryHeader, entry};
251}
252
Brandon Kim4662b1b2022-06-16 21:38:02 -0700253std::vector<EntryPair> BufferImpl::readErrorLogs()
254{
255 // Reading the buffer header will update the cachedBufferHeader
256 readBufferHeader();
257
Brandon Kim3def3c82022-09-13 05:29:20 +0000258 const size_t maxOffset = getMaxOffset();
Brandon Kim4662b1b2022-06-16 21:38:02 -0700259 size_t currentBiosWritePtr =
260 boost::endian::little_to_native(cachedBufferHeader.biosWritePtr);
Brandon Kim3def3c82022-09-13 05:29:20 +0000261 if (currentBiosWritePtr > maxOffset)
Brandon Kim4662b1b2022-06-16 21:38:02 -0700262 {
Patrick Williams5de90612024-02-13 21:31:53 -0600263 throw std::runtime_error(std::format(
Brandon Kim4662b1b2022-06-16 21:38:02 -0700264 "[readErrorLogs] currentBiosWritePtr was '{}' which was bigger "
Brandon Kim3def3c82022-09-13 05:29:20 +0000265 "than maxOffset '{}'",
266 currentBiosWritePtr, maxOffset));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700267 }
268 size_t currentReadPtr =
269 boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr);
Brandon Kim3def3c82022-09-13 05:29:20 +0000270 if (currentReadPtr > maxOffset)
Brandon Kim4662b1b2022-06-16 21:38:02 -0700271 {
Patrick Williams5de90612024-02-13 21:31:53 -0600272 throw std::runtime_error(std::format(
Brandon Kim4662b1b2022-06-16 21:38:02 -0700273 "[readErrorLogs] currentReadPtr was '{}' which was bigger "
Brandon Kim3def3c82022-09-13 05:29:20 +0000274 "than maxOffset '{}'",
275 currentReadPtr, maxOffset));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700276 }
277
278 size_t bytesToRead;
279 if (currentBiosWritePtr == currentReadPtr)
280 {
281 // No new payload was detected, return an empty vector gracefully
282 return {};
283 }
284
285 if (currentBiosWritePtr > currentReadPtr)
286 {
287 // Simply subtract in this case
288 bytesToRead = currentBiosWritePtr - currentReadPtr;
289 }
290 else
291 {
Brandon Kim3def3c82022-09-13 05:29:20 +0000292 // Calculate the bytes to the "end" (maxOffset - ReadPtr) +
Brandon Kim4662b1b2022-06-16 21:38:02 -0700293 // bytes to read from the "beginning" (0 + WritePtr)
Brandon Kim3def3c82022-09-13 05:29:20 +0000294 bytesToRead = (maxOffset - currentReadPtr) + currentBiosWritePtr;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700295 }
296
297 size_t byteRead = 0;
298 std::vector<EntryPair> entryPairs;
299 while (byteRead < bytesToRead)
300 {
Brandon Kim613ba532022-09-12 20:55:21 +0000301 EntryPair entryPair = readEntry();
Brandon Kim4662b1b2022-06-16 21:38:02 -0700302 byteRead += sizeof(struct QueueEntryHeader) + entryPair.second.size();
303 entryPairs.push_back(entryPair);
304
305 // Note: readEntry() will update cachedBufferHeader.bmcReadPtr
306 currentReadPtr =
307 boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr);
308 }
309 if (currentBiosWritePtr != currentReadPtr)
310 {
Patrick Williams5de90612024-02-13 21:31:53 -0600311 throw std::runtime_error(std::format(
Brandon Kim4662b1b2022-06-16 21:38:02 -0700312 "[readErrorLogs] biosWritePtr '{}' and bmcReaddPtr '{}' "
313 "are not identical after reading through all the logs",
314 currentBiosWritePtr, currentReadPtr));
315 }
316 return entryPairs;
317}
318
Brandon Kim3def3c82022-09-13 05:29:20 +0000319size_t BufferImpl::getMaxOffset()
320{
321 size_t queueSize =
322 boost::endian::little_to_native(cachedBufferHeader.queueSize);
323 size_t ueRegionSize =
324 boost::endian::little_to_native(cachedBufferHeader.ueRegionSize);
325
Brandon Kim82ab8322023-08-17 00:50:18 +0000326 if (queueSize != QUEUE_REGION_SIZE)
327 {
Patrick Williams5de90612024-02-13 21:31:53 -0600328 throw std::runtime_error(std::format(
Brandon Kim82ab8322023-08-17 00:50:18 +0000329 "[{}] runtime queueSize '{}' did not match compile-time queueSize "
330 "'{}'. This indicates that the buffer was corrupted",
331 __FUNCTION__, queueSize, QUEUE_REGION_SIZE));
332 }
333 if (ueRegionSize != UE_REGION_SIZE)
334 {
Patrick Williams5de90612024-02-13 21:31:53 -0600335 throw std::runtime_error(std::format(
Brandon Kim82ab8322023-08-17 00:50:18 +0000336 "[{}] runtime ueRegionSize '{}' did not match compile-time "
337 "ueRegionSize '{}'. This indicates that the buffer was corrupted",
338 __FUNCTION__, ueRegionSize, UE_REGION_SIZE));
339 }
340
Brandon Kim3def3c82022-09-13 05:29:20 +0000341 return queueSize - ueRegionSize - sizeof(struct CircularBufferHeader);
342}
343
Brandon Kim82ab8322023-08-17 00:50:18 +0000344size_t BufferImpl::getQueueOffset()
345{
346 size_t ueRegionSize =
347 boost::endian::little_to_native(cachedBufferHeader.ueRegionSize);
348
349 if (ueRegionSize != UE_REGION_SIZE)
350 {
Patrick Williams5de90612024-02-13 21:31:53 -0600351 throw std::runtime_error(std::format(
Brandon Kim82ab8322023-08-17 00:50:18 +0000352 "[{}] runtime ueRegionSize '{}' did not match compile-time "
353 "ueRegionSize '{}'. This indicates that the buffer was corrupted",
354 __FUNCTION__, ueRegionSize, UE_REGION_SIZE));
355 }
356 return sizeof(struct CircularBufferHeader) + ueRegionSize;
357}
358
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700359} // namespace bios_bmc_smm_error_logger