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 // 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-bus/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 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-bus/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 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 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 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 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 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-bus/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-bus/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-bus/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{{"H", "P1-C19", 523 "/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