1 #include "config.h" 2 3 #include "softoff.hpp" 4 5 #include "libpldm/entity.h" 6 #include "libpldm/platform.h" 7 #include "libpldm/requester/pldm.h" 8 #include "libpldm/state_set.h" 9 10 #include "common/utils.hpp" 11 12 #include <sdbusplus/bus.hpp> 13 #include <sdeventplus/clock.hpp> 14 #include <sdeventplus/exception.hpp> 15 #include <sdeventplus/source/io.hpp> 16 #include <sdeventplus/source/time.hpp> 17 18 #include <array> 19 #include <iostream> 20 21 namespace pldm 22 { 23 24 using namespace sdeventplus; 25 using namespace sdeventplus::source; 26 constexpr auto clockId = sdeventplus::ClockId::RealTime; 27 using Clock = Clock<clockId>; 28 using Timer = Time<clockId>; 29 30 constexpr pldm::pdr::TerminusID TID = 0; // TID will be implemented later. 31 namespace sdbusRule = sdbusplus::bus::match::rules; 32 33 SoftPowerOff::SoftPowerOff(sdbusplus::bus::bus& bus, sd_event* event) : 34 bus(bus), timer(event) 35 { 36 auto rc = getHostState(); 37 if (hasError || completed) 38 { 39 return; 40 } 41 42 rc = getEffecterID(); 43 if (completed) 44 { 45 std::cerr 46 << "pldm-softpoweroff: effecter to initiate softoff not found \n"; 47 return; 48 } 49 else if (rc != PLDM_SUCCESS) 50 { 51 hasError = true; 52 return; 53 } 54 55 rc = getSensorInfo(); 56 if (rc != PLDM_SUCCESS) 57 { 58 std::cerr << "Message get Sensor PDRs error. PLDM " 59 "error code = " 60 << std::hex << std::showbase << rc << "\n"; 61 hasError = true; 62 return; 63 } 64 65 // Matches on the pldm StateSensorEvent signal 66 pldmEventSignal = std::make_unique<sdbusplus::bus::match_t>( 67 bus, 68 sdbusRule::type::signal() + sdbusRule::member("StateSensorEvent") + 69 sdbusRule::path("/xyz/openbmc_project/pldm") + 70 sdbusRule::interface("xyz.openbmc_project.PLDM.Event"), 71 std::bind(std::mem_fn(&SoftPowerOff::hostSoftOffComplete), this, 72 std::placeholders::_1)); 73 } 74 75 int SoftPowerOff::getHostState() 76 { 77 try 78 { 79 pldm::utils::PropertyValue propertyValue = 80 pldm::utils::DBusHandler().getDbusPropertyVariant( 81 "/xyz/openbmc_project/state/host0", "CurrentHostState", 82 "xyz.openbmc_project.State.Host"); 83 84 if ((std::get<std::string>(propertyValue) != 85 "xyz.openbmc_project.State.Host.HostState.Running") && 86 (std::get<std::string>(propertyValue) != 87 "xyz.openbmc_project.State.Host.HostState.TransitioningToOff")) 88 { 89 // Host state is not "Running", this app should return success 90 completed = true; 91 return PLDM_SUCCESS; 92 } 93 } 94 catch (const std::exception& e) 95 { 96 std::cerr << "PLDM host soft off: Can't get current host state.\n"; 97 hasError = true; 98 return PLDM_ERROR; 99 } 100 101 return PLDM_SUCCESS; 102 } 103 104 void SoftPowerOff::hostSoftOffComplete(sdbusplus::message::message& msg) 105 { 106 pldm::pdr::TerminusID msgTID; 107 pldm::pdr::SensorID msgSensorID; 108 pldm::pdr::SensorOffset msgSensorOffset; 109 pldm::pdr::EventState msgEventState; 110 pldm::pdr::EventState msgPreviousEventState; 111 112 // Read the msg and populate each variable 113 msg.read(msgTID, msgSensorID, msgSensorOffset, msgEventState, 114 msgPreviousEventState); 115 116 if (msgSensorID == sensorID && msgSensorOffset == sensorOffset && 117 msgEventState == PLDM_SW_TERM_GRACEFUL_SHUTDOWN) 118 { 119 // Receive Graceful shutdown completion event message. Disable the timer 120 auto rc = timer.stop(); 121 if (rc < 0) 122 { 123 std::cerr << "PLDM soft off: Failure to STOP the timer. ERRNO=" 124 << rc << "\n"; 125 } 126 127 // This marks the completion of pldm soft power off. 128 completed = true; 129 } 130 } 131 132 int SoftPowerOff::getEffecterID() 133 { 134 auto& bus = pldm::utils::DBusHandler::getBus(); 135 136 // VMM is a logical entity, so the bit 15 in entity type is set. 137 pdr::EntityType entityType = PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER | 0x8000; 138 139 try 140 { 141 std::vector<std::vector<uint8_t>> VMMResponse{}; 142 auto VMMMethod = bus.new_method_call( 143 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", 144 "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR"); 145 VMMMethod.append(TID, entityType, 146 (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS); 147 148 auto VMMResponseMsg = bus.call(VMMMethod); 149 150 VMMResponseMsg.read(VMMResponse); 151 if (VMMResponse.size() != 0) 152 { 153 for (auto& rep : VMMResponse) 154 { 155 auto VMMPdr = 156 reinterpret_cast<pldm_state_effecter_pdr*>(rep.data()); 157 effecterID = VMMPdr->effecter_id; 158 } 159 } 160 else 161 { 162 VMMPdrExist = false; 163 } 164 } 165 catch (const sdbusplus::exception::exception& e) 166 { 167 std::cerr << "PLDM soft off: Error get VMM PDR,ERROR=" << e.what() 168 << "\n"; 169 VMMPdrExist = false; 170 } 171 172 if (VMMPdrExist) 173 { 174 return PLDM_SUCCESS; 175 } 176 177 // If the Virtual Machine Manager PDRs doesn't exist, go find the System 178 // Firmware PDRs. 179 // System Firmware is a logical entity, so the bit 15 in entity type is set 180 entityType = PLDM_ENTITY_SYS_FIRMWARE | 0x8000; 181 try 182 { 183 std::vector<std::vector<uint8_t>> sysFwResponse{}; 184 auto sysFwMethod = bus.new_method_call( 185 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", 186 "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR"); 187 sysFwMethod.append(TID, entityType, 188 (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS); 189 190 auto sysFwResponseMsg = bus.call(sysFwMethod); 191 192 sysFwResponseMsg.read(sysFwResponse); 193 194 if (sysFwResponse.size() == 0) 195 { 196 std::cerr 197 << "No effecter ID has been found that matches the criteria" 198 << "\n"; 199 return PLDM_ERROR; 200 } 201 202 for (auto& rep : sysFwResponse) 203 { 204 auto sysFwPdr = 205 reinterpret_cast<pldm_state_effecter_pdr*>(rep.data()); 206 effecterID = sysFwPdr->effecter_id; 207 } 208 } 209 catch (const sdbusplus::exception::exception& e) 210 { 211 std::cerr << "PLDM soft off: Error get system firmware PDR,ERROR=" 212 << e.what() << "\n"; 213 completed = true; 214 return PLDM_ERROR; 215 } 216 217 return PLDM_SUCCESS; 218 } 219 220 int SoftPowerOff::getSensorInfo() 221 { 222 pldm::pdr::EntityType entityType; 223 224 entityType = VMMPdrExist ? PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER 225 : PLDM_ENTITY_SYS_FIRMWARE; 226 227 // The Virtual machine manager/System firmware is logical entity, so bit 15 228 // need to be set. 229 entityType = entityType | 0x8000; 230 231 try 232 { 233 auto& bus = pldm::utils::DBusHandler::getBus(); 234 std::vector<std::vector<uint8_t>> Response{}; 235 auto method = bus.new_method_call( 236 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", 237 "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR"); 238 method.append(TID, entityType, 239 (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS); 240 241 auto ResponseMsg = bus.call(method); 242 243 ResponseMsg.read(Response); 244 245 if (Response.size() == 0) 246 { 247 std::cerr 248 << "No sensor PDR has been found that matches the criteria" 249 << "\n"; 250 return PLDM_ERROR; 251 } 252 253 pldm_state_sensor_pdr* pdr; 254 for (auto& rep : Response) 255 { 256 pdr = reinterpret_cast<pldm_state_sensor_pdr*>(rep.data()); 257 } 258 259 if (!pdr) 260 { 261 std::cerr << "Failed to get state sensor PDR.\n"; 262 return PLDM_ERROR; 263 } 264 265 sensorID = pdr->sensor_id; 266 267 auto compositeSensorCount = pdr->composite_sensor_count; 268 auto possibleStatesStart = pdr->possible_states; 269 270 for (auto offset = 0; offset < compositeSensorCount; offset++) 271 { 272 auto possibleStates = 273 reinterpret_cast<state_sensor_possible_states*>( 274 possibleStatesStart); 275 auto setId = possibleStates->state_set_id; 276 auto possibleStateSize = possibleStates->possible_states_size; 277 278 if (setId == PLDM_STATE_SET_SW_TERMINATION_STATUS) 279 { 280 sensorOffset = offset; 281 break; 282 } 283 possibleStatesStart += 284 possibleStateSize + sizeof(setId) + sizeof(possibleStateSize); 285 } 286 } 287 catch (const sdbusplus::exception::exception& e) 288 { 289 std::cerr << "PLDM soft off: Error get State Sensor PDR,ERROR=" 290 << e.what() << "\n"; 291 return PLDM_ERROR; 292 } 293 294 return PLDM_SUCCESS; 295 } 296 297 int SoftPowerOff::hostSoftOff(sdeventplus::Event& event) 298 { 299 constexpr uint8_t effecterCount = 1; 300 uint8_t mctpEID; 301 uint8_t instanceID; 302 303 mctpEID = pldm::utils::readHostEID(); 304 305 // Get instanceID 306 try 307 { 308 auto& bus = pldm::utils::DBusHandler::getBus(); 309 auto method = bus.new_method_call( 310 "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", 311 "xyz.openbmc_project.PLDM.Requester", "GetInstanceId"); 312 method.append(mctpEID); 313 314 auto ResponseMsg = bus.call(method); 315 316 ResponseMsg.read(instanceID); 317 } 318 catch (const sdbusplus::exception::exception& e) 319 { 320 std::cerr << "PLDM soft off: Error get instanceID,ERROR=" << e.what() 321 << "\n"; 322 return PLDM_ERROR; 323 } 324 325 std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterID) + 326 sizeof(effecterCount) + 327 sizeof(set_effecter_state_field)> 328 requestMsg{}; 329 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 330 set_effecter_state_field stateField{ 331 PLDM_REQUEST_SET, PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED}; 332 auto rc = encode_set_state_effecter_states_req( 333 instanceID, effecterID, effecterCount, &stateField, request); 334 if (rc != PLDM_SUCCESS) 335 { 336 std::cerr << "Message encode failure. PLDM error code = " << std::hex 337 << std::showbase << rc << "\n"; 338 return PLDM_ERROR; 339 } 340 341 // Open connection to MCTP socket 342 int fd = pldm_open(); 343 if (-1 == fd) 344 { 345 std::cerr << "Failed to connect to mctp demux daemon" 346 << "\n"; 347 return PLDM_ERROR; 348 } 349 350 // Add a timer to the event loop, default 30s. 351 auto timerCallback = [=, this](Timer& /*source*/, 352 Timer::TimePoint /*time*/) { 353 if (!responseReceived) 354 { 355 std::cerr << "PLDM soft off: ERROR! Can't get the response for the " 356 "PLDM request msg. Time out!\n" 357 << "Exit the pldm-softpoweroff\n"; 358 exit(-1); 359 } 360 return; 361 }; 362 Timer time(event, (Clock(event).now() + std::chrono::seconds{30}), 363 std::chrono::seconds{1}, std::move(timerCallback)); 364 365 // Add a callback to handle EPOLLIN on fd 366 auto callback = [=, this](IO& io, int fd, uint32_t revents) { 367 if (!(revents & EPOLLIN)) 368 { 369 return; 370 } 371 372 uint8_t* responseMsg = nullptr; 373 size_t responseMsgSize{}; 374 375 auto rc = pldm_recv(mctpEID, fd, request->hdr.instance_id, &responseMsg, 376 &responseMsgSize); 377 if (rc) 378 { 379 return; 380 } 381 382 std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{ 383 responseMsg, std::free}; 384 385 // We've got the response meant for the PLDM request msg that was 386 // sent out 387 io.set_enabled(Enabled::Off); 388 auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get()); 389 std::cerr << "Getting the response. PLDM RC = " << std::hex 390 << std::showbase 391 << static_cast<uint16_t>(response->payload[0]) << "\n"; 392 393 responseReceived = true; 394 395 // Start Timer 396 using namespace std::chrono; 397 auto timeMicroseconds = 398 duration_cast<microseconds>(seconds(SOFTOFF_TIMEOUT_SECONDS)); 399 400 auto ret = startTimer(timeMicroseconds); 401 if (ret < 0) 402 { 403 std::cerr << "Failure to start Host soft off wait timer, ERRNO = " 404 << ret << "Exit the pldm-softpoweroff\n"; 405 exit(-1); 406 } 407 else 408 { 409 std::cerr << "Timer started waiting for host soft off, " 410 "TIMEOUT_IN_SEC = " 411 << SOFTOFF_TIMEOUT_SECONDS << "\n"; 412 } 413 return; 414 }; 415 IO io(event, fd, EPOLLIN, std::move(callback)); 416 417 // Send PLDM Request message - pldm_send doesn't wait for response 418 rc = pldm_send(mctpEID, fd, requestMsg.data(), requestMsg.size()); 419 if (0 > rc) 420 { 421 std::cerr << "Failed to send message/receive response. RC = " << rc 422 << ", errno = " << errno << "\n"; 423 return PLDM_ERROR; 424 } 425 426 // Time out or soft off complete 427 while (!isCompleted() && !isTimerExpired()) 428 { 429 try 430 { 431 event.run(std::nullopt); 432 } 433 catch (const sdeventplus::SdEventError& e) 434 { 435 std::cerr 436 << "PLDM host soft off: Failure in processing request.ERROR= " 437 << e.what() << "\n"; 438 return PLDM_ERROR; 439 } 440 } 441 442 return PLDM_SUCCESS; 443 } 444 445 int SoftPowerOff::startTimer(const std::chrono::microseconds& usec) 446 { 447 return timer.start(usec); 448 } 449 } // namespace pldm 450