1 /** 2 * Copyright © 2019 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 #include "extensions/openpower-pels/src.hpp" 17 #include "mocks.hpp" 18 #include "pel_utils.hpp" 19 20 #include <fstream> 21 22 #include <gtest/gtest.h> 23 24 using namespace openpower::pels; 25 using ::testing::NiceMock; 26 using ::testing::Return; 27 namespace fs = std::filesystem; 28 29 const auto testRegistry = R"( 30 { 31 "PELs": 32 [ 33 { 34 "Name": "xyz.openbmc_project.Error.Test", 35 "Subsystem": "bmc_firmware", 36 "SRC": 37 { 38 "ReasonCode": "0xABCD", 39 "Words6To9": 40 { 41 "6": 42 { 43 "Description": "Component ID", 44 "AdditionalDataPropSource": "COMPID" 45 }, 46 "7": 47 { 48 "Description": "Failure count", 49 "AdditionalDataPropSource": "FREQUENCY" 50 }, 51 "8": 52 { 53 "Description": "Time period", 54 "AdditionalDataPropSource": "DURATION" 55 }, 56 "9": 57 { 58 "Description": "Error code", 59 "AdditionalDataPropSource": "ERRORCODE" 60 } 61 } 62 }, 63 "Documentation": 64 { 65 "Description": "A Component Fault", 66 "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4", 67 "MessageArgSources": 68 [ 69 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9" 70 ] 71 } 72 } 73 ] 74 } 75 )"; 76 77 class SRCTest : public ::testing::Test 78 { 79 protected: 80 static void SetUpTestCase() 81 { 82 char path[] = "/tmp/srctestXXXXXX"; 83 regDir = mkdtemp(path); 84 } 85 86 static void TearDownTestCase() 87 { 88 fs::remove_all(regDir); 89 } 90 91 static std::string writeData(const char* data) 92 { 93 fs::path path = regDir / "registry.json"; 94 std::ofstream stream{path}; 95 stream << data; 96 return path; 97 } 98 99 static fs::path regDir; 100 }; 101 102 fs::path SRCTest::regDir{}; 103 104 TEST_F(SRCTest, UnflattenFlattenTestNoCallouts) 105 { 106 auto data = pelDataFactory(TestPELType::primarySRCSection); 107 108 Stream stream{data}; 109 SRC src{stream}; 110 111 EXPECT_TRUE(src.valid()); 112 113 EXPECT_EQ(src.header().id, 0x5053); 114 EXPECT_EQ(src.header().size, 0x50); 115 EXPECT_EQ(src.header().version, 0x01); 116 EXPECT_EQ(src.header().subType, 0x01); 117 EXPECT_EQ(src.header().componentID, 0x0202); 118 119 EXPECT_EQ(src.version(), 0x02); 120 EXPECT_EQ(src.flags(), 0x00); 121 EXPECT_EQ(src.hexWordCount(), 9); 122 EXPECT_EQ(src.size(), 0x48); 123 124 const auto& hexwords = src.hexwordData(); 125 EXPECT_EQ(0x02020255, hexwords[0]); 126 EXPECT_EQ(0x03030310, hexwords[1]); 127 EXPECT_EQ(0x04040404, hexwords[2]); 128 EXPECT_EQ(0x05050505, hexwords[3]); 129 EXPECT_EQ(0x06060606, hexwords[4]); 130 EXPECT_EQ(0x07070707, hexwords[5]); 131 EXPECT_EQ(0x08080808, hexwords[6]); 132 EXPECT_EQ(0x09090909, hexwords[7]); 133 134 EXPECT_EQ(src.asciiString(), "BD8D5678 "); 135 EXPECT_FALSE(src.callouts()); 136 137 // Flatten 138 std::vector<uint8_t> newData; 139 Stream newStream{newData}; 140 141 src.flatten(newStream); 142 EXPECT_EQ(data, newData); 143 } 144 145 TEST_F(SRCTest, UnflattenFlattenTest2Callouts) 146 { 147 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts); 148 149 Stream stream{data}; 150 SRC src{stream}; 151 152 EXPECT_TRUE(src.valid()); 153 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC. 154 155 // Spot check the SRC fields, but they're the same as above 156 EXPECT_EQ(src.asciiString(), "BD8D5678 "); 157 158 // There should be 2 callouts 159 const auto& calloutsSection = src.callouts(); 160 ASSERT_TRUE(calloutsSection); 161 const auto& callouts = calloutsSection->callouts(); 162 EXPECT_EQ(callouts.size(), 2); 163 164 // spot check that each callout has the right substructures 165 EXPECT_TRUE(callouts.front()->fruIdentity()); 166 EXPECT_FALSE(callouts.front()->pceIdentity()); 167 EXPECT_FALSE(callouts.front()->mru()); 168 169 EXPECT_TRUE(callouts.back()->fruIdentity()); 170 EXPECT_TRUE(callouts.back()->pceIdentity()); 171 EXPECT_TRUE(callouts.back()->mru()); 172 173 // Flatten 174 std::vector<uint8_t> newData; 175 Stream newStream{newData}; 176 177 src.flatten(newStream); 178 EXPECT_EQ(data, newData); 179 } 180 181 // Create an SRC from the message registry 182 TEST_F(SRCTest, CreateTestNoCallouts) 183 { 184 message::Entry entry; 185 entry.src.type = 0xBD; 186 entry.src.reasonCode = 0xABCD; 187 entry.subsystem = 0x42; 188 entry.src.powerFault = true; 189 entry.src.hexwordADFields = {{5, "TEST1"}, // Not a user defined word 190 {6, "TEST1"}, 191 {7, "TEST2"}, 192 {8, "TEST3"}, 193 {9, "TEST4"}}; 194 195 // Values for the SRC words pointed to above 196 std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678", 197 "TEST3=0XDEF", "TEST4=Z"}; 198 AdditionalData ad{adData}; 199 NiceMock<MockDataInterface> dataIface; 200 201 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD")); 202 203 SRC src{entry, ad, dataIface}; 204 205 EXPECT_TRUE(src.valid()); 206 EXPECT_TRUE(src.isPowerFaultEvent()); 207 EXPECT_EQ(src.size(), baseSRCSize); 208 209 const auto& hexwords = src.hexwordData(); 210 211 // The spec always refers to SRC words 2 - 9, and as the hexwordData() 212 // array index starts at 0 use the math in the [] below to make it easier 213 // to tell what is being accessed. 214 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status 215 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type 216 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format 217 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position 218 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN 219 220 // Validate more fields here as the code starts filling them in. 221 222 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents 223 EXPECT_EQ(hexwords[5 - 2], 0); 224 225 // The user defined hex word fields specifed in the additional data. 226 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1 227 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2 228 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3 229 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z' 230 231 EXPECT_EQ(src.asciiString(), "BD42ABCD "); 232 233 // No callouts 234 EXPECT_FALSE(src.callouts()); 235 236 // May as well spot check the flatten/unflatten 237 std::vector<uint8_t> data; 238 Stream stream{data}; 239 src.flatten(stream); 240 241 stream.offset(0); 242 SRC newSRC{stream}; 243 244 EXPECT_TRUE(newSRC.valid()); 245 EXPECT_EQ(newSRC.isPowerFaultEvent(), src.isPowerFaultEvent()); 246 EXPECT_EQ(newSRC.asciiString(), src.asciiString()); 247 EXPECT_FALSE(newSRC.callouts()); 248 } 249 250 // Test when the CCIN string isn't a 4 character number 251 TEST_F(SRCTest, BadCCINTest) 252 { 253 message::Entry entry; 254 entry.src.type = 0xBD; 255 entry.src.reasonCode = 0xABCD; 256 entry.subsystem = 0x42; 257 entry.src.powerFault = false; 258 259 std::vector<std::string> adData{}; 260 AdditionalData ad{adData}; 261 NiceMock<MockDataInterface> dataIface; 262 263 // First it isn't a number, then it is too long, 264 // then it is empty. 265 EXPECT_CALL(dataIface, getMotherboardCCIN) 266 .WillOnce(Return("X")) 267 .WillOnce(Return("12345")) 268 .WillOnce(Return("")); 269 270 // The CCIN in the first half should still be 0 each time. 271 { 272 SRC src{entry, ad, dataIface}; 273 EXPECT_TRUE(src.valid()); 274 const auto& hexwords = src.hexwordData(); 275 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 276 } 277 278 { 279 SRC src{entry, ad, dataIface}; 280 EXPECT_TRUE(src.valid()); 281 const auto& hexwords = src.hexwordData(); 282 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 283 } 284 285 { 286 SRC src{entry, ad, dataIface}; 287 EXPECT_TRUE(src.valid()); 288 const auto& hexwords = src.hexwordData(); 289 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 290 } 291 } 292 293 // Test the getErrorDetails function 294 TEST_F(SRCTest, MessageSubstitutionTest) 295 { 296 auto path = SRCTest::writeData(testRegistry); 297 message::Registry registry{path}; 298 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode); 299 300 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4", 301 "DURATION=30", "ERRORCODE=0x01ABCDEF"}; 302 AdditionalData ad{adData}; 303 NiceMock<MockDataInterface> dataIface; 304 305 SRC src{*entry, ad, dataIface}; 306 EXPECT_TRUE(src.valid()); 307 308 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message); 309 ASSERT_TRUE(errorDetails); 310 EXPECT_EQ( 311 errorDetails.value(), 312 "Comp 0x1 failed 0x4 times over 0x1E secs with ErrorCode 0x1ABCDEF"); 313 } 314