#include "buffer.hpp" #include "pci_handler.hpp" #include #include #include #include #include #include #include #include #include #include namespace bios_bmc_smm_error_logger { BufferImpl::BufferImpl(std::unique_ptr dataInterface) : dataInterface(std::move(dataInterface)){}; void BufferImpl::initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize, uint16_t ueRegionSize, const std::array& magicNumber) { // Initialize the whole buffer with 0x00 const size_t memoryRegionSize = dataInterface->getMemoryRegionSize(); const std::vector emptyVector(memoryRegionSize, 0); size_t byteWritten = dataInterface->write(0, emptyVector); if (byteWritten != memoryRegionSize) { throw std::runtime_error( fmt::format("Buffer initialization only erased '{}'", byteWritten)); } // Create an initial buffer header and write to it struct CircularBufferHeader initializationHeader = {}; initializationHeader.bmcInterfaceVersion = boost::endian::native_to_little(bmcInterfaceVersion); initializationHeader.queueSize = boost::endian::native_to_little(queueSize); initializationHeader.ueRegionSize = boost::endian::native_to_little(ueRegionSize); std::transform(magicNumber.begin(), magicNumber.end(), initializationHeader.magicNumber.begin(), [](uint32_t number) -> little_uint32_t { return boost::endian::native_to_little(number); }); uint8_t* initializationHeaderPtr = reinterpret_cast(&initializationHeader); size_t initializationHeaderSize = sizeof(initializationHeader); byteWritten = dataInterface->write( 0, std::span(initializationHeaderPtr, initializationHeaderPtr + initializationHeaderSize)); if (byteWritten != initializationHeaderSize) { throw std::runtime_error(fmt::format( "Buffer initialization buffer header write only wrote '{}'", byteWritten)); } cachedBufferHeader = initializationHeader; } void BufferImpl::readBufferHeader() { size_t headerSize = sizeof(struct CircularBufferHeader); std::vector bytesRead = dataInterface->read(/* offset */ 0, headerSize); if (bytesRead.size() != headerSize) { throw std::runtime_error( fmt::format("Buffer header read only read '{}', expected '{}'", bytesRead.size(), headerSize)); } cachedBufferHeader = *reinterpret_cast(bytesRead.data()); }; struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const { return cachedBufferHeader; } void BufferImpl::updateReadPtr(const uint32_t newReadPtr) { constexpr uint8_t bmcReadPtrOffset = offsetof(struct CircularBufferHeader, bmcReadPtr); little_uint16_t truncatedReadPtr = boost::endian::native_to_little(newReadPtr & 0xffff); uint8_t* truncatedReadPtrPtr = reinterpret_cast(&truncatedReadPtr); size_t writtenSize = dataInterface->write( bmcReadPtrOffset, std::span{ truncatedReadPtrPtr, truncatedReadPtrPtr + sizeof(little_uint16_t)}); if (writtenSize != sizeof(little_uint16_t)) { throw std::runtime_error(fmt::format( "[updateReadPtr] Wrote '{}' bytes, instead of expected '{}'", writtenSize, sizeof(little_uint16_t))); } cachedBufferHeader.bmcReadPtr = truncatedReadPtr; } size_t BufferImpl::getQueueOffset() { return sizeof(struct CircularBufferHeader) + boost::endian::little_to_native(cachedBufferHeader.ueRegionSize); } std::vector BufferImpl::wraparoundRead(const uint32_t offset, const uint32_t length, const uint32_t additionalBoundaryCheck) { const size_t memoryRegionSize = dataInterface->getMemoryRegionSize(); size_t queueOffset = getQueueOffset(); if (queueOffset + length + additionalBoundaryCheck > memoryRegionSize) { throw std::runtime_error(fmt::format( "[wraparoundRead] queueOffset '{}' + length '{}' " "+ additionalBoundaryCheck '{}' + was bigger " "than memoryRegionSize '{}'", queueOffset, length, additionalBoundaryCheck, memoryRegionSize)); } // Do a first read up to the end of the buffer (dataInerface->read should // only read up to the end of the buffer) std::vector bytesRead = dataInterface->read(offset, length); size_t updatedReadOffset = offset + bytesRead.size(); size_t bytesRemaining = length - bytesRead.size(); // If there are any more bytes to be read beyond the buffer, wrap around and // read from the beginning of the buffer (offset by the queueOffset) if (bytesRemaining > 0) { std::vector wrappedBytesRead = dataInterface->read(queueOffset, bytesRemaining); bytesRemaining -= wrappedBytesRead.size(); if (bytesRemaining != 0) { throw std::runtime_error(fmt::format( "[wraparoundRead] Buffer wrapped around but was not able to read " "all of the requested info. Bytes remaining to read '{}' of '{}'", bytesRemaining, length)); } bytesRead.insert(bytesRead.end(), wrappedBytesRead.begin(), wrappedBytesRead.end()); updatedReadOffset = queueOffset + wrappedBytesRead.size(); } updateReadPtr(updatedReadOffset); return bytesRead; } struct QueueEntryHeader BufferImpl::readEntryHeader(size_t offset) { size_t headerSize = sizeof(struct QueueEntryHeader); // wraparonudRead will throw if it did not read all the bytes, let it // propagate up the stack std::vector bytesRead = wraparoundRead(offset, headerSize); return *reinterpret_cast(bytesRead.data()); } } // namespace bios_bmc_smm_error_logger