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