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