blob: 1afb3bd4d585454be5082ea9dbd8ff81b731927a [file] [log] [blame]
Shawn McCarney472101c2024-04-17 16:31:09 -05001/**
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"
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050019#include "services.hpp"
Shawn McCarney472101c2024-04-17 16:31:09 -050020#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
33using namespace phosphor::power::sequencer;
34
35using ::testing::Return;
36using ::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 */
51class 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
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050069 MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services),
70 (override));
Shawn McCarney472101c2024-04-17 16:31:09 -050071 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));
Shawn McCarney16275832024-06-27 10:14:11 -050075
76 // Override empty implementation with mock so we can verify it is called
77 MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services),
78 (override));
Shawn McCarney472101c2024-04-17 16:31:09 -050079};
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 */
90std::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 */
112std::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
Shawn McCarney16275832024-06-27 10:14:11 -0500126/**
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 */
135std::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
Shawn McCarney472101c2024-04-17 16:31:09 -0500148TEST(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));
Shawn McCarney16275832024-06-27 10:14:11 -0500163 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500164 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
175TEST(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
183TEST(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));
Shawn McCarney16275832024-06-27 10:14:11 -0500197 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500198 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
208TEST(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));
Shawn McCarney16275832024-06-27 10:14:11 -0500214 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500215 rails.emplace_back(createRailStatusVout("VIO", false, 7));
216 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
217
Shawn McCarney16275832024-06-27 10:14:11 -0500218 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500219 std::vector<int> gpioValues{1, 1, 1};
220 EXPECT_CALL(device, getGPIOValues)
221 .Times(1)
222 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500223 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));
Shawn McCarney472101c2024-04-17 16:31:09 -0500227 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
Shawn McCarney16275832024-06-27 10:14:11 -0500239 // First rail has a pgood fault detected via GPIO
Shawn McCarney472101c2024-04-17 16:31:09 -0500240 // 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));
Shawn McCarney16275832024-06-27 10:14:11 -0500244 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500245 rails.emplace_back(createRailStatusVout("VIO", false, 7));
246 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
247
Shawn McCarney16275832024-06-27 10:14:11 -0500248 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500249 std::vector<int> gpioValues{1, 1, 0};
250 EXPECT_CALL(device, getGPIOValues)
251 .Times(1)
252 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500253 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));
Shawn McCarney472101c2024-04-17 16:31:09 -0500256
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
Shawn McCarney16275832024-06-27 10:14:11 -0500288 // First rail has a pgood fault detected via GPIO
Shawn McCarney472101c2024-04-17 16:31:09 -0500289 // Is a PSU rail: PSU error specified
290 {
291 std::vector<std::unique_ptr<Rail>> rails{};
292 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500293 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500294 rails.emplace_back(createRailStatusVout("VIO", false, 7));
295 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
296
Shawn McCarney16275832024-06-27 10:14:11 -0500297 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500298 std::vector<int> gpioValues{1, 1, 0};
299 EXPECT_CALL(device, getGPIOValues)
300 .Times(1)
301 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500302 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));
Shawn McCarney472101c2024-04-17 16:31:09 -0500305
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
Shawn McCarney16275832024-06-27 10:14:11 -0500336 // Second rail has a pgood fault detected via output voltage
Shawn McCarney472101c2024-04-17 16:31:09 -0500337 // Not a PSU rail: PSU error specified
338 {
339 std::vector<std::unique_ptr<Rail>> rails{};
340 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500341 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500342 rails.emplace_back(createRailStatusVout("VIO", false, 7));
343 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
344
Shawn McCarney16275832024-06-27 10:14:11 -0500345 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500346 std::vector<int> gpioValues{1, 1, 1};
347 EXPECT_CALL(device, getGPIOValues)
348 .Times(1)
349 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500350 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));
Shawn McCarney472101c2024-04-17 16:31:09 -0500355 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,
Shawn McCarney16275832024-06-27 10:14:11 -0500372 logErrorMsg(
373 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
Shawn McCarney472101c2024-04-17 16:31:09 -0500374 .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");
Shawn McCarney16275832024-06-27 10:14:11 -0500382 EXPECT_EQ(additionalData.size(), 6);
Shawn McCarney472101c2024-04-17 16:31:09 -0500383 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
384 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]");
385 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
Shawn McCarney16275832024-06-27 10:14:11 -0500386 EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
387 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
Shawn McCarney472101c2024-04-17 16:31:09 -0500388 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
389 }
390
Shawn McCarney16275832024-06-27 10:14:11 -0500391 // Third rail has a pgood fault detected via STATUS_VOUT
Shawn McCarney472101c2024-04-17 16:31:09 -0500392 // 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));
Shawn McCarney16275832024-06-27 10:14:11 -0500397 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500398 rails.emplace_back(createRailStatusVout("VIO", false, 7));
399 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
400
Shawn McCarney16275832024-06-27 10:14:11 -0500401 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500402 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));
Shawn McCarney16275832024-06-27 10:14:11 -0500407 EXPECT_CALL(device, getReadVout(5)).Times(0);
408 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
Shawn McCarney472101c2024-04-17 16:31:09 -0500409 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
Shawn McCarney16275832024-06-27 10:14:11 -0500440 // Third rail has a pgood fault detected via STATUS_VOUT
Shawn McCarney472101c2024-04-17 16:31:09 -0500441 // 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));
Shawn McCarney16275832024-06-27 10:14:11 -0500446 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500447 rails.emplace_back(createRailStatusVout("VIO", false, 7));
448 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
449
Shawn McCarney16275832024-06-27 10:14:11 -0500450 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500451 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));
Shawn McCarney16275832024-06-27 10:14:11 -0500455 EXPECT_CALL(device, getReadVout(5)).Times(0);
456 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
Shawn McCarney472101c2024-04-17 16:31:09 -0500457 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
Shawn McCarney16275832024-06-27 10:14:11 -0500488 // 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
Shawn McCarney472101c2024-04-17 16:31:09 -0500594 // Exception is thrown during pgood fault detection
595 {
596 std::vector<std::unique_ptr<Rail>> rails{};
597 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500598 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500599 rails.emplace_back(createRailStatusVout("VIO", false, 7));
600 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
601
Shawn McCarney16275832024-06-27 10:14:11 -0500602 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500603 std::vector<int> gpioValues{1, 1, 1};
604 EXPECT_CALL(device, getGPIOValues)
605 .Times(1)
606 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500607 EXPECT_CALL(device, getReadVout(5)).Times(0);
608 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
609 EXPECT_CALL(device, getStatusVout(7))
Shawn McCarney472101c2024-04-17 16:31:09 -0500610 .Times(1)
611 .WillOnce(Throw(std::runtime_error{"File does not exist"}));
Shawn McCarney472101c2024-04-17 16:31:09 -0500612
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: "
Shawn McCarney16275832024-06-27 10:14:11 -0500627 "Unable to read STATUS_VOUT value for rail VIO: "
Shawn McCarney472101c2024-04-17 16:31:09 -0500628 "File does not exist");
629 }
630 }
631}