1 /** 2 * Copyright © 2020 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 #include "action.hpp" 17 #include "chassis.hpp" 18 #include "configuration.hpp" 19 #include "device.hpp" 20 #include "i2c_interface.hpp" 21 #include "i2c_write_byte_action.hpp" 22 #include "mock_action.hpp" 23 #include "mock_error_logging.hpp" 24 #include "mock_journal.hpp" 25 #include "mock_services.hpp" 26 #include "mocked_i2c_interface.hpp" 27 #include "phase_fault_detection.hpp" 28 #include "pmbus_utils.hpp" 29 #include "pmbus_write_vout_command_action.hpp" 30 #include "presence_detection.hpp" 31 #include "rail.hpp" 32 #include "rule.hpp" 33 #include "system.hpp" 34 35 #include <cstdint> 36 #include <memory> 37 #include <optional> 38 #include <utility> 39 #include <vector> 40 41 #include <gmock/gmock.h> 42 #include <gtest/gtest.h> 43 44 using namespace phosphor::power::regulators; 45 using namespace phosphor::power::regulators::pmbus_utils; 46 47 using ::testing::A; 48 using ::testing::Ref; 49 using ::testing::Return; 50 using ::testing::Throw; 51 using ::testing::TypedEq; 52 53 static const std::string chassisInvPath{ 54 "/xyz/openbmc_project/inventory/system/chassis"}; 55 56 TEST(ConfigurationTests, Constructor) 57 { 58 // Test where volts value specified 59 { 60 std::optional<double> volts{1.3}; 61 62 std::vector<std::unique_ptr<Action>> actions{}; 63 actions.push_back(std::make_unique<MockAction>()); 64 actions.push_back(std::make_unique<MockAction>()); 65 66 Configuration configuration(volts, std::move(actions)); 67 EXPECT_EQ(configuration.getVolts().has_value(), true); 68 EXPECT_EQ(configuration.getVolts().value(), 1.3); 69 EXPECT_EQ(configuration.getActions().size(), 2); 70 } 71 72 // Test where volts value not specified 73 { 74 std::optional<double> volts{}; 75 76 std::vector<std::unique_ptr<Action>> actions{}; 77 actions.push_back(std::make_unique<MockAction>()); 78 79 Configuration configuration(volts, std::move(actions)); 80 EXPECT_EQ(configuration.getVolts().has_value(), false); 81 EXPECT_EQ(configuration.getActions().size(), 1); 82 } 83 } 84 85 // Test for execute(Services&, System&, Chassis&, Device&) 86 TEST(ConfigurationTests, ExecuteForDevice) 87 { 88 // Test where works: Volts value not specified 89 { 90 // Create mock services. Expect logDebug() to be called. 91 MockServices services{}; 92 MockJournal& journal = services.getMockJournal(); 93 EXPECT_CALL(journal, logDebug("Configuring vdd_reg")).Times(1); 94 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 95 96 // Create I2CWriteByteAction with register 0x7C and value 0x0A 97 std::unique_ptr<I2CWriteByteAction> action = 98 std::make_unique<I2CWriteByteAction>(0x7C, 0x0A); 99 100 // Create mock I2CInterface. Expect action to write 0x0A to 0x7C. 101 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 102 std::make_unique<i2c::MockedI2CInterface>(); 103 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 104 EXPECT_CALL(*i2cInterface, 105 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A))) 106 .Times(1); 107 108 // Create Configuration with no volts value specified 109 std::optional<double> volts{}; 110 std::vector<std::unique_ptr<Action>> actions{}; 111 actions.emplace_back(std::move(action)); 112 std::unique_ptr<Configuration> configuration = 113 std::make_unique<Configuration>(volts, std::move(actions)); 114 Configuration* configurationPtr = configuration.get(); 115 116 // Create Device that contains Configuration 117 std::unique_ptr<PresenceDetection> presenceDetection{}; 118 std::unique_ptr<Device> device = std::make_unique<Device>( 119 "vdd_reg", true, 120 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2", 121 std::move(i2cInterface), std::move(presenceDetection), 122 std::move(configuration)); 123 Device* devicePtr = device.get(); 124 125 // Create Chassis that contains Device 126 std::vector<std::unique_ptr<Device>> devices{}; 127 devices.emplace_back(std::move(device)); 128 std::unique_ptr<Chassis> chassis = 129 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices)); 130 Chassis* chassisPtr = chassis.get(); 131 132 // Create System that contains Chassis 133 std::vector<std::unique_ptr<Rule>> rules{}; 134 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 135 chassisVec.emplace_back(std::move(chassis)); 136 System system{std::move(rules), std::move(chassisVec)}; 137 138 // Execute Configuration 139 configurationPtr->execute(services, system, *chassisPtr, *devicePtr); 140 } 141 142 // Test where works: Volts value specified 143 { 144 // Create mock services. Expect logDebug() to be called. 145 MockServices services{}; 146 MockJournal& journal = services.getMockJournal(); 147 EXPECT_CALL(journal, logDebug("Configuring vdd_reg: volts=1.300000")) 148 .Times(1); 149 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 150 151 // Create PMBusWriteVoutCommandAction. Do not specify a volts value 152 // because it will get a value of 1.3V from the 153 // ActionEnvironment/Configuration. Specify a -8 exponent. 154 // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D. 155 std::optional<double> volts{}; 156 std::unique_ptr<PMBusWriteVoutCommandAction> action = 157 std::make_unique<PMBusWriteVoutCommandAction>( 158 volts, pmbus_utils::VoutDataFormat::linear, -8, false); 159 160 // Create mock I2CInterface. Expect action to write 0x014D to 161 // VOUT_COMMAND (command/register 0x21). 162 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 163 std::make_unique<i2c::MockedI2CInterface>(); 164 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 165 EXPECT_CALL(*i2cInterface, 166 write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D))) 167 .Times(1); 168 169 // Create Configuration with volts value 1.3V 170 std::vector<std::unique_ptr<Action>> actions{}; 171 actions.emplace_back(std::move(action)); 172 std::unique_ptr<Configuration> configuration = 173 std::make_unique<Configuration>(1.3, std::move(actions)); 174 Configuration* configurationPtr = configuration.get(); 175 176 // Create Device that contains Configuration 177 std::unique_ptr<PresenceDetection> presenceDetection{}; 178 std::unique_ptr<Device> device = std::make_unique<Device>( 179 "vdd_reg", true, 180 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2", 181 std::move(i2cInterface), std::move(presenceDetection), 182 std::move(configuration)); 183 Device* devicePtr = device.get(); 184 185 // Create Chassis that contains Device 186 std::vector<std::unique_ptr<Device>> devices{}; 187 devices.emplace_back(std::move(device)); 188 std::unique_ptr<Chassis> chassis = 189 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices)); 190 Chassis* chassisPtr = chassis.get(); 191 192 // Create System that contains Chassis 193 std::vector<std::unique_ptr<Rule>> rules{}; 194 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 195 chassisVec.emplace_back(std::move(chassis)); 196 System system{std::move(rules), std::move(chassisVec)}; 197 198 // Execute Configuration 199 configurationPtr->execute(services, system, *chassisPtr, *devicePtr); 200 } 201 202 // Test where fails 203 { 204 // Create mock services. Expect logDebug(), logError(), and 205 // logI2CError() to be called. 206 MockServices services{}; 207 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 208 MockJournal& journal = services.getMockJournal(); 209 std::vector<std::string> expectedErrMessagesException{ 210 "I2CException: Failed to write byte: bus /dev/i2c-1, addr 0x70", 211 "ActionError: i2c_write_byte: { register: 0x7C, value: 0xA, mask: " 212 "0xFF }"}; 213 EXPECT_CALL(journal, logDebug("Configuring vdd_reg")).Times(1); 214 EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1); 215 EXPECT_CALL(journal, logError("Unable to configure vdd_reg")).Times(1); 216 EXPECT_CALL(errorLogging, 217 logI2CError(Entry::Level::Warning, Ref(journal), 218 "/dev/i2c-1", 0x70, 0)) 219 .Times(1); 220 221 // Create I2CWriteByteAction with register 0x7C and value 0x0A 222 std::unique_ptr<I2CWriteByteAction> action = 223 std::make_unique<I2CWriteByteAction>(0x7C, 0x0A); 224 225 // Create mock I2CInterface. write() throws an I2CException. 226 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 227 std::make_unique<i2c::MockedI2CInterface>(); 228 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 229 EXPECT_CALL(*i2cInterface, 230 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A))) 231 .Times(1) 232 .WillOnce(Throw( 233 i2c::I2CException{"Failed to write byte", "/dev/i2c-1", 0x70})); 234 235 // Create Configuration with no volts value specified 236 std::optional<double> volts{}; 237 std::vector<std::unique_ptr<Action>> actions{}; 238 actions.emplace_back(std::move(action)); 239 std::unique_ptr<Configuration> configuration = 240 std::make_unique<Configuration>(volts, std::move(actions)); 241 Configuration* configurationPtr = configuration.get(); 242 243 // Create Device that contains Configuration 244 std::unique_ptr<PresenceDetection> presenceDetection{}; 245 std::unique_ptr<Device> device = std::make_unique<Device>( 246 "vdd_reg", true, 247 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2", 248 std::move(i2cInterface), std::move(presenceDetection), 249 std::move(configuration)); 250 Device* devicePtr = device.get(); 251 252 // Create Chassis that contains Device 253 std::vector<std::unique_ptr<Device>> devices{}; 254 devices.emplace_back(std::move(device)); 255 std::unique_ptr<Chassis> chassis = 256 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices)); 257 Chassis* chassisPtr = chassis.get(); 258 259 // Create System that contains Chassis 260 std::vector<std::unique_ptr<Rule>> rules{}; 261 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 262 chassisVec.emplace_back(std::move(chassis)); 263 System system{std::move(rules), std::move(chassisVec)}; 264 265 // Execute Configuration 266 configurationPtr->execute(services, system, *chassisPtr, *devicePtr); 267 } 268 } 269 270 // Test for execute(Services&, System&, Chassis&, Device&, Rail&) 271 TEST(ConfigurationTests, ExecuteForRail) 272 { 273 // Test where works: Volts value not specified 274 { 275 // Create mock services. Expect logDebug() to be called. 276 MockServices services{}; 277 MockJournal& journal = services.getMockJournal(); 278 EXPECT_CALL(journal, logDebug("Configuring vio2")).Times(1); 279 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 280 281 // Create I2CWriteByteAction with register 0x7C and value 0x0A 282 std::unique_ptr<I2CWriteByteAction> action = 283 std::make_unique<I2CWriteByteAction>(0x7C, 0x0A); 284 285 // Create mock I2CInterface. Expect action to write 0x0A to 0x7C. 286 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 287 std::make_unique<i2c::MockedI2CInterface>(); 288 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 289 EXPECT_CALL(*i2cInterface, 290 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A))) 291 .Times(1); 292 293 // Create Configuration with no volts value specified 294 std::optional<double> volts{}; 295 std::vector<std::unique_ptr<Action>> actions{}; 296 actions.emplace_back(std::move(action)); 297 std::unique_ptr<Configuration> configuration = 298 std::make_unique<Configuration>(volts, std::move(actions)); 299 Configuration* configurationPtr = configuration.get(); 300 301 // Create Rail that contains Configuration 302 std::unique_ptr<Rail> rail = 303 std::make_unique<Rail>("vio2", std::move(configuration)); 304 Rail* railPtr = rail.get(); 305 306 // Create Device that contains Rail 307 std::unique_ptr<PresenceDetection> presenceDetection{}; 308 std::unique_ptr<Configuration> deviceConfiguration{}; 309 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{}; 310 std::vector<std::unique_ptr<Rail>> rails{}; 311 rails.emplace_back(std::move(rail)); 312 std::unique_ptr<Device> device = std::make_unique<Device>( 313 "reg1", true, 314 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 315 std::move(i2cInterface), std::move(presenceDetection), 316 std::move(deviceConfiguration), std::move(phaseFaultDetection), 317 std::move(rails)); 318 Device* devicePtr = device.get(); 319 320 // Create Chassis that contains Device 321 std::vector<std::unique_ptr<Device>> devices{}; 322 devices.emplace_back(std::move(device)); 323 std::unique_ptr<Chassis> chassis = 324 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices)); 325 Chassis* chassisPtr = chassis.get(); 326 327 // Create System that contains Chassis 328 std::vector<std::unique_ptr<Rule>> rules{}; 329 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 330 chassisVec.emplace_back(std::move(chassis)); 331 System system{std::move(rules), std::move(chassisVec)}; 332 333 // Execute Configuration 334 configurationPtr->execute(services, system, *chassisPtr, *devicePtr, 335 *railPtr); 336 } 337 338 // Test where works: Volts value specified 339 { 340 // Create mock services. Expect logDebug() to be called. 341 MockServices services{}; 342 MockJournal& journal = services.getMockJournal(); 343 EXPECT_CALL(journal, logDebug("Configuring vio2: volts=1.300000")) 344 .Times(1); 345 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 346 347 // Create PMBusWriteVoutCommandAction. Do not specify a volts value 348 // because it will get a value of 1.3V from the 349 // ActionEnvironment/Configuration. Specify a -8 exponent. 350 // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D. 351 std::optional<double> volts{}; 352 std::unique_ptr<PMBusWriteVoutCommandAction> action = 353 std::make_unique<PMBusWriteVoutCommandAction>( 354 volts, pmbus_utils::VoutDataFormat::linear, -8, false); 355 356 // Create mock I2CInterface. Expect action to write 0x014D to 357 // VOUT_COMMAND (command/register 0x21). 358 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 359 std::make_unique<i2c::MockedI2CInterface>(); 360 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 361 EXPECT_CALL(*i2cInterface, 362 write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D))) 363 .Times(1); 364 365 // Create Configuration with volts value 1.3V 366 std::vector<std::unique_ptr<Action>> actions{}; 367 actions.emplace_back(std::move(action)); 368 std::unique_ptr<Configuration> configuration = 369 std::make_unique<Configuration>(1.3, std::move(actions)); 370 Configuration* configurationPtr = configuration.get(); 371 372 // Create Rail that contains Configuration 373 std::unique_ptr<Rail> rail = 374 std::make_unique<Rail>("vio2", std::move(configuration)); 375 Rail* railPtr = rail.get(); 376 377 // Create Device that contains Rail 378 std::unique_ptr<PresenceDetection> presenceDetection{}; 379 std::unique_ptr<Configuration> deviceConfiguration{}; 380 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{}; 381 std::vector<std::unique_ptr<Rail>> rails{}; 382 rails.emplace_back(std::move(rail)); 383 std::unique_ptr<Device> device = std::make_unique<Device>( 384 "reg1", true, 385 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 386 std::move(i2cInterface), std::move(presenceDetection), 387 std::move(deviceConfiguration), std::move(phaseFaultDetection), 388 std::move(rails)); 389 Device* devicePtr = device.get(); 390 391 // Create Chassis that contains Device 392 std::vector<std::unique_ptr<Device>> devices{}; 393 devices.emplace_back(std::move(device)); 394 std::unique_ptr<Chassis> chassis = 395 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices)); 396 Chassis* chassisPtr = chassis.get(); 397 398 // Create System that contains Chassis 399 std::vector<std::unique_ptr<Rule>> rules{}; 400 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 401 chassisVec.emplace_back(std::move(chassis)); 402 System system{std::move(rules), std::move(chassisVec)}; 403 404 // Execute Configuration 405 configurationPtr->execute(services, system, *chassisPtr, *devicePtr, 406 *railPtr); 407 } 408 409 // Test where fails 410 { 411 // Create mock services. Expect logDebug(), logError(), and logI2CError 412 // to be called. 413 MockServices services{}; 414 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 415 MockJournal& journal = services.getMockJournal(); 416 std::vector<std::string> expectedErrMessagesException{ 417 "I2CException: Failed to write byte: bus /dev/i2c-1, addr 0x70", 418 "ActionError: i2c_write_byte: { register: 0x7C, value: 0xA, mask: " 419 "0xFF }"}; 420 EXPECT_CALL(journal, logDebug("Configuring vio2")).Times(1); 421 EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1); 422 EXPECT_CALL(journal, logError("Unable to configure vio2")).Times(1); 423 EXPECT_CALL(errorLogging, 424 logI2CError(Entry::Level::Warning, Ref(journal), 425 "/dev/i2c-1", 0x70, 0)) 426 .Times(1); 427 428 // Create I2CWriteByteAction with register 0x7C and value 0x0A 429 std::unique_ptr<I2CWriteByteAction> action = 430 std::make_unique<I2CWriteByteAction>(0x7C, 0x0A); 431 432 // Create mock I2CInterface. write() throws an I2CException. 433 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 434 std::make_unique<i2c::MockedI2CInterface>(); 435 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 436 EXPECT_CALL(*i2cInterface, 437 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A))) 438 .Times(1) 439 .WillOnce(Throw( 440 i2c::I2CException{"Failed to write byte", "/dev/i2c-1", 0x70})); 441 442 // Create Configuration with no volts value specified 443 std::optional<double> volts{}; 444 std::vector<std::unique_ptr<Action>> actions{}; 445 actions.emplace_back(std::move(action)); 446 std::unique_ptr<Configuration> configuration = 447 std::make_unique<Configuration>(volts, std::move(actions)); 448 Configuration* configurationPtr = configuration.get(); 449 450 // Create Rail that contains Configuration 451 std::unique_ptr<Rail> rail = 452 std::make_unique<Rail>("vio2", std::move(configuration)); 453 Rail* railPtr = rail.get(); 454 455 // Create Device that contains Rail 456 std::unique_ptr<PresenceDetection> presenceDetection{}; 457 std::unique_ptr<Configuration> deviceConfiguration{}; 458 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{}; 459 std::vector<std::unique_ptr<Rail>> rails{}; 460 rails.emplace_back(std::move(rail)); 461 std::unique_ptr<Device> device = std::make_unique<Device>( 462 "reg1", true, 463 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 464 std::move(i2cInterface), std::move(presenceDetection), 465 std::move(deviceConfiguration), std::move(phaseFaultDetection), 466 std::move(rails)); 467 Device* devicePtr = device.get(); 468 469 // Create Chassis that contains Device 470 std::vector<std::unique_ptr<Device>> devices{}; 471 devices.emplace_back(std::move(device)); 472 std::unique_ptr<Chassis> chassis = 473 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices)); 474 Chassis* chassisPtr = chassis.get(); 475 476 // Create System that contains Chassis 477 std::vector<std::unique_ptr<Rule>> rules{}; 478 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 479 chassisVec.emplace_back(std::move(chassis)); 480 System system{std::move(rules), std::move(chassisVec)}; 481 482 // Execute Configuration 483 configurationPtr->execute(services, system, *chassisPtr, *devicePtr, 484 *railPtr); 485 } 486 } 487 488 TEST(ConfigurationTests, GetActions) 489 { 490 std::optional<double> volts{1.3}; 491 492 std::vector<std::unique_ptr<Action>> actions{}; 493 494 MockAction* action1 = new MockAction{}; 495 actions.push_back(std::unique_ptr<MockAction>{action1}); 496 497 MockAction* action2 = new MockAction{}; 498 actions.push_back(std::unique_ptr<MockAction>{action2}); 499 500 Configuration configuration(volts, std::move(actions)); 501 EXPECT_EQ(configuration.getActions().size(), 2); 502 EXPECT_EQ(configuration.getActions()[0].get(), action1); 503 EXPECT_EQ(configuration.getActions()[1].get(), action2); 504 } 505 506 TEST(ConfigurationTests, GetVolts) 507 { 508 // Test where volts value specified 509 { 510 std::optional<double> volts{3.2}; 511 512 std::vector<std::unique_ptr<Action>> actions{}; 513 actions.push_back(std::make_unique<MockAction>()); 514 515 Configuration configuration(volts, std::move(actions)); 516 EXPECT_EQ(configuration.getVolts().has_value(), true); 517 EXPECT_EQ(configuration.getVolts().value(), 3.2); 518 } 519 520 // Test where volts value not specified 521 { 522 std::optional<double> volts{}; 523 524 std::vector<std::unique_ptr<Action>> actions{}; 525 actions.push_back(std::make_unique<MockAction>()); 526 527 Configuration configuration(volts, std::move(actions)); 528 EXPECT_EQ(configuration.getVolts().has_value(), false); 529 } 530 } 531