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> 1540ce08e1SBrandon 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 const size_t memoryRegionSize = dataInterface->getMemoryRegionSize(); 3026660e9bSBrandon Kim const size_t proposedBufferSize = 3126660e9bSBrandon Kim sizeof(struct CircularBufferHeader) + ueRegionSize + queueSize; 3226660e9bSBrandon Kim if (proposedBufferSize > memoryRegionSize) 3326660e9bSBrandon Kim { 3426660e9bSBrandon Kim throw std::runtime_error(fmt::format( 3526660e9bSBrandon Kim "[initialize] Proposed region size '{}' is bigger than the " 3626660e9bSBrandon Kim "BMC's allocated MMIO region of '{}'", 3726660e9bSBrandon Kim proposedBufferSize, memoryRegionSize)); 3826660e9bSBrandon Kim } 3926660e9bSBrandon Kim 4026660e9bSBrandon Kim // Initialize the whole buffer with 0x00 4126660e9bSBrandon Kim const std::vector<uint8_t> emptyVector(proposedBufferSize, 0); 42fcbc3db1SBrandon Kim size_t byteWritten = dataInterface->write(0, emptyVector); 4326660e9bSBrandon Kim if (byteWritten != proposedBufferSize) 44fcbc3db1SBrandon Kim { 45fcbc3db1SBrandon Kim throw std::runtime_error( 4626660e9bSBrandon Kim fmt::format("[initialize] Only erased '{}'", byteWritten)); 47fcbc3db1SBrandon Kim } 48fcbc3db1SBrandon Kim 49fcbc3db1SBrandon Kim // Create an initial buffer header and write to it 50fcbc3db1SBrandon Kim struct CircularBufferHeader initializationHeader = {}; 5117ee1a93SBrandon Kim initializationHeader.bmcInterfaceVersion = 5217ee1a93SBrandon Kim boost::endian::native_to_little(bmcInterfaceVersion); 5317ee1a93SBrandon Kim initializationHeader.queueSize = boost::endian::native_to_little(queueSize); 5417ee1a93SBrandon Kim initializationHeader.ueRegionSize = 5517ee1a93SBrandon Kim boost::endian::native_to_little(ueRegionSize); 5617ee1a93SBrandon Kim std::transform(magicNumber.begin(), magicNumber.end(), 5717ee1a93SBrandon Kim initializationHeader.magicNumber.begin(), 5817ee1a93SBrandon Kim [](uint32_t number) -> little_uint32_t { 5917ee1a93SBrandon Kim return boost::endian::native_to_little(number); 6017ee1a93SBrandon Kim }); 61fcbc3db1SBrandon Kim 62fcbc3db1SBrandon Kim uint8_t* initializationHeaderPtr = 63fcbc3db1SBrandon Kim reinterpret_cast<uint8_t*>(&initializationHeader); 64fcbc3db1SBrandon Kim size_t initializationHeaderSize = sizeof(initializationHeader); 65fcbc3db1SBrandon Kim byteWritten = dataInterface->write( 66fcbc3db1SBrandon Kim 0, std::span<const uint8_t>(initializationHeaderPtr, 67fcbc3db1SBrandon Kim initializationHeaderPtr + 68fcbc3db1SBrandon Kim initializationHeaderSize)); 69fcbc3db1SBrandon Kim if (byteWritten != initializationHeaderSize) 70fcbc3db1SBrandon Kim { 71fcbc3db1SBrandon Kim throw std::runtime_error(fmt::format( 7226660e9bSBrandon Kim "[initialize] Only wrote '{}' bytes of the header", byteWritten)); 73fcbc3db1SBrandon Kim } 7460cab57fSBrandon Kim cachedBufferHeader = initializationHeader; 75fcbc3db1SBrandon Kim } 76fcbc3db1SBrandon Kim 7717ee1a93SBrandon Kim void BufferImpl::readBufferHeader() 7817ee1a93SBrandon Kim { 7917ee1a93SBrandon Kim size_t headerSize = sizeof(struct CircularBufferHeader); 8017ee1a93SBrandon Kim std::vector<uint8_t> bytesRead = 81*4662b1bdSBrandon Kim dataInterface->read(/*offset=*/0, headerSize); 8217ee1a93SBrandon Kim 8317ee1a93SBrandon Kim if (bytesRead.size() != headerSize) 8417ee1a93SBrandon Kim { 8517ee1a93SBrandon Kim throw std::runtime_error( 8617ee1a93SBrandon Kim fmt::format("Buffer header read only read '{}', expected '{}'", 8717ee1a93SBrandon Kim bytesRead.size(), headerSize)); 8817ee1a93SBrandon Kim } 8917ee1a93SBrandon Kim 9017ee1a93SBrandon Kim cachedBufferHeader = 9160cab57fSBrandon Kim *reinterpret_cast<struct CircularBufferHeader*>(bytesRead.data()); 9217ee1a93SBrandon Kim }; 9317ee1a93SBrandon Kim 9417ee1a93SBrandon Kim struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const 9517ee1a93SBrandon Kim { 9617ee1a93SBrandon Kim return cachedBufferHeader; 9717ee1a93SBrandon Kim } 9817ee1a93SBrandon Kim 99cf0b9752SBrandon Kim void BufferImpl::updateReadPtr(const uint32_t newReadPtr) 100cf0b9752SBrandon Kim { 101cf0b9752SBrandon Kim constexpr uint8_t bmcReadPtrOffset = 102cf0b9752SBrandon Kim offsetof(struct CircularBufferHeader, bmcReadPtr); 103cf0b9752SBrandon Kim 104cf0b9752SBrandon Kim little_uint16_t truncatedReadPtr = 105cf0b9752SBrandon Kim boost::endian::native_to_little(newReadPtr & 0xffff); 106cf0b9752SBrandon Kim uint8_t* truncatedReadPtrPtr = 107cf0b9752SBrandon Kim reinterpret_cast<uint8_t*>(&truncatedReadPtr); 108cf0b9752SBrandon Kim 109cf0b9752SBrandon Kim size_t writtenSize = dataInterface->write( 110cf0b9752SBrandon Kim bmcReadPtrOffset, std::span<const uint8_t>{ 111cf0b9752SBrandon Kim truncatedReadPtrPtr, 112cf0b9752SBrandon Kim truncatedReadPtrPtr + sizeof(little_uint16_t)}); 113cf0b9752SBrandon Kim if (writtenSize != sizeof(little_uint16_t)) 114cf0b9752SBrandon Kim { 115cf0b9752SBrandon Kim throw std::runtime_error(fmt::format( 116cf0b9752SBrandon Kim "[updateReadPtr] Wrote '{}' bytes, instead of expected '{}'", 117cf0b9752SBrandon Kim writtenSize, sizeof(little_uint16_t))); 118cf0b9752SBrandon Kim } 119cf0b9752SBrandon Kim cachedBufferHeader.bmcReadPtr = truncatedReadPtr; 120cf0b9752SBrandon Kim } 121cf0b9752SBrandon Kim 1229836cfa6SBrandon Kim size_t BufferImpl::getQueueOffset() 1239836cfa6SBrandon Kim { 1249836cfa6SBrandon Kim return sizeof(struct CircularBufferHeader) + 1259836cfa6SBrandon Kim boost::endian::little_to_native(cachedBufferHeader.ueRegionSize); 1269836cfa6SBrandon Kim } 1279836cfa6SBrandon Kim 12835d4335eSBrandon Kim std::vector<uint8_t> BufferImpl::wraparoundRead(const uint32_t relativeOffset, 12935d4335eSBrandon Kim const uint32_t length) 1309836cfa6SBrandon Kim { 13126660e9bSBrandon Kim const size_t queueSize = 13226660e9bSBrandon Kim boost::endian::little_to_native(cachedBufferHeader.queueSize); 1339836cfa6SBrandon Kim 13435d4335eSBrandon Kim if (relativeOffset > queueSize) 13535d4335eSBrandon Kim { 13635d4335eSBrandon Kim throw std::runtime_error( 13735d4335eSBrandon Kim fmt::format("[wraparoundRead] relativeOffset '{}' was bigger " 13835d4335eSBrandon Kim "than queueSize '{}'", 13935d4335eSBrandon Kim relativeOffset, queueSize)); 14035d4335eSBrandon Kim } 14135d4335eSBrandon Kim if (length > queueSize) 1429836cfa6SBrandon Kim { 1439836cfa6SBrandon Kim throw std::runtime_error(fmt::format( 14435d4335eSBrandon Kim "[wraparoundRead] length '{}' was bigger than queueSize '{}'", 14535d4335eSBrandon Kim length, queueSize)); 1469836cfa6SBrandon Kim } 1479836cfa6SBrandon Kim 14835d4335eSBrandon Kim // Do a calculation to see if the read will wraparound 14935d4335eSBrandon Kim const size_t queueOffset = getQueueOffset(); 15035d4335eSBrandon Kim const size_t queueSizeToQueueEnd = queueSize - relativeOffset; 15135d4335eSBrandon Kim size_t numWraparoundBytesToRead = 0; 15235d4335eSBrandon Kim if (length > queueSizeToQueueEnd) 15335d4335eSBrandon Kim { 15435d4335eSBrandon Kim // This means we will wrap, count the bytes that are left to read 15535d4335eSBrandon Kim numWraparoundBytesToRead = length - queueSizeToQueueEnd; 15635d4335eSBrandon Kim } 15735d4335eSBrandon Kim const size_t numBytesToReadTillQueueEnd = length - numWraparoundBytesToRead; 15835d4335eSBrandon Kim 15935d4335eSBrandon Kim std::vector<uint8_t> bytesRead = dataInterface->read( 16035d4335eSBrandon Kim queueOffset + relativeOffset, numBytesToReadTillQueueEnd); 16135d4335eSBrandon Kim if (bytesRead.size() != numBytesToReadTillQueueEnd) 16235d4335eSBrandon Kim { 16335d4335eSBrandon Kim throw std::runtime_error( 16435d4335eSBrandon Kim fmt::format("[wraparoundRead] Read '{}' which was not " 16535d4335eSBrandon Kim "the requested length of '{}'", 16635d4335eSBrandon Kim bytesRead.size(), numBytesToReadTillQueueEnd)); 16735d4335eSBrandon Kim } 16835d4335eSBrandon Kim size_t updatedReadPtr = relativeOffset + numBytesToReadTillQueueEnd; 169*4662b1bdSBrandon Kim if (updatedReadPtr == queueSize) 170*4662b1bdSBrandon Kim { 171*4662b1bdSBrandon Kim // If we read all the way up to the end of the queue, we need to 172*4662b1bdSBrandon Kim // manually wrap the updateReadPtr around to 0 173*4662b1bdSBrandon Kim updatedReadPtr = 0; 174*4662b1bdSBrandon Kim } 1759836cfa6SBrandon Kim 1769836cfa6SBrandon Kim // If there are any more bytes to be read beyond the buffer, wrap around and 1779836cfa6SBrandon Kim // read from the beginning of the buffer (offset by the queueOffset) 17835d4335eSBrandon Kim if (numWraparoundBytesToRead > 0) 1799836cfa6SBrandon Kim { 1809836cfa6SBrandon Kim std::vector<uint8_t> wrappedBytesRead = 18135d4335eSBrandon Kim dataInterface->read(queueOffset, numWraparoundBytesToRead); 18235d4335eSBrandon Kim if (numWraparoundBytesToRead != wrappedBytesRead.size()) 1839836cfa6SBrandon Kim { 1849836cfa6SBrandon Kim throw std::runtime_error(fmt::format( 18535d4335eSBrandon Kim "[wraparoundRead] Buffer wrapped around but read '{}' which " 18635d4335eSBrandon Kim "was not the requested lenght of '{}'", 18735d4335eSBrandon Kim wrappedBytesRead.size(), numWraparoundBytesToRead)); 1889836cfa6SBrandon Kim } 1899836cfa6SBrandon Kim bytesRead.insert(bytesRead.end(), wrappedBytesRead.begin(), 1909836cfa6SBrandon Kim wrappedBytesRead.end()); 19135d4335eSBrandon Kim updatedReadPtr = numWraparoundBytesToRead; 1929836cfa6SBrandon Kim } 19335d4335eSBrandon Kim updateReadPtr(updatedReadPtr); 1949836cfa6SBrandon Kim 1959836cfa6SBrandon Kim return bytesRead; 1969836cfa6SBrandon Kim } 1979836cfa6SBrandon Kim 19835d4335eSBrandon Kim struct QueueEntryHeader BufferImpl::readEntryHeader(size_t relativeOffset) 1997bac2d69SBrandon Kim { 2007bac2d69SBrandon Kim size_t headerSize = sizeof(struct QueueEntryHeader); 2017bac2d69SBrandon Kim // wraparonudRead will throw if it did not read all the bytes, let it 2027bac2d69SBrandon Kim // propagate up the stack 20335d4335eSBrandon Kim std::vector<uint8_t> bytesRead = wraparoundRead(relativeOffset, headerSize); 2047bac2d69SBrandon Kim 2057bac2d69SBrandon Kim return *reinterpret_cast<struct QueueEntryHeader*>(bytesRead.data()); 2067bac2d69SBrandon Kim } 2077bac2d69SBrandon Kim 20835d4335eSBrandon Kim EntryPair BufferImpl::readEntry(size_t relativeOffset) 20940ce08e1SBrandon Kim { 21035d4335eSBrandon Kim struct QueueEntryHeader entryHeader = readEntryHeader(relativeOffset); 21135d4335eSBrandon Kim size_t entrySize = boost::endian::little_to_native(entryHeader.entrySize); 21240ce08e1SBrandon Kim 21340ce08e1SBrandon Kim // wraparonudRead may throw if entrySize was bigger than the buffer or if it 21426660e9bSBrandon Kim // was not able to read all the bytes, let it propagate up the stack 21535d4335eSBrandon Kim std::vector<uint8_t> entry = wraparoundRead( 21635d4335eSBrandon Kim relativeOffset + sizeof(struct QueueEntryHeader), entrySize); 21740ce08e1SBrandon Kim 21840ce08e1SBrandon Kim // Calculate the checksum 21940ce08e1SBrandon Kim uint8_t* entryHeaderPtr = reinterpret_cast<uint8_t*>(&entryHeader); 220f0e4adc9SBrandon Kim uint8_t checksum = 221f0e4adc9SBrandon Kim std::accumulate(entryHeaderPtr, 222f0e4adc9SBrandon Kim entryHeaderPtr + sizeof(struct QueueEntryHeader), 0, 223f0e4adc9SBrandon Kim std::bit_xor<void>()) ^ 224f0e4adc9SBrandon Kim std::accumulate(entry.begin(), entry.end(), 0, std::bit_xor<void>()); 225f0e4adc9SBrandon Kim 22640ce08e1SBrandon Kim if (checksum != 0) 22740ce08e1SBrandon Kim { 22840ce08e1SBrandon Kim throw std::runtime_error(fmt::format( 22940ce08e1SBrandon Kim "[readEntry] Checksum was '{}', expected '0'", checksum)); 23040ce08e1SBrandon Kim } 23140ce08e1SBrandon Kim 23240ce08e1SBrandon Kim return {entryHeader, entry}; 23340ce08e1SBrandon Kim } 23440ce08e1SBrandon Kim 235*4662b1bdSBrandon Kim std::vector<EntryPair> BufferImpl::readErrorLogs() 236*4662b1bdSBrandon Kim { 237*4662b1bdSBrandon Kim // Reading the buffer header will update the cachedBufferHeader 238*4662b1bdSBrandon Kim readBufferHeader(); 239*4662b1bdSBrandon Kim 240*4662b1bdSBrandon Kim const size_t queueSize = 241*4662b1bdSBrandon Kim boost::endian::little_to_native(cachedBufferHeader.queueSize); 242*4662b1bdSBrandon Kim size_t currentBiosWritePtr = 243*4662b1bdSBrandon Kim boost::endian::little_to_native(cachedBufferHeader.biosWritePtr); 244*4662b1bdSBrandon Kim if (currentBiosWritePtr > queueSize) 245*4662b1bdSBrandon Kim { 246*4662b1bdSBrandon Kim throw std::runtime_error(fmt::format( 247*4662b1bdSBrandon Kim "[readErrorLogs] currentBiosWritePtr was '{}' which was bigger " 248*4662b1bdSBrandon Kim "than queueSize '{}'", 249*4662b1bdSBrandon Kim currentBiosWritePtr, queueSize)); 250*4662b1bdSBrandon Kim } 251*4662b1bdSBrandon Kim size_t currentReadPtr = 252*4662b1bdSBrandon Kim boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr); 253*4662b1bdSBrandon Kim if (currentReadPtr > queueSize) 254*4662b1bdSBrandon Kim { 255*4662b1bdSBrandon Kim throw std::runtime_error(fmt::format( 256*4662b1bdSBrandon Kim "[readErrorLogs] currentReadPtr was '{}' which was bigger " 257*4662b1bdSBrandon Kim "than queueSize '{}'", 258*4662b1bdSBrandon Kim currentReadPtr, queueSize)); 259*4662b1bdSBrandon Kim } 260*4662b1bdSBrandon Kim 261*4662b1bdSBrandon Kim size_t bytesToRead; 262*4662b1bdSBrandon Kim if (currentBiosWritePtr == currentReadPtr) 263*4662b1bdSBrandon Kim { 264*4662b1bdSBrandon Kim // No new payload was detected, return an empty vector gracefully 265*4662b1bdSBrandon Kim return {}; 266*4662b1bdSBrandon Kim } 267*4662b1bdSBrandon Kim 268*4662b1bdSBrandon Kim if (currentBiosWritePtr > currentReadPtr) 269*4662b1bdSBrandon Kim { 270*4662b1bdSBrandon Kim // Simply subtract in this case 271*4662b1bdSBrandon Kim bytesToRead = currentBiosWritePtr - currentReadPtr; 272*4662b1bdSBrandon Kim } 273*4662b1bdSBrandon Kim else 274*4662b1bdSBrandon Kim { 275*4662b1bdSBrandon Kim // Calculate the bytes to the "end" (QueueSize - ReadPtr) + 276*4662b1bdSBrandon Kim // bytes to read from the "beginning" (0 + WritePtr) 277*4662b1bdSBrandon Kim bytesToRead = (queueSize - currentReadPtr) + currentBiosWritePtr; 278*4662b1bdSBrandon Kim } 279*4662b1bdSBrandon Kim 280*4662b1bdSBrandon Kim size_t byteRead = 0; 281*4662b1bdSBrandon Kim std::vector<EntryPair> entryPairs; 282*4662b1bdSBrandon Kim while (byteRead < bytesToRead) 283*4662b1bdSBrandon Kim { 284*4662b1bdSBrandon Kim EntryPair entryPair = readEntry(currentReadPtr); 285*4662b1bdSBrandon Kim byteRead += sizeof(struct QueueEntryHeader) + entryPair.second.size(); 286*4662b1bdSBrandon Kim entryPairs.push_back(entryPair); 287*4662b1bdSBrandon Kim 288*4662b1bdSBrandon Kim // Note: readEntry() will update cachedBufferHeader.bmcReadPtr 289*4662b1bdSBrandon Kim currentReadPtr = 290*4662b1bdSBrandon Kim boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr); 291*4662b1bdSBrandon Kim } 292*4662b1bdSBrandon Kim if (currentBiosWritePtr != currentReadPtr) 293*4662b1bdSBrandon Kim { 294*4662b1bdSBrandon Kim throw std::runtime_error(fmt::format( 295*4662b1bdSBrandon Kim "[readErrorLogs] biosWritePtr '{}' and bmcReaddPtr '{}' " 296*4662b1bdSBrandon Kim "are not identical after reading through all the logs", 297*4662b1bdSBrandon Kim currentBiosWritePtr, currentReadPtr)); 298*4662b1bdSBrandon Kim } 299*4662b1bdSBrandon Kim return entryPairs; 300*4662b1bdSBrandon Kim } 301*4662b1bdSBrandon Kim 302fcbc3db1SBrandon Kim } // namespace bios_bmc_smm_error_logger 303