1 
2 #include "platform.hpp"
3 
4 #include "common/types.hpp"
5 #include "common/utils.hpp"
6 #include "event_parser.hpp"
7 #include "pdr.hpp"
8 #include "pdr_numeric_effecter.hpp"
9 #include "pdr_state_effecter.hpp"
10 #include "pdr_state_sensor.hpp"
11 #include "pdr_utils.hpp"
12 #include "platform_numeric_effecter.hpp"
13 #include "platform_state_effecter.hpp"
14 #include "platform_state_sensor.hpp"
15 
16 namespace pldm
17 {
18 namespace responder
19 {
20 namespace platform
21 {
22 
23 using InternalFailure =
24     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
25 
26 static const Json empty{};
27 
28 void Handler::addDbusObjMaps(
29     uint16_t id,
30     std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> dbusObj,
31     TypeId typeId)
32 {
33     if (typeId == TypeId::PLDM_SENSOR_ID)
34     {
35         sensorDbusObjMaps.emplace(id, dbusObj);
36     }
37     else
38     {
39         effecterDbusObjMaps.emplace(id, dbusObj);
40     }
41 }
42 
43 const std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>&
44     Handler::getDbusObjMaps(uint16_t id, TypeId typeId) const
45 {
46     if (typeId == TypeId::PLDM_SENSOR_ID)
47     {
48         return sensorDbusObjMaps.at(id);
49     }
50     else
51     {
52         return effecterDbusObjMaps.at(id);
53     }
54 }
55 
56 void Handler::generate(const pldm::utils::DBusHandler& dBusIntf,
57                        const std::string& dir, Repo& repo)
58 {
59     if (!fs::exists(dir))
60     {
61         return;
62     }
63 
64     // A map of PDR type to a lambda that handles creation of that PDR type.
65     // The lambda essentially would parse the platform specific PDR JSONs to
66     // generate the PDR structures. This function iterates through the map to
67     // invoke all lambdas, so that all PDR types can be created.
68 
69     const std::map<Type, generatePDR> generateHandlers = {
70         {PLDM_STATE_EFFECTER_PDR,
71          [this](const DBusHandler& dBusIntf, const auto& json,
72                 RepoInterface& repo) {
73              pdr_state_effecter::generateStateEffecterPDR<
74                  pldm::utils::DBusHandler, Handler>(dBusIntf, json, *this,
75                                                     repo);
76          }},
77         {PLDM_NUMERIC_EFFECTER_PDR,
78          [this](const DBusHandler& dBusIntf, const auto& json,
79                 RepoInterface& repo) {
80              pdr_numeric_effecter::generateNumericEffecterPDR<
81                  pldm::utils::DBusHandler, Handler>(dBusIntf, json, *this,
82                                                     repo);
83          }},
84         {PLDM_STATE_SENSOR_PDR, [this](const DBusHandler& dBusIntf,
85                                        const auto& json, RepoInterface& repo) {
86              pdr_state_sensor::generateStateSensorPDR<pldm::utils::DBusHandler,
87                                                       Handler>(dBusIntf, json,
88                                                                *this, repo);
89          }}};
90 
91     Type pdrType{};
92     for (const auto& dirEntry : fs::directory_iterator(dir))
93     {
94         try
95         {
96             auto json = readJson(dirEntry.path().string());
97             if (!json.empty())
98             {
99                 auto effecterPDRs = json.value("effecterPDRs", empty);
100                 for (const auto& effecter : effecterPDRs)
101                 {
102                     pdrType = effecter.value("pdrType", 0);
103                     generateHandlers.at(pdrType)(dBusIntf, effecter, repo);
104                 }
105 
106                 auto sensorPDRs = json.value("sensorPDRs", empty);
107                 for (const auto& sensor : sensorPDRs)
108                 {
109                     pdrType = sensor.value("pdrType", 0);
110                     generateHandlers.at(pdrType)(dBusIntf, sensor, repo);
111                 }
112             }
113         }
114         catch (const InternalFailure& e)
115         {
116             std::cerr << "PDR config directory does not exist or empty, TYPE= "
117                       << pdrType << "PATH= " << dirEntry
118                       << " ERROR=" << e.what() << "\n";
119         }
120         catch (const Json::exception& e)
121         {
122             std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType
123                       << " ERROR=" << e.what() << "\n";
124             pldm::utils::reportError(
125                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
126         }
127         catch (const std::exception& e)
128         {
129             std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType
130                       << " ERROR=" << e.what() << "\n";
131             pldm::utils::reportError(
132                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
133         }
134     }
135 }
136 
137 Response Handler::getPDR(const pldm_msg* request, size_t payloadLength)
138 {
139     // Build FRU table if not built, since entity association PDR's are built
140     // when the FRU table is constructed.
141     if (fruHandler)
142     {
143         fruHandler->buildFRUTable();
144     }
145 
146     if (!pdrCreated)
147     {
148         generateTerminusLocatorPDR(pdrRepo);
149         generate(*dBusIntf, pdrJsonsDir, pdrRepo);
150         pdrCreated = true;
151     }
152 
153     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_MIN_RESP_BYTES, 0);
154     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
155 
156     if (payloadLength != PLDM_GET_PDR_REQ_BYTES)
157     {
158         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
159     }
160 
161     uint32_t recordHandle{};
162     uint32_t dataTransferHandle{};
163     uint8_t transferOpFlag{};
164     uint16_t reqSizeBytes{};
165     uint16_t recordChangeNum{};
166 
167     auto rc = decode_get_pdr_req(request, payloadLength, &recordHandle,
168                                  &dataTransferHandle, &transferOpFlag,
169                                  &reqSizeBytes, &recordChangeNum);
170     if (rc != PLDM_SUCCESS)
171     {
172         return CmdHandler::ccOnlyResponse(request, rc);
173     }
174 
175     uint16_t respSizeBytes{};
176     uint8_t* recordData = nullptr;
177     try
178     {
179         pdr_utils::PdrEntry e;
180         auto record = pdr::getRecordByHandle(pdrRepo, recordHandle, e);
181         if (record == NULL)
182         {
183             return CmdHandler::ccOnlyResponse(
184                 request, PLDM_PLATFORM_INVALID_RECORD_HANDLE);
185         }
186 
187         if (reqSizeBytes)
188         {
189             respSizeBytes = e.size;
190             if (respSizeBytes > reqSizeBytes)
191             {
192                 respSizeBytes = reqSizeBytes;
193             }
194             recordData = e.data;
195         }
196         response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_MIN_RESP_BYTES +
197                             respSizeBytes,
198                         0);
199         responsePtr = reinterpret_cast<pldm_msg*>(response.data());
200         rc = encode_get_pdr_resp(
201             request->hdr.instance_id, PLDM_SUCCESS, e.handle.nextRecordHandle,
202             0, PLDM_START_AND_END, respSizeBytes, recordData, 0, responsePtr);
203         if (rc != PLDM_SUCCESS)
204         {
205             return ccOnlyResponse(request, rc);
206         }
207     }
208     catch (const std::exception& e)
209     {
210         std::cerr << "Error accessing PDR, HANDLE=" << recordHandle
211                   << " ERROR=" << e.what() << "\n";
212         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR);
213     }
214     return response;
215 }
216 
217 Response Handler::setStateEffecterStates(const pldm_msg* request,
218                                          size_t payloadLength)
219 {
220     Response response(
221         sizeof(pldm_msg_hdr) + PLDM_SET_STATE_EFFECTER_STATES_RESP_BYTES, 0);
222     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
223     uint16_t effecterId;
224     uint8_t compEffecterCnt;
225     constexpr auto maxCompositeEffecterCnt = 8;
226     std::vector<set_effecter_state_field> stateField(maxCompositeEffecterCnt,
227                                                      {0, 0});
228 
229     if ((payloadLength > PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES) ||
230         (payloadLength < sizeof(effecterId) + sizeof(compEffecterCnt) +
231                              sizeof(set_effecter_state_field)))
232     {
233         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
234     }
235 
236     int rc = decode_set_state_effecter_states_req(request, payloadLength,
237                                                   &effecterId, &compEffecterCnt,
238                                                   stateField.data());
239 
240     if (rc != PLDM_SUCCESS)
241     {
242         return CmdHandler::ccOnlyResponse(request, rc);
243     }
244 
245     stateField.resize(compEffecterCnt);
246     const pldm::utils::DBusHandler dBusIntf;
247     rc = platform_state_effecter::setStateEffecterStatesHandler<
248         pldm::utils::DBusHandler, Handler>(dBusIntf, *this, effecterId,
249                                            stateField);
250     if (rc != PLDM_SUCCESS)
251     {
252         return CmdHandler::ccOnlyResponse(request, rc);
253     }
254 
255     rc = encode_set_state_effecter_states_resp(request->hdr.instance_id, rc,
256                                                responsePtr);
257     if (rc != PLDM_SUCCESS)
258     {
259         return ccOnlyResponse(request, rc);
260     }
261 
262     return response;
263 }
264 
265 Response Handler::platformEventMessage(const pldm_msg* request,
266                                        size_t payloadLength)
267 {
268     uint8_t formatVersion{};
269     uint8_t tid{};
270     uint8_t eventClass{};
271     size_t offset{};
272 
273     auto rc = decode_platform_event_message_req(
274         request, payloadLength, &formatVersion, &tid, &eventClass, &offset);
275     if (rc != PLDM_SUCCESS)
276     {
277         return CmdHandler::ccOnlyResponse(request, rc);
278     }
279 
280     try
281     {
282         const auto& handlers = eventHandlers.at(eventClass);
283         for (const auto& handler : handlers)
284         {
285             auto rc =
286                 handler(request, payloadLength, formatVersion, tid, offset);
287             if (rc != PLDM_SUCCESS)
288             {
289                 return CmdHandler::ccOnlyResponse(request, rc);
290             }
291         }
292     }
293     catch (const std::out_of_range& e)
294     {
295         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_DATA);
296     }
297 
298     Response response(
299         sizeof(pldm_msg_hdr) + PLDM_PLATFORM_EVENT_MESSAGE_RESP_BYTES, 0);
300     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
301 
302     rc = encode_platform_event_message_resp(request->hdr.instance_id, rc,
303                                             PLDM_EVENT_NO_LOGGING, responsePtr);
304     if (rc != PLDM_SUCCESS)
305     {
306         return ccOnlyResponse(request, rc);
307     }
308 
309     return response;
310 }
311 
312 int Handler::sensorEvent(const pldm_msg* request, size_t payloadLength,
313                          uint8_t /*formatVersion*/, uint8_t tid,
314                          size_t eventDataOffset)
315 {
316     uint16_t sensorId{};
317     uint8_t eventClass{};
318     size_t eventClassDataOffset{};
319     auto eventData =
320         reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
321     auto eventDataSize = payloadLength - eventDataOffset;
322 
323     auto rc = decode_sensor_event_data(eventData, eventDataSize, &sensorId,
324                                        &eventClass, &eventClassDataOffset);
325     if (rc != PLDM_SUCCESS)
326     {
327         return rc;
328     }
329 
330     auto eventClassData = reinterpret_cast<const uint8_t*>(request->payload) +
331                           eventDataOffset + eventClassDataOffset;
332     auto eventClassDataSize =
333         payloadLength - eventDataOffset - eventClassDataOffset;
334 
335     if (eventClass == PLDM_STATE_SENSOR_STATE)
336     {
337         uint8_t sensorOffset{};
338         uint8_t eventState{};
339         uint8_t previousEventState{};
340 
341         rc = decode_state_sensor_data(eventClassData, eventClassDataSize,
342                                       &sensorOffset, &eventState,
343                                       &previousEventState);
344         if (rc != PLDM_SUCCESS)
345         {
346             return PLDM_ERROR;
347         }
348 
349         // Emitting state sensor event signal
350         emitStateSensorEventSignal(tid, sensorId, sensorOffset, eventState,
351                                    previousEventState);
352 
353         // If there are no HOST PDR's, there is no further action
354         if (hostPDRHandler == NULL)
355         {
356             return PLDM_SUCCESS;
357         }
358 
359         // Handle PLDM events for which PDR is available
360         SensorEntry sensorEntry{tid, sensorId};
361 
362         pldm::pdr::EntityInfo entityInfo{};
363         pldm::pdr::CompositeSensorStates compositeSensorStates{};
364 
365         try
366         {
367             std::tie(entityInfo, compositeSensorStates) =
368                 hostPDRHandler->lookupSensorInfo(sensorEntry);
369         }
370         catch (const std::out_of_range& e)
371         {
372             // If there is no mapping for tid, sensorId combination, try
373             // PLDM_TID_RESERVED, sensorId for terminus that is yet to
374             // implement TL PDR.
375             try
376             {
377                 sensorEntry.terminusID = PLDM_TID_RESERVED;
378                 std::tie(entityInfo, compositeSensorStates) =
379                     hostPDRHandler->lookupSensorInfo(sensorEntry);
380             }
381             // If there is no mapping for events return PLDM_SUCCESS
382             catch (const std::out_of_range& e)
383             {
384                 return PLDM_SUCCESS;
385             }
386         }
387 
388         if (sensorOffset >= compositeSensorStates.size())
389         {
390             return PLDM_ERROR_INVALID_DATA;
391         }
392 
393         const auto& possibleStates = compositeSensorStates[sensorOffset];
394         if (possibleStates.find(eventState) == possibleStates.end())
395         {
396             return PLDM_ERROR_INVALID_DATA;
397         }
398 
399         const auto& [containerId, entityType, entityInstance] = entityInfo;
400         events::StateSensorEntry stateSensorEntry{containerId, entityType,
401                                                   entityInstance, sensorOffset};
402         return stateSensorHandler.eventAction(stateSensorEntry, eventState);
403     }
404     else
405     {
406         return PLDM_ERROR_INVALID_DATA;
407     }
408 
409     return PLDM_SUCCESS;
410 }
411 
412 int Handler::pldmPDRRepositoryChgEvent(const pldm_msg* request,
413                                        size_t payloadLength,
414                                        uint8_t /*formatVersion*/,
415                                        uint8_t /*tid*/, size_t eventDataOffset)
416 {
417     uint8_t eventDataFormat{};
418     uint8_t numberOfChangeRecords{};
419     size_t dataOffset{};
420 
421     auto eventData =
422         reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
423     auto eventDataSize = payloadLength - eventDataOffset;
424 
425     auto rc = decode_pldm_pdr_repository_chg_event_data(
426         eventData, eventDataSize, &eventDataFormat, &numberOfChangeRecords,
427         &dataOffset);
428     if (rc != PLDM_SUCCESS)
429     {
430         return rc;
431     }
432 
433     PDRRecordHandles pdrRecordHandles;
434 
435     if (eventDataFormat == FORMAT_IS_PDR_TYPES)
436     {
437         return PLDM_ERROR_INVALID_DATA;
438     }
439 
440     if (eventDataFormat == FORMAT_IS_PDR_HANDLES)
441     {
442         uint8_t eventDataOperation{};
443         uint8_t numberOfChangeEntries{};
444 
445         auto changeRecordData = eventData + dataOffset;
446         auto changeRecordDataSize = eventDataSize - dataOffset;
447 
448         while (changeRecordDataSize)
449         {
450             rc = decode_pldm_pdr_repository_change_record_data(
451                 changeRecordData, changeRecordDataSize, &eventDataOperation,
452                 &numberOfChangeEntries, &dataOffset);
453 
454             if (rc != PLDM_SUCCESS)
455             {
456                 return rc;
457             }
458 
459             if (eventDataOperation == PLDM_RECORDS_ADDED)
460             {
461                 rc = getPDRRecordHandles(
462                     reinterpret_cast<const ChangeEntry*>(changeRecordData +
463                                                          dataOffset),
464                     changeRecordDataSize - dataOffset,
465                     static_cast<size_t>(numberOfChangeEntries),
466                     pdrRecordHandles);
467 
468                 if (rc != PLDM_SUCCESS)
469                 {
470                     return rc;
471                 }
472             }
473 
474             changeRecordData +=
475                 dataOffset + (numberOfChangeEntries * sizeof(ChangeEntry));
476             changeRecordDataSize -=
477                 dataOffset + (numberOfChangeEntries * sizeof(ChangeEntry));
478         }
479     }
480     if (hostPDRHandler)
481     {
482         hostPDRHandler->fetchPDR(std::move(pdrRecordHandles));
483     }
484 
485     return PLDM_SUCCESS;
486 }
487 
488 int Handler::getPDRRecordHandles(const ChangeEntry* changeEntryData,
489                                  size_t changeEntryDataSize,
490                                  size_t numberOfChangeEntries,
491                                  PDRRecordHandles& pdrRecordHandles)
492 {
493     if (numberOfChangeEntries > (changeEntryDataSize / sizeof(ChangeEntry)))
494     {
495         return PLDM_ERROR_INVALID_DATA;
496     }
497     for (size_t i = 0; i < numberOfChangeEntries; i++)
498     {
499         pdrRecordHandles.push_back(changeEntryData[i]);
500     }
501     return PLDM_SUCCESS;
502 }
503 
504 Response Handler::setNumericEffecterValue(const pldm_msg* request,
505                                           size_t payloadLength)
506 {
507     Response response(sizeof(pldm_msg_hdr) +
508                       PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES);
509     uint16_t effecterId{};
510     uint8_t effecterDataSize{};
511     uint8_t effecterValue[4] = {};
512 
513     if ((payloadLength > sizeof(effecterId) + sizeof(effecterDataSize) +
514                              sizeof(union_effecter_data_size)) ||
515         (payloadLength < sizeof(effecterId) + sizeof(effecterDataSize) + 1))
516     {
517         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
518     }
519 
520     int rc = decode_set_numeric_effecter_value_req(
521         request, payloadLength, &effecterId, &effecterDataSize,
522         reinterpret_cast<uint8_t*>(&effecterValue));
523 
524     if (rc == PLDM_SUCCESS)
525     {
526         const pldm::utils::DBusHandler dBusIntf;
527         rc = platform_numeric_effecter::setNumericEffecterValueHandler<
528             pldm::utils::DBusHandler, Handler>(dBusIntf, *this, effecterId,
529                                                effecterDataSize, effecterValue,
530                                                sizeof(effecterValue));
531     }
532 
533     return ccOnlyResponse(request, rc);
534 }
535 
536 void Handler::generateTerminusLocatorPDR(Repo& repo)
537 {
538     std::vector<uint8_t> pdrBuffer(sizeof(pldm_terminus_locator_pdr));
539 
540     auto pdr = reinterpret_cast<pldm_terminus_locator_pdr*>(pdrBuffer.data());
541 
542     pdr->hdr.record_handle = 0;
543     pdr->hdr.version = 1;
544     pdr->hdr.type = PLDM_TERMINUS_LOCATOR_PDR;
545     pdr->hdr.record_change_num = 0;
546     pdr->hdr.length = sizeof(pldm_terminus_locator_pdr) - sizeof(pldm_pdr_hdr);
547     pdr->terminus_handle = BmcPldmTerminusHandle;
548     pdr->validity = PLDM_TL_PDR_VALID;
549     pdr->tid = BmcTerminusId;
550     pdr->container_id = 0x0;
551     pdr->terminus_locator_type = PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID;
552     pdr->terminus_locator_value_size =
553         sizeof(pldm_terminus_locator_type_mctp_eid);
554     auto locatorValue = reinterpret_cast<pldm_terminus_locator_type_mctp_eid*>(
555         pdr->terminus_locator_value);
556     locatorValue->eid = BmcMctpEid;
557 
558     PdrEntry pdrEntry{};
559     pdrEntry.data = pdrBuffer.data();
560     pdrEntry.size = pdrBuffer.size();
561     repo.addRecord(pdrEntry);
562 }
563 
564 Response Handler::getStateSensorReadings(const pldm_msg* request,
565                                          size_t payloadLength)
566 {
567     uint16_t sensorId{};
568     bitfield8_t sensorRearm{};
569     uint8_t reserved{};
570 
571     if (payloadLength != PLDM_GET_SENSOR_READING_REQ_BYTES)
572     {
573         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
574     }
575 
576     int rc = decode_get_state_sensor_readings_req(
577         request, payloadLength, &sensorId, &sensorRearm, &reserved);
578 
579     if (rc != PLDM_SUCCESS)
580     {
581         return ccOnlyResponse(request, rc);
582     }
583 
584     // 0x01 to 0x08
585     uint8_t sensorRearmCout = getBitfieldCount(sensorRearm);
586     std::vector<get_sensor_state_field> stateField(sensorRearmCout);
587     uint8_t comSensorCnt{};
588     const pldm::utils::DBusHandler dBusIntf;
589     rc = platform_state_sensor::getStateSensorReadingsHandler<
590         pldm::utils::DBusHandler, Handler>(
591         dBusIntf, *this, sensorId, sensorRearmCout, comSensorCnt, stateField);
592 
593     if (rc != PLDM_SUCCESS)
594     {
595         return ccOnlyResponse(request, rc);
596     }
597 
598     Response response(sizeof(pldm_msg_hdr) +
599                       PLDM_GET_STATE_SENSOR_READINGS_MIN_RESP_BYTES +
600                       sizeof(get_sensor_state_field) * comSensorCnt);
601     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
602     rc = encode_get_state_sensor_readings_resp(request->hdr.instance_id, rc,
603                                                comSensorCnt, stateField.data(),
604                                                responsePtr);
605     if (rc != PLDM_SUCCESS)
606     {
607         return ccOnlyResponse(request, rc);
608     }
609 
610     return response;
611 }
612 
613 } // namespace platform
614 } // namespace responder
615 } // namespace pldm
616