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