| /** |
| * Copyright © 2020 IBM Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "extensions/openpower-pels/device_callouts.hpp" |
| #include "extensions/openpower-pels/paths.hpp" |
| |
| #include <fstream> |
| |
| #include <gtest/gtest.h> |
| |
| using namespace openpower::pels; |
| using namespace openpower::pels::device_callouts; |
| namespace fs = std::filesystem; |
| |
| // The callout JSON looks like: |
| // "I2C": |
| // "<bus>": |
| // "<address>": |
| // "Callouts": ... |
| // |
| // "FSI": |
| // "<fsi link>": |
| // "Callouts": ... |
| // |
| // "FSI-I2C": |
| // "<fsi link>": |
| // "<bus>": |
| // "<address>": |
| // "Callouts": ... |
| // |
| // "FSI-SPI": |
| // "<fsi link>": |
| // "<bus>": |
| // "Callouts": ... |
| |
| const auto calloutJSON = R"( |
| { |
| "I2C": |
| { |
| "0": |
| { |
| "32": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu0", |
| "LocationCode": "P1-C19", |
| "Priority": "H" |
| } |
| ], |
| "Dest": "proc-0 target" |
| }, |
| "81": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu0", |
| "LocationCode": "P1-C19", |
| "Priority": "H" |
| } |
| ], |
| "Dest": "proc-0 target" |
| }, |
| "90": |
| { |
| "Callouts":[ |
| { |
| "Name": "This is missing the location code", |
| "Priority": "H" |
| } |
| ], |
| "Dest": "proc-0 target" |
| } |
| }, |
| "14": |
| { |
| "112": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu0", |
| "LocationCode": "P1-C19", |
| "Priority": "H" |
| } |
| ], |
| "Dest": "proc-0 target" |
| }, |
| "114": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu0", |
| "LocationCode": "P1-C19", |
| "Priority": "H", |
| "MRU": "core0" |
| }, |
| { |
| "Name": "/chassis/motherboard", |
| "LocationCode": "P1", |
| "Priority": "M" |
| } |
| ], |
| "Dest": "proc-0 target" |
| } |
| } |
| }, |
| "FSI": |
| { |
| "0": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu0", |
| "LocationCode": "P1-C19", |
| "Priority": "H" |
| } |
| ], |
| "Dest": "proc-0 target" |
| }, |
| "0-1": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu0", |
| "LocationCode": "P1-C19", |
| "Priority": "H", |
| "MRU": "core" |
| } |
| ], |
| "Dest": "proc-0 target" |
| } |
| }, |
| "FSI-I2C": |
| { |
| "0-3": |
| { |
| "7": |
| { |
| "24": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu0", |
| "LocationCode": "P1-C19", |
| "Priority": "H" |
| } |
| ], |
| "Dest": "proc-0 target" |
| }, |
| "25": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu5", |
| "LocationCode": "P1-C25", |
| "Priority": "H" |
| }, |
| { |
| "Name": "/chassis/motherboard", |
| "LocationCode": "P1", |
| "Priority": "M" |
| }, |
| { |
| "Name": "/chassis/motherboard/bmc", |
| "LocationCode": "P2", |
| "Priority": "L" |
| } |
| ], |
| "Dest": "proc-5 target" |
| } |
| } |
| } |
| }, |
| "FSI-SPI": |
| { |
| "8": |
| { |
| "3": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu0", |
| "LocationCode": "P1-C19", |
| "Priority": "H" |
| } |
| ], |
| "Dest": "proc-0 target" |
| }, |
| "4": |
| { |
| "Callouts":[ |
| { |
| "Name": "/chassis/motherboard/cpu2", |
| "LocationCode": "P1-C12", |
| "Priority": "M" |
| } |
| ], |
| "Dest": "proc-0 target" |
| } |
| } |
| } |
| })"_json; |
| |
| class DeviceCalloutsTest : public ::testing::Test |
| { |
| public: |
| static void SetUpTestCase() |
| { |
| dataPath = getPELReadOnlyDataPath(); |
| std::ofstream file{dataPath / filename}; |
| file << calloutJSON.dump(); |
| } |
| |
| static void TearDownTestCase() |
| { |
| fs::remove_all(dataPath); |
| } |
| |
| static std::string filename; |
| static fs::path dataPath; |
| }; |
| |
| std::string DeviceCalloutsTest::filename = "systemA_dev_callouts.json"; |
| fs::path DeviceCalloutsTest::dataPath; |
| |
| namespace openpower::pels::device_callouts |
| { |
| |
| // Helpers to compair vectors of Callout objects |
| bool operator!=(const Callout& left, const Callout& right) |
| { |
| return (left.priority != right.priority) || |
| (left.locationCode != right.locationCode) || |
| (left.name != right.name) || (left.mru != right.mru) || |
| (left.debug != right.debug); |
| } |
| |
| bool operator==(const std::vector<Callout>& left, |
| const std::vector<Callout>& right) |
| { |
| if (left.size() != right.size()) |
| { |
| return false; |
| } |
| |
| for (size_t i = 0; i < left.size(); i++) |
| { |
| if (left[i] != right[i]) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace openpower::pels::device_callouts |
| |
| // Test looking up the JSON file based on the system compatible names |
| TEST_F(DeviceCalloutsTest, getJSONFilenameTest) |
| { |
| { |
| std::vector<std::string> compatibles{"system1", "systemA", "system3"}; |
| EXPECT_EQ(util::getJSONFilename(compatibles), |
| fs::path{dataPath / filename}); |
| } |
| |
| // Actual filename not in compatibles |
| { |
| std::vector<std::string> compatibles{"system5", "system6"}; |
| EXPECT_THROW(util::getJSONFilename(compatibles), std::invalid_argument); |
| } |
| |
| // Test using the fallback name |
| { |
| // If _dev_callouts.json is there, it will be used if no other |
| // match is found. |
| fs::path fallbackFile{dataPath / "_dev_callouts.json"}; |
| std::ofstream file{fallbackFile}; |
| file << calloutJSON.dump(); |
| file.close(); |
| |
| // Fallback shouldn't be used because the actual systemA file is there |
| { |
| std::vector<std::string> compatibles{"system1", "systemA", |
| "system3"}; |
| EXPECT_EQ(util::getJSONFilename(compatibles), |
| fs::path{dataPath / filename}); |
| } |
| |
| // Fallback should be used because no other match |
| { |
| std::vector<std::string> compatibles{"systemX", "systemY"}; |
| EXPECT_EQ(util::getJSONFilename(compatibles), fallbackFile); |
| } |
| } |
| } |
| |
| // Test determining the callout type from the device path |
| TEST_F(DeviceCalloutsTest, getCalloutTypeTest) |
| { |
| |
| // Invalid |
| { |
| EXPECT_EQ(util::getCalloutType("/some/bad/device/path"), |
| util::CalloutType::unknown); |
| } |
| |
| // I2C |
| { |
| EXPECT_EQ(util::getCalloutType( |
| "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" |
| "1e78a340.i2c-bus/i2c-14/14-0072"), |
| util::CalloutType::i2c); |
| } |
| |
| // FSI |
| { |
| EXPECT_EQ(util::getCalloutType( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2"), |
| util::CalloutType::fsi); |
| } |
| |
| // FSI-I2C |
| { |
| EXPECT_EQ(util::getCalloutType( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@01:00/01:01:00:03/i2c-211/211-0055"), |
| util::CalloutType::fsii2c); |
| } |
| |
| // FSI-SPI |
| { |
| |
| EXPECT_EQ( |
| util::getCalloutType( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/" |
| "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/" |
| "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"), |
| util::CalloutType::fsispi); |
| } |
| } |
| |
| // Test getting I2C search keys |
| TEST_F(DeviceCalloutsTest, getI2CSearchKeysTest) |
| { |
| |
| { |
| EXPECT_EQ(util::getI2CSearchKeys( |
| "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" |
| "1e78a340.i2c-bus/i2c-10/10-0022"), |
| (std::tuple{10, 0x22})); |
| |
| EXPECT_EQ(util::getI2CSearchKeys( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@01:00/01:01:00:03/i2c-211/211-0055"), |
| (std::tuple{11, 0x55})); |
| } |
| |
| { |
| EXPECT_THROW(util::getI2CSearchKeys("/sys/some/bad/path"), |
| std::invalid_argument); |
| } |
| } |
| // |
| // Test getting SPI search keys |
| TEST_F(DeviceCalloutsTest, getSPISearchKeysTest) |
| { |
| { |
| EXPECT_EQ( |
| util::getSPISearchKeys( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/" |
| "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/" |
| "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"), |
| 9); |
| } |
| |
| { |
| EXPECT_THROW(util::getSPISearchKeys("/sys/some/bad/path"), |
| std::invalid_argument); |
| } |
| } |
| |
| // Test getting FSI search keys |
| TEST_F(DeviceCalloutsTest, getFSISearchKeysTest) |
| { |
| { |
| EXPECT_EQ(util::getFSISearchKeys( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:04/spi_master/spi2/" |
| "spi2.0/spi2.00/nvmem"), |
| "0"); |
| } |
| |
| { |
| EXPECT_EQ(util::getFSISearchKeys( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2"), |
| "0-1"); |
| } |
| |
| { |
| EXPECT_EQ( |
| util::getFSISearchKeys( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@01:00/01:01:00:0a:/fsi-master/slave@04:00/01:01:00:0a"), |
| "0-1-4"); |
| } |
| |
| { |
| EXPECT_THROW(util::getFSISearchKeys("/sys/some/bad/path"), |
| std::invalid_argument); |
| } |
| } |
| |
| // Test getting FSI-I2C search keys |
| TEST_F(DeviceCalloutsTest, getFSII2CSearchKeysTest) |
| { |
| { |
| // Link 0-1 bus 11 address 0x55 |
| EXPECT_EQ(util::getFSII2CSearchKeys( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@01:00/01:01:00:03/i2c-211/211-0055"), |
| (std::tuple{"0-1", std::tuple{11, 0x55}})); |
| } |
| |
| { |
| EXPECT_THROW(util::getFSII2CSearchKeys("/sys/some/bad/path"), |
| std::invalid_argument); |
| } |
| } |
| |
| // Test getting FSI-SPI search keys |
| TEST_F(DeviceCalloutsTest, getFSISPISearchKeysTest) |
| { |
| { |
| // Link 0-8 SPI 9 |
| EXPECT_EQ( |
| util::getFSISPISearchKeys( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/" |
| "fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@08:00/" |
| "01:03:00:04/spi_master/spi9/spi9.0/spi9.00/nvmem"), |
| (std::tuple{"0-8", 9})); |
| } |
| |
| { |
| EXPECT_THROW(util::getFSISPISearchKeys("/sys/some/bad/path"), |
| std::invalid_argument); |
| } |
| } |
| |
| TEST_F(DeviceCalloutsTest, getCalloutsTest) |
| { |
| std::vector<std::string> systemTypes{"systemA", "systemB"}; |
| |
| // A really bogus path |
| { |
| EXPECT_THROW(getCallouts("/bad/path", systemTypes), |
| std::invalid_argument); |
| } |
| |
| // I2C |
| { |
| auto callouts = getCallouts( |
| "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" |
| "1e78a340.i2c-bus/i2c-14/14-0072", |
| systemTypes); |
| |
| std::vector<Callout> expected{ |
| {"H", "P1-C19", "/chassis/motherboard/cpu0", "core0", |
| "I2C: bus: 14 address: 114 dest: proc-0 target"}, |
| {"M", "P1", "/chassis/motherboard", "", ""}}; |
| |
| EXPECT_EQ(callouts, expected); |
| |
| // Use the bus/address API instead of the device path one |
| callouts = getI2CCallouts(14, 0x72, systemTypes); |
| EXPECT_EQ(callouts, expected); |
| |
| // I2C address not in JSON |
| EXPECT_THROW( |
| getCallouts( |
| "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" |
| "1e78a340.i2c-bus/i2c-14/14-0099", |
| systemTypes), |
| std::invalid_argument); |
| |
| // A bad JSON entry, missing the location code |
| EXPECT_THROW( |
| getCallouts( |
| "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/" |
| "1e78a340.i2c-bus/i2c-0/0-005a", |
| systemTypes), |
| std::runtime_error); |
| } |
| |
| // FSI |
| { |
| auto callouts = getCallouts( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2", |
| systemTypes); |
| |
| std::vector<Callout> expected{{"H", "P1-C19", |
| "/chassis/motherboard/cpu0", "core", |
| "FSI: links: 0-1 dest: proc-0 target"}}; |
| |
| EXPECT_EQ(callouts, expected); |
| |
| // link 9-1 not in JSON |
| EXPECT_THROW( |
| getCallouts( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@09:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@01:00/01:01:00:06/sbefifo2-dev0/occ-hwmon.2", |
| systemTypes), |
| std::invalid_argument); |
| } |
| |
| // FSI-I2C |
| { |
| auto callouts = getCallouts( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@03:00/01:01:00:03/i2c-207/207-0019", |
| systemTypes); |
| |
| std::vector<Callout> expected{ |
| {"H", "P1-C25", "/chassis/motherboard/cpu5", "", |
| "FSI-I2C: links: 0-3 bus: 7 addr: 25 dest: proc-5 target"}, |
| {"M", "P1", "/chassis/motherboard", "", ""}, |
| {"L", "P2", "/chassis/motherboard/bmc", "", ""}}; |
| |
| EXPECT_EQ(callouts, expected); |
| |
| // Bus 2 not in JSON |
| EXPECT_THROW( |
| getCallouts( |
| "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/" |
| "slave@03:00/01:01:00:03/i2c-202/202-0019", |
| systemTypes), |
| std::invalid_argument); |
| } |
| |
| // FSI-SPI |
| { |
| auto callouts = |
| getCallouts("/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@08:00/00:00:00:04/spi_master/" |
| "spi3/spi3.0/spi3.00/nvmem", |
| systemTypes); |
| |
| std::vector<Callout> expected{ |
| {"H", "P1-C19", "/chassis/motherboard/cpu0", "", |
| "FSI-SPI: links: 8 bus: 3 dest: proc-0 target"}}; |
| |
| EXPECT_EQ(callouts, expected); |
| |
| // Bus 7 not in the JSON |
| EXPECT_THROW( |
| getCallouts("/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/" |
| "fsi-master/fsi0/slave@08:00/00:00:00:04/spi_master/" |
| "spi7/spi7.0/spi7.00/nvmem", |
| systemTypes), |
| std::invalid_argument); |
| } |
| } |