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