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