1 #pragma once 2 3 #include "common/utils.hpp" 4 #include "event_parser.hpp" 5 #include "fru.hpp" 6 #include "host-bmc/dbus_to_event_handler.hpp" 7 #include "host-bmc/host_pdr_handler.hpp" 8 #include "libpldmresponder/pdr.hpp" 9 #include "libpldmresponder/pdr_utils.hpp" 10 #include "libpldmresponder/platform_config.hpp" 11 #include "oem_handler.hpp" 12 #include "pldmd/handler.hpp" 13 14 #include <libpldm/pdr.h> 15 #include <libpldm/platform.h> 16 #include <libpldm/states.h> 17 18 #include <phosphor-logging/lg2.hpp> 19 20 #include <cstdint> 21 #include <map> 22 23 PHOSPHOR_LOG2_USING; 24 25 namespace pldm 26 { 27 namespace responder 28 { 29 namespace platform 30 { 31 using generatePDR = std::function<void( 32 const pldm::utils::DBusHandler& dBusIntf, const pldm::utils::Json& json, 33 pdr_utils::RepoInterface& repo)>; 34 35 using EffecterId = uint16_t; 36 using DbusObjMaps = 37 std::map<EffecterId, 38 std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>>; 39 using DbusPath = std::string; 40 using EffecterObjs = std::vector<DbusPath>; 41 using EventType = uint8_t; 42 using EventHandler = std::function<int( 43 const pldm_msg* request, size_t payloadLength, uint8_t formatVersion, 44 uint8_t tid, size_t eventDataOffset)>; 45 using EventHandlers = std::vector<EventHandler>; 46 using EventMap = std::map<EventType, EventHandlers>; 47 using AssociatedEntityMap = std::map<DbusPath, pldm_entity>; 48 49 class Handler : public CmdHandler 50 { 51 public: Handler(const pldm::utils::DBusHandler * dBusIntf,uint8_t eid,pldm::InstanceIdDb * instanceIdDb,const fs::path & pdrJsonDir,pldm_pdr * repo,HostPDRHandler * hostPDRHandler,pldm::state_sensor::DbusToPLDMEvent * dbusToPLDMEventHandler,fru::Handler * fruHandler,pldm::responder::platform_config::Handler * platformConfigHandler,pldm::requester::Handler<pldm::requester::Request> * handler,sdeventplus::Event & event,bool buildPDRLazily=false,const std::optional<EventMap> & addOnHandlersMap=std::nullopt)52 Handler(const pldm::utils::DBusHandler* dBusIntf, uint8_t eid, 53 pldm::InstanceIdDb* instanceIdDb, const fs::path& pdrJsonDir, 54 pldm_pdr* repo, HostPDRHandler* hostPDRHandler, 55 pldm::state_sensor::DbusToPLDMEvent* dbusToPLDMEventHandler, 56 fru::Handler* fruHandler, 57 pldm::responder::platform_config::Handler* platformConfigHandler, 58 pldm::requester::Handler<pldm::requester::Request>* handler, 59 sdeventplus::Event& event, bool buildPDRLazily = false, 60 const std::optional<EventMap>& addOnHandlersMap = std::nullopt) : 61 eid(eid), instanceIdDb(instanceIdDb), pdrRepo(repo), 62 hostPDRHandler(hostPDRHandler), 63 dbusToPLDMEventHandler(dbusToPLDMEventHandler), fruHandler(fruHandler), 64 dBusIntf(dBusIntf), platformConfigHandler(platformConfigHandler), 65 handler(handler), event(event), pdrJsonDir(pdrJsonDir), 66 pdrCreated(false), pdrJsonsDir({pdrJsonDir}) 67 { 68 if (!buildPDRLazily) 69 { 70 generateTerminusLocatorPDR(pdrRepo); 71 generate(*dBusIntf, pdrJsonsDir, pdrRepo); 72 pdrCreated = true; 73 } 74 75 handlers.emplace( 76 PLDM_GET_PDR, __anon58b9ed2e0102(pldm_tid_t, const pldm_msg* request, size_t payloadLength) 77 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 78 return this->getPDR(request, payloadLength); 79 }); 80 handlers.emplace( 81 PLDM_SET_NUMERIC_EFFECTER_VALUE, __anon58b9ed2e0202(pldm_tid_t, const pldm_msg* request, size_t payloadLength) 82 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 83 return this->setNumericEffecterValue(request, payloadLength); 84 }); 85 handlers.emplace( 86 PLDM_GET_NUMERIC_EFFECTER_VALUE, __anon58b9ed2e0302(pldm_tid_t, const pldm_msg* request, size_t payloadLength) 87 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 88 return this->getNumericEffecterValue(request, payloadLength); 89 }); 90 handlers.emplace( 91 PLDM_SET_STATE_EFFECTER_STATES, __anon58b9ed2e0402(pldm_tid_t, const pldm_msg* request, size_t payloadLength) 92 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 93 return this->setStateEffecterStates(request, payloadLength); 94 }); 95 handlers.emplace( 96 PLDM_PLATFORM_EVENT_MESSAGE, __anon58b9ed2e0502(pldm_tid_t, const pldm_msg* request, size_t payloadLength) 97 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 98 return this->platformEventMessage(request, payloadLength); 99 }); 100 handlers.emplace( 101 PLDM_GET_STATE_SENSOR_READINGS, __anon58b9ed2e0602(pldm_tid_t, const pldm_msg* request, size_t payloadLength) 102 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 103 return this->getStateSensorReadings(request, payloadLength); 104 }); 105 106 // Default handler for PLDM Events 107 eventHandlers[PLDM_SENSOR_EVENT].emplace_back( 108 [this](const pldm_msg* request, size_t payloadLength, __anon58b9ed2e0702(const pldm_msg* request, size_t payloadLength, uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) 109 uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) { 110 return this->sensorEvent(request, payloadLength, formatVersion, 111 tid, eventDataOffset); 112 }); 113 eventHandlers[PLDM_PDR_REPOSITORY_CHG_EVENT].emplace_back( 114 [this](const pldm_msg* request, size_t payloadLength, __anon58b9ed2e0802(const pldm_msg* request, size_t payloadLength, uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) 115 uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) { 116 return this->pldmPDRRepositoryChgEvent( 117 request, payloadLength, formatVersion, tid, 118 eventDataOffset); 119 }); 120 121 // Additional OEM event handlers for PLDM events, append it to the 122 // standard handlers 123 if (addOnHandlersMap) 124 { 125 auto addOnHandlers = addOnHandlersMap.value(); 126 for (EventMap::iterator iter = addOnHandlers.begin(); 127 iter != addOnHandlers.end(); ++iter) 128 { 129 auto search = eventHandlers.find(iter->first); 130 if (search != eventHandlers.end()) 131 { 132 search->second.insert(std::end(search->second), 133 std::begin(iter->second), 134 std::end(iter->second)); 135 } 136 else 137 { 138 eventHandlers.emplace(iter->first, iter->second); 139 } 140 } 141 } 142 } 143 getRepo()144 pdr_utils::Repo& getRepo() 145 { 146 return this->pdrRepo; 147 } 148 149 /** @brief Add D-Bus mapping and value mapping(stateId to D-Bus) for the 150 * Id. If the same id is added, the previous dbusObjs will 151 * be "over-written". 152 * 153 * @param[in] Id - effecter/sensor id 154 * @param[in] dbusObj - list of D-Bus object structure and list of D-Bus 155 * property value to attribute value 156 * @param[in] typeId - the type id of enum 157 */ 158 void addDbusObjMaps( 159 uint16_t id, 160 std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> dbusObj, 161 pldm::responder::pdr_utils::TypeId typeId = 162 pldm::responder::pdr_utils::TypeId::PLDM_EFFECTER_ID); 163 164 /** @brief Retrieve an id -> D-Bus objects mapping 165 * 166 * @param[in] Id - id 167 * @param[in] typeId - the type id of enum 168 * 169 * @return std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> - 170 * list of D-Bus object structure and list of D-Bus property value 171 * to attribute value 172 */ 173 const std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>& 174 getDbusObjMaps( 175 uint16_t id, 176 pldm::responder::pdr_utils::TypeId typeId = 177 pldm::responder::pdr_utils::TypeId::PLDM_EFFECTER_ID) const; 178 getNextEffecterId()179 uint16_t getNextEffecterId() 180 { 181 return ++nextEffecterId; 182 } 183 getNextSensorId()184 uint16_t getNextSensorId() 185 { 186 return ++nextSensorId; 187 } 188 189 /** @brief Parse PDR JSONs and build PDR repository 190 * 191 * @param[in] dBusIntf - The interface object 192 * @param[in] dir - directory housing platform specific PDR JSON files 193 * @param[in] repo - instance of concrete implementation of Repo 194 */ 195 void generate(const pldm::utils::DBusHandler& dBusIntf, 196 const std::vector<fs::path>& dir, 197 pldm::responder::pdr_utils::Repo& repo); 198 199 /** @brief Parse PDR JSONs and build state effecter PDR repository 200 * 201 * @param[in] json - platform specific PDR JSON files 202 * @param[in] repo - instance of state effecter implementation of Repo 203 */ 204 void generateStateEffecterRepo(const pldm::utils::Json& json, 205 pldm::responder::pdr_utils::Repo& repo); 206 207 /** @brief map of PLDM event type to EventHandlers 208 * 209 */ 210 EventMap eventHandlers; 211 212 /* @brief Method to set the oem platform handler in platform handler class 213 * 214 * @param[in] handler - oem platform handler 215 */ 216 inline void setOemPlatformHandler(pldm::responder::oem_platform::Handler * handler)217 setOemPlatformHandler(pldm::responder::oem_platform::Handler* handler) 218 { 219 oemPlatformHandler = handler; 220 } 221 222 /* @brief Method to register event handlers 223 * 224 * @param[in] handler - oem event handlers 225 */ registerEventHandlers(EventType eventId,EventHandlers handlers)226 inline void registerEventHandlers(EventType eventId, EventHandlers handlers) 227 { 228 // Try to emplace the eventId with an empty vector if it doesn't exist 229 auto [iter, 230 inserted] = eventHandlers.try_emplace(eventId, EventHandlers{}); 231 232 for (const auto& handler : handlers) 233 { 234 iter->second.emplace_back(handler); 235 } 236 } 237 238 /** @brief Handler for GetPDR 239 * 240 * @param[in] request - Request message payload 241 * @param[in] payloadLength - Request payload length 242 * @param[out] Response - Response message written here 243 */ 244 Response getPDR(const pldm_msg* request, size_t payloadLength); 245 246 /** @brief Handler for setNumericEffecterValue 247 * 248 * @param[in] request - Request message 249 * @param[in] payloadLength - Request payload length 250 * @return Response - PLDM Response message 251 */ 252 Response setNumericEffecterValue(const pldm_msg* request, 253 size_t payloadLength); 254 255 /** @brief Handler for getNumericEffecterValue 256 * 257 * @param[in] request - Request message 258 * @param[in] payloadLength - Request payload length 259 * @return Response - PLDM Response message 260 */ 261 Response getNumericEffecterValue(const pldm_msg* request, 262 size_t payloadLength); 263 264 /** @brief Handler for getStateSensorReadings 265 * 266 * @param[in] request - Request message 267 * @param[in] payloadLength - Request payload length 268 * @return Response - PLDM Response message 269 */ 270 Response getStateSensorReadings(const pldm_msg* request, 271 size_t payloadLength); 272 273 /** @brief Handler for setStateEffecterStates 274 * 275 * @param[in] request - Request message 276 * @param[in] payloadLength - Request payload length 277 * @return Response - PLDM Response message 278 */ 279 Response setStateEffecterStates(const pldm_msg* request, 280 size_t payloadLength); 281 282 /** @brief Handler for PlatformEventMessage 283 * 284 * @param[in] request - Request message 285 * @param[in] payloadLength - Request payload length 286 * @return Response - PLDM Response message 287 */ 288 Response platformEventMessage(const pldm_msg* request, 289 size_t payloadLength); 290 291 /** @brief Handler for event class Sensor event 292 * 293 * @param[in] request - Request message 294 * @param[in] payloadLength - Request payload length 295 * @param[in] formatVersion - Version of the event format 296 * @param[in] tid - Terminus ID of the event's originator 297 * @param[in] eventDataOffset - Offset of the event data in the request 298 * message 299 * @return PLDM completion code 300 */ 301 int sensorEvent(const pldm_msg* request, size_t payloadLength, 302 uint8_t formatVersion, uint8_t tid, size_t eventDataOffset); 303 304 /** @brief Handler for pldmPDRRepositoryChgEvent 305 * 306 * @param[in] request - Request message 307 * @param[in] payloadLength - Request payload length 308 * @param[in] formatVersion - Version of the event format 309 * @param[in] tid - Terminus ID of the event's originator 310 * @param[in] eventDataOffset - Offset of the event data in the request 311 * message 312 * @return PLDM completion code 313 */ 314 int pldmPDRRepositoryChgEvent(const pldm_msg* request, size_t payloadLength, 315 uint8_t formatVersion, uint8_t tid, 316 size_t eventDataOffset); 317 318 /** @brief Handler for extracting the PDR handles from changeEntries 319 * 320 * @param[in] changeEntryData - ChangeEntry data from changeRecord 321 * @param[in] changeEntryDataSize - total size of changeEntryData 322 * @param[in] numberOfChangeEntries - total number of changeEntries to 323 * extract 324 * @param[out] pdrRecordHandles - std::vector where the extracted PDR 325 * handles are placed 326 * @return PLDM completion code 327 */ 328 int getPDRRecordHandles(const ChangeEntry* changeEntryData, 329 size_t changeEntryDataSize, 330 size_t numberOfChangeEntries, 331 PDRRecordHandles& pdrRecordHandles); 332 333 /** @brief Function to set the effecter requested by pldm requester 334 * @param[in] dBusIntf - The interface object 335 * @param[in] effecterId - Effecter ID sent by the requester to act on 336 * @param[in] stateField - The state field data for each of the states, 337 * equal to composite effecter count in number 338 * @return - Success or failure in setting the states. Returns failure in 339 * terms of PLDM completion codes if at least one state fails to be set 340 */ 341 template <class DBusInterface> setStateEffecterStatesHandler(const DBusInterface & dBusIntf,uint16_t effecterId,const std::vector<set_effecter_state_field> & stateField)342 int setStateEffecterStatesHandler( 343 const DBusInterface& dBusIntf, uint16_t effecterId, 344 const std::vector<set_effecter_state_field>& stateField) 345 { 346 using namespace pldm::responder::pdr; 347 using namespace pldm::utils; 348 using StateSetNum = uint8_t; 349 350 state_effecter_possible_states* states = nullptr; 351 pldm_state_effecter_pdr* pdr = nullptr; 352 uint8_t compEffecterCnt = stateField.size(); 353 354 std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> 355 stateEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy); 356 if (!stateEffecterPdrRepo) 357 { 358 throw std::runtime_error( 359 "Failed to instantiate state effecter PDR repository"); 360 } 361 pldm::responder::pdr_utils::Repo stateEffecterPDRs( 362 stateEffecterPdrRepo.get()); 363 getRepoByType(pdrRepo, stateEffecterPDRs, PLDM_STATE_EFFECTER_PDR); 364 if (stateEffecterPDRs.empty()) 365 { 366 error("Failed to get record by PDR type"); 367 return PLDM_PLATFORM_INVALID_EFFECTER_ID; 368 } 369 370 pldm::responder::pdr_utils::PdrEntry pdrEntry{}; 371 auto pdrRecord = stateEffecterPDRs.getFirstRecord(pdrEntry); 372 while (pdrRecord) 373 { 374 pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data); 375 if (pdr->effecter_id != effecterId) 376 { 377 pdr = nullptr; 378 pdrRecord = 379 stateEffecterPDRs.getNextRecord(pdrRecord, pdrEntry); 380 continue; 381 } 382 383 states = reinterpret_cast<state_effecter_possible_states*>( 384 pdr->possible_states); 385 if (compEffecterCnt > pdr->composite_effecter_count) 386 { 387 error( 388 "The requester sent wrong composite effecter count '{COMPOSITE_EFFECTER_COUNT}' for the effecter ID '{EFFECTERID}'.", 389 "COMPOSITE_EFFECTER_COUNT", compEffecterCnt, "EFFECTERID", 390 effecterId); 391 return PLDM_ERROR_INVALID_DATA; 392 } 393 break; 394 } 395 396 if (!pdr) 397 { 398 return PLDM_PLATFORM_INVALID_EFFECTER_ID; 399 } 400 401 int rc = PLDM_SUCCESS; 402 try 403 { 404 const auto& [dbusMappings, dbusValMaps] = 405 effecterDbusObjMaps.at(effecterId); 406 for (uint8_t currState = 0; currState < compEffecterCnt; 407 ++currState) 408 { 409 std::vector<StateSetNum> allowed{}; 410 // computation is based on table 79 from DSP0248 v1.1.1 411 uint8_t bitfieldIndex = 412 stateField[currState].effecter_state / 8; 413 uint8_t bit = stateField[currState].effecter_state - 414 (8 * bitfieldIndex); 415 if (states->possible_states_size < bitfieldIndex || 416 !(states->states[bitfieldIndex].byte & (1 << bit))) 417 { 418 error( 419 "Invalid state set value for effecter ID '{EFFECTERID}', effecter state '{EFFECTER_STATE}', composite effecter ID '{COMPOSITE_EFFECTER_ID}' and path '{PATH}'.", 420 "EFFECTERID", effecterId, "EFFECTER_STATE", 421 stateField[currState].effecter_state, 422 "COMPOSITE_EFFECTER_COUNT", currState, "PATH", 423 dbusMappings[currState].objectPath); 424 rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE; 425 break; 426 } 427 const DBusMapping& dbusMapping = dbusMappings[currState]; 428 const pldm::responder::pdr_utils::StatestoDbusVal& 429 dbusValToMap = dbusValMaps[currState]; 430 431 if (stateField[currState].set_request == PLDM_REQUEST_SET) 432 { 433 try 434 { 435 dBusIntf.setDbusProperty( 436 dbusMapping, 437 dbusValToMap.at( 438 stateField[currState].effecter_state)); 439 } 440 catch (const std::exception& e) 441 { 442 error( 443 "Failed to set property '{PROPERTY}' of interface '{INTERFACE}' at path '{PATH}', error - {ERROR}", 444 "PROPERTY", dbusMapping.propertyName, "DBUS_INTF", 445 dbusMapping.interface, "DBUS_OBJ_PATH", 446 dbusMapping.objectPath, "ERROR", e); 447 return PLDM_ERROR; 448 } 449 } 450 uint8_t* nextState = 451 reinterpret_cast<uint8_t*>(states) + 452 sizeof(state_effecter_possible_states) - 453 sizeof(states->states) + 454 (states->possible_states_size * sizeof(states->states)); 455 states = reinterpret_cast<state_effecter_possible_states*>( 456 nextState); 457 } 458 } 459 catch (const std::out_of_range& e) 460 { 461 error( 462 "The effecter ID '{EFFECTERID}' does not exist, error - {ERROR}.", 463 "EFFECTERID", effecterId, "ERROR", e); 464 } 465 466 return rc; 467 } 468 469 /** @brief Build BMC Terminus Locator PDR 470 * 471 * @param[in] repo - instance of concrete implementation of Repo 472 */ 473 void generateTerminusLocatorPDR(pldm::responder::pdr_utils::Repo& repo); 474 475 /** @brief Get std::map associated with the entity 476 * key: object path 477 * value: pldm_entity 478 * 479 * @return std::map<ObjectPath, pldm_entity> 480 */ getAssociateEntityMap() const481 inline const AssociatedEntityMap& getAssociateEntityMap() const 482 { 483 if (fruHandler == nullptr) 484 { 485 throw InternalFailure(); 486 } 487 return fruHandler->getAssociateEntityMap(); 488 } 489 490 /** @brief update the sensor cache map 491 * @param[in] sensorId - sensor id that needs an update 492 * @param[in] sensorRearm - rearm value within the sensor 493 * @param[in] value - value that needs to be cached 494 */ 495 updateSensorCache(pldm::pdr::SensorID sensorId,size_t sensorRearm,uint8_t value)496 inline void updateSensorCache(pldm::pdr::SensorID sensorId, 497 size_t sensorRearm, uint8_t value) 498 { 499 if (dbusToPLDMEventHandler) 500 { 501 dbusToPLDMEventHandler->updateSensorCacheMaps(sensorId, sensorRearm, 502 value); 503 } 504 } 505 506 /** @brief process the actions that needs to be performed after a GetPDR 507 * call is received 508 * @param[in] source - sdeventplus event source 509 */ 510 void _processPostGetPDRActions(sdeventplus::source::EventBase& source); 511 512 /** @brief Method for setEventreceiver */ 513 void setEventReceiver(); 514 515 private: 516 uint8_t eid; 517 InstanceIdDb* instanceIdDb; 518 pdr_utils::Repo pdrRepo; 519 uint16_t nextEffecterId{}; 520 uint16_t nextSensorId{}; 521 DbusObjMaps effecterDbusObjMaps{}; 522 DbusObjMaps sensorDbusObjMaps{}; 523 HostPDRHandler* hostPDRHandler; 524 pldm::state_sensor::DbusToPLDMEvent* dbusToPLDMEventHandler; 525 fru::Handler* fruHandler; 526 const pldm::utils::DBusHandler* dBusIntf; 527 pldm::responder::oem_platform::Handler* oemPlatformHandler = nullptr; 528 pldm::responder::platform_config::Handler* platformConfigHandler; 529 pldm::requester::Handler<pldm::requester::Request>* handler; 530 sdeventplus::Event& event; 531 fs::path pdrJsonDir; 532 bool pdrCreated; 533 std::vector<fs::path> pdrJsonsDir; 534 std::unique_ptr<sdeventplus::source::Defer> deferredGetPDREvent; 535 }; 536 537 /** @brief Function to check if a sensor falls in OEM range 538 * A sensor is considered to be oem if either of entity 539 * type or state set or both falls in oem range 540 * 541 * @param[in] handler - the interface object 542 * @param[in] sensorId - sensor id 543 * @param[in] sensorRearmCount - sensor rearm count 544 * @param[out] compSensorCnt - composite sensor count 545 * @param[out] entityType - entity type 546 * @param[out] entityInstance - entity instance number 547 * @param[out] stateSetId - state set id 548 * @param[out] containerId - container id 549 * 550 * @return true if the sensor is OEM. All out parameters are invalid 551 * for a non OEM sensor 552 */ 553 bool isOemStateSensor(Handler& handler, uint16_t sensorId, 554 uint8_t sensorRearmCount, uint8_t& compSensorCnt, 555 uint16_t& entityType, uint16_t& entityInstance, 556 uint16_t& stateSetId, uint16_t& containerId); 557 558 /** @brief Function to check if an effecter falls in OEM range 559 * An effecter is considered to be oem if either of entity 560 * type or state set or both falls in oem range 561 * 562 * @param[in] handler - the interface object 563 * @param[in] effecterId - effecter id 564 * @param[in] compEffecterCnt - composite effecter count 565 * @param[out] entityType - entity type 566 * @param[out] entityInstance - entity instance number 567 * @param[out] stateSetId - state set id 568 * 569 * @return true if the effecter is OEM. All out parameters are invalid 570 * for a non OEM effecter 571 */ 572 bool isOemStateEffecter(Handler& handler, uint16_t effecterId, 573 uint8_t compEffecterCnt, uint16_t& entityType, 574 uint16_t& entityInstance, uint16_t& stateSetId); 575 576 } // namespace platform 577 } // namespace responder 578 } // namespace pldm 579