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, uint8_t bus, 64 uint16_t address, 65 const std::string& powerControlGPIOName, 66 const std::string& powerGoodGPIOName, 67 std::vector<std::unique_ptr<Rail>> rails) : 68 StandardDevice(name, bus, address, powerControlGPIOName, 69 powerGoodGPIOName, std::move(rails)) 70 {} 71 72 // Mock pure virtual methods 73 MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services), 74 (override)); 75 MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override)); 76 MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override)); 77 MOCK_METHOD(double, getReadVout, (uint8_t page), (override)); 78 MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override)); 79 80 // Override empty implementation with mock so we can verify it is called 81 MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services), 82 (override)); 83 }; 84 85 /** 86 * Creates a Rail object that checks for a pgood fault using STATUS_VOUT. 87 * 88 * @param name Unique name for the rail 89 * @param isPowerSupplyRail Specifies whether the rail is produced by a 90 power supply 91 * @param pageNum PMBus PAGE number of the rail 92 * @return Rail object 93 */ 94 std::unique_ptr<Rail> createRailStatusVout( 95 const std::string& name, bool isPowerSupplyRail, uint8_t pageNum) 96 { 97 std::optional<std::string> presence{}; 98 std::optional<uint8_t> page{pageNum}; 99 bool checkStatusVout{true}; 100 bool compareVoltageToLimit{false}; 101 std::optional<GPIO> gpio{}; 102 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, 103 checkStatusVout, compareVoltageToLimit, gpio); 104 } 105 106 /** 107 * Creates a Rail object that checks for a pgood fault using a GPIO. 108 * 109 * @param name Unique name for the rail 110 * @param isPowerSupplyRail Specifies whether the rail is produced by a 111 power supply 112 * @param gpio GPIO line to read to determine the pgood status of the rail 113 * @return Rail object 114 */ 115 std::unique_ptr<Rail> createRailGPIO( 116 const std::string& name, bool isPowerSupplyRail, unsigned int gpioLine) 117 { 118 std::optional<std::string> presence{}; 119 std::optional<uint8_t> page{}; 120 bool checkStatusVout{false}; 121 bool compareVoltageToLimit{false}; 122 bool activeLow{false}; 123 std::optional<GPIO> gpio{GPIO{gpioLine, activeLow}}; 124 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, 125 checkStatusVout, compareVoltageToLimit, gpio); 126 } 127 128 /** 129 * Creates a Rail object that checks for a pgood fault using output voltage. 130 * 131 * @param name Unique name for the rail 132 * @param isPowerSupplyRail Specifies whether the rail is produced by a 133 power supply 134 * @param pageNum PMBus PAGE number of the rail 135 * @return Rail object 136 */ 137 std::unique_ptr<Rail> createRailOutputVoltage( 138 const std::string& name, bool isPowerSupplyRail, uint8_t pageNum) 139 { 140 std::optional<std::string> presence{}; 141 std::optional<uint8_t> page{pageNum}; 142 bool checkStatusVout{false}; 143 bool compareVoltageToLimit{true}; 144 std::optional<GPIO> gpio{}; 145 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, 146 checkStatusVout, compareVoltageToLimit, gpio); 147 } 148 149 TEST(StandardDeviceTests, Constructor) 150 { 151 // Empty vector of rails 152 { 153 std::string name{"xyz_pseq"}; 154 uint8_t bus{3}; 155 uint16_t address{0x72}; 156 std::string powerControlGPIOName{"power-chassis-control"}; 157 std::string powerGoodGPIOName{"power-chassis-good"}; 158 std::vector<std::unique_ptr<Rail>> rails{}; 159 StandardDeviceImpl device{ 160 name, 161 bus, 162 address, 163 powerControlGPIOName, 164 powerGoodGPIOName, 165 std::move(rails)}; 166 167 EXPECT_EQ(device.getName(), name); 168 EXPECT_EQ(device.getBus(), bus); 169 EXPECT_EQ(device.getAddress(), address); 170 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName); 171 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName); 172 EXPECT_TRUE(device.getRails().empty()); 173 } 174 175 // Non-empty vector of rails 176 { 177 std::string name{"abc_pseq"}; 178 uint8_t bus{0}; 179 uint16_t address{0x23}; 180 std::string powerControlGPIOName{"power-chassis-control"}; 181 std::string powerGoodGPIOName{"power-chassis-good"}; 182 std::vector<std::unique_ptr<Rail>> rails{}; 183 rails.emplace_back(createRailGPIO("PSU", true, 3)); 184 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 185 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 186 StandardDeviceImpl device{ 187 name, 188 bus, 189 address, 190 powerControlGPIOName, 191 powerGoodGPIOName, 192 std::move(rails)}; 193 194 EXPECT_EQ(device.getName(), name); 195 EXPECT_EQ(device.getBus(), bus); 196 EXPECT_EQ(device.getAddress(), address); 197 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName); 198 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName); 199 EXPECT_EQ(device.getRails().size(), 3); 200 EXPECT_EQ(device.getRails()[0]->getName(), "PSU"); 201 EXPECT_EQ(device.getRails()[1]->getName(), "VDD"); 202 EXPECT_EQ(device.getRails()[2]->getName(), "VIO"); 203 } 204 } 205 206 TEST(StandardDeviceTests, GetName) 207 { 208 std::string name{"xyz_pseq"}; 209 uint8_t bus{0}; 210 uint16_t address{0x23}; 211 std::string powerControlGPIOName{"power-chassis-control"}; 212 std::string powerGoodGPIOName{"power-chassis-good"}; 213 std::vector<std::unique_ptr<Rail>> rails{}; 214 StandardDeviceImpl device{ 215 name, 216 bus, 217 address, 218 powerControlGPIOName, 219 powerGoodGPIOName, 220 std::move(rails)}; 221 222 EXPECT_EQ(device.getName(), name); 223 } 224 225 TEST(StandardDeviceTests, GetBus) 226 { 227 std::string name{"abc_pseq"}; 228 uint8_t bus{1}; 229 uint16_t address{0x23}; 230 std::string powerControlGPIOName{"power-chassis-control"}; 231 std::string powerGoodGPIOName{"power-chassis-good"}; 232 std::vector<std::unique_ptr<Rail>> rails{}; 233 StandardDeviceImpl device{ 234 name, 235 bus, 236 address, 237 powerControlGPIOName, 238 powerGoodGPIOName, 239 std::move(rails)}; 240 241 EXPECT_EQ(device.getBus(), bus); 242 } 243 244 TEST(StandardDeviceTests, GetAddress) 245 { 246 std::string name{"abc_pseq"}; 247 uint8_t bus{1}; 248 uint16_t address{0x24}; 249 std::string powerControlGPIOName{"power-chassis-control"}; 250 std::string powerGoodGPIOName{"power-chassis-good"}; 251 std::vector<std::unique_ptr<Rail>> rails{}; 252 StandardDeviceImpl device{ 253 name, 254 bus, 255 address, 256 powerControlGPIOName, 257 powerGoodGPIOName, 258 std::move(rails)}; 259 260 EXPECT_EQ(device.getAddress(), address); 261 } 262 263 TEST(StandardDeviceTests, GetPowerControlGPIOName) 264 { 265 std::string name{"xyz_pseq"}; 266 uint8_t bus{0}; 267 uint16_t address{0x23}; 268 std::string powerControlGPIOName{"power-on"}; 269 std::string powerGoodGPIOName{"chassis-pgood"}; 270 std::vector<std::unique_ptr<Rail>> rails{}; 271 StandardDeviceImpl device{ 272 name, 273 bus, 274 address, 275 powerControlGPIOName, 276 powerGoodGPIOName, 277 std::move(rails)}; 278 279 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName); 280 } 281 282 TEST(StandardDeviceTests, GetPowerGoodGPIOName) 283 { 284 std::string name{"xyz_pseq"}; 285 uint8_t bus{0}; 286 uint16_t address{0x23}; 287 std::string powerControlGPIOName{"power-on"}; 288 std::string powerGoodGPIOName{"chassis-pgood"}; 289 std::vector<std::unique_ptr<Rail>> rails{}; 290 StandardDeviceImpl device{ 291 name, 292 bus, 293 address, 294 powerControlGPIOName, 295 powerGoodGPIOName, 296 std::move(rails)}; 297 298 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName); 299 } 300 301 TEST(StandardDeviceTests, GetRails) 302 { 303 // Empty vector of rails 304 { 305 std::string name{"xyz_pseq"}; 306 uint8_t bus{0}; 307 uint16_t address{0x23}; 308 std::string powerControlGPIOName{"power-chassis-control"}; 309 std::string powerGoodGPIOName{"power-chassis-good"}; 310 std::vector<std::unique_ptr<Rail>> rails{}; 311 StandardDeviceImpl device{ 312 name, 313 bus, 314 address, 315 powerControlGPIOName, 316 powerGoodGPIOName, 317 std::move(rails)}; 318 319 EXPECT_TRUE(device.getRails().empty()); 320 } 321 322 // Non-empty vector of rails 323 { 324 std::string name{"abc_pseq"}; 325 uint8_t bus{0}; 326 uint16_t address{0x23}; 327 std::string powerControlGPIOName{"power-chassis-control"}; 328 std::string powerGoodGPIOName{"power-chassis-good"}; 329 std::vector<std::unique_ptr<Rail>> rails{}; 330 rails.emplace_back(createRailGPIO("PSU", true, 3)); 331 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 332 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 333 StandardDeviceImpl device{ 334 name, 335 bus, 336 address, 337 powerControlGPIOName, 338 powerGoodGPIOName, 339 std::move(rails)}; 340 341 EXPECT_EQ(device.getRails().size(), 3); 342 EXPECT_EQ(device.getRails()[0]->getName(), "PSU"); 343 EXPECT_EQ(device.getRails()[1]->getName(), "VDD"); 344 EXPECT_EQ(device.getRails()[2]->getName(), "VIO"); 345 } 346 } 347 348 TEST(StandardDeviceTests, FindPgoodFault) 349 { 350 // No rail has a pgood fault 351 { 352 std::string name{"abc_pseq"}; 353 uint8_t bus{0}; 354 uint16_t address{0x23}; 355 std::string powerControlGPIOName{"power-chassis-control"}; 356 std::string powerGoodGPIOName{"power-chassis-good"}; 357 std::vector<std::unique_ptr<Rail>> rails{}; 358 rails.emplace_back(createRailGPIO("PSU", true, 2)); 359 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 360 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 361 StandardDeviceImpl device{ 362 name, 363 bus, 364 address, 365 powerControlGPIOName, 366 powerGoodGPIOName, 367 std::move(rails)}; 368 369 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 370 std::vector<int> gpioValues{1, 1, 1}; 371 EXPECT_CALL(device, getGPIOValues) 372 .Times(1) 373 .WillOnce(Return(gpioValues)); 374 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.2)); 375 EXPECT_CALL(device, getVoutUVFaultLimit(5)) 376 .Times(1) 377 .WillOnce(Return(1.1)); 378 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 379 380 MockServices services{}; 381 382 std::string powerSupplyError{}; 383 std::map<std::string, std::string> additionalData{}; 384 std::string error = 385 device.findPgoodFault(services, powerSupplyError, additionalData); 386 EXPECT_TRUE(error.empty()); 387 EXPECT_EQ(additionalData.size(), 0); 388 } 389 390 // First rail has a pgood fault detected via GPIO 391 // Is a PSU rail: No PSU error specified 392 { 393 std::string name{"abc_pseq"}; 394 uint8_t bus{0}; 395 uint16_t address{0x23}; 396 std::string powerControlGPIOName{"power-chassis-control"}; 397 std::string powerGoodGPIOName{"power-chassis-good"}; 398 std::vector<std::unique_ptr<Rail>> rails{}; 399 rails.emplace_back(createRailGPIO("PSU", true, 2)); 400 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 401 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 402 StandardDeviceImpl device{ 403 name, 404 bus, 405 address, 406 powerControlGPIOName, 407 powerGoodGPIOName, 408 std::move(rails)}; 409 410 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 411 std::vector<int> gpioValues{1, 1, 0}; 412 EXPECT_CALL(device, getGPIOValues) 413 .Times(1) 414 .WillOnce(Return(gpioValues)); 415 EXPECT_CALL(device, getReadVout(5)).Times(0); 416 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 417 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 418 419 MockServices services{}; 420 EXPECT_CALL(services, 421 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]")) 422 .Times(1); 423 EXPECT_CALL( 424 services, 425 logErrorMsg( 426 "Pgood fault found in rail monitored by device abc_pseq")) 427 .Times(1); 428 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU")) 429 .Times(1); 430 EXPECT_CALL( 431 services, 432 logErrorMsg( 433 "Rail PSU pgood GPIO line offset 2 has inactive value 0")) 434 .Times(1); 435 436 std::string powerSupplyError{}; 437 std::map<std::string, std::string> additionalData{}; 438 std::string error = 439 device.findPgoodFault(services, powerSupplyError, additionalData); 440 EXPECT_EQ(error, 441 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 442 EXPECT_EQ(additionalData.size(), 5); 443 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 444 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]"); 445 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU"); 446 EXPECT_EQ(additionalData["GPIO_LINE"], "2"); 447 EXPECT_EQ(additionalData["GPIO_VALUE"], "0"); 448 } 449 450 // First rail has a pgood fault detected via GPIO 451 // Is a PSU rail: PSU error specified 452 { 453 std::string name{"abc_pseq"}; 454 uint8_t bus{0}; 455 uint16_t address{0x23}; 456 std::string powerControlGPIOName{"power-chassis-control"}; 457 std::string powerGoodGPIOName{"power-chassis-good"}; 458 std::vector<std::unique_ptr<Rail>> rails{}; 459 rails.emplace_back(createRailGPIO("PSU", true, 2)); 460 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 461 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 462 StandardDeviceImpl device{ 463 name, 464 bus, 465 address, 466 powerControlGPIOName, 467 powerGoodGPIOName, 468 std::move(rails)}; 469 470 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 471 std::vector<int> gpioValues{1, 1, 0}; 472 EXPECT_CALL(device, getGPIOValues) 473 .Times(1) 474 .WillOnce(Return(gpioValues)); 475 EXPECT_CALL(device, getReadVout(5)).Times(0); 476 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 477 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 478 479 MockServices services{}; 480 EXPECT_CALL(services, 481 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]")) 482 .Times(1); 483 EXPECT_CALL( 484 services, 485 logErrorMsg( 486 "Pgood fault found in rail monitored by device abc_pseq")) 487 .Times(1); 488 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU")) 489 .Times(1); 490 EXPECT_CALL( 491 services, 492 logErrorMsg( 493 "Rail PSU pgood GPIO line offset 2 has inactive value 0")) 494 .Times(1); 495 496 std::string powerSupplyError{"Undervoltage fault: PSU1"}; 497 std::map<std::string, std::string> additionalData{}; 498 std::string error = 499 device.findPgoodFault(services, powerSupplyError, additionalData); 500 EXPECT_EQ(error, powerSupplyError); 501 EXPECT_EQ(additionalData.size(), 5); 502 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 503 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]"); 504 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU"); 505 EXPECT_EQ(additionalData["GPIO_LINE"], "2"); 506 EXPECT_EQ(additionalData["GPIO_VALUE"], "0"); 507 } 508 509 // Second rail has a pgood fault detected via output voltage 510 // Not a PSU rail: PSU error specified 511 { 512 std::string name{"abc_pseq"}; 513 uint8_t bus{0}; 514 uint16_t address{0x23}; 515 std::string powerControlGPIOName{"power-chassis-control"}; 516 std::string powerGoodGPIOName{"power-chassis-good"}; 517 std::vector<std::unique_ptr<Rail>> rails{}; 518 rails.emplace_back(createRailGPIO("PSU", true, 2)); 519 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 520 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 521 StandardDeviceImpl device{ 522 name, 523 bus, 524 address, 525 powerControlGPIOName, 526 powerGoodGPIOName, 527 std::move(rails)}; 528 529 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 530 std::vector<int> gpioValues{1, 1, 1}; 531 EXPECT_CALL(device, getGPIOValues) 532 .Times(1) 533 .WillOnce(Return(gpioValues)); 534 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1)); 535 EXPECT_CALL(device, getVoutUVFaultLimit(5)) 536 .Times(1) 537 .WillOnce(Return(1.2)); 538 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 539 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef)); 540 541 MockServices services{}; 542 EXPECT_CALL(services, 543 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 1]")) 544 .Times(1); 545 EXPECT_CALL( 546 services, 547 logErrorMsg( 548 "Pgood fault found in rail monitored by device abc_pseq")) 549 .Times(1); 550 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef")) 551 .Times(1); 552 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD")) 553 .Times(1); 554 EXPECT_CALL( 555 services, 556 logErrorMsg( 557 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V")) 558 .Times(1); 559 560 std::string powerSupplyError{"Undervoltage fault: PSU1"}; 561 std::map<std::string, std::string> additionalData{}; 562 std::string error = 563 device.findPgoodFault(services, powerSupplyError, additionalData); 564 EXPECT_EQ(error, 565 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 566 EXPECT_EQ(additionalData.size(), 6); 567 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 568 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]"); 569 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD"); 570 EXPECT_EQ(additionalData["READ_VOUT"], "1.1"); 571 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2"); 572 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 573 } 574 575 // Third rail has a pgood fault detected via STATUS_VOUT 576 // Device returns 0 GPIO values 577 // Does not halt pgood fault detection because GPIO values not used by rails 578 { 579 std::string name{"abc_pseq"}; 580 uint8_t bus{0}; 581 uint16_t address{0x23}; 582 std::string powerControlGPIOName{"power-chassis-control"}; 583 std::string powerGoodGPIOName{"power-chassis-good"}; 584 std::vector<std::unique_ptr<Rail>> rails{}; 585 rails.emplace_back(createRailStatusVout("PSU", true, 3)); 586 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 587 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 588 StandardDeviceImpl device{ 589 name, 590 bus, 591 address, 592 powerControlGPIOName, 593 powerGoodGPIOName, 594 std::move(rails)}; 595 596 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 597 std::vector<int> gpioValues{}; 598 EXPECT_CALL(device, getGPIOValues) 599 .Times(1) 600 .WillOnce(Return(gpioValues)); 601 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00)); 602 EXPECT_CALL(device, getReadVout(5)).Times(0); 603 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 604 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11)); 605 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef)); 606 607 MockServices services{}; 608 EXPECT_CALL( 609 services, 610 logErrorMsg( 611 "Pgood fault found in rail monitored by device abc_pseq")) 612 .Times(1); 613 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef")) 614 .Times(1); 615 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO")) 616 .Times(1); 617 EXPECT_CALL( 618 services, 619 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11")) 620 .Times(1); 621 622 std::string powerSupplyError{}; 623 std::map<std::string, std::string> additionalData{}; 624 std::string error = 625 device.findPgoodFault(services, powerSupplyError, additionalData); 626 EXPECT_EQ(error, 627 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 628 EXPECT_EQ(additionalData.size(), 4); 629 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 630 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO"); 631 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11"); 632 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 633 } 634 635 // Third rail has a pgood fault detected via STATUS_VOUT 636 // Exception occurs trying to obtain GPIO values from device 637 // Does not halt pgood fault detection because GPIO values not used by rails 638 { 639 std::string name{"abc_pseq"}; 640 uint8_t bus{0}; 641 uint16_t address{0x23}; 642 std::string powerControlGPIOName{"power-chassis-control"}; 643 std::string powerGoodGPIOName{"power-chassis-good"}; 644 std::vector<std::unique_ptr<Rail>> rails{}; 645 rails.emplace_back(createRailStatusVout("PSU", true, 3)); 646 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 647 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 648 StandardDeviceImpl device{ 649 name, 650 bus, 651 address, 652 powerControlGPIOName, 653 powerGoodGPIOName, 654 std::move(rails)}; 655 656 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 657 EXPECT_CALL(device, getGPIOValues) 658 .Times(1) 659 .WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"})); 660 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00)); 661 EXPECT_CALL(device, getReadVout(5)).Times(0); 662 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 663 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11)); 664 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef)); 665 666 MockServices services{}; 667 EXPECT_CALL( 668 services, 669 logErrorMsg( 670 "Pgood fault found in rail monitored by device abc_pseq")) 671 .Times(1); 672 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef")) 673 .Times(1); 674 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO")) 675 .Times(1); 676 EXPECT_CALL( 677 services, 678 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11")) 679 .Times(1); 680 681 std::string powerSupplyError{}; 682 std::map<std::string, std::string> additionalData{}; 683 std::string error = 684 device.findPgoodFault(services, powerSupplyError, additionalData); 685 EXPECT_EQ(error, 686 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 687 EXPECT_EQ(additionalData.size(), 4); 688 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 689 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO"); 690 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11"); 691 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 692 } 693 694 // All three rails appear to have a pgood fault. Verify third rail is 695 // selected, even though it is last in the power on sequence, because it is 696 // checked using STATUS_VOUT. That check happens before the other checks. 697 { 698 std::string name{"abc_pseq"}; 699 uint8_t bus{0}; 700 uint16_t address{0x23}; 701 std::string powerControlGPIOName{"power-chassis-control"}; 702 std::string powerGoodGPIOName{"power-chassis-good"}; 703 std::vector<std::unique_ptr<Rail>> rails{}; 704 rails.emplace_back(createRailGPIO("PSU", true, 2)); 705 rails.emplace_back(createRailGPIO("VDD", false, 1)); 706 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 707 StandardDeviceImpl device{ 708 name, 709 bus, 710 address, 711 powerControlGPIOName, 712 powerGoodGPIOName, 713 std::move(rails)}; 714 715 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 716 std::vector<int> gpioValues{0, 0, 0}; 717 EXPECT_CALL(device, getGPIOValues) 718 .Times(1) 719 .WillOnce(Return(gpioValues)); 720 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11)); 721 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef)); 722 723 MockServices services{}; 724 EXPECT_CALL(services, 725 logInfoMsg("Device abc_pseq GPIO values: [0, 0, 0]")) 726 .Times(1); 727 EXPECT_CALL( 728 services, 729 logErrorMsg( 730 "Pgood fault found in rail monitored by device abc_pseq")) 731 .Times(1); 732 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef")) 733 .Times(1); 734 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO")) 735 .Times(1); 736 EXPECT_CALL( 737 services, 738 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11")) 739 .Times(1); 740 741 std::string powerSupplyError{}; 742 std::map<std::string, std::string> additionalData{}; 743 std::string error = 744 device.findPgoodFault(services, powerSupplyError, additionalData); 745 EXPECT_EQ(error, 746 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 747 EXPECT_EQ(additionalData.size(), 5); 748 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 749 EXPECT_EQ(additionalData["GPIO_VALUES"], "[0, 0, 0]"); 750 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO"); 751 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11"); 752 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 753 } 754 755 // Two rails appear to have a pgood fault. One is found via output voltage 756 // and one is found via a GPIO. Verify the first rail in the sequence with 757 // a fault is selected. 758 { 759 std::string name{"abc_pseq"}; 760 uint8_t bus{0}; 761 uint16_t address{0x23}; 762 std::string powerControlGPIOName{"power-chassis-control"}; 763 std::string powerGoodGPIOName{"power-chassis-good"}; 764 std::vector<std::unique_ptr<Rail>> rails{}; 765 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 766 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 767 rails.emplace_back(createRailGPIO("PSU", true, 2)); 768 StandardDeviceImpl device{ 769 name, 770 bus, 771 address, 772 powerControlGPIOName, 773 powerGoodGPIOName, 774 std::move(rails)}; 775 776 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 777 std::vector<int> gpioValues{1, 1, 0}; 778 EXPECT_CALL(device, getGPIOValues) 779 .Times(1) 780 .WillOnce(Return(gpioValues)); 781 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); 782 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1)); 783 EXPECT_CALL(device, getVoutUVFaultLimit(5)) 784 .Times(1) 785 .WillOnce(Return(1.2)); 786 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef)); 787 788 MockServices services{}; 789 EXPECT_CALL(services, 790 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]")) 791 .Times(1); 792 EXPECT_CALL( 793 services, 794 logErrorMsg( 795 "Pgood fault found in rail monitored by device abc_pseq")) 796 .Times(1); 797 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef")) 798 .Times(1); 799 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD")) 800 .Times(1); 801 EXPECT_CALL( 802 services, 803 logErrorMsg( 804 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V")) 805 .Times(1); 806 807 std::string powerSupplyError{}; 808 std::map<std::string, std::string> additionalData{}; 809 std::string error = 810 device.findPgoodFault(services, powerSupplyError, additionalData); 811 EXPECT_EQ(error, 812 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); 813 EXPECT_EQ(additionalData.size(), 6); 814 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); 815 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]"); 816 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD"); 817 EXPECT_EQ(additionalData["READ_VOUT"], "1.1"); 818 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2"); 819 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); 820 } 821 822 // Exception is thrown during pgood fault detection 823 { 824 std::string name{"abc_pseq"}; 825 uint8_t bus{0}; 826 uint16_t address{0x23}; 827 std::string powerControlGPIOName{"power-chassis-control"}; 828 std::string powerGoodGPIOName{"power-chassis-good"}; 829 std::vector<std::unique_ptr<Rail>> rails{}; 830 rails.emplace_back(createRailGPIO("PSU", true, 2)); 831 rails.emplace_back(createRailOutputVoltage("VDD", false, 5)); 832 rails.emplace_back(createRailStatusVout("VIO", false, 7)); 833 StandardDeviceImpl device{ 834 name, 835 bus, 836 address, 837 powerControlGPIOName, 838 powerGoodGPIOName, 839 std::move(rails)}; 840 841 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1); 842 std::vector<int> gpioValues{1, 1, 1}; 843 EXPECT_CALL(device, getGPIOValues) 844 .Times(1) 845 .WillOnce(Return(gpioValues)); 846 EXPECT_CALL(device, getReadVout(5)).Times(0); 847 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0); 848 EXPECT_CALL(device, getStatusVout(7)) 849 .Times(1) 850 .WillOnce(Throw(std::runtime_error{"File does not exist"})); 851 852 MockServices services{}; 853 854 std::string powerSupplyError{}; 855 std::map<std::string, std::string> additionalData{}; 856 try 857 { 858 device.findPgoodFault(services, powerSupplyError, additionalData); 859 ADD_FAILURE() << "Should not have reached this line."; 860 } 861 catch (const std::exception& e) 862 { 863 EXPECT_STREQ( 864 e.what(), 865 "Unable to determine if a pgood fault occurred in device abc_pseq: " 866 "Unable to read STATUS_VOUT value for rail VIO: " 867 "File does not exist"); 868 } 869 } 870 } 871