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