blob: fca8837d3f80c203c2f5382e9cd6d155a0a764ce [file] [log] [blame]
Brandon Kimfcbc3db2022-06-06 21:26:18 -07001#include "buffer.hpp"
2
3#include "pci_handler.hpp"
4
5#include <fmt/format.h>
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>
14#include <memory>
Brandon Kim40ce08e2022-06-15 16:58:44 -070015#include <numeric>
Brandon Kimfcbc3db2022-06-06 21:26:18 -070016#include <span>
17#include <vector>
18
19namespace bios_bmc_smm_error_logger
20{
21
22BufferImpl::BufferImpl(std::unique_ptr<DataInterface> dataInterface) :
23 dataInterface(std::move(dataInterface)){};
24
25void BufferImpl::initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize,
26 uint16_t ueRegionSize,
27 const std::array<uint32_t, 4>& magicNumber)
28{
29 // Initialize the whole buffer with 0x00
30 const size_t memoryRegionSize = dataInterface->getMemoryRegionSize();
31 const std::vector<uint8_t> emptyVector(memoryRegionSize, 0);
32 size_t byteWritten = dataInterface->write(0, emptyVector);
33 if (byteWritten != memoryRegionSize)
34 {
35 throw std::runtime_error(
36 fmt::format("Buffer initialization only erased '{}'", byteWritten));
37 }
38
39 // Create an initial buffer header and write to it
40 struct CircularBufferHeader initializationHeader = {};
Brandon Kim17ee1a92022-06-07 21:04:08 -070041 initializationHeader.bmcInterfaceVersion =
42 boost::endian::native_to_little(bmcInterfaceVersion);
43 initializationHeader.queueSize = boost::endian::native_to_little(queueSize);
44 initializationHeader.ueRegionSize =
45 boost::endian::native_to_little(ueRegionSize);
46 std::transform(magicNumber.begin(), magicNumber.end(),
47 initializationHeader.magicNumber.begin(),
48 [](uint32_t number) -> little_uint32_t {
49 return boost::endian::native_to_little(number);
50 });
Brandon Kimfcbc3db2022-06-06 21:26:18 -070051
52 uint8_t* initializationHeaderPtr =
53 reinterpret_cast<uint8_t*>(&initializationHeader);
54 size_t initializationHeaderSize = sizeof(initializationHeader);
55 byteWritten = dataInterface->write(
56 0, std::span<const uint8_t>(initializationHeaderPtr,
57 initializationHeaderPtr +
58 initializationHeaderSize));
59 if (byteWritten != initializationHeaderSize)
60 {
61 throw std::runtime_error(fmt::format(
62 "Buffer initialization buffer header write only wrote '{}'",
63 byteWritten));
64 }
Brandon Kim60cab572022-06-15 14:20:05 -070065 cachedBufferHeader = initializationHeader;
Brandon Kimfcbc3db2022-06-06 21:26:18 -070066}
67
Brandon Kim17ee1a92022-06-07 21:04:08 -070068void BufferImpl::readBufferHeader()
69{
70 size_t headerSize = sizeof(struct CircularBufferHeader);
71 std::vector<uint8_t> bytesRead =
72 dataInterface->read(/* offset */ 0, headerSize);
73
74 if (bytesRead.size() != headerSize)
75 {
76 throw std::runtime_error(
77 fmt::format("Buffer header read only read '{}', expected '{}'",
78 bytesRead.size(), headerSize));
79 }
80
81 cachedBufferHeader =
Brandon Kim60cab572022-06-15 14:20:05 -070082 *reinterpret_cast<struct CircularBufferHeader*>(bytesRead.data());
Brandon Kim17ee1a92022-06-07 21:04:08 -070083};
84
85struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const
86{
87 return cachedBufferHeader;
88}
89
Brandon Kimcf0b9752022-06-15 10:32:21 -070090void BufferImpl::updateReadPtr(const uint32_t newReadPtr)
91{
92 constexpr uint8_t bmcReadPtrOffset =
93 offsetof(struct CircularBufferHeader, bmcReadPtr);
94
95 little_uint16_t truncatedReadPtr =
96 boost::endian::native_to_little(newReadPtr & 0xffff);
97 uint8_t* truncatedReadPtrPtr =
98 reinterpret_cast<uint8_t*>(&truncatedReadPtr);
99
100 size_t writtenSize = dataInterface->write(
101 bmcReadPtrOffset, std::span<const uint8_t>{
102 truncatedReadPtrPtr,
103 truncatedReadPtrPtr + sizeof(little_uint16_t)});
104 if (writtenSize != sizeof(little_uint16_t))
105 {
106 throw std::runtime_error(fmt::format(
107 "[updateReadPtr] Wrote '{}' bytes, instead of expected '{}'",
108 writtenSize, sizeof(little_uint16_t)));
109 }
110 cachedBufferHeader.bmcReadPtr = truncatedReadPtr;
111}
112
Brandon Kim9836cfa2022-06-15 11:21:11 -0700113size_t BufferImpl::getQueueOffset()
114{
115 return sizeof(struct CircularBufferHeader) +
116 boost::endian::little_to_native(cachedBufferHeader.ueRegionSize);
117}
118
119std::vector<uint8_t>
120 BufferImpl::wraparoundRead(const uint32_t offset, const uint32_t length,
121 const uint32_t additionalBoundaryCheck)
122{
123 const size_t memoryRegionSize = dataInterface->getMemoryRegionSize();
124
125 size_t queueOffset = getQueueOffset();
126 if (queueOffset + length + additionalBoundaryCheck > memoryRegionSize)
127 {
128 throw std::runtime_error(fmt::format(
129 "[wraparoundRead] queueOffset '{}' + length '{}' "
130 "+ additionalBoundaryCheck '{}' + was bigger "
131 "than memoryRegionSize '{}'",
132 queueOffset, length, additionalBoundaryCheck, memoryRegionSize));
133 }
134
135 // Do a first read up to the end of the buffer (dataInerface->read should
136 // only read up to the end of the buffer)
137 std::vector<uint8_t> bytesRead = dataInterface->read(offset, length);
138 size_t updatedReadOffset = offset + bytesRead.size();
139 size_t bytesRemaining = length - bytesRead.size();
140
141 // If there are any more bytes to be read beyond the buffer, wrap around and
142 // read from the beginning of the buffer (offset by the queueOffset)
143 if (bytesRemaining > 0)
144 {
145 std::vector<uint8_t> wrappedBytesRead =
146 dataInterface->read(queueOffset, bytesRemaining);
147 bytesRemaining -= wrappedBytesRead.size();
148 if (bytesRemaining != 0)
149 {
150 throw std::runtime_error(fmt::format(
151 "[wraparoundRead] Buffer wrapped around but was not able to read "
152 "all of the requested info. Bytes remaining to read '{}' of '{}'",
153 bytesRemaining, length));
154 }
155 bytesRead.insert(bytesRead.end(), wrappedBytesRead.begin(),
156 wrappedBytesRead.end());
157 updatedReadOffset = queueOffset + wrappedBytesRead.size();
158 }
159 updateReadPtr(updatedReadOffset);
160
161 return bytesRead;
162}
163
Brandon Kim7bac2d62022-06-07 21:37:51 -0700164struct QueueEntryHeader BufferImpl::readEntryHeader(size_t offset)
165{
166 size_t headerSize = sizeof(struct QueueEntryHeader);
167 // wraparonudRead will throw if it did not read all the bytes, let it
168 // propagate up the stack
169 std::vector<uint8_t> bytesRead = wraparoundRead(offset, headerSize);
170
171 return *reinterpret_cast<struct QueueEntryHeader*>(bytesRead.data());
172}
173
Brandon Kim40ce08e2022-06-15 16:58:44 -0700174EntryPair BufferImpl::readEntry(size_t offset)
175{
176 struct QueueEntryHeader entryHeader = readEntryHeader(offset);
177
178 size_t entrySize = entryHeader.entrySize;
179
180 // wraparonudRead may throw if entrySize was bigger than the buffer or if it
181 // was not able to read all bytes, let it propagate up the stack
182 // - Use additionalBoundaryCheck parameter to tighten the boundary check
183 std::vector<uint8_t> entry =
184 wraparoundRead(offset + sizeof(struct QueueEntryHeader), entrySize,
185 sizeof(struct QueueEntryHeader));
186
187 // Calculate the checksum
188 uint8_t* entryHeaderPtr = reinterpret_cast<uint8_t*>(&entryHeader);
189 uint8_t checksum = std::accumulate(
190 entryHeaderPtr, entryHeaderPtr + sizeof(struct QueueEntryHeader), 0);
191 checksum = std::accumulate(std::begin(entry), std::end(entry), checksum);
192 if (checksum != 0)
193 {
194 throw std::runtime_error(fmt::format(
195 "[readEntry] Checksum was '{}', expected '0'", checksum));
196 }
197
198 return {entryHeader, entry};
199}
200
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700201} // namespace bios_bmc_smm_error_logger