xref: /openbmc/entity-manager/test/test_fru-utils.cpp (revision 849f13ab73fb85a6b8b002bc16414b67562f3513)
1 #include "fru_utils.hpp"
2 
3 #include <algorithm>
4 #include <array>
5 #include <iterator>
6 
7 #include "gtest/gtest.h"
8 
9 extern "C"
10 {
11 // Include for I2C_SMBUS_BLOCK_MAX
12 #include <linux/i2c.h>
13 }
14 
15 static constexpr size_t blockSize = I2C_SMBUS_BLOCK_MAX;
16 
TEST(ValidateHeaderTest,InvalidFruVersionReturnsFalse)17 TEST(ValidateHeaderTest, InvalidFruVersionReturnsFalse)
18 {
19     // Validates the FruVersion is checked for the only legal value.
20     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
21         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
22 
23     EXPECT_FALSE(validateHeader(fruHeader));
24 }
25 
TEST(ValidateHeaderTest,InvalidReservedReturnsFalse)26 TEST(ValidateHeaderTest, InvalidReservedReturnsFalse)
27 {
28     // Validates the reserved bit(7:4) of first bytes.
29     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
30         0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
31 
32     EXPECT_FALSE(validateHeader(fruHeader));
33 }
34 
TEST(ValidateHeaderTest,InvalidPaddingReturnsFalse)35 TEST(ValidateHeaderTest, InvalidPaddingReturnsFalse)
36 {
37     // Validates the padding byte (7th byte).
38     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
39         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00};
40 
41     EXPECT_FALSE(validateHeader(fruHeader));
42 }
43 
TEST(ValidateHeaderTest,InvalidChecksumReturnsFalse)44 TEST(ValidateHeaderTest, InvalidChecksumReturnsFalse)
45 {
46     // Validates the checksum, check for incorrect value.
47     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
48         0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00};
49 
50     EXPECT_FALSE(validateHeader(fruHeader));
51 }
52 
TEST(ValidateHeaderTest,ValidChecksumReturnsTrue)53 TEST(ValidateHeaderTest, ValidChecksumReturnsTrue)
54 {
55     // Validates the checksum, check for correct value.
56     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
57         0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
58 
59     EXPECT_TRUE(validateHeader(fruHeader));
60 }
61 
TEST(VerifyOffsetTest,EmptyFruDataReturnsFalse)62 TEST(VerifyOffsetTest, EmptyFruDataReturnsFalse)
63 {
64     // Validates the FruData size is checked for non empty.
65     std::vector<uint8_t> fruData = {};
66 
67     EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaChassis, 0));
68 }
69 
TEST(VerifyOffsetTest,AreaOutOfRangeReturnsFalse)70 TEST(VerifyOffsetTest, AreaOutOfRangeReturnsFalse)
71 {
72     // Validates the FruArea value, check if it is within range.
73     const std::vector<uint8_t> fruData = {0x01, 0x00, 0x00, 0x00, 0x00,
74                                           0x00, 0x00, 0x00, 0x00};
75 
76     unsigned int areaOutOfRange = 8;
77     EXPECT_FALSE(
78         verifyOffset(fruData, static_cast<fruAreas>(areaOutOfRange), 0));
79 }
80 
TEST(VerifyOffsetTest,OverlapNextAreaReturnsFalse)81 TEST(VerifyOffsetTest, OverlapNextAreaReturnsFalse)
82 {
83     // Validates the Overlap of offsets with overlapped values.
84     const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x02, 0x03,
85                                           0x04, 0x00, 0x00, 0x00};
86 
87     EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaChassis, 2));
88 }
89 
TEST(VerifyOffsetTest,OverlapPrevAreaReturnsFalse)90 TEST(VerifyOffsetTest, OverlapPrevAreaReturnsFalse)
91 {
92     // Validates the Overlap of offsets with overlapped values.
93     const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x03, 0x02,
94                                           0x07, 0x00, 0x00, 0x00};
95 
96     EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaProduct, 2));
97 }
98 
TEST(VerifyOffsetTest,ValidInputDataNoOverlapReturnsTrue)99 TEST(VerifyOffsetTest, ValidInputDataNoOverlapReturnsTrue)
100 {
101     // Validates all inputs with expected value and no overlap.
102     const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x02, 0x03,
103                                           0x04, 0x00, 0x00, 0x00};
104 
105     EXPECT_TRUE(verifyOffset(fruData, fruAreas::fruAreaChassis, 1));
106 }
107 
TEST(VerifyChecksumTest,EmptyInput)108 TEST(VerifyChecksumTest, EmptyInput)
109 {
110     std::vector<uint8_t> data = {};
111 
112     EXPECT_EQ(calculateChecksum(data), 0);
113 }
114 
TEST(VerifyChecksumTest,SingleOneInput)115 TEST(VerifyChecksumTest, SingleOneInput)
116 {
117     std::vector<uint8_t> data(1, 1);
118 
119     EXPECT_EQ(calculateChecksum(data), 255);
120 }
121 
TEST(VerifyChecksumTest,AllOneInput)122 TEST(VerifyChecksumTest, AllOneInput)
123 {
124     std::vector<uint8_t> data(256, 1);
125 
126     EXPECT_EQ(calculateChecksum(data), 0);
127 }
128 
TEST(VerifyChecksumTest,WrapBoundaryLow)129 TEST(VerifyChecksumTest, WrapBoundaryLow)
130 {
131     std::vector<uint8_t> data = {255, 0};
132 
133     EXPECT_EQ(calculateChecksum(data), 1);
134 }
135 
TEST(VerifyChecksumTest,WrapBoundaryExact)136 TEST(VerifyChecksumTest, WrapBoundaryExact)
137 {
138     std::vector<uint8_t> data = {255, 1};
139 
140     EXPECT_EQ(calculateChecksum(data), 0);
141 }
142 
TEST(VerifyChecksumTest,WrapBoundaryHigh)143 TEST(VerifyChecksumTest, WrapBoundaryHigh)
144 {
145     std::vector<uint8_t> data = {255, 2};
146 
147     EXPECT_EQ(calculateChecksum(data), 255);
148 }
149 
getDataTempl(const std::vector<uint8_t> & data,off_t offset,size_t length,uint8_t * outBuf)150 int64_t getDataTempl(const std::vector<uint8_t>& data, off_t offset,
151                      size_t length, uint8_t* outBuf)
152 {
153     if (offset >= static_cast<off_t>(data.size()))
154     {
155         return 0;
156     }
157 
158     uint16_t idx = offset;
159     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
160     for (; idx < std::min(data.size(), offset + length); ++idx, ++outBuf)
161     {
162         *outBuf = data[idx];
163     }
164 
165     return idx - offset;
166 }
167 
TEST(FRUReaderTest,ReadData)168 TEST(FRUReaderTest, ReadData)
169 {
170     std::vector<uint8_t> data = {};
171     data.reserve(blockSize * 2);
172     for (size_t i = 0; i < blockSize * 2; i++)
173     {
174         data.push_back(i);
175     }
176     std::array<uint8_t, blockSize * 2> rdbuf{};
177     auto getData = [&data](auto o, auto l, auto* b) {
178         return getDataTempl(data, o, l, b);
179     };
180     FRUReader reader(getData);
181 
182     EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()),
183               static_cast<ssize_t>(data.size()));
184     EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin()));
185     for (size_t i = 0; i < blockSize * 2; i++)
186     {
187         EXPECT_EQ(reader.read(i, 1, rdbuf.data()), 1);
188         EXPECT_EQ(rdbuf[i], i);
189     }
190     EXPECT_EQ(reader.read(blockSize - 1, 2, rdbuf.data()), 2);
191     EXPECT_EQ(rdbuf[0], blockSize - 1);
192     EXPECT_EQ(rdbuf[1], blockSize);
193 }
194 
TEST(FRUReaderTest,StartPastUnknownEOF)195 TEST(FRUReaderTest, StartPastUnknownEOF)
196 {
197     const std::vector<uint8_t> data = {};
198     auto getData = [&data](auto o, auto l, auto* b) {
199         return getDataTempl(data, o, l, b);
200     };
201     FRUReader reader(getData);
202 
203     EXPECT_EQ(reader.read(1, 1, nullptr), 0);
204 }
205 
TEST(FRUReaderTest,StartPastKnownEOF)206 TEST(FRUReaderTest, StartPastKnownEOF)
207 {
208     std::vector<uint8_t> data = {};
209     data.resize(blockSize / 2);
210     std::array<uint8_t, blockSize> blockData{};
211     auto getData = [&data](auto o, auto l, auto* b) {
212         return getDataTempl(data, o, l, b);
213     };
214     FRUReader reader(getData);
215 
216     EXPECT_EQ(reader.read(0, blockSize, blockData.data()),
217               static_cast<ssize_t>(data.size()));
218     EXPECT_EQ(reader.read(data.size(), 1, nullptr), 0);
219     EXPECT_EQ(reader.read(data.size() + 1, 1, nullptr), 0);
220     EXPECT_EQ(reader.read(blockSize, 1, nullptr), 0);
221     EXPECT_EQ(reader.read(blockSize + 1, 1, nullptr), 0);
222 }
223 
TEST(FRUReaderTest,DecreasingEOF)224 TEST(FRUReaderTest, DecreasingEOF)
225 {
226     const std::vector<uint8_t> data = {};
227     auto getData = [&data](auto o, auto l, auto* b) {
228         return getDataTempl(data, o, l, b);
229     };
230     FRUReader reader(getData);
231 
232     EXPECT_EQ(reader.read(blockSize * 2, 1, nullptr), 0);
233     EXPECT_EQ(reader.read(blockSize + (blockSize / 2), 1, nullptr), 0);
234     EXPECT_EQ(reader.read(blockSize, 1, nullptr), 0);
235     EXPECT_EQ(reader.read(blockSize / 2, 1, nullptr), 0);
236     EXPECT_EQ(reader.read(0, 1, nullptr), 0);
237 }
238 
TEST(FRUReaderTest,CacheHit)239 TEST(FRUReaderTest, CacheHit)
240 {
241     std::vector<uint8_t> data = {'X'};
242     std::array<uint8_t, blockSize> read1{};
243     std::array<uint8_t, blockSize> read2{};
244     auto getData = [&data](auto o, auto l, auto* b) {
245         return getDataTempl(data, o, l, b);
246     };
247     FRUReader reader(getData);
248 
249     // cache hit should return the same data for the second read even if we
250     // change it behind the FRUReader's back after the first
251     EXPECT_EQ(reader.read(0, blockSize, read1.data()), 1);
252     data[0] = 'Y';
253     EXPECT_EQ(reader.read(0, blockSize, read2.data()), 1);
254     EXPECT_EQ(read1[0], read2[0]);
255 }
256 
TEST(FRUReaderTest,ReadPastKnownEnd)257 TEST(FRUReaderTest, ReadPastKnownEnd)
258 {
259     const std::vector<uint8_t> data = {'X', 'Y'};
260     std::array<uint8_t, blockSize> rdbuf{};
261     auto getData = [&data](auto o, auto l, auto* b) {
262         return getDataTempl(data, o, l, b);
263     };
264     FRUReader reader(getData);
265 
266     EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()),
267               static_cast<ssize_t>(data.size()));
268     EXPECT_EQ(rdbuf[0], 'X');
269     EXPECT_EQ(rdbuf[1], 'Y');
270     EXPECT_EQ(reader.read(1, data.size(), rdbuf.data()),
271               static_cast<ssize_t>(data.size() - 1));
272     EXPECT_EQ(rdbuf[0], 'Y');
273 }
274 
TEST(FRUReaderTest,MultiBlockRead)275 TEST(FRUReaderTest, MultiBlockRead)
276 {
277     std::vector<uint8_t> data = {};
278     data.resize(blockSize, 'X');
279     data.resize(2 * blockSize, 'Y');
280     std::array<uint8_t, 2 * blockSize> rdbuf{};
281     auto getData = [&data](auto o, auto l, auto* b) {
282         return getDataTempl(data, o, l, b);
283     };
284     FRUReader reader(getData);
285 
286     EXPECT_EQ(reader.read(0, 2 * blockSize, rdbuf.data()),
287               static_cast<ssize_t>(2 * blockSize));
288     EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin()));
289 }
290 
TEST(FRUReaderTest,ShrinkingEEPROM)291 TEST(FRUReaderTest, ShrinkingEEPROM)
292 {
293     std::vector<uint8_t> data = {};
294     data.resize(3 * blockSize, 'X');
295     std::array<uint8_t, blockSize> rdbuf{};
296     auto getData = [&data](auto o, auto l, auto* b) {
297         return getDataTempl(data, o, l, b);
298     };
299     FRUReader reader(getData);
300 
301     EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1);
302     data.resize(blockSize);
303     EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1);
304 }
305 
TEST(FindFRUHeaderTest,InvalidHeader)306 TEST(FindFRUHeaderTest, InvalidHeader)
307 {
308     const std::vector<uint8_t> data = {255, 16};
309     off_t offset = 0;
310     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
311     auto getData = [&data](auto o, auto l, auto* b) {
312         return getDataTempl(data, o, l, b);
313     };
314     FRUReader reader(getData);
315 
316     EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
317 }
318 
TEST(FindFRUHeaderTest,NoData)319 TEST(FindFRUHeaderTest, NoData)
320 {
321     const std::vector<uint8_t> data = {};
322     off_t offset = 0;
323     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
324     auto getData = [&data](auto o, auto l, auto* b) {
325         return getDataTempl(data, o, l, b);
326     };
327     FRUReader reader(getData);
328 
329     EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
330 }
331 
TEST(FindFRUHeaderTest,ValidHeader)332 TEST(FindFRUHeaderTest, ValidHeader)
333 {
334     const std::vector<uint8_t> data = {0x01, 0x00, 0x01, 0x02,
335                                        0x03, 0x04, 0x00, 0xf5};
336     off_t offset = 0;
337     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
338     auto getData = [&data](auto o, auto l, auto* b) {
339         return getDataTempl(data, o, l, b);
340     };
341     FRUReader reader(getData);
342 
343     EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset));
344     EXPECT_EQ(0, offset);
345 }
346 
TEST(FindFRUHeaderTest,TyanInvalidHeader)347 TEST(FindFRUHeaderTest, TyanInvalidHeader)
348 {
349     std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
350     data.resize(0x6000 + I2C_SMBUS_BLOCK_MAX);
351     off_t offset = 0;
352     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
353     auto getData = [&data](auto o, auto l, auto* b) {
354         return getDataTempl(data, o, l, b);
355     };
356     FRUReader reader(getData);
357 
358     EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
359 }
360 
TEST(FindFRUHeaderTest,TyanNoData)361 TEST(FindFRUHeaderTest, TyanNoData)
362 {
363     const std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
364     off_t offset = 0;
365     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
366     auto getData = [&data](auto o, auto l, auto* b) {
367         return getDataTempl(data, o, l, b);
368     };
369     FRUReader reader(getData);
370 
371     EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
372 }
373 
TEST(FindFRUHeaderTest,TyanValidHeader)374 TEST(FindFRUHeaderTest, TyanValidHeader)
375 {
376     std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
377     data.resize(0x6000);
378     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
379         0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
380     copy(fruHeader.begin(), fruHeader.end(), back_inserter(data));
381 
382     off_t offset = 0;
383     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
384     auto getData = [&data](auto o, auto l, auto* b) {
385         return getDataTempl(data, o, l, b);
386     };
387     FRUReader reader(getData);
388 
389     EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset));
390     EXPECT_EQ(0x6000, offset);
391 }
392