xref: /openbmc/pldm/libpldmresponder/platform.cpp (revision 8fc3edbc65b164eb8c90155b935c91e0e714c461)
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 hostPDRHandler->handleStateSensorEvent(stateSensorEntry,
403                                                       eventState);
404     }
405     else
406     {
407         return PLDM_ERROR_INVALID_DATA;
408     }
409 
410     return PLDM_SUCCESS;
411 }
412 
413 int Handler::pldmPDRRepositoryChgEvent(const pldm_msg* request,
414                                        size_t payloadLength,
415                                        uint8_t /*formatVersion*/,
416                                        uint8_t /*tid*/, size_t eventDataOffset)
417 {
418     uint8_t eventDataFormat{};
419     uint8_t numberOfChangeRecords{};
420     size_t dataOffset{};
421 
422     auto eventData =
423         reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
424     auto eventDataSize = payloadLength - eventDataOffset;
425 
426     auto rc = decode_pldm_pdr_repository_chg_event_data(
427         eventData, eventDataSize, &eventDataFormat, &numberOfChangeRecords,
428         &dataOffset);
429     if (rc != PLDM_SUCCESS)
430     {
431         return rc;
432     }
433 
434     PDRRecordHandles pdrRecordHandles;
435 
436     if (eventDataFormat == FORMAT_IS_PDR_TYPES)
437     {
438         return PLDM_ERROR_INVALID_DATA;
439     }
440 
441     if (eventDataFormat == FORMAT_IS_PDR_HANDLES)
442     {
443         uint8_t eventDataOperation{};
444         uint8_t numberOfChangeEntries{};
445 
446         auto changeRecordData = eventData + dataOffset;
447         auto changeRecordDataSize = eventDataSize - dataOffset;
448 
449         while (changeRecordDataSize)
450         {
451             rc = decode_pldm_pdr_repository_change_record_data(
452                 changeRecordData, changeRecordDataSize, &eventDataOperation,
453                 &numberOfChangeEntries, &dataOffset);
454 
455             if (rc != PLDM_SUCCESS)
456             {
457                 return rc;
458             }
459 
460             if (eventDataOperation == PLDM_RECORDS_ADDED)
461             {
462                 rc = getPDRRecordHandles(
463                     reinterpret_cast<const ChangeEntry*>(changeRecordData +
464                                                          dataOffset),
465                     changeRecordDataSize - dataOffset,
466                     static_cast<size_t>(numberOfChangeEntries),
467                     pdrRecordHandles);
468 
469                 if (rc != PLDM_SUCCESS)
470                 {
471                     return rc;
472                 }
473             }
474 
475             changeRecordData +=
476                 dataOffset + (numberOfChangeEntries * sizeof(ChangeEntry));
477             changeRecordDataSize -=
478                 dataOffset + (numberOfChangeEntries * sizeof(ChangeEntry));
479         }
480     }
481     if (hostPDRHandler)
482     {
483         hostPDRHandler->fetchPDR(std::move(pdrRecordHandles));
484     }
485 
486     return PLDM_SUCCESS;
487 }
488 
489 int Handler::getPDRRecordHandles(const ChangeEntry* changeEntryData,
490                                  size_t changeEntryDataSize,
491                                  size_t numberOfChangeEntries,
492                                  PDRRecordHandles& pdrRecordHandles)
493 {
494     if (numberOfChangeEntries > (changeEntryDataSize / sizeof(ChangeEntry)))
495     {
496         return PLDM_ERROR_INVALID_DATA;
497     }
498     for (size_t i = 0; i < numberOfChangeEntries; i++)
499     {
500         pdrRecordHandles.push_back(changeEntryData[i]);
501     }
502     return PLDM_SUCCESS;
503 }
504 
505 Response Handler::setNumericEffecterValue(const pldm_msg* request,
506                                           size_t payloadLength)
507 {
508     Response response(sizeof(pldm_msg_hdr) +
509                       PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES);
510     uint16_t effecterId{};
511     uint8_t effecterDataSize{};
512     uint8_t effecterValue[4] = {};
513 
514     if ((payloadLength > sizeof(effecterId) + sizeof(effecterDataSize) +
515                              sizeof(union_effecter_data_size)) ||
516         (payloadLength < sizeof(effecterId) + sizeof(effecterDataSize) + 1))
517     {
518         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
519     }
520 
521     int rc = decode_set_numeric_effecter_value_req(
522         request, payloadLength, &effecterId, &effecterDataSize,
523         reinterpret_cast<uint8_t*>(&effecterValue));
524 
525     if (rc == PLDM_SUCCESS)
526     {
527         const pldm::utils::DBusHandler dBusIntf;
528         rc = platform_numeric_effecter::setNumericEffecterValueHandler<
529             pldm::utils::DBusHandler, Handler>(dBusIntf, *this, effecterId,
530                                                effecterDataSize, effecterValue,
531                                                sizeof(effecterValue));
532     }
533 
534     return ccOnlyResponse(request, rc);
535 }
536 
537 void Handler::generateTerminusLocatorPDR(Repo& repo)
538 {
539     std::vector<uint8_t> pdrBuffer(sizeof(pldm_terminus_locator_pdr));
540 
541     auto pdr = reinterpret_cast<pldm_terminus_locator_pdr*>(pdrBuffer.data());
542 
543     pdr->hdr.record_handle = 0;
544     pdr->hdr.version = 1;
545     pdr->hdr.type = PLDM_TERMINUS_LOCATOR_PDR;
546     pdr->hdr.record_change_num = 0;
547     pdr->hdr.length = sizeof(pldm_terminus_locator_pdr) - sizeof(pldm_pdr_hdr);
548     pdr->terminus_handle = BmcPldmTerminusHandle;
549     pdr->validity = PLDM_TL_PDR_VALID;
550     pdr->tid = BmcTerminusId;
551     pdr->container_id = 0x0;
552     pdr->terminus_locator_type = PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID;
553     pdr->terminus_locator_value_size =
554         sizeof(pldm_terminus_locator_type_mctp_eid);
555     auto locatorValue = reinterpret_cast<pldm_terminus_locator_type_mctp_eid*>(
556         pdr->terminus_locator_value);
557     locatorValue->eid = BmcMctpEid;
558 
559     PdrEntry pdrEntry{};
560     pdrEntry.data = pdrBuffer.data();
561     pdrEntry.size = pdrBuffer.size();
562     repo.addRecord(pdrEntry);
563 }
564 
565 Response Handler::getStateSensorReadings(const pldm_msg* request,
566                                          size_t payloadLength)
567 {
568     uint16_t sensorId{};
569     bitfield8_t sensorRearm{};
570     uint8_t reserved{};
571 
572     if (payloadLength != PLDM_GET_SENSOR_READING_REQ_BYTES)
573     {
574         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
575     }
576 
577     int rc = decode_get_state_sensor_readings_req(
578         request, payloadLength, &sensorId, &sensorRearm, &reserved);
579 
580     if (rc != PLDM_SUCCESS)
581     {
582         return ccOnlyResponse(request, rc);
583     }
584 
585     // 0x01 to 0x08
586     uint8_t sensorRearmCout = getBitfieldCount(sensorRearm);
587     std::vector<get_sensor_state_field> stateField(sensorRearmCout);
588     uint8_t comSensorCnt{};
589     const pldm::utils::DBusHandler dBusIntf;
590     rc = platform_state_sensor::getStateSensorReadingsHandler<
591         pldm::utils::DBusHandler, Handler>(
592         dBusIntf, *this, sensorId, sensorRearmCout, comSensorCnt, stateField);
593 
594     if (rc != PLDM_SUCCESS)
595     {
596         return ccOnlyResponse(request, rc);
597     }
598 
599     Response response(sizeof(pldm_msg_hdr) +
600                       PLDM_GET_STATE_SENSOR_READINGS_MIN_RESP_BYTES +
601                       sizeof(get_sensor_state_field) * comSensorCnt);
602     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
603     rc = encode_get_state_sensor_readings_resp(request->hdr.instance_id, rc,
604                                                comSensorCnt, stateField.data(),
605                                                responsePtr);
606     if (rc != PLDM_SUCCESS)
607     {
608         return ccOnlyResponse(request, rc);
609     }
610 
611     return response;
612 }
613 
614 } // namespace platform
615 } // namespace responder
616 } // namespace pldm
617