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: 220 static void SetUpTestCase() 221 { 222 dataPath = getPELReadOnlyDataPath(); 223 std::ofstream file{dataPath / filename}; 224 file << calloutJSON.dump(); 225 } 226 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 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 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 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 313 TEST_F(DeviceCalloutsTest, getCalloutTypeTest) 314 { 315 316 // Invalid 317 { 318 EXPECT_EQ(util::getCalloutType("/some/bad/device/path"), 319 util::CalloutType::unknown); 320 } 321 322 // I2C 323 { 324 EXPECT_EQ(util::getCalloutType( 325 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" 326 "1e78a340.i2c-bus/i2c-14/14-0072"), 327 util::CalloutType::i2c); 328 } 329 330 // FSI 331 { 332 EXPECT_EQ(util::getCalloutType( 333 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 334 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 335 "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2"), 336 util::CalloutType::fsi); 337 } 338 339 // FSI-I2C 340 { 341 EXPECT_EQ(util::getCalloutType( 342 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 343 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 344 "slave@01:00/01:01:00:03/i2c-211/211-0055"), 345 util::CalloutType::fsii2c); 346 } 347 348 // FSI-SPI 349 { 350 351 EXPECT_EQ( 352 util::getCalloutType( 353 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/" 354 "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/" 355 "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"), 356 util::CalloutType::fsispi); 357 } 358 } 359 360 // Test getting I2C search keys 361 TEST_F(DeviceCalloutsTest, getI2CSearchKeysTest) 362 { 363 364 { 365 EXPECT_EQ(util::getI2CSearchKeys( 366 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" 367 "1e78a340.i2c-bus/i2c-10/10-0022"), 368 (std::tuple{10, 0x22})); 369 370 EXPECT_EQ(util::getI2CSearchKeys( 371 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 372 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 373 "slave@01:00/01:01:00:03/i2c-211/211-0055"), 374 (std::tuple{11, 0x55})); 375 } 376 377 { 378 EXPECT_THROW(util::getI2CSearchKeys("/sys/some/bad/path"), 379 std::invalid_argument); 380 } 381 } 382 // 383 // Test getting SPI search keys 384 TEST_F(DeviceCalloutsTest, getSPISearchKeysTest) 385 { 386 { 387 EXPECT_EQ( 388 util::getSPISearchKeys( 389 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/" 390 "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/" 391 "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"), 392 9); 393 } 394 395 { 396 EXPECT_THROW(util::getSPISearchKeys("/sys/some/bad/path"), 397 std::invalid_argument); 398 } 399 } 400 401 // Test getting FSI search keys 402 TEST_F(DeviceCalloutsTest, getFSISearchKeysTest) 403 { 404 { 405 EXPECT_EQ(util::getFSISearchKeys( 406 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 407 "fsi-master/fsi0/slave@00:00/00:00:00:04/spi_master/spi2/" 408 "spi2.0/spi2.00/nvmem"), 409 "0"); 410 } 411 412 { 413 EXPECT_EQ(util::getFSISearchKeys( 414 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 415 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 416 "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2"), 417 "0-1"); 418 } 419 420 { 421 EXPECT_EQ( 422 util::getFSISearchKeys( 423 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 424 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 425 "slave@01:00/01:01:00:0a:/fsi-master/slave@04:00/01:01:00:0a"), 426 "0-1-4"); 427 } 428 429 { 430 EXPECT_THROW(util::getFSISearchKeys("/sys/some/bad/path"), 431 std::invalid_argument); 432 } 433 } 434 435 // Test getting FSI-I2C search keys 436 TEST_F(DeviceCalloutsTest, getFSII2CSearchKeysTest) 437 { 438 { 439 // Link 0-1 bus 11 address 0x55 440 EXPECT_EQ(util::getFSII2CSearchKeys( 441 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 442 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 443 "slave@01:00/01:01:00:03/i2c-211/211-0055"), 444 (std::tuple{"0-1", std::tuple{11, 0x55}})); 445 } 446 447 { 448 EXPECT_THROW(util::getFSII2CSearchKeys("/sys/some/bad/path"), 449 std::invalid_argument); 450 } 451 } 452 453 // Test getting FSI-SPI search keys 454 TEST_F(DeviceCalloutsTest, getFSISPISearchKeysTest) 455 { 456 { 457 // Link 0-8 SPI 9 458 EXPECT_EQ( 459 util::getFSISPISearchKeys( 460 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/" 461 "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/" 462 "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"), 463 (std::tuple{"0-8", 9})); 464 } 465 466 { 467 EXPECT_THROW(util::getFSISPISearchKeys("/sys/some/bad/path"), 468 std::invalid_argument); 469 } 470 } 471 472 TEST_F(DeviceCalloutsTest, getCalloutsTest) 473 { 474 std::vector<std::string> systemTypes{"systemA", "systemB"}; 475 476 // A really bogus path 477 { 478 EXPECT_THROW(getCallouts("/bad/path", systemTypes), 479 std::invalid_argument); 480 } 481 482 // I2C 483 { 484 auto callouts = getCallouts( 485 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" 486 "1e78a340.i2c-bus/i2c-14/14-0072", 487 systemTypes); 488 489 std::vector<Callout> expected{ 490 {"H", "P1-C19", "/chassis/motherboard/cpu0", "core0", 491 "I2C: bus: 14 address: 114 dest: proc-0 target"}, 492 {"M", "P1", "/chassis/motherboard", "", ""}}; 493 494 EXPECT_EQ(callouts, expected); 495 496 // Use the bus/address API instead of the device path one 497 callouts = getI2CCallouts(14, 0x72, systemTypes); 498 EXPECT_EQ(callouts, expected); 499 500 // I2C address not in JSON 501 EXPECT_THROW( 502 getCallouts( 503 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" 504 "1e78a340.i2c-bus/i2c-14/14-0099", 505 systemTypes), 506 std::invalid_argument); 507 508 // A bad JSON entry, missing the location code 509 EXPECT_THROW( 510 getCallouts( 511 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" 512 "1e78a340.i2c-bus/i2c-0/0-005a", 513 systemTypes), 514 std::runtime_error); 515 } 516 517 // FSI 518 { 519 auto callouts = getCallouts( 520 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 521 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 522 "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2", 523 systemTypes); 524 525 std::vector<Callout> expected{{"H", "P1-C19", 526 "/chassis/motherboard/cpu0", "core", 527 "FSI: links: 0-1 dest: proc-0 target"}}; 528 529 EXPECT_EQ(callouts, expected); 530 531 // link 9-1 not in JSON 532 EXPECT_THROW( 533 getCallouts( 534 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 535 "fsi-master/fsi0/slave@09:00/00:00:00:0a/fsi-master/fsi1/" 536 "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2", 537 systemTypes), 538 std::invalid_argument); 539 } 540 541 // FSI-I2C 542 { 543 auto callouts = getCallouts( 544 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 545 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 546 "slave@03:00/01:01:00:03/i2c-207/207-0019", 547 systemTypes); 548 549 std::vector<Callout> expected{ 550 {"H", "P1-C25", "/chassis/motherboard/cpu5", "", 551 "FSI-I2C: links: 0-3 bus: 7 addr: 25 dest: proc-5 target"}, 552 {"M", "P1", "/chassis/motherboard", "", ""}, 553 {"L", "P2", "/chassis/motherboard/bmc", "", ""}}; 554 555 EXPECT_EQ(callouts, expected); 556 557 // Bus 2 not in JSON 558 EXPECT_THROW( 559 getCallouts( 560 "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 561 "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" 562 "slave@03:00/01:01:00:03/i2c-202/202-0019", 563 systemTypes), 564 std::invalid_argument); 565 } 566 567 // FSI-SPI 568 { 569 auto callouts = 570 getCallouts("/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 571 "fsi-master/fsi0/slave@08:00/00:00:00:04/spi_master/" 572 "spi3/spi3.0/spi3.00/nvmem", 573 systemTypes); 574 575 std::vector<Callout> expected{ 576 {"H", "P1-C19", "/chassis/motherboard/cpu0", "", 577 "FSI-SPI: links: 8 bus: 3 dest: proc-0 target"}}; 578 579 EXPECT_EQ(callouts, expected); 580 581 // Bus 7 not in the JSON 582 EXPECT_THROW( 583 getCallouts("/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" 584 "fsi-master/fsi0/slave@08:00/00:00:00:04/spi_master/" 585 "spi7/spi7.0/spi7.00/nvmem", 586 systemTypes), 587 std::invalid_argument); 588 } 589 } 590