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
StandardDeviceImpl(const std::string & name,std::vector<std::unique_ptr<Rail>> rails)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 */
createRailStatusVout(const std::string & name,bool isPowerSupplyRail,uint8_t pageNum)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 */
createRailGPIO(const std::string & name,bool isPowerSupplyRail,unsigned int gpioLine)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 */
createRailOutputVoltage(const std::string & name,bool isPowerSupplyRail,uint8_t pageNum)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
TEST(StandardDeviceTests,Constructor)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
TEST(StandardDeviceTests,GetName)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
TEST(StandardDeviceTests,GetRails)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
TEST(StandardDeviceTests,FindPgoodFault)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