blob: 87d9fa267a96d1dc761bd5fb2e78db2edc8d0b7c [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>
15#include <span>
16#include <vector>
17
18namespace bios_bmc_smm_error_logger
19{
20
21BufferImpl::BufferImpl(std::unique_ptr<DataInterface> dataInterface) :
22 dataInterface(std::move(dataInterface)){};
23
24void BufferImpl::initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize,
25 uint16_t ueRegionSize,
26 const std::array<uint32_t, 4>& magicNumber)
27{
28 // Initialize the whole buffer with 0x00
29 const size_t memoryRegionSize = dataInterface->getMemoryRegionSize();
30 const std::vector<uint8_t> emptyVector(memoryRegionSize, 0);
31 size_t byteWritten = dataInterface->write(0, emptyVector);
32 if (byteWritten != memoryRegionSize)
33 {
34 throw std::runtime_error(
35 fmt::format("Buffer initialization only erased '{}'", byteWritten));
36 }
37
38 // Create an initial buffer header and write to it
39 struct CircularBufferHeader initializationHeader = {};
Brandon Kim17ee1a92022-06-07 21:04:08 -070040 initializationHeader.bmcInterfaceVersion =
41 boost::endian::native_to_little(bmcInterfaceVersion);
42 initializationHeader.queueSize = boost::endian::native_to_little(queueSize);
43 initializationHeader.ueRegionSize =
44 boost::endian::native_to_little(ueRegionSize);
45 std::transform(magicNumber.begin(), magicNumber.end(),
46 initializationHeader.magicNumber.begin(),
47 [](uint32_t number) -> little_uint32_t {
48 return boost::endian::native_to_little(number);
49 });
Brandon Kimfcbc3db2022-06-06 21:26:18 -070050
51 uint8_t* initializationHeaderPtr =
52 reinterpret_cast<uint8_t*>(&initializationHeader);
53 size_t initializationHeaderSize = sizeof(initializationHeader);
54 byteWritten = dataInterface->write(
55 0, std::span<const uint8_t>(initializationHeaderPtr,
56 initializationHeaderPtr +
57 initializationHeaderSize));
58 if (byteWritten != initializationHeaderSize)
59 {
60 throw std::runtime_error(fmt::format(
61 "Buffer initialization buffer header write only wrote '{}'",
62 byteWritten));
63 }
Brandon Kim60cab572022-06-15 14:20:05 -070064 cachedBufferHeader = initializationHeader;
Brandon Kimfcbc3db2022-06-06 21:26:18 -070065}
66
Brandon Kim17ee1a92022-06-07 21:04:08 -070067void BufferImpl::readBufferHeader()
68{
69 size_t headerSize = sizeof(struct CircularBufferHeader);
70 std::vector<uint8_t> bytesRead =
71 dataInterface->read(/* offset */ 0, headerSize);
72
73 if (bytesRead.size() != headerSize)
74 {
75 throw std::runtime_error(
76 fmt::format("Buffer header read only read '{}', expected '{}'",
77 bytesRead.size(), headerSize));
78 }
79
80 cachedBufferHeader =
Brandon Kim60cab572022-06-15 14:20:05 -070081 *reinterpret_cast<struct CircularBufferHeader*>(bytesRead.data());
Brandon Kim17ee1a92022-06-07 21:04:08 -070082};
83
84struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const
85{
86 return cachedBufferHeader;
87}
88
Brandon Kimcf0b9752022-06-15 10:32:21 -070089void BufferImpl::updateReadPtr(const uint32_t newReadPtr)
90{
91 constexpr uint8_t bmcReadPtrOffset =
92 offsetof(struct CircularBufferHeader, bmcReadPtr);
93
94 little_uint16_t truncatedReadPtr =
95 boost::endian::native_to_little(newReadPtr & 0xffff);
96 uint8_t* truncatedReadPtrPtr =
97 reinterpret_cast<uint8_t*>(&truncatedReadPtr);
98
99 size_t writtenSize = dataInterface->write(
100 bmcReadPtrOffset, std::span<const uint8_t>{
101 truncatedReadPtrPtr,
102 truncatedReadPtrPtr + sizeof(little_uint16_t)});
103 if (writtenSize != sizeof(little_uint16_t))
104 {
105 throw std::runtime_error(fmt::format(
106 "[updateReadPtr] Wrote '{}' bytes, instead of expected '{}'",
107 writtenSize, sizeof(little_uint16_t)));
108 }
109 cachedBufferHeader.bmcReadPtr = truncatedReadPtr;
110}
111
Brandon Kim9836cfa2022-06-15 11:21:11 -0700112size_t BufferImpl::getQueueOffset()
113{
114 return sizeof(struct CircularBufferHeader) +
115 boost::endian::little_to_native(cachedBufferHeader.ueRegionSize);
116}
117
118std::vector<uint8_t>
119 BufferImpl::wraparoundRead(const uint32_t offset, const uint32_t length,
120 const uint32_t additionalBoundaryCheck)
121{
122 const size_t memoryRegionSize = dataInterface->getMemoryRegionSize();
123
124 size_t queueOffset = getQueueOffset();
125 if (queueOffset + length + additionalBoundaryCheck > memoryRegionSize)
126 {
127 throw std::runtime_error(fmt::format(
128 "[wraparoundRead] queueOffset '{}' + length '{}' "
129 "+ additionalBoundaryCheck '{}' + was bigger "
130 "than memoryRegionSize '{}'",
131 queueOffset, length, additionalBoundaryCheck, memoryRegionSize));
132 }
133
134 // Do a first read up to the end of the buffer (dataInerface->read should
135 // only read up to the end of the buffer)
136 std::vector<uint8_t> bytesRead = dataInterface->read(offset, length);
137 size_t updatedReadOffset = offset + bytesRead.size();
138 size_t bytesRemaining = length - bytesRead.size();
139
140 // If there are any more bytes to be read beyond the buffer, wrap around and
141 // read from the beginning of the buffer (offset by the queueOffset)
142 if (bytesRemaining > 0)
143 {
144 std::vector<uint8_t> wrappedBytesRead =
145 dataInterface->read(queueOffset, bytesRemaining);
146 bytesRemaining -= wrappedBytesRead.size();
147 if (bytesRemaining != 0)
148 {
149 throw std::runtime_error(fmt::format(
150 "[wraparoundRead] Buffer wrapped around but was not able to read "
151 "all of the requested info. Bytes remaining to read '{}' of '{}'",
152 bytesRemaining, length));
153 }
154 bytesRead.insert(bytesRead.end(), wrappedBytesRead.begin(),
155 wrappedBytesRead.end());
156 updatedReadOffset = queueOffset + wrappedBytesRead.size();
157 }
158 updateReadPtr(updatedReadOffset);
159
160 return bytesRead;
161}
162
Brandon Kim7bac2d62022-06-07 21:37:51 -0700163struct QueueEntryHeader BufferImpl::readEntryHeader(size_t offset)
164{
165 size_t headerSize = sizeof(struct QueueEntryHeader);
166 // wraparonudRead will throw if it did not read all the bytes, let it
167 // propagate up the stack
168 std::vector<uint8_t> bytesRead = wraparoundRead(offset, headerSize);
169
170 return *reinterpret_cast<struct QueueEntryHeader*>(bytesRead.data());
171}
172
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700173} // namespace bios_bmc_smm_error_logger