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> 15*40ce08e1SBrandon Kim #include <numeric> 16fcbc3db1SBrandon Kim #include <span> 17fcbc3db1SBrandon Kim #include <vector> 18fcbc3db1SBrandon Kim 19fcbc3db1SBrandon Kim namespace bios_bmc_smm_error_logger 20fcbc3db1SBrandon Kim { 21fcbc3db1SBrandon Kim 22fcbc3db1SBrandon Kim BufferImpl::BufferImpl(std::unique_ptr<DataInterface> dataInterface) : 23fcbc3db1SBrandon Kim dataInterface(std::move(dataInterface)){}; 24fcbc3db1SBrandon Kim 25fcbc3db1SBrandon Kim void BufferImpl::initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize, 26fcbc3db1SBrandon Kim uint16_t ueRegionSize, 27fcbc3db1SBrandon Kim const std::array<uint32_t, 4>& magicNumber) 28fcbc3db1SBrandon Kim { 29fcbc3db1SBrandon Kim // Initialize the whole buffer with 0x00 30fcbc3db1SBrandon Kim const size_t memoryRegionSize = dataInterface->getMemoryRegionSize(); 31fcbc3db1SBrandon Kim const std::vector<uint8_t> emptyVector(memoryRegionSize, 0); 32fcbc3db1SBrandon Kim size_t byteWritten = dataInterface->write(0, emptyVector); 33fcbc3db1SBrandon Kim if (byteWritten != memoryRegionSize) 34fcbc3db1SBrandon Kim { 35fcbc3db1SBrandon Kim throw std::runtime_error( 36fcbc3db1SBrandon Kim fmt::format("Buffer initialization only erased '{}'", byteWritten)); 37fcbc3db1SBrandon Kim } 38fcbc3db1SBrandon Kim 39fcbc3db1SBrandon Kim // Create an initial buffer header and write to it 40fcbc3db1SBrandon Kim struct CircularBufferHeader initializationHeader = {}; 4117ee1a93SBrandon Kim initializationHeader.bmcInterfaceVersion = 4217ee1a93SBrandon Kim boost::endian::native_to_little(bmcInterfaceVersion); 4317ee1a93SBrandon Kim initializationHeader.queueSize = boost::endian::native_to_little(queueSize); 4417ee1a93SBrandon Kim initializationHeader.ueRegionSize = 4517ee1a93SBrandon Kim boost::endian::native_to_little(ueRegionSize); 4617ee1a93SBrandon Kim std::transform(magicNumber.begin(), magicNumber.end(), 4717ee1a93SBrandon Kim initializationHeader.magicNumber.begin(), 4817ee1a93SBrandon Kim [](uint32_t number) -> little_uint32_t { 4917ee1a93SBrandon Kim return boost::endian::native_to_little(number); 5017ee1a93SBrandon Kim }); 51fcbc3db1SBrandon Kim 52fcbc3db1SBrandon Kim uint8_t* initializationHeaderPtr = 53fcbc3db1SBrandon Kim reinterpret_cast<uint8_t*>(&initializationHeader); 54fcbc3db1SBrandon Kim size_t initializationHeaderSize = sizeof(initializationHeader); 55fcbc3db1SBrandon Kim byteWritten = dataInterface->write( 56fcbc3db1SBrandon Kim 0, std::span<const uint8_t>(initializationHeaderPtr, 57fcbc3db1SBrandon Kim initializationHeaderPtr + 58fcbc3db1SBrandon Kim initializationHeaderSize)); 59fcbc3db1SBrandon Kim if (byteWritten != initializationHeaderSize) 60fcbc3db1SBrandon Kim { 61fcbc3db1SBrandon Kim throw std::runtime_error(fmt::format( 62fcbc3db1SBrandon Kim "Buffer initialization buffer header write only wrote '{}'", 63fcbc3db1SBrandon Kim byteWritten)); 64fcbc3db1SBrandon Kim } 6560cab57fSBrandon Kim cachedBufferHeader = initializationHeader; 66fcbc3db1SBrandon Kim } 67fcbc3db1SBrandon Kim 6817ee1a93SBrandon Kim void BufferImpl::readBufferHeader() 6917ee1a93SBrandon Kim { 7017ee1a93SBrandon Kim size_t headerSize = sizeof(struct CircularBufferHeader); 7117ee1a93SBrandon Kim std::vector<uint8_t> bytesRead = 7217ee1a93SBrandon Kim dataInterface->read(/* offset */ 0, headerSize); 7317ee1a93SBrandon Kim 7417ee1a93SBrandon Kim if (bytesRead.size() != headerSize) 7517ee1a93SBrandon Kim { 7617ee1a93SBrandon Kim throw std::runtime_error( 7717ee1a93SBrandon Kim fmt::format("Buffer header read only read '{}', expected '{}'", 7817ee1a93SBrandon Kim bytesRead.size(), headerSize)); 7917ee1a93SBrandon Kim } 8017ee1a93SBrandon Kim 8117ee1a93SBrandon Kim cachedBufferHeader = 8260cab57fSBrandon Kim *reinterpret_cast<struct CircularBufferHeader*>(bytesRead.data()); 8317ee1a93SBrandon Kim }; 8417ee1a93SBrandon Kim 8517ee1a93SBrandon Kim struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const 8617ee1a93SBrandon Kim { 8717ee1a93SBrandon Kim return cachedBufferHeader; 8817ee1a93SBrandon Kim } 8917ee1a93SBrandon Kim 90cf0b9752SBrandon Kim void BufferImpl::updateReadPtr(const uint32_t newReadPtr) 91cf0b9752SBrandon Kim { 92cf0b9752SBrandon Kim constexpr uint8_t bmcReadPtrOffset = 93cf0b9752SBrandon Kim offsetof(struct CircularBufferHeader, bmcReadPtr); 94cf0b9752SBrandon Kim 95cf0b9752SBrandon Kim little_uint16_t truncatedReadPtr = 96cf0b9752SBrandon Kim boost::endian::native_to_little(newReadPtr & 0xffff); 97cf0b9752SBrandon Kim uint8_t* truncatedReadPtrPtr = 98cf0b9752SBrandon Kim reinterpret_cast<uint8_t*>(&truncatedReadPtr); 99cf0b9752SBrandon Kim 100cf0b9752SBrandon Kim size_t writtenSize = dataInterface->write( 101cf0b9752SBrandon Kim bmcReadPtrOffset, std::span<const uint8_t>{ 102cf0b9752SBrandon Kim truncatedReadPtrPtr, 103cf0b9752SBrandon Kim truncatedReadPtrPtr + sizeof(little_uint16_t)}); 104cf0b9752SBrandon Kim if (writtenSize != sizeof(little_uint16_t)) 105cf0b9752SBrandon Kim { 106cf0b9752SBrandon Kim throw std::runtime_error(fmt::format( 107cf0b9752SBrandon Kim "[updateReadPtr] Wrote '{}' bytes, instead of expected '{}'", 108cf0b9752SBrandon Kim writtenSize, sizeof(little_uint16_t))); 109cf0b9752SBrandon Kim } 110cf0b9752SBrandon Kim cachedBufferHeader.bmcReadPtr = truncatedReadPtr; 111cf0b9752SBrandon Kim } 112cf0b9752SBrandon Kim 1139836cfa6SBrandon Kim size_t BufferImpl::getQueueOffset() 1149836cfa6SBrandon Kim { 1159836cfa6SBrandon Kim return sizeof(struct CircularBufferHeader) + 1169836cfa6SBrandon Kim boost::endian::little_to_native(cachedBufferHeader.ueRegionSize); 1179836cfa6SBrandon Kim } 1189836cfa6SBrandon Kim 1199836cfa6SBrandon Kim std::vector<uint8_t> 1209836cfa6SBrandon Kim BufferImpl::wraparoundRead(const uint32_t offset, const uint32_t length, 1219836cfa6SBrandon Kim const uint32_t additionalBoundaryCheck) 1229836cfa6SBrandon Kim { 1239836cfa6SBrandon Kim const size_t memoryRegionSize = dataInterface->getMemoryRegionSize(); 1249836cfa6SBrandon Kim 1259836cfa6SBrandon Kim size_t queueOffset = getQueueOffset(); 1269836cfa6SBrandon Kim if (queueOffset + length + additionalBoundaryCheck > memoryRegionSize) 1279836cfa6SBrandon Kim { 1289836cfa6SBrandon Kim throw std::runtime_error(fmt::format( 1299836cfa6SBrandon Kim "[wraparoundRead] queueOffset '{}' + length '{}' " 1309836cfa6SBrandon Kim "+ additionalBoundaryCheck '{}' + was bigger " 1319836cfa6SBrandon Kim "than memoryRegionSize '{}'", 1329836cfa6SBrandon Kim queueOffset, length, additionalBoundaryCheck, memoryRegionSize)); 1339836cfa6SBrandon Kim } 1349836cfa6SBrandon Kim 1359836cfa6SBrandon Kim // Do a first read up to the end of the buffer (dataInerface->read should 1369836cfa6SBrandon Kim // only read up to the end of the buffer) 1379836cfa6SBrandon Kim std::vector<uint8_t> bytesRead = dataInterface->read(offset, length); 1389836cfa6SBrandon Kim size_t updatedReadOffset = offset + bytesRead.size(); 1399836cfa6SBrandon Kim size_t bytesRemaining = length - bytesRead.size(); 1409836cfa6SBrandon Kim 1419836cfa6SBrandon Kim // If there are any more bytes to be read beyond the buffer, wrap around and 1429836cfa6SBrandon Kim // read from the beginning of the buffer (offset by the queueOffset) 1439836cfa6SBrandon Kim if (bytesRemaining > 0) 1449836cfa6SBrandon Kim { 1459836cfa6SBrandon Kim std::vector<uint8_t> wrappedBytesRead = 1469836cfa6SBrandon Kim dataInterface->read(queueOffset, bytesRemaining); 1479836cfa6SBrandon Kim bytesRemaining -= wrappedBytesRead.size(); 1489836cfa6SBrandon Kim if (bytesRemaining != 0) 1499836cfa6SBrandon Kim { 1509836cfa6SBrandon Kim throw std::runtime_error(fmt::format( 1519836cfa6SBrandon Kim "[wraparoundRead] Buffer wrapped around but was not able to read " 1529836cfa6SBrandon Kim "all of the requested info. Bytes remaining to read '{}' of '{}'", 1539836cfa6SBrandon Kim bytesRemaining, length)); 1549836cfa6SBrandon Kim } 1559836cfa6SBrandon Kim bytesRead.insert(bytesRead.end(), wrappedBytesRead.begin(), 1569836cfa6SBrandon Kim wrappedBytesRead.end()); 1579836cfa6SBrandon Kim updatedReadOffset = queueOffset + wrappedBytesRead.size(); 1589836cfa6SBrandon Kim } 1599836cfa6SBrandon Kim updateReadPtr(updatedReadOffset); 1609836cfa6SBrandon Kim 1619836cfa6SBrandon Kim return bytesRead; 1629836cfa6SBrandon Kim } 1639836cfa6SBrandon Kim 1647bac2d69SBrandon Kim struct QueueEntryHeader BufferImpl::readEntryHeader(size_t offset) 1657bac2d69SBrandon Kim { 1667bac2d69SBrandon Kim size_t headerSize = sizeof(struct QueueEntryHeader); 1677bac2d69SBrandon Kim // wraparonudRead will throw if it did not read all the bytes, let it 1687bac2d69SBrandon Kim // propagate up the stack 1697bac2d69SBrandon Kim std::vector<uint8_t> bytesRead = wraparoundRead(offset, headerSize); 1707bac2d69SBrandon Kim 1717bac2d69SBrandon Kim return *reinterpret_cast<struct QueueEntryHeader*>(bytesRead.data()); 1727bac2d69SBrandon Kim } 1737bac2d69SBrandon Kim 174*40ce08e1SBrandon Kim EntryPair BufferImpl::readEntry(size_t offset) 175*40ce08e1SBrandon Kim { 176*40ce08e1SBrandon Kim struct QueueEntryHeader entryHeader = readEntryHeader(offset); 177*40ce08e1SBrandon Kim 178*40ce08e1SBrandon Kim size_t entrySize = entryHeader.entrySize; 179*40ce08e1SBrandon Kim 180*40ce08e1SBrandon Kim // wraparonudRead may throw if entrySize was bigger than the buffer or if it 181*40ce08e1SBrandon Kim // was not able to read all bytes, let it propagate up the stack 182*40ce08e1SBrandon Kim // - Use additionalBoundaryCheck parameter to tighten the boundary check 183*40ce08e1SBrandon Kim std::vector<uint8_t> entry = 184*40ce08e1SBrandon Kim wraparoundRead(offset + sizeof(struct QueueEntryHeader), entrySize, 185*40ce08e1SBrandon Kim sizeof(struct QueueEntryHeader)); 186*40ce08e1SBrandon Kim 187*40ce08e1SBrandon Kim // Calculate the checksum 188*40ce08e1SBrandon Kim uint8_t* entryHeaderPtr = reinterpret_cast<uint8_t*>(&entryHeader); 189*40ce08e1SBrandon Kim uint8_t checksum = std::accumulate( 190*40ce08e1SBrandon Kim entryHeaderPtr, entryHeaderPtr + sizeof(struct QueueEntryHeader), 0); 191*40ce08e1SBrandon Kim checksum = std::accumulate(std::begin(entry), std::end(entry), checksum); 192*40ce08e1SBrandon Kim if (checksum != 0) 193*40ce08e1SBrandon Kim { 194*40ce08e1SBrandon Kim throw std::runtime_error(fmt::format( 195*40ce08e1SBrandon Kim "[readEntry] Checksum was '{}', expected '0'", checksum)); 196*40ce08e1SBrandon Kim } 197*40ce08e1SBrandon Kim 198*40ce08e1SBrandon Kim return {entryHeader, entry}; 199*40ce08e1SBrandon Kim } 200*40ce08e1SBrandon Kim 201fcbc3db1SBrandon Kim } // namespace bios_bmc_smm_error_logger 202