1fcbc3db1SBrandon Kim #include "buffer.hpp" 2fcbc3db1SBrandon Kim 3fcbc3db1SBrandon Kim #include "pci_handler.hpp" 4fcbc3db1SBrandon Kim 5fcbc3db1SBrandon Kim #include <fmt/format.h> 6fcbc3db1SBrandon Kim 717ee1a93SBrandon Kim #include <boost/endian/arithmetic.hpp> 817ee1a93SBrandon Kim #include <boost/endian/conversion.hpp> 917ee1a93SBrandon Kim 1017ee1a93SBrandon Kim #include <algorithm> 11fcbc3db1SBrandon Kim #include <array> 12cf0b9752SBrandon Kim #include <cstddef> 13fcbc3db1SBrandon Kim #include <cstdint> 14fcbc3db1SBrandon Kim #include <memory> 15fcbc3db1SBrandon Kim #include <span> 16fcbc3db1SBrandon Kim #include <vector> 17fcbc3db1SBrandon Kim 18fcbc3db1SBrandon Kim namespace bios_bmc_smm_error_logger 19fcbc3db1SBrandon Kim { 20fcbc3db1SBrandon Kim 21fcbc3db1SBrandon Kim BufferImpl::BufferImpl(std::unique_ptr<DataInterface> dataInterface) : 22fcbc3db1SBrandon Kim dataInterface(std::move(dataInterface)){}; 23fcbc3db1SBrandon Kim 24fcbc3db1SBrandon Kim void BufferImpl::initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize, 25fcbc3db1SBrandon Kim uint16_t ueRegionSize, 26fcbc3db1SBrandon Kim const std::array<uint32_t, 4>& magicNumber) 27fcbc3db1SBrandon Kim { 28fcbc3db1SBrandon Kim // Initialize the whole buffer with 0x00 29fcbc3db1SBrandon Kim const size_t memoryRegionSize = dataInterface->getMemoryRegionSize(); 30fcbc3db1SBrandon Kim const std::vector<uint8_t> emptyVector(memoryRegionSize, 0); 31fcbc3db1SBrandon Kim size_t byteWritten = dataInterface->write(0, emptyVector); 32fcbc3db1SBrandon Kim if (byteWritten != memoryRegionSize) 33fcbc3db1SBrandon Kim { 34fcbc3db1SBrandon Kim throw std::runtime_error( 35fcbc3db1SBrandon Kim fmt::format("Buffer initialization only erased '{}'", byteWritten)); 36fcbc3db1SBrandon Kim } 37fcbc3db1SBrandon Kim 38fcbc3db1SBrandon Kim // Create an initial buffer header and write to it 39fcbc3db1SBrandon Kim struct CircularBufferHeader initializationHeader = {}; 4017ee1a93SBrandon Kim initializationHeader.bmcInterfaceVersion = 4117ee1a93SBrandon Kim boost::endian::native_to_little(bmcInterfaceVersion); 4217ee1a93SBrandon Kim initializationHeader.queueSize = boost::endian::native_to_little(queueSize); 4317ee1a93SBrandon Kim initializationHeader.ueRegionSize = 4417ee1a93SBrandon Kim boost::endian::native_to_little(ueRegionSize); 4517ee1a93SBrandon Kim std::transform(magicNumber.begin(), magicNumber.end(), 4617ee1a93SBrandon Kim initializationHeader.magicNumber.begin(), 4717ee1a93SBrandon Kim [](uint32_t number) -> little_uint32_t { 4817ee1a93SBrandon Kim return boost::endian::native_to_little(number); 4917ee1a93SBrandon Kim }); 50fcbc3db1SBrandon Kim 51fcbc3db1SBrandon Kim uint8_t* initializationHeaderPtr = 52fcbc3db1SBrandon Kim reinterpret_cast<uint8_t*>(&initializationHeader); 53fcbc3db1SBrandon Kim size_t initializationHeaderSize = sizeof(initializationHeader); 54fcbc3db1SBrandon Kim byteWritten = dataInterface->write( 55fcbc3db1SBrandon Kim 0, std::span<const uint8_t>(initializationHeaderPtr, 56fcbc3db1SBrandon Kim initializationHeaderPtr + 57fcbc3db1SBrandon Kim initializationHeaderSize)); 58fcbc3db1SBrandon Kim if (byteWritten != initializationHeaderSize) 59fcbc3db1SBrandon Kim { 60fcbc3db1SBrandon Kim throw std::runtime_error(fmt::format( 61fcbc3db1SBrandon Kim "Buffer initialization buffer header write only wrote '{}'", 62fcbc3db1SBrandon Kim byteWritten)); 63fcbc3db1SBrandon Kim } 6460cab57fSBrandon Kim cachedBufferHeader = initializationHeader; 65fcbc3db1SBrandon Kim } 66fcbc3db1SBrandon Kim 6717ee1a93SBrandon Kim void BufferImpl::readBufferHeader() 6817ee1a93SBrandon Kim { 6917ee1a93SBrandon Kim size_t headerSize = sizeof(struct CircularBufferHeader); 7017ee1a93SBrandon Kim std::vector<uint8_t> bytesRead = 7117ee1a93SBrandon Kim dataInterface->read(/* offset */ 0, headerSize); 7217ee1a93SBrandon Kim 7317ee1a93SBrandon Kim if (bytesRead.size() != headerSize) 7417ee1a93SBrandon Kim { 7517ee1a93SBrandon Kim throw std::runtime_error( 7617ee1a93SBrandon Kim fmt::format("Buffer header read only read '{}', expected '{}'", 7717ee1a93SBrandon Kim bytesRead.size(), headerSize)); 7817ee1a93SBrandon Kim } 7917ee1a93SBrandon Kim 8017ee1a93SBrandon Kim cachedBufferHeader = 8160cab57fSBrandon Kim *reinterpret_cast<struct CircularBufferHeader*>(bytesRead.data()); 8217ee1a93SBrandon Kim }; 8317ee1a93SBrandon Kim 8417ee1a93SBrandon Kim struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const 8517ee1a93SBrandon Kim { 8617ee1a93SBrandon Kim return cachedBufferHeader; 8717ee1a93SBrandon Kim } 8817ee1a93SBrandon Kim 89cf0b9752SBrandon Kim void BufferImpl::updateReadPtr(const uint32_t newReadPtr) 90cf0b9752SBrandon Kim { 91cf0b9752SBrandon Kim constexpr uint8_t bmcReadPtrOffset = 92cf0b9752SBrandon Kim offsetof(struct CircularBufferHeader, bmcReadPtr); 93cf0b9752SBrandon Kim 94cf0b9752SBrandon Kim little_uint16_t truncatedReadPtr = 95cf0b9752SBrandon Kim boost::endian::native_to_little(newReadPtr & 0xffff); 96cf0b9752SBrandon Kim uint8_t* truncatedReadPtrPtr = 97cf0b9752SBrandon Kim reinterpret_cast<uint8_t*>(&truncatedReadPtr); 98cf0b9752SBrandon Kim 99cf0b9752SBrandon Kim size_t writtenSize = dataInterface->write( 100cf0b9752SBrandon Kim bmcReadPtrOffset, std::span<const uint8_t>{ 101cf0b9752SBrandon Kim truncatedReadPtrPtr, 102cf0b9752SBrandon Kim truncatedReadPtrPtr + sizeof(little_uint16_t)}); 103cf0b9752SBrandon Kim if (writtenSize != sizeof(little_uint16_t)) 104cf0b9752SBrandon Kim { 105cf0b9752SBrandon Kim throw std::runtime_error(fmt::format( 106cf0b9752SBrandon Kim "[updateReadPtr] Wrote '{}' bytes, instead of expected '{}'", 107cf0b9752SBrandon Kim writtenSize, sizeof(little_uint16_t))); 108cf0b9752SBrandon Kim } 109cf0b9752SBrandon Kim cachedBufferHeader.bmcReadPtr = truncatedReadPtr; 110cf0b9752SBrandon Kim } 111cf0b9752SBrandon Kim 112*9836cfa6SBrandon Kim size_t BufferImpl::getQueueOffset() 113*9836cfa6SBrandon Kim { 114*9836cfa6SBrandon Kim return sizeof(struct CircularBufferHeader) + 115*9836cfa6SBrandon Kim boost::endian::little_to_native(cachedBufferHeader.ueRegionSize); 116*9836cfa6SBrandon Kim } 117*9836cfa6SBrandon Kim 118*9836cfa6SBrandon Kim std::vector<uint8_t> 119*9836cfa6SBrandon Kim BufferImpl::wraparoundRead(const uint32_t offset, const uint32_t length, 120*9836cfa6SBrandon Kim const uint32_t additionalBoundaryCheck) 121*9836cfa6SBrandon Kim { 122*9836cfa6SBrandon Kim const size_t memoryRegionSize = dataInterface->getMemoryRegionSize(); 123*9836cfa6SBrandon Kim 124*9836cfa6SBrandon Kim size_t queueOffset = getQueueOffset(); 125*9836cfa6SBrandon Kim if (queueOffset + length + additionalBoundaryCheck > memoryRegionSize) 126*9836cfa6SBrandon Kim { 127*9836cfa6SBrandon Kim throw std::runtime_error(fmt::format( 128*9836cfa6SBrandon Kim "[wraparoundRead] queueOffset '{}' + length '{}' " 129*9836cfa6SBrandon Kim "+ additionalBoundaryCheck '{}' + was bigger " 130*9836cfa6SBrandon Kim "than memoryRegionSize '{}'", 131*9836cfa6SBrandon Kim queueOffset, length, additionalBoundaryCheck, memoryRegionSize)); 132*9836cfa6SBrandon Kim } 133*9836cfa6SBrandon Kim 134*9836cfa6SBrandon Kim // Do a first read up to the end of the buffer (dataInerface->read should 135*9836cfa6SBrandon Kim // only read up to the end of the buffer) 136*9836cfa6SBrandon Kim std::vector<uint8_t> bytesRead = dataInterface->read(offset, length); 137*9836cfa6SBrandon Kim size_t updatedReadOffset = offset + bytesRead.size(); 138*9836cfa6SBrandon Kim size_t bytesRemaining = length - bytesRead.size(); 139*9836cfa6SBrandon Kim 140*9836cfa6SBrandon Kim // If there are any more bytes to be read beyond the buffer, wrap around and 141*9836cfa6SBrandon Kim // read from the beginning of the buffer (offset by the queueOffset) 142*9836cfa6SBrandon Kim if (bytesRemaining > 0) 143*9836cfa6SBrandon Kim { 144*9836cfa6SBrandon Kim std::vector<uint8_t> wrappedBytesRead = 145*9836cfa6SBrandon Kim dataInterface->read(queueOffset, bytesRemaining); 146*9836cfa6SBrandon Kim bytesRemaining -= wrappedBytesRead.size(); 147*9836cfa6SBrandon Kim if (bytesRemaining != 0) 148*9836cfa6SBrandon Kim { 149*9836cfa6SBrandon Kim throw std::runtime_error(fmt::format( 150*9836cfa6SBrandon Kim "[wraparoundRead] Buffer wrapped around but was not able to read " 151*9836cfa6SBrandon Kim "all of the requested info. Bytes remaining to read '{}' of '{}'", 152*9836cfa6SBrandon Kim bytesRemaining, length)); 153*9836cfa6SBrandon Kim } 154*9836cfa6SBrandon Kim bytesRead.insert(bytesRead.end(), wrappedBytesRead.begin(), 155*9836cfa6SBrandon Kim wrappedBytesRead.end()); 156*9836cfa6SBrandon Kim updatedReadOffset = queueOffset + wrappedBytesRead.size(); 157*9836cfa6SBrandon Kim } 158*9836cfa6SBrandon Kim updateReadPtr(updatedReadOffset); 159*9836cfa6SBrandon Kim 160*9836cfa6SBrandon Kim return bytesRead; 161*9836cfa6SBrandon Kim } 162*9836cfa6SBrandon Kim 163fcbc3db1SBrandon Kim } // namespace bios_bmc_smm_error_logger 164