1 /**
2 * Copyright © 2020 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "extensions/openpower-pels/device_callouts.hpp"
18 #include "extensions/openpower-pels/paths.hpp"
19
20 #include <fstream>
21
22 #include <gtest/gtest.h>
23
24 using namespace openpower::pels;
25 using namespace openpower::pels::device_callouts;
26 namespace fs = std::filesystem;
27
28 // The callout JSON looks like:
29 // "I2C":
30 // "<bus>":
31 // "<address>":
32 // "Callouts": ...
33 //
34 // "FSI":
35 // "<fsi link>":
36 // "Callouts": ...
37 //
38 // "FSI-I2C":
39 // "<fsi link>":
40 // "<bus>":
41 // "<address>":
42 // "Callouts": ...
43 //
44 // "FSI-SPI":
45 // "<fsi link>":
46 // "<bus>":
47 // "Callouts": ...
48
49 const auto calloutJSON = R"(
50 {
51 "I2C":
52 {
53 "0":
54 {
55 "32":
56 {
57 "Callouts":[
58 {
59 "Name": "/chassis/motherboard/cpu0",
60 "LocationCode": "P1-C19",
61 "Priority": "H"
62 }
63 ],
64 "Dest": "proc-0 target"
65 },
66 "81":
67 {
68 "Callouts":[
69 {
70 "Name": "/chassis/motherboard/cpu0",
71 "LocationCode": "P1-C19",
72 "Priority": "H"
73 }
74 ],
75 "Dest": "proc-0 target"
76 },
77 "90":
78 {
79 "Callouts":[
80 {
81 "Name": "This is missing the location code",
82 "Priority": "H"
83 }
84 ],
85 "Dest": "proc-0 target"
86 }
87 },
88 "14":
89 {
90 "112":
91 {
92 "Callouts":[
93 {
94 "Name": "/chassis/motherboard/cpu0",
95 "LocationCode": "P1-C19",
96 "Priority": "H"
97 }
98 ],
99 "Dest": "proc-0 target"
100 },
101 "114":
102 {
103 "Callouts":[
104 {
105 "Name": "/chassis/motherboard/cpu0",
106 "LocationCode": "P1-C19",
107 "Priority": "H",
108 "MRU": "core0"
109 },
110 {
111 "Name": "/chassis/motherboard",
112 "LocationCode": "P1",
113 "Priority": "M"
114 }
115 ],
116 "Dest": "proc-0 target"
117 }
118 }
119 },
120 "FSI":
121 {
122 "0":
123 {
124 "Callouts":[
125 {
126 "Name": "/chassis/motherboard/cpu0",
127 "LocationCode": "P1-C19",
128 "Priority": "H"
129 }
130 ],
131 "Dest": "proc-0 target"
132 },
133 "0-1":
134 {
135 "Callouts":[
136 {
137 "Name": "/chassis/motherboard/cpu0",
138 "LocationCode": "P1-C19",
139 "Priority": "H",
140 "MRU": "core"
141 }
142 ],
143 "Dest": "proc-0 target"
144 }
145 },
146 "FSI-I2C":
147 {
148 "0-3":
149 {
150 "7":
151 {
152 "24":
153 {
154 "Callouts":[
155 {
156 "Name": "/chassis/motherboard/cpu0",
157 "LocationCode": "P1-C19",
158 "Priority": "H"
159 }
160 ],
161 "Dest": "proc-0 target"
162 },
163 "25":
164 {
165 "Callouts":[
166 {
167 "Name": "/chassis/motherboard/cpu5",
168 "LocationCode": "P1-C25",
169 "Priority": "H"
170 },
171 {
172 "Name": "/chassis/motherboard",
173 "LocationCode": "P1",
174 "Priority": "M"
175 },
176 {
177 "Name": "/chassis/motherboard/bmc",
178 "LocationCode": "P2",
179 "Priority": "L"
180 }
181 ],
182 "Dest": "proc-5 target"
183 }
184 }
185 }
186 },
187 "FSI-SPI":
188 {
189 "8":
190 {
191 "3":
192 {
193 "Callouts":[
194 {
195 "Name": "/chassis/motherboard/cpu0",
196 "LocationCode": "P1-C19",
197 "Priority": "H"
198 }
199 ],
200 "Dest": "proc-0 target"
201 },
202 "4":
203 {
204 "Callouts":[
205 {
206 "Name": "/chassis/motherboard/cpu2",
207 "LocationCode": "P1-C12",
208 "Priority": "M"
209 }
210 ],
211 "Dest": "proc-0 target"
212 }
213 }
214 }
215 })"_json;
216
217 class DeviceCalloutsTest : public ::testing::Test
218 {
219 public:
SetUpTestCase()220 static void SetUpTestCase()
221 {
222 dataPath = getPELReadOnlyDataPath();
223 std::ofstream file{dataPath / filename};
224 file << calloutJSON.dump();
225 }
226
TearDownTestCase()227 static void TearDownTestCase()
228 {
229 fs::remove_all(dataPath);
230 }
231
232 static std::string filename;
233 static fs::path dataPath;
234 };
235
236 std::string DeviceCalloutsTest::filename = "systemA_dev_callouts.json";
237 fs::path DeviceCalloutsTest::dataPath;
238
239 namespace openpower::pels::device_callouts
240 {
241
242 // Helpers to compair vectors of Callout objects
operator !=(const Callout & left,const Callout & right)243 bool operator!=(const Callout& left, const Callout& right)
244 {
245 return (left.priority != right.priority) ||
246 (left.locationCode != right.locationCode) ||
247 (left.name != right.name) || (left.mru != right.mru) ||
248 (left.debug != right.debug);
249 }
250
operator ==(const std::vector<Callout> & left,const std::vector<Callout> & right)251 bool operator==(const std::vector<Callout>& left,
252 const std::vector<Callout>& right)
253 {
254 if (left.size() != right.size())
255 {
256 return false;
257 }
258
259 for (size_t i = 0; i < left.size(); i++)
260 {
261 if (left[i] != right[i])
262 {
263 return false;
264 }
265 }
266
267 return true;
268 }
269
270 } // namespace openpower::pels::device_callouts
271
272 // Test looking up the JSON file based on the system compatible names
TEST_F(DeviceCalloutsTest,getJSONFilenameTest)273 TEST_F(DeviceCalloutsTest, getJSONFilenameTest)
274 {
275 {
276 std::vector<std::string> compatibles{"system1", "systemA", "system3"};
277 EXPECT_EQ(util::getJSONFilename(compatibles),
278 fs::path{dataPath / filename});
279 }
280
281 // Actual filename not in compatibles
282 {
283 std::vector<std::string> compatibles{"system5", "system6"};
284 EXPECT_THROW(util::getJSONFilename(compatibles), std::invalid_argument);
285 }
286
287 // Test using the fallback name
288 {
289 // If _dev_callouts.json is there, it will be used if no other
290 // match is found.
291 fs::path fallbackFile{dataPath / "_dev_callouts.json"};
292 std::ofstream file{fallbackFile};
293 file << calloutJSON.dump();
294 file.close();
295
296 // Fallback shouldn't be used because the actual systemA file is there
297 {
298 std::vector<std::string> compatibles{"system1", "systemA",
299 "system3"};
300 EXPECT_EQ(util::getJSONFilename(compatibles),
301 fs::path{dataPath / filename});
302 }
303
304 // Fallback should be used because no other match
305 {
306 std::vector<std::string> compatibles{"systemX", "systemY"};
307 EXPECT_EQ(util::getJSONFilename(compatibles), fallbackFile);
308 }
309 }
310 }
311
312 // Test determining the callout type from the device path
TEST_F(DeviceCalloutsTest,getCalloutTypeTest)313 TEST_F(DeviceCalloutsTest, getCalloutTypeTest)
314 {
315 // Invalid
316 {
317 EXPECT_EQ(util::getCalloutType("/some/bad/device/path"),
318 util::CalloutType::unknown);
319 }
320
321 // I2C
322 {
323 EXPECT_EQ(util::getCalloutType(
324 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/"
325 "1e78a340.i2c/i2c-14/14-0072"),
326 util::CalloutType::i2c);
327 }
328
329 // FSI
330 {
331 EXPECT_EQ(util::getCalloutType(
332 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
333 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
334 "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2"),
335 util::CalloutType::fsi);
336 }
337
338 // FSI-I2C
339 {
340 EXPECT_EQ(util::getCalloutType(
341 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
342 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
343 "slave@01:00/01:01:00:03/i2c-211/211-0055"),
344 util::CalloutType::fsii2c);
345 }
346
347 // FSI-SPI
348 {
349 EXPECT_EQ(
350 util::getCalloutType(
351 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/"
352 "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/"
353 "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"),
354 util::CalloutType::fsispi);
355 }
356 }
357
358 // Test getting I2C search keys
TEST_F(DeviceCalloutsTest,getI2CSearchKeysTest)359 TEST_F(DeviceCalloutsTest, getI2CSearchKeysTest)
360 {
361 {
362 EXPECT_EQ(util::getI2CSearchKeys(
363 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/"
364 "1e78a340.i2c/i2c-10/10-0022"),
365 (std::tuple{10, 0x22}));
366
367 EXPECT_EQ(util::getI2CSearchKeys(
368 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
369 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
370 "slave@01:00/01:01:00:03/i2c-211/211-0055"),
371 (std::tuple{11, 0x55}));
372 }
373
374 {
375 EXPECT_THROW(util::getI2CSearchKeys("/sys/some/bad/path"),
376 std::invalid_argument);
377 }
378 }
379 //
380 // Test getting SPI search keys
TEST_F(DeviceCalloutsTest,getSPISearchKeysTest)381 TEST_F(DeviceCalloutsTest, getSPISearchKeysTest)
382 {
383 {
384 EXPECT_EQ(
385 util::getSPISearchKeys(
386 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/"
387 "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/"
388 "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"),
389 9);
390 }
391
392 {
393 EXPECT_THROW(util::getSPISearchKeys("/sys/some/bad/path"),
394 std::invalid_argument);
395 }
396 }
397
398 // Test getting FSI search keys
TEST_F(DeviceCalloutsTest,getFSISearchKeysTest)399 TEST_F(DeviceCalloutsTest, getFSISearchKeysTest)
400 {
401 {
402 EXPECT_EQ(util::getFSISearchKeys(
403 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
404 "fsi-master/fsi0/slave@00:00/00:00:00:04/spi_master/spi2/"
405 "spi2.0/spi2.00/nvmem"),
406 "0");
407 }
408
409 {
410 EXPECT_EQ(util::getFSISearchKeys(
411 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
412 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
413 "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2"),
414 "0-1");
415 }
416
417 {
418 EXPECT_EQ(
419 util::getFSISearchKeys(
420 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
421 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
422 "slave@01:00/01:01:00:0a:/fsi-master/slave@04:00/01:01:00:0a"),
423 "0-1-4");
424 }
425
426 {
427 EXPECT_THROW(util::getFSISearchKeys("/sys/some/bad/path"),
428 std::invalid_argument);
429 }
430 }
431
432 // Test getting FSI-I2C search keys
TEST_F(DeviceCalloutsTest,getFSII2CSearchKeysTest)433 TEST_F(DeviceCalloutsTest, getFSII2CSearchKeysTest)
434 {
435 {
436 // Link 0-1 bus 11 address 0x55
437 EXPECT_EQ(util::getFSII2CSearchKeys(
438 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
439 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
440 "slave@01:00/01:01:00:03/i2c-211/211-0055"),
441 (std::tuple{"0-1", std::tuple{11, 0x55}}));
442 }
443
444 {
445 EXPECT_THROW(util::getFSII2CSearchKeys("/sys/some/bad/path"),
446 std::invalid_argument);
447 }
448 }
449
450 // Test getting FSI-SPI search keys
TEST_F(DeviceCalloutsTest,getFSISPISearchKeysTest)451 TEST_F(DeviceCalloutsTest, getFSISPISearchKeysTest)
452 {
453 {
454 // Link 0-8 SPI 9
455 EXPECT_EQ(
456 util::getFSISPISearchKeys(
457 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/"
458 "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/"
459 "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"),
460 (std::tuple{"0-8", 9}));
461 }
462
463 {
464 EXPECT_THROW(util::getFSISPISearchKeys("/sys/some/bad/path"),
465 std::invalid_argument);
466 }
467 }
468
TEST_F(DeviceCalloutsTest,getCalloutsTest)469 TEST_F(DeviceCalloutsTest, getCalloutsTest)
470 {
471 std::vector<std::string> systemTypes{"systemA", "systemB"};
472
473 // A really bogus path
474 {
475 EXPECT_THROW(getCallouts("/bad/path", systemTypes),
476 std::invalid_argument);
477 }
478
479 // I2C
480 {
481 auto callouts = getCallouts(
482 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/"
483 "1e78a340.i2c/i2c-14/14-0072",
484 systemTypes);
485
486 std::vector<Callout> expected{
487 {"H", "P1-C19", "/chassis/motherboard/cpu0", "core0",
488 "I2C: bus: 14 address: 114 dest: proc-0 target"},
489 {"M", "P1", "/chassis/motherboard", "", ""}};
490
491 EXPECT_EQ(callouts, expected);
492
493 // Use the bus/address API instead of the device path one
494 callouts = getI2CCallouts(14, 0x72, systemTypes);
495 EXPECT_EQ(callouts, expected);
496
497 // I2C address not in JSON
498 EXPECT_THROW(
499 getCallouts(
500 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/"
501 "1e78a340.i2c/i2c-14/14-0099",
502 systemTypes),
503 std::invalid_argument);
504
505 // A bad JSON entry, missing the location code
506 EXPECT_THROW(
507 getCallouts(
508 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/"
509 "1e78a340.i2c/i2c-0/0-005a",
510 systemTypes),
511 std::runtime_error);
512 }
513
514 // FSI
515 {
516 auto callouts = getCallouts(
517 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
518 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
519 "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2",
520 systemTypes);
521
522 std::vector<Callout> expected{
523 {"H", "P1-C19", "/chassis/motherboard/cpu0", "core",
524 "FSI: links: 0-1 dest: proc-0 target"}};
525
526 EXPECT_EQ(callouts, expected);
527
528 // link 9-1 not in JSON
529 EXPECT_THROW(
530 getCallouts(
531 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
532 "fsi-master/fsi0/slave@09:00/00:00:00:0a/fsi-master/fsi1/"
533 "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2",
534 systemTypes),
535 std::invalid_argument);
536 }
537
538 // FSI-I2C
539 {
540 auto callouts = getCallouts(
541 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
542 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
543 "slave@03:00/01:01:00:03/i2c-207/207-0019",
544 systemTypes);
545
546 std::vector<Callout> expected{
547 {"H", "P1-C25", "/chassis/motherboard/cpu5", "",
548 "FSI-I2C: links: 0-3 bus: 7 addr: 25 dest: proc-5 target"},
549 {"M", "P1", "/chassis/motherboard", "", ""},
550 {"L", "P2", "/chassis/motherboard/bmc", "", ""}};
551
552 EXPECT_EQ(callouts, expected);
553
554 // Bus 2 not in JSON
555 EXPECT_THROW(
556 getCallouts(
557 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
558 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/"
559 "slave@03:00/01:01:00:03/i2c-202/202-0019",
560 systemTypes),
561 std::invalid_argument);
562 }
563
564 // FSI-SPI
565 {
566 auto callouts =
567 getCallouts("/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
568 "fsi-master/fsi0/slave@08:00/00:00:00:04/spi_master/"
569 "spi3/spi3.0/spi3.00/nvmem",
570 systemTypes);
571
572 std::vector<Callout> expected{
573 {"H", "P1-C19", "/chassis/motherboard/cpu0", "",
574 "FSI-SPI: links: 8 bus: 3 dest: proc-0 target"}};
575
576 EXPECT_EQ(callouts, expected);
577
578 // Bus 7 not in the JSON
579 EXPECT_THROW(
580 getCallouts("/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/"
581 "fsi-master/fsi0/slave@08:00/00:00:00:04/spi_master/"
582 "spi7/spi7.0/spi7.00/nvmem",
583 systemTypes),
584 std::invalid_argument);
585 }
586 }
587