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