1 /** 2 * Copyright © 2024 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 "mock_services.hpp" 18 #include "rail.hpp" 19 #include "services.hpp" 20 #include "standard_device.hpp" 21 22 #include <cstdint> 23 #include <map> 24 #include <memory> 25 #include <optional> 26 #include <string> 27 #include <utility> 28 #include <vector> 29 30 #include <gmock/gmock.h> 31 #include <gtest/gtest.h> 32 33 using namespace phosphor::power::sequencer; 34 35 using ::testing::Return; 36 using ::testing::Throw; 37 38 /** 39 * @class StandardDeviceImpl 40 * 41 * Concrete subclass of the StandardDevice abstract class. 42 * 43 * This subclass is required for two reasons: 44 * - StandardDevice has some pure virtual methods so it cannot be instantiated. 45 * - The pure virtual methods provide the PMBus and GPIO information. Mocking 46 * these makes it possible to test the pgood fault detection algorithm. 47 * 48 * This class is not intended to be used outside of this file. It is 49 * implementation detail for testing the the StandardDevice class. 50 */ 51 class StandardDeviceImpl : public StandardDevice 52 { 53 public: 54 // Specify which compiler-generated methods we want 55 StandardDeviceImpl() = delete; 56 StandardDeviceImpl(const StandardDeviceImpl&) = delete; 57 StandardDeviceImpl(StandardDeviceImpl&&) = delete; 58 StandardDeviceImpl& operator=(const StandardDeviceImpl&) = delete; 59 StandardDeviceImpl& operator=(StandardDeviceImpl&&) = delete; 60 virtual ~StandardDeviceImpl() = default; 61 62 // Constructor just calls StandardDevice constructor 63 explicit StandardDeviceImpl(const std::string& name, 64 std::vector<std::unique_ptr<Rail>> rails) : 65 StandardDevice(name, std::move(rails)) 66 {} 67 68 // Mock pure virtual methods 69 MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services), 70 (override)); 71 MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override)); 72 MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override)); 73 MOCK_METHOD(double, getReadVout, (uint8_t page), (override)); 74 MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override)); 75 76 // Override empty implementation with mock so we can verify it is called 77 MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services), 78 (override)); 79 }; 80 81 /** 82 * Creates a Rail object that checks for a pgood fault using STATUS_VOUT. 83 * 84 * @param name Unique name for the rail 85 * @param isPowerSupplyRail Specifies whether the rail is produced by a 86 power supply 87 * @param pageNum PMBus PAGE number of the rail 88 * @return Rail object 89 */ 90 std::unique_ptr<Rail> createRailStatusVout(const std::string& name, 91 bool isPowerSupplyRail, 92 uint8_t pageNum) 93 { 94 std::optional<std::string> presence{}; 95 std::optional<uint8_t> page{pageNum}; 96 bool checkStatusVout{true}; 97 bool compareVoltageToLimit{false}; 98 std::optional<GPIO> gpio{}; 99 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, 100 checkStatusVout, compareVoltageToLimit, gpio); 101 } 102 103 /** 104 * Creates a Rail object that checks for a pgood fault using a GPIO. 105 * 106 * @param name Unique name for the rail 107 * @param isPowerSupplyRail Specifies whether the rail is produced by a 108 power supply 109 * @param gpio GPIO line to read to determine the pgood status of the rail 110 * @return Rail object 111 */ 112 std::unique_ptr<Rail> createRailGPIO(const std::string& name, 113 bool isPowerSupplyRail, 114 unsigned int gpioLine) 115 { 116 std::optional<std::string> presence{}; 117 std::optional<uint8_t> page{}; 118 bool checkStatusVout{false}; 119 bool compareVoltageToLimit{false}; 120 bool activeLow{false}; 121 std::optional<GPIO> gpio{GPIO{gpioLine, activeLow}}; 122 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, 123 checkStatusVout, compareVoltageToLimit, gpio); 124 } 125 126 /** 127 * Creates a Rail object that checks for a pgood fault using output voltage. 128 * 129 * @param name Unique name for the rail 130 * @param isPowerSupplyRail Specifies whether the rail is produced by a 131 power supply 132 * @param pageNum PMBus PAGE number of the rail 133 * @return Rail object 134 */ 135 std::unique_ptr<Rail> createRailOutputVoltage(const std::string& name, 136 bool isPowerSupplyRail, 137 uint8_t pageNum) 138 { 139 std::optional<std::string> presence{}; 140 std::optional<uint8_t> page{pageNum}; 141 bool checkStatusVout{false}; 142 bool compareVoltageToLimit{true}; 143 std::optional<GPIO> gpio{}; 144 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, 145 checkStatusVout, compareVoltageToLimit, gpio); 146 } 147 148 TEST(StandardDeviceTests, Constructor) 149 { 150 // Empty vector of rails 151 { 152 std::vector<std::unique_ptr<Rail>> rails{}; 153 StandardDeviceImpl device{"xyz_pseq", std::move(rails)}; 154 155 EXPECT_EQ(device.getName(), "xyz_pseq"); 156 EXPECT_TRUE(device.getRails().empty()); 157 } 158 159 // Non-empty vector of rails 160 { 161 std::vector<std::unique_ptr<Rail>> rails{}; 162 rails.emplace_back(createRailGPIO("PSU", true, 3)); 163 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 164 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 165 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 166 167 EXPECT_EQ(device.getName(), "abc_pseq"); 168 EXPECT_EQ(device.getRails().size(), 3); 169 EXPECT_EQ(device.getRails()[0]->getName(), "PSU"); 170 EXPECT_EQ(device.getRails()[1]->getName(), "VDD"); 171 EXPECT_EQ(device.getRails()[2]->getName(), "VIO"); 172 } 173 } 174 175 TEST(StandardDeviceTests, GetName) 176 { 177 std::vector<std::unique_ptr<Rail>> rails{}; 178 StandardDeviceImpl device{"xyz_pseq", std::move(rails)}; 179 180 EXPECT_EQ(device.getName(), "xyz_pseq"); 181 } 182 183 TEST(StandardDeviceTests, GetRails) 184 { 185 // Empty vector of rails 186 { 187 std::vector<std::unique_ptr<Rail>> rails{}; 188 StandardDeviceImpl device{"xyz_pseq", std::move(rails)}; 189 190 EXPECT_TRUE(device.getRails().empty()); 191 } 192 193 // Non-empty vector of rails 194 { 195 std::vector<std::unique_ptr<Rail>> rails{}; 196 rails.emplace_back(createRailGPIO("PSU", true, 3)); 197 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 198 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 199 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 200 201 EXPECT_EQ(device.getRails().size(), 3); 202 EXPECT_EQ(device.getRails()[0]->getName(), "PSU"); 203 EXPECT_EQ(device.getRails()[1]->getName(), "VDD"); 204 EXPECT_EQ(device.getRails()[2]->getName(), "VIO"); 205 } 206 } 207 208 TEST(StandardDeviceTests, FindPgoodFault) 209 { 210 // No rail has a pgood fault 211 { 212 std::vector<std::unique_ptr<Rail>> rails{}; 213 rails.emplace_back(createRailGPIO("PSU", true, 2)); 214 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 215 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 216 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 217 218 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 219 std::vector<int> gpioValues{1, 1, 1}; 220 EXPECT_CALL(device, getGPIOValues) 221 .Times(1) 222 .WillOnce(Return(gpioValues)); 223 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.2)); 224 EXPECT_CALL(device, getVoutUVFaultLimit(5)) 225 .Times(1) 226 .WillOnce(Return(1.1)); 227 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 228 229 MockServices services{}; 230 231 std::string powerSupplyError{}; 232 std::map<std::string, std::string> additionalData{}; 233 std::string error = device.findPgoodFault(services, powerSupplyError, 234 additionalData); 235 EXPECT_TRUE(error.empty()); 236 EXPECT_EQ(additionalData.size(), 0); 237 } 238 239 // First rail has a pgood fault detected via GPIO 240 // Is a PSU rail: No PSU error specified 241 { 242 std::vector<std::unique_ptr<Rail>> rails{}; 243 rails.emplace_back(createRailGPIO("PSU", true, 2)); 244 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 245 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 246 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 247 248 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 249 std::vector<int> gpioValues{1, 1, 0}; 250 EXPECT_CALL(device, getGPIOValues) 251 .Times(1) 252 .WillOnce(Return(gpioValues)); 253 EXPECT_CALL(device, getReadVout(5)).Times(0); 254 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 255 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 256 257 MockServices services{}; 258 EXPECT_CALL(services, 259 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]")) 260 .Times(1); 261 EXPECT_CALL( 262 services, 263 logErrorMsg( 264 "Pgood fault found in rail monitored by device abc_pseq")) 265 .Times(1); 266 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU")) 267 .Times(1); 268 EXPECT_CALL( 269 services, 270 logErrorMsg( 271 "Rail PSU pgood GPIO line offset 2 has inactive value 0")) 272 .Times(1); 273 274 std::string powerSupplyError{}; 275 std::map<std::string, std::string> additionalData{}; 276 std::string error = device.findPgoodFault(services, powerSupplyError, 277 additionalData); 278 EXPECT_EQ(error, 279 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 280 EXPECT_EQ(additionalData.size(), 5); 281 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 282 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]"); 283 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU"); 284 EXPECT_EQ(additionalData["GPIO_LINE"], "2"); 285 EXPECT_EQ(additionalData["GPIO_VALUE"], "0"); 286 } 287 288 // First rail has a pgood fault detected via GPIO 289 // Is a PSU rail: PSU error specified 290 { 291 std::vector<std::unique_ptr<Rail>> rails{}; 292 rails.emplace_back(createRailGPIO("PSU", true, 2)); 293 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 294 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 295 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 296 297 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 298 std::vector<int> gpioValues{1, 1, 0}; 299 EXPECT_CALL(device, getGPIOValues) 300 .Times(1) 301 .WillOnce(Return(gpioValues)); 302 EXPECT_CALL(device, getReadVout(5)).Times(0); 303 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 304 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 305 306 MockServices services{}; 307 EXPECT_CALL(services, 308 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]")) 309 .Times(1); 310 EXPECT_CALL( 311 services, 312 logErrorMsg( 313 "Pgood fault found in rail monitored by device abc_pseq")) 314 .Times(1); 315 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU")) 316 .Times(1); 317 EXPECT_CALL( 318 services, 319 logErrorMsg( 320 "Rail PSU pgood GPIO line offset 2 has inactive value 0")) 321 .Times(1); 322 323 std::string powerSupplyError{"Undervoltage fault: PSU1"}; 324 std::map<std::string, std::string> additionalData{}; 325 std::string error = device.findPgoodFault(services, powerSupplyError, 326 additionalData); 327 EXPECT_EQ(error, powerSupplyError); 328 EXPECT_EQ(additionalData.size(), 5); 329 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 330 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]"); 331 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU"); 332 EXPECT_EQ(additionalData["GPIO_LINE"], "2"); 333 EXPECT_EQ(additionalData["GPIO_VALUE"], "0"); 334 } 335 336 // Second rail has a pgood fault detected via output voltage 337 // Not a PSU rail: PSU error specified 338 { 339 std::vector<std::unique_ptr<Rail>> rails{}; 340 rails.emplace_back(createRailGPIO("PSU", true, 2)); 341 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 342 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 343 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 344 345 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 346 std::vector<int> gpioValues{1, 1, 1}; 347 EXPECT_CALL(device, getGPIOValues) 348 .Times(1) 349 .WillOnce(Return(gpioValues)); 350 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1)); 351 EXPECT_CALL(device, getVoutUVFaultLimit(5)) 352 .Times(1) 353 .WillOnce(Return(1.2)); 354 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 355 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef)); 356 357 MockServices services{}; 358 EXPECT_CALL(services, 359 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 1]")) 360 .Times(1); 361 EXPECT_CALL( 362 services, 363 logErrorMsg( 364 "Pgood fault found in rail monitored by device abc_pseq")) 365 .Times(1); 366 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef")) 367 .Times(1); 368 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD")) 369 .Times(1); 370 EXPECT_CALL( 371 services, 372 logErrorMsg( 373 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V")) 374 .Times(1); 375 376 std::string powerSupplyError{"Undervoltage fault: PSU1"}; 377 std::map<std::string, std::string> additionalData{}; 378 std::string error = device.findPgoodFault(services, powerSupplyError, 379 additionalData); 380 EXPECT_EQ(error, 381 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 382 EXPECT_EQ(additionalData.size(), 6); 383 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 384 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]"); 385 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD"); 386 EXPECT_EQ(additionalData["READ_VOUT"], "1.1"); 387 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2"); 388 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 389 } 390 391 // Third rail has a pgood fault detected via STATUS_VOUT 392 // Device returns 0 GPIO values 393 // Does not halt pgood fault detection because GPIO values not used by rails 394 { 395 std::vector<std::unique_ptr<Rail>> rails{}; 396 rails.emplace_back(createRailStatusVout("PSU", true, 3)); 397 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 398 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 399 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 400 401 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 402 std::vector<int> gpioValues{}; 403 EXPECT_CALL(device, getGPIOValues) 404 .Times(1) 405 .WillOnce(Return(gpioValues)); 406 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00)); 407 EXPECT_CALL(device, getReadVout(5)).Times(0); 408 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 409 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11)); 410 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef)); 411 412 MockServices services{}; 413 EXPECT_CALL( 414 services, 415 logErrorMsg( 416 "Pgood fault found in rail monitored by device abc_pseq")) 417 .Times(1); 418 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef")) 419 .Times(1); 420 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO")) 421 .Times(1); 422 EXPECT_CALL( 423 services, 424 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11")) 425 .Times(1); 426 427 std::string powerSupplyError{}; 428 std::map<std::string, std::string> additionalData{}; 429 std::string error = device.findPgoodFault(services, powerSupplyError, 430 additionalData); 431 EXPECT_EQ(error, 432 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 433 EXPECT_EQ(additionalData.size(), 4); 434 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 435 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO"); 436 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11"); 437 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 438 } 439 440 // Third rail has a pgood fault detected via STATUS_VOUT 441 // Exception occurs trying to obtain GPIO values from device 442 // Does not halt pgood fault detection because GPIO values not used by rails 443 { 444 std::vector<std::unique_ptr<Rail>> rails{}; 445 rails.emplace_back(createRailStatusVout("PSU", true, 3)); 446 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 447 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 448 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 449 450 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 451 EXPECT_CALL(device, getGPIOValues) 452 .Times(1) 453 .WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"})); 454 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00)); 455 EXPECT_CALL(device, getReadVout(5)).Times(0); 456 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 457 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11)); 458 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef)); 459 460 MockServices services{}; 461 EXPECT_CALL( 462 services, 463 logErrorMsg( 464 "Pgood fault found in rail monitored by device abc_pseq")) 465 .Times(1); 466 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef")) 467 .Times(1); 468 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO")) 469 .Times(1); 470 EXPECT_CALL( 471 services, 472 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11")) 473 .Times(1); 474 475 std::string powerSupplyError{}; 476 std::map<std::string, std::string> additionalData{}; 477 std::string error = device.findPgoodFault(services, powerSupplyError, 478 additionalData); 479 EXPECT_EQ(error, 480 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 481 EXPECT_EQ(additionalData.size(), 4); 482 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 483 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO"); 484 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11"); 485 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 486 } 487 488 // All three rails appear to have a pgood fault. Verify third rail is 489 // selected, even though it is last in the power on sequence, because it is 490 // checked using STATUS_VOUT. That check happens before the other checks. 491 { 492 std::vector<std::unique_ptr<Rail>> rails{}; 493 rails.emplace_back(createRailGPIO("PSU", true, 2)); 494 rails.emplace_back(createRailGPIO("VDD", false, 1)); 495 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 496 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 497 498 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 499 std::vector<int> gpioValues{0, 0, 0}; 500 EXPECT_CALL(device, getGPIOValues) 501 .Times(1) 502 .WillOnce(Return(gpioValues)); 503 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11)); 504 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef)); 505 506 MockServices services{}; 507 EXPECT_CALL(services, 508 logInfoMsg("Device abc_pseq GPIO values: [0, 0, 0]")) 509 .Times(1); 510 EXPECT_CALL( 511 services, 512 logErrorMsg( 513 "Pgood fault found in rail monitored by device abc_pseq")) 514 .Times(1); 515 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef")) 516 .Times(1); 517 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO")) 518 .Times(1); 519 EXPECT_CALL( 520 services, 521 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11")) 522 .Times(1); 523 524 std::string powerSupplyError{}; 525 std::map<std::string, std::string> additionalData{}; 526 std::string error = device.findPgoodFault(services, powerSupplyError, 527 additionalData); 528 EXPECT_EQ(error, 529 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 530 EXPECT_EQ(additionalData.size(), 5); 531 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 532 EXPECT_EQ(additionalData["GPIO_VALUES"], "[0, 0, 0]"); 533 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO"); 534 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11"); 535 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 536 } 537 538 // Two rails appear to have a pgood fault. One is found via output voltage 539 // and one is found via a GPIO. Verify the first rail in the sequence with 540 // a fault is selected. 541 { 542 std::vector<std::unique_ptr<Rail>> rails{}; 543 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 544 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 545 rails.emplace_back(createRailGPIO("PSU", true, 2)); 546 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 547 548 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 549 std::vector<int> gpioValues{1, 1, 0}; 550 EXPECT_CALL(device, getGPIOValues) 551 .Times(1) 552 .WillOnce(Return(gpioValues)); 553 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 554 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1)); 555 EXPECT_CALL(device, getVoutUVFaultLimit(5)) 556 .Times(1) 557 .WillOnce(Return(1.2)); 558 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef)); 559 560 MockServices services{}; 561 EXPECT_CALL(services, 562 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]")) 563 .Times(1); 564 EXPECT_CALL( 565 services, 566 logErrorMsg( 567 "Pgood fault found in rail monitored by device abc_pseq")) 568 .Times(1); 569 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef")) 570 .Times(1); 571 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD")) 572 .Times(1); 573 EXPECT_CALL( 574 services, 575 logErrorMsg( 576 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V")) 577 .Times(1); 578 579 std::string powerSupplyError{}; 580 std::map<std::string, std::string> additionalData{}; 581 std::string error = device.findPgoodFault(services, powerSupplyError, 582 additionalData); 583 EXPECT_EQ(error, 584 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 585 EXPECT_EQ(additionalData.size(), 6); 586 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 587 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]"); 588 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD"); 589 EXPECT_EQ(additionalData["READ_VOUT"], "1.1"); 590 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2"); 591 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 592 } 593 594 // Exception is thrown during pgood fault detection 595 { 596 std::vector<std::unique_ptr<Rail>> rails{}; 597 rails.emplace_back(createRailGPIO("PSU", true, 2)); 598 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 599 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 600 StandardDeviceImpl device{"abc_pseq", std::move(rails)}; 601 602 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 603 std::vector<int> gpioValues{1, 1, 1}; 604 EXPECT_CALL(device, getGPIOValues) 605 .Times(1) 606 .WillOnce(Return(gpioValues)); 607 EXPECT_CALL(device, getReadVout(5)).Times(0); 608 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 609 EXPECT_CALL(device, getStatusVout(7)) 610 .Times(1) 611 .WillOnce(Throw(std::runtime_error{"File does not exist"})); 612 613 MockServices services{}; 614 615 std::string powerSupplyError{}; 616 std::map<std::string, std::string> additionalData{}; 617 try 618 { 619 device.findPgoodFault(services, powerSupplyError, additionalData); 620 ADD_FAILURE() << "Should not have reached this line."; 621 } 622 catch (const std::exception& e) 623 { 624 EXPECT_STREQ( 625 e.what(), 626 "Unable to determine if a pgood fault occurred in device abc_pseq: " 627 "Unable to read STATUS_VOUT value for rail VIO: " 628 "File does not exist"); 629 } 630 } 631 } 632