1 #include "buffer.hpp" 2 3 #include "pci_handler.hpp" 4 5 #include <fmt/format.h> 6 7 #include <boost/endian/arithmetic.hpp> 8 #include <boost/endian/conversion.hpp> 9 10 #include <algorithm> 11 #include <array> 12 #include <cstddef> 13 #include <cstdint> 14 #include <memory> 15 #include <span> 16 #include <vector> 17 18 namespace bios_bmc_smm_error_logger 19 { 20 21 BufferImpl::BufferImpl(std::unique_ptr<DataInterface> dataInterface) : 22 dataInterface(std::move(dataInterface)){}; 23 24 void 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 = {}; 40 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 }); 50 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 } 64 cachedBufferHeader = initializationHeader; 65 } 66 67 void 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 = 81 *reinterpret_cast<struct CircularBufferHeader*>(bytesRead.data()); 82 }; 83 84 struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const 85 { 86 return cachedBufferHeader; 87 } 88 89 void 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 112 size_t BufferImpl::getQueueOffset() 113 { 114 return sizeof(struct CircularBufferHeader) + 115 boost::endian::little_to_native(cachedBufferHeader.ueRegionSize); 116 } 117 118 std::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 163 } // namespace bios_bmc_smm_error_logger 164