xref: /openbmc/pldm/common/utils.cpp (revision e3596384)
1 #include "utils.hpp"
2 
3 #include <libpldm/pdr.h>
4 #include <libpldm/pldm_types.h>
5 #include <linux/mctp.h>
6 
7 #include <phosphor-logging/lg2.hpp>
8 #include <xyz/openbmc_project/Common/error.hpp>
9 #include <xyz/openbmc_project/Logging/Create/client.hpp>
10 #include <xyz/openbmc_project/ObjectMapper/client.hpp>
11 
12 #include <algorithm>
13 #include <array>
14 #include <cctype>
15 #include <ctime>
16 #include <fstream>
17 #include <iostream>
18 #include <map>
19 #include <stdexcept>
20 #include <string>
21 #include <vector>
22 
23 PHOSPHOR_LOG2_USING;
24 
25 namespace pldm
26 {
27 namespace utils
28 {
29 
30 std::vector<std::vector<uint8_t>> findStateEffecterPDR(uint8_t /*tid*/,
31                                                        uint16_t entityID,
32                                                        uint16_t stateSetId,
33                                                        const pldm_pdr* repo)
34 {
35     uint8_t* outData = nullptr;
36     uint32_t size{};
37     const pldm_pdr_record* record{};
38     std::vector<std::vector<uint8_t>> pdrs;
39     try
40     {
41         do
42         {
43             record = pldm_pdr_find_record_by_type(repo, PLDM_STATE_EFFECTER_PDR,
44                                                   record, &outData, &size);
45             if (record)
46             {
47                 auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(outData);
48                 auto compositeEffecterCount = pdr->composite_effecter_count;
49                 auto possible_states_start = pdr->possible_states;
50 
51                 for (auto effecters = 0x00; effecters < compositeEffecterCount;
52                      effecters++)
53                 {
54                     auto possibleStates =
55                         reinterpret_cast<state_effecter_possible_states*>(
56                             possible_states_start);
57                     auto setId = possibleStates->state_set_id;
58                     auto possibleStateSize =
59                         possibleStates->possible_states_size;
60 
61                     if (pdr->entity_type == entityID && setId == stateSetId)
62                     {
63                         std::vector<uint8_t> effecter_pdr(&outData[0],
64                                                           &outData[size]);
65                         pdrs.emplace_back(std::move(effecter_pdr));
66                         break;
67                     }
68                     possible_states_start += possibleStateSize + sizeof(setId) +
69                                              sizeof(possibleStateSize);
70                 }
71             }
72 
73         } while (record);
74     }
75     catch (const std::exception& e)
76     {
77         error("Failed to obtain a record, error - {ERROR}", "ERROR", e);
78     }
79 
80     return pdrs;
81 }
82 
83 std::vector<std::vector<uint8_t>> findStateSensorPDR(uint8_t /*tid*/,
84                                                      uint16_t entityID,
85                                                      uint16_t stateSetId,
86                                                      const pldm_pdr* repo)
87 {
88     uint8_t* outData = nullptr;
89     uint32_t size{};
90     const pldm_pdr_record* record{};
91     std::vector<std::vector<uint8_t>> pdrs;
92     try
93     {
94         do
95         {
96             record = pldm_pdr_find_record_by_type(repo, PLDM_STATE_SENSOR_PDR,
97                                                   record, &outData, &size);
98             if (record)
99             {
100                 auto pdr = reinterpret_cast<pldm_state_sensor_pdr*>(outData);
101                 auto compositeSensorCount = pdr->composite_sensor_count;
102                 auto possible_states_start = pdr->possible_states;
103 
104                 for (auto sensors = 0x00; sensors < compositeSensorCount;
105                      sensors++)
106                 {
107                     auto possibleStates =
108                         reinterpret_cast<state_sensor_possible_states*>(
109                             possible_states_start);
110                     auto setId = possibleStates->state_set_id;
111                     auto possibleStateSize =
112                         possibleStates->possible_states_size;
113 
114                     if (pdr->entity_type == entityID && setId == stateSetId)
115                     {
116                         std::vector<uint8_t> sensor_pdr(&outData[0],
117                                                         &outData[size]);
118                         pdrs.emplace_back(std::move(sensor_pdr));
119                         break;
120                     }
121                     possible_states_start += possibleStateSize + sizeof(setId) +
122                                              sizeof(possibleStateSize);
123                 }
124             }
125 
126         } while (record);
127     }
128     catch (const std::exception& e)
129     {
130         error(
131             "Failed to obtain a record with entity ID '{ENTITYID}', error - {ERROR}",
132             "ENTITYID", entityID, "ERROR", e);
133     }
134 
135     return pdrs;
136 }
137 
138 uint8_t readHostEID()
139 {
140     uint8_t eid{};
141     std::ifstream eidFile{HOST_EID_PATH};
142     if (!eidFile.good())
143     {
144         error("Failed to open remote terminus EID file at path '{PATH}'",
145               "PATH", static_cast<std::string>(HOST_EID_PATH));
146     }
147     else
148     {
149         std::string eidStr;
150         eidFile >> eidStr;
151         if (!eidStr.empty())
152         {
153             eid = atoi(eidStr.c_str());
154         }
155         else
156         {
157             error("Remote terminus EID file was empty");
158         }
159     }
160 
161     return eid;
162 }
163 
164 bool isValidEID(eid mctpEid)
165 {
166     if (mctpEid == MCTP_ADDR_NULL || mctpEid < MCTP_START_VALID_EID ||
167         mctpEid == MCTP_ADDR_ANY)
168     {
169         return false;
170     }
171 
172     return true;
173 }
174 
175 uint8_t getNumPadBytes(uint32_t data)
176 {
177     uint8_t pad;
178     pad = ((data % 4) ? (4 - data % 4) : 0);
179     return pad;
180 } // end getNumPadBytes
181 
182 bool uintToDate(uint64_t data, uint16_t* year, uint8_t* month, uint8_t* day,
183                 uint8_t* hour, uint8_t* min, uint8_t* sec)
184 {
185     constexpr uint64_t max_data = 29991231115959;
186     constexpr uint64_t min_data = 19700101000000;
187     if (data < min_data || data > max_data)
188     {
189         return false;
190     }
191 
192     *year = data / 10000000000;
193     data = data % 10000000000;
194     *month = data / 100000000;
195     data = data % 100000000;
196     *day = data / 1000000;
197     data = data % 1000000;
198     *hour = data / 10000;
199     data = data % 10000;
200     *min = data / 100;
201     *sec = data % 100;
202 
203     return true;
204 }
205 
206 std::optional<std::vector<set_effecter_state_field>>
207     parseEffecterData(const std::vector<uint8_t>& effecterData,
208                       uint8_t effecterCount)
209 {
210     std::vector<set_effecter_state_field> stateField;
211 
212     if (effecterData.size() != effecterCount * 2)
213     {
214         return std::nullopt;
215     }
216 
217     for (uint8_t i = 0; i < effecterCount; ++i)
218     {
219         uint8_t set_request = effecterData[i * 2] == PLDM_REQUEST_SET
220                                   ? PLDM_REQUEST_SET
221                                   : PLDM_NO_CHANGE;
222         set_effecter_state_field filed{set_request, effecterData[i * 2 + 1]};
223         stateField.emplace_back(std::move(filed));
224     }
225 
226     return std::make_optional(std::move(stateField));
227 }
228 
229 std::string DBusHandler::getService(const char* path,
230                                     const char* interface) const
231 {
232     using DbusInterfaceList = std::vector<std::string>;
233     std::map<std::string, std::vector<std::string>> mapperResponse;
234     auto& bus = DBusHandler::getBus();
235 
236     auto mapper = bus.new_method_call(ObjectMapper::default_service,
237                                       ObjectMapper::instance_path,
238                                       ObjectMapper::interface, "GetObject");
239 
240     if (interface)
241     {
242         mapper.append(path, DbusInterfaceList({interface}));
243     }
244     else
245     {
246         mapper.append(path, DbusInterfaceList({}));
247     }
248 
249     auto mapperResponseMsg = bus.call(mapper, dbusTimeout);
250     mapperResponseMsg.read(mapperResponse);
251     return mapperResponse.begin()->first;
252 }
253 
254 GetSubTreeResponse
255     DBusHandler::getSubtree(const std::string& searchPath, int depth,
256                             const std::vector<std::string>& ifaceList) const
257 {
258     auto& bus = pldm::utils::DBusHandler::getBus();
259     auto method = bus.new_method_call(ObjectMapper::default_service,
260                                       ObjectMapper::instance_path,
261                                       ObjectMapper::interface, "GetSubTree");
262     method.append(searchPath, depth, ifaceList);
263     auto reply = bus.call(method, dbusTimeout);
264     GetSubTreeResponse response;
265     reply.read(response);
266     return response;
267 }
268 
269 GetSubTreePathsResponse DBusHandler::getSubTreePaths(
270     const std::string& objectPath, int depth,
271     const std::vector<std::string>& ifaceList) const
272 {
273     std::vector<std::string> paths;
274     auto& bus = pldm::utils::DBusHandler::getBus();
275     auto method = bus.new_method_call(
276         ObjectMapper::default_service, ObjectMapper::instance_path,
277         ObjectMapper::interface, "GetSubTreePaths");
278     method.append(objectPath, depth, ifaceList);
279     auto reply = bus.call(method, dbusTimeout);
280 
281     reply.read(paths);
282     return paths;
283 }
284 
285 void reportError(const char* errorMsg)
286 {
287     auto& bus = pldm::utils::DBusHandler::getBus();
288     using LoggingCreate =
289         sdbusplus::client::xyz::openbmc_project::logging::Create<>;
290     try
291     {
292         using namespace sdbusplus::xyz::openbmc_project::Logging::server;
293         auto severity =
294             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
295                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
296                     Error);
297         auto method = bus.new_method_call(LoggingCreate::default_service,
298                                           LoggingCreate::instance_path,
299                                           LoggingCreate::interface, "Create");
300 
301         std::map<std::string, std::string> addlData{};
302         method.append(errorMsg, severity, addlData);
303         bus.call_noreply(method, dbusTimeout);
304     }
305     catch (const std::exception& e)
306     {
307         error(
308             "Failed to do dbus call for creating error log for '{ERRMSG}' at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}",
309             "ERRMSG", errorMsg, "PATH", LoggingCreate::instance_path,
310             "INTERFACE", LoggingCreate::interface, "ERROR", e);
311     }
312 }
313 
314 void DBusHandler::setDbusProperty(const DBusMapping& dBusMap,
315                                   const PropertyValue& value) const
316 {
317     auto setDbusValue = [&dBusMap, this](const auto& variant) {
318         auto& bus = getBus();
319         auto service = getService(dBusMap.objectPath.c_str(),
320                                   dBusMap.interface.c_str());
321         auto method = bus.new_method_call(
322             service.c_str(), dBusMap.objectPath.c_str(), dbusProperties, "Set");
323         method.append(dBusMap.interface.c_str(), dBusMap.propertyName.c_str(),
324                       variant);
325         bus.call_noreply(method, dbusTimeout);
326     };
327 
328     if (dBusMap.propertyType == "uint8_t")
329     {
330         std::variant<uint8_t> v = std::get<uint8_t>(value);
331         setDbusValue(v);
332     }
333     else if (dBusMap.propertyType == "bool")
334     {
335         std::variant<bool> v = std::get<bool>(value);
336         setDbusValue(v);
337     }
338     else if (dBusMap.propertyType == "int16_t")
339     {
340         std::variant<int16_t> v = std::get<int16_t>(value);
341         setDbusValue(v);
342     }
343     else if (dBusMap.propertyType == "uint16_t")
344     {
345         std::variant<uint16_t> v = std::get<uint16_t>(value);
346         setDbusValue(v);
347     }
348     else if (dBusMap.propertyType == "int32_t")
349     {
350         std::variant<int32_t> v = std::get<int32_t>(value);
351         setDbusValue(v);
352     }
353     else if (dBusMap.propertyType == "uint32_t")
354     {
355         std::variant<uint32_t> v = std::get<uint32_t>(value);
356         setDbusValue(v);
357     }
358     else if (dBusMap.propertyType == "int64_t")
359     {
360         std::variant<int64_t> v = std::get<int64_t>(value);
361         setDbusValue(v);
362     }
363     else if (dBusMap.propertyType == "uint64_t")
364     {
365         std::variant<uint64_t> v = std::get<uint64_t>(value);
366         setDbusValue(v);
367     }
368     else if (dBusMap.propertyType == "double")
369     {
370         std::variant<double> v = std::get<double>(value);
371         setDbusValue(v);
372     }
373     else if (dBusMap.propertyType == "string")
374     {
375         std::variant<std::string> v = std::get<std::string>(value);
376         setDbusValue(v);
377     }
378     else
379     {
380         error("Unsupported property type '{TYPE}'", "TYPE",
381               dBusMap.propertyType);
382         throw std::invalid_argument("UnSupported Dbus Type");
383     }
384 }
385 
386 PropertyValue DBusHandler::getDbusPropertyVariant(
387     const char* objPath, const char* dbusProp, const char* dbusInterface) const
388 {
389     auto& bus = DBusHandler::getBus();
390     auto service = getService(objPath, dbusInterface);
391     auto method = bus.new_method_call(service.c_str(), objPath, dbusProperties,
392                                       "Get");
393     method.append(dbusInterface, dbusProp);
394     return bus.call(method, dbusTimeout).unpack<PropertyValue>();
395 }
396 
397 ObjectValueTree DBusHandler::getManagedObj(const char* service,
398                                            const char* rootPath)
399 {
400     auto& bus = DBusHandler::getBus();
401     auto method = bus.new_method_call(service, rootPath,
402                                       "org.freedesktop.DBus.ObjectManager",
403                                       "GetManagedObjects");
404     return bus.call(method).unpack<ObjectValueTree>();
405 }
406 
407 PropertyMap
408     DBusHandler::getDbusPropertiesVariant(const char* serviceName,
409                                           const char* objPath,
410                                           const char* dbusInterface) const
411 {
412     auto& bus = DBusHandler::getBus();
413     auto method = bus.new_method_call(serviceName, objPath, dbusProperties,
414                                       "GetAll");
415     method.append(dbusInterface);
416     return bus.call(method, dbusTimeout).unpack<PropertyMap>();
417 }
418 
419 PropertyValue jsonEntryToDbusVal(std::string_view type,
420                                  const nlohmann::json& value)
421 {
422     PropertyValue propValue{};
423     if (type == "uint8_t")
424     {
425         propValue = static_cast<uint8_t>(value);
426     }
427     else if (type == "uint16_t")
428     {
429         propValue = static_cast<uint16_t>(value);
430     }
431     else if (type == "uint32_t")
432     {
433         propValue = static_cast<uint32_t>(value);
434     }
435     else if (type == "uint64_t")
436     {
437         propValue = static_cast<uint64_t>(value);
438     }
439     else if (type == "int16_t")
440     {
441         propValue = static_cast<int16_t>(value);
442     }
443     else if (type == "int32_t")
444     {
445         propValue = static_cast<int32_t>(value);
446     }
447     else if (type == "int64_t")
448     {
449         propValue = static_cast<int64_t>(value);
450     }
451     else if (type == "bool")
452     {
453         propValue = static_cast<bool>(value);
454     }
455     else if (type == "double")
456     {
457         propValue = static_cast<double>(value);
458     }
459     else if (type == "string")
460     {
461         propValue = static_cast<std::string>(value);
462     }
463     else
464     {
465         error("Unknown D-Bus property type '{TYPE}'", "TYPE", type);
466     }
467 
468     return propValue;
469 }
470 
471 uint16_t findStateEffecterId(const pldm_pdr* pdrRepo, uint16_t entityType,
472                              uint16_t entityInstance, uint16_t containerId,
473                              uint16_t stateSetId, bool localOrRemote)
474 {
475     uint8_t* pdrData = nullptr;
476     uint32_t pdrSize{};
477     const pldm_pdr_record* record{};
478     do
479     {
480         record = pldm_pdr_find_record_by_type(pdrRepo, PLDM_STATE_EFFECTER_PDR,
481                                               record, &pdrData, &pdrSize);
482         if (record && (localOrRemote ^ pldm_pdr_record_is_remote(record)))
483         {
484             auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrData);
485             auto compositeEffecterCount = pdr->composite_effecter_count;
486             auto possible_states_start = pdr->possible_states;
487 
488             for (auto effecters = 0x00; effecters < compositeEffecterCount;
489                  effecters++)
490             {
491                 auto possibleStates =
492                     reinterpret_cast<state_effecter_possible_states*>(
493                         possible_states_start);
494                 auto setId = possibleStates->state_set_id;
495                 auto possibleStateSize = possibleStates->possible_states_size;
496 
497                 if (entityType == pdr->entity_type &&
498                     entityInstance == pdr->entity_instance &&
499                     containerId == pdr->container_id && stateSetId == setId)
500                 {
501                     return pdr->effecter_id;
502                 }
503                 possible_states_start += possibleStateSize + sizeof(setId) +
504                                          sizeof(possibleStateSize);
505             }
506         }
507     } while (record);
508 
509     return PLDM_INVALID_EFFECTER_ID;
510 }
511 
512 int emitStateSensorEventSignal(uint8_t tid, uint16_t sensorId,
513                                uint8_t sensorOffset, uint8_t eventState,
514                                uint8_t previousEventState)
515 {
516     try
517     {
518         auto& bus = DBusHandler::getBus();
519         auto msg = bus.new_signal("/xyz/openbmc_project/pldm",
520                                   "xyz.openbmc_project.PLDM.Event",
521                                   "StateSensorEvent");
522         msg.append(tid, sensorId, sensorOffset, eventState, previousEventState);
523 
524         msg.signal_send();
525     }
526     catch (const std::exception& e)
527     {
528         error("Failed to emit pldm event signal, error - {ERROR}", "ERROR", e);
529         return PLDM_ERROR;
530     }
531 
532     return PLDM_SUCCESS;
533 }
534 
535 uint16_t findStateSensorId(const pldm_pdr* pdrRepo, uint8_t tid,
536                            uint16_t entityType, uint16_t entityInstance,
537                            uint16_t containerId, uint16_t stateSetId)
538 {
539     auto pdrs = findStateSensorPDR(tid, entityType, stateSetId, pdrRepo);
540     for (auto pdr : pdrs)
541     {
542         auto sensorPdr = reinterpret_cast<pldm_state_sensor_pdr*>(pdr.data());
543         auto compositeSensorCount = sensorPdr->composite_sensor_count;
544         auto possible_states_start = sensorPdr->possible_states;
545 
546         for (auto sensors = 0x00; sensors < compositeSensorCount; sensors++)
547         {
548             auto possibleStates =
549                 reinterpret_cast<state_sensor_possible_states*>(
550                     possible_states_start);
551             auto setId = possibleStates->state_set_id;
552             auto possibleStateSize = possibleStates->possible_states_size;
553             if (entityType == sensorPdr->entity_type &&
554                 entityInstance == sensorPdr->entity_instance &&
555                 stateSetId == setId && containerId == sensorPdr->container_id)
556             {
557                 return sensorPdr->sensor_id;
558             }
559             possible_states_start += possibleStateSize + sizeof(setId) +
560                                      sizeof(possibleStateSize);
561         }
562     }
563     return PLDM_INVALID_EFFECTER_ID;
564 }
565 
566 void printBuffer(bool isTx, const std::vector<uint8_t>& buffer)
567 {
568     if (buffer.empty())
569     {
570         return;
571     }
572 
573     std::cout << (isTx ? "Tx: " : "Rx: ");
574 
575     std::ranges::for_each(buffer, [](uint8_t byte) {
576         std::cout << std::format("{:02x} ", byte);
577     });
578 
579     std::cout << std::endl;
580 }
581 
582 std::string toString(const struct variable_field& var)
583 {
584     if (var.ptr == nullptr || !var.length)
585     {
586         return "";
587     }
588 
589     std::string str(reinterpret_cast<const char*>(var.ptr), var.length);
590     std::replace_if(
591         str.begin(), str.end(), [](const char& c) { return !isprint(c); }, ' ');
592     return str;
593 }
594 
595 std::vector<std::string> split(std::string_view srcStr, std::string_view delim,
596                                std::string_view trimStr)
597 {
598     std::vector<std::string> out;
599     size_t start;
600     size_t end = 0;
601 
602     while ((start = srcStr.find_first_not_of(delim, end)) != std::string::npos)
603     {
604         end = srcStr.find(delim, start);
605         std::string_view dstStr = srcStr.substr(start, end - start);
606         if (!trimStr.empty())
607         {
608             dstStr.remove_prefix(dstStr.find_first_not_of(trimStr));
609             dstStr.remove_suffix(dstStr.size() - 1 -
610                                  dstStr.find_last_not_of(trimStr));
611         }
612 
613         if (!dstStr.empty())
614         {
615             out.push_back(std::string(dstStr));
616         }
617     }
618 
619     return out;
620 }
621 
622 std::string getCurrentSystemTime()
623 {
624     const auto zonedTime{std::chrono::zoned_time{
625         std::chrono::current_zone(), std::chrono::system_clock::now()}};
626     return std::format("{:%F %Z %T}", zonedTime);
627 }
628 
629 bool checkForFruPresence(const std::string& objPath)
630 {
631     bool isPresent = false;
632     static constexpr auto presentInterface =
633         "xyz.openbmc_project.Inventory.Item";
634     static constexpr auto presentProperty = "Present";
635     try
636     {
637         auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant(
638             objPath.c_str(), presentProperty, presentInterface);
639         isPresent = std::get<bool>(propVal);
640     }
641     catch (const sdbusplus::exception::SdBusError& e)
642     {
643         error("Failed to check for FRU presence at {PATH}, error - {ERROR}",
644               "PATH", objPath, "ERROR", e);
645     }
646     return isPresent;
647 }
648 
649 bool checkIfLogicalBitSet(const uint16_t& containerId)
650 {
651     return !(containerId & 0x8000);
652 }
653 
654 void setFruPresence(const std::string& fruObjPath, bool present)
655 {
656     pldm::utils::PropertyValue value{present};
657     pldm::utils::DBusMapping dbusMapping;
658     dbusMapping.objectPath = fruObjPath;
659     dbusMapping.interface = "xyz.openbmc_project.Inventory.Item";
660     dbusMapping.propertyName = "Present";
661     dbusMapping.propertyType = "bool";
662     try
663     {
664         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
665     }
666     catch (const std::exception& e)
667     {
668         error(
669             "Failed to set the present property on path '{PATH}', error - {ERROR}.",
670             "PATH", fruObjPath, "ERROR", e);
671     }
672 }
673 
674 } // namespace utils
675 } // namespace pldm
676