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