1 #pragma once
2 
3 #include "pci_handler.hpp"
4 
5 #include <boost/endian/arithmetic.hpp>
6 
7 #include <array>
8 #include <cstdint>
9 #include <memory>
10 #include <tuple>
11 
12 namespace bios_bmc_smm_error_logger
13 {
14 
15 /* Adding endianness */
16 using boost::endian::little_uint16_t;
17 using boost::endian::little_uint32_t;
18 using boost::endian::little_uint64_t;
19 
20 // EntryPair.first = QueueEntryHeader
21 // EntryPair.second = Error entry in vector of bytes
22 using EntryPair = std::pair<struct QueueEntryHeader, std::vector<uint8_t>>;
23 
24 struct CircularBufferHeader
25 {
26     little_uint32_t bmcInterfaceVersion;        // Offset 0x0
27     little_uint32_t biosInterfaceVersion;       // Offset 0x4
28     std::array<little_uint32_t, 4> magicNumber; // Offset 0x8
29     little_uint16_t queueSize;                  // Offset 0x18
30     little_uint16_t ueRegionSize;               // Offset 0x1a
31     little_uint32_t bmcFlags;                   // Offset 0x1c
32     little_uint16_t bmcReadPtr;                 // Offset 0x20
33     std::array<uint8_t, 6> reserved1;           // Offset 0x22
34     little_uint32_t biosFlags;                  // Offset 0x28
35     little_uint16_t biosWritePtr;               // Offset 0x2c
36     std::array<uint8_t, 2> reserved2;           // Offset 0x2e
37     // UE reserved region:                         Offset 0x30
38     // Error log queue:       Offset 0x30 + UE reserved region
39 
40     bool operator==(const CircularBufferHeader& other) const
41     {
42         /* Skip comparing reserved1 and reserved2 */
43         return std::tie(this->bmcInterfaceVersion, this->biosInterfaceVersion,
44                         this->magicNumber, this->queueSize, this->ueRegionSize,
45                         this->bmcFlags, this->bmcReadPtr, this->biosFlags,
46                         this->biosWritePtr) ==
47                std::tie(other.bmcInterfaceVersion, other.biosInterfaceVersion,
48                         other.magicNumber, other.queueSize, other.ueRegionSize,
49                         other.bmcFlags, other.bmcReadPtr, other.biosFlags,
50                         other.biosWritePtr);
51     }
52 };
53 static_assert(sizeof(CircularBufferHeader) == 0x30,
54               "Size of CircularBufferHeader struct is incorrect.");
55 
56 struct QueueEntryHeader
57 {
58     little_uint16_t sequenceId; // Offset 0x0
59     little_uint16_t entrySize;  // Offset 0x2
60     uint8_t checksum;           // Offset 0x4
61     uint8_t rdeCommandType;     // Offset 0x5
62     // RDE Command                 Offset 0x6
63     bool operator==(const QueueEntryHeader& other) const
64     {
65         return std::tie(this->sequenceId, this->entrySize, this->checksum,
66                         this->rdeCommandType) ==
67                std::tie(other.sequenceId, other.entrySize, other.checksum,
68                         other.rdeCommandType);
69     }
70 };
71 static_assert(sizeof(QueueEntryHeader) == 0x6,
72               "Size of QueueEntryHeader struct is incorrect.");
73 
74 /**
75  * An interface class for the buffer helper APIs
76  */
77 class BufferInterface
78 {
79   public:
80     virtual ~BufferInterface() = default;
81 
82     /**
83      * Zero out the buffer first before populating the header
84      *
85      * @param[in] bmcInterfaceVersion - Used to initialize the header
86      * @param[in] queueSize - Used to initialize the header
87      * @param[in] ueRegionSize - Used to initialize the header
88      * @param[in] magicNumber - Used to initialize the header
89      * @return true if successful
90      */
91     virtual void initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize,
92                             uint16_t ueRegionSize,
93                             const std::array<uint32_t, 4>& magicNumber) = 0;
94 
95     /**
96      * Read the buffer header from shared buffer
97      */
98     virtual void readBufferHeader() = 0;
99 
100     /**
101      * Getter API for the cached buffer header
102      * @return cached CircularBufferHeader
103      */
104     virtual struct CircularBufferHeader getCachedBufferHeader() const = 0;
105 
106     /**
107      * Write to the bufferHeader and update the read pointer
108      * @param[in] newReadPtr - read pointer to update to
109      */
110     virtual void updateReadPtr(const uint32_t newReadPtr) = 0;
111 
112     /**
113      * Wrapper for the dataInterface->read, performs wraparound read
114      *
115      * @param[in] relativeOffset - offset relative the "Error Log
116      *  Queue region" = (sizeof(CircularBufferHeader) + UE reserved region)
117      * @param[in] length - bytes to read
118      * @return the bytes read
119      */
120     virtual std::vector<uint8_t> wraparoundRead(const uint32_t relativeOffset,
121                                                 const uint32_t length) = 0;
122     /**
123      * Read the entry header from shared buffer
124      *
125      * @param[in] relativeOffset - offset relative the "Error Log
126      *  Queue region" = (sizeof(CircularBufferHeader) + UE reserved region)
127      * @return the entry header
128      */
129     virtual struct QueueEntryHeader readEntryHeader(size_t relativeOffset) = 0;
130 
131     /**
132      * Read the queue entry from the error log queue
133      *
134      * @param[in] relativeOffset - offset relative the "Error Log
135      *  Queue region" = (sizeof(CircularBufferHeader) + UE reserved region)
136      * * @return entry header and entry pair read from buffer
137      */
138     virtual EntryPair readEntry(size_t relativeOffset) = 0;
139 
140     /**
141      * Read the buffer - this API should be used instead of individual functions
142      * above
143      *
144      * @return vector of EntryPair which consists of entry header and entry
145      */
146     virtual std::vector<EntryPair> readErrorLogs() = 0;
147 };
148 
149 /**
150  * Buffer implementation class
151  */
152 class BufferImpl : public BufferInterface
153 {
154   public:
155     /** @brief Constructor for BufferImpl
156      *  @param[in] dataInterface     - DataInterface for this object
157      */
158     explicit BufferImpl(std::unique_ptr<DataInterface> dataInterface);
159     void initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize,
160                     uint16_t ueRegionSize,
161                     const std::array<uint32_t, 4>& magicNumber) override;
162     void readBufferHeader() override;
163     struct CircularBufferHeader getCachedBufferHeader() const override;
164     void updateReadPtr(const uint32_t newReadPtr) override;
165     std::vector<uint8_t> wraparoundRead(const uint32_t relativeOffset,
166                                         const uint32_t length) override;
167     struct QueueEntryHeader readEntryHeader(size_t relativeOffset) override;
168     EntryPair readEntry(size_t relativeOffset) override;
169     std::vector<EntryPair> readErrorLogs() override;
170 
171   private:
172     /** @brief The Error log queue starts after the UE region, which is where
173      * the read and write pointers are offset from relatively
174      *  @return relative offset for read and write pointers
175      */
176     size_t getQueueOffset();
177     /** @brief Calculate the checksum by XOR each bytes in the span
178      *  @param[in] entry     - Span to calculate the checksum on
179      *  @return calculated checksum
180      */
181     uint8_t calculateChecksum(std::span<uint8_t> entry);
182 
183     std::unique_ptr<DataInterface> dataInterface;
184     struct CircularBufferHeader cachedBufferHeader = {};
185 };
186 
187 } // namespace bios_bmc_smm_error_logger
188