xref: /openbmc/pldm/common/utils.cpp (revision 3618064e)
1 #include "config.h"
2 
3 #include "utils.hpp"
4 
5 #include "libpldm/pdr.h"
6 #include "libpldm/pldm_types.h"
7 
8 #include <xyz/openbmc_project/Common/error.hpp>
9 
10 #include <algorithm>
11 #include <array>
12 #include <cctype>
13 #include <ctime>
14 #include <fstream>
15 #include <iostream>
16 #include <map>
17 #include <stdexcept>
18 #include <string>
19 #include <vector>
20 
21 namespace pldm
22 {
23 namespace utils
24 {
25 constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
26 constexpr auto mapperPath = "/xyz/openbmc_project/object_mapper";
27 constexpr auto mapperInterface = "xyz.openbmc_project.ObjectMapper";
28 
29 std::vector<std::vector<uint8_t>> findStateEffecterPDR(uint8_t /*tid*/,
30                                                        uint16_t entityID,
31                                                        uint16_t stateSetId,
32                                                        const pldm_pdr* repo)
33 {
34     uint8_t* outData = nullptr;
35     uint32_t size{};
36     const pldm_pdr_record* record{};
37     std::vector<std::vector<uint8_t>> pdrs;
38     try
39     {
40         do
41         {
42             record = pldm_pdr_find_record_by_type(repo, PLDM_STATE_EFFECTER_PDR,
43                                                   record, &outData, &size);
44             if (record)
45             {
46                 auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(outData);
47                 auto compositeEffecterCount = pdr->composite_effecter_count;
48                 auto possible_states_start = pdr->possible_states;
49 
50                 for (auto effecters = 0x00; effecters < compositeEffecterCount;
51                      effecters++)
52                 {
53                     auto possibleStates =
54                         reinterpret_cast<state_effecter_possible_states*>(
55                             possible_states_start);
56                     auto setId = possibleStates->state_set_id;
57                     auto possibleStateSize =
58                         possibleStates->possible_states_size;
59 
60                     if (pdr->entity_type == entityID && setId == stateSetId)
61                     {
62                         std::vector<uint8_t> effecter_pdr(&outData[0],
63                                                           &outData[size]);
64                         pdrs.emplace_back(std::move(effecter_pdr));
65                         break;
66                     }
67                     possible_states_start += possibleStateSize + sizeof(setId) +
68                                              sizeof(possibleStateSize);
69                 }
70             }
71 
72         } while (record);
73     }
74     catch (const std::exception& e)
75     {
76         std::cerr << " Failed to obtain a record. ERROR =" << e.what()
77                   << std::endl;
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         std::cerr << " Failed to obtain a record. ERROR =" << e.what()
131                   << std::endl;
132     }
133 
134     return pdrs;
135 }
136 
137 uint8_t readHostEID()
138 {
139     uint8_t eid{};
140     std::ifstream eidFile{HOST_EID_PATH};
141     if (!eidFile.good())
142     {
143         std::cerr << "Could not open host EID file: " << HOST_EID_PATH << "\n";
144     }
145     else
146     {
147         std::string eidStr;
148         eidFile >> eidStr;
149         if (!eidStr.empty())
150         {
151             eid = atoi(eidStr.c_str());
152         }
153         else
154         {
155             std::cerr << "Host EID file was empty"
156                       << "\n";
157         }
158     }
159 
160     return eid;
161 }
162 
163 uint8_t getNumPadBytes(uint32_t data)
164 {
165     uint8_t pad;
166     pad = ((data % 4) ? (4 - data % 4) : 0);
167     return pad;
168 } // end getNumPadBytes
169 
170 bool uintToDate(uint64_t data, uint16_t* year, uint8_t* month, uint8_t* day,
171                 uint8_t* hour, uint8_t* min, uint8_t* sec)
172 {
173     constexpr uint64_t max_data = 29991231115959;
174     constexpr uint64_t min_data = 19700101000000;
175     if (data < min_data || data > max_data)
176     {
177         return false;
178     }
179 
180     *year = data / 10000000000;
181     data = data % 10000000000;
182     *month = data / 100000000;
183     data = data % 100000000;
184     *day = data / 1000000;
185     data = data % 1000000;
186     *hour = data / 10000;
187     data = data % 10000;
188     *min = data / 100;
189     *sec = data % 100;
190 
191     return true;
192 }
193 
194 std::optional<std::vector<set_effecter_state_field>>
195     parseEffecterData(const std::vector<uint8_t>& effecterData,
196                       uint8_t effecterCount)
197 {
198     std::vector<set_effecter_state_field> stateField;
199 
200     if (effecterData.size() != effecterCount * 2)
201     {
202         return std::nullopt;
203     }
204 
205     for (uint8_t i = 0; i < effecterCount; ++i)
206     {
207         uint8_t set_request = effecterData[i * 2] == PLDM_REQUEST_SET
208                                   ? PLDM_REQUEST_SET
209                                   : PLDM_NO_CHANGE;
210         set_effecter_state_field filed{set_request, effecterData[i * 2 + 1]};
211         stateField.emplace_back(std::move(filed));
212     }
213 
214     return std::make_optional(std::move(stateField));
215 }
216 
217 std::string DBusHandler::getService(const char* path,
218                                     const char* interface) const
219 {
220     using DbusInterfaceList = std::vector<std::string>;
221     std::map<std::string, std::vector<std::string>> mapperResponse;
222     auto& bus = DBusHandler::getBus();
223 
224     auto mapper = bus.new_method_call(mapperBusName, mapperPath,
225                                       mapperInterface, "GetObject");
226     mapper.append(path, DbusInterfaceList({interface}));
227 
228     auto mapperResponseMsg = bus.call(mapper);
229     mapperResponseMsg.read(mapperResponse);
230     return mapperResponse.begin()->first;
231 }
232 
233 GetSubTreeResponse
234     DBusHandler::getSubtree(const std::string& searchPath, int depth,
235                             const std::vector<std::string>& ifaceList) const
236 {
237     auto& bus = pldm::utils::DBusHandler::getBus();
238     auto method = bus.new_method_call(mapperBusName, mapperPath,
239                                       mapperInterface, "GetSubTree");
240     method.append(searchPath, depth, ifaceList);
241     auto reply = bus.call(method);
242     GetSubTreeResponse response;
243     reply.read(response);
244     return response;
245 }
246 
247 void reportError(const char* errorMsg)
248 {
249     static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
250     static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create";
251 
252     auto& bus = pldm::utils::DBusHandler::getBus();
253 
254     try
255     {
256         auto service = DBusHandler().getService(logObjPath, logInterface);
257         using namespace sdbusplus::xyz::openbmc_project::Logging::server;
258         auto severity =
259             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
260                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
261                     Error);
262         auto method = bus.new_method_call(service.c_str(), logObjPath,
263                                           logInterface, "Create");
264         std::map<std::string, std::string> addlData{};
265         method.append(errorMsg, severity, addlData);
266         bus.call_noreply(method);
267     }
268     catch (const std::exception& e)
269     {
270         std::cerr << "failed to make a d-bus call to create error log, ERROR="
271                   << e.what() << "\n";
272     }
273 }
274 
275 void DBusHandler::setDbusProperty(const DBusMapping& dBusMap,
276                                   const PropertyValue& value) const
277 {
278     auto setDbusValue = [&dBusMap, this](const auto& variant) {
279         auto& bus = getBus();
280         auto service =
281             getService(dBusMap.objectPath.c_str(), dBusMap.interface.c_str());
282         auto method = bus.new_method_call(
283             service.c_str(), dBusMap.objectPath.c_str(), dbusProperties, "Set");
284         method.append(dBusMap.interface.c_str(), dBusMap.propertyName.c_str(),
285                       variant);
286         bus.call_noreply(method);
287     };
288 
289     if (dBusMap.propertyType == "uint8_t")
290     {
291         std::variant<uint8_t> v = std::get<uint8_t>(value);
292         setDbusValue(v);
293     }
294     else if (dBusMap.propertyType == "bool")
295     {
296         std::variant<bool> v = std::get<bool>(value);
297         setDbusValue(v);
298     }
299     else if (dBusMap.propertyType == "int16_t")
300     {
301         std::variant<int16_t> v = std::get<int16_t>(value);
302         setDbusValue(v);
303     }
304     else if (dBusMap.propertyType == "uint16_t")
305     {
306         std::variant<uint16_t> v = std::get<uint16_t>(value);
307         setDbusValue(v);
308     }
309     else if (dBusMap.propertyType == "int32_t")
310     {
311         std::variant<int32_t> v = std::get<int32_t>(value);
312         setDbusValue(v);
313     }
314     else if (dBusMap.propertyType == "uint32_t")
315     {
316         std::variant<uint32_t> v = std::get<uint32_t>(value);
317         setDbusValue(v);
318     }
319     else if (dBusMap.propertyType == "int64_t")
320     {
321         std::variant<int64_t> v = std::get<int64_t>(value);
322         setDbusValue(v);
323     }
324     else if (dBusMap.propertyType == "uint64_t")
325     {
326         std::variant<uint64_t> v = std::get<uint64_t>(value);
327         setDbusValue(v);
328     }
329     else if (dBusMap.propertyType == "double")
330     {
331         std::variant<double> v = std::get<double>(value);
332         setDbusValue(v);
333     }
334     else if (dBusMap.propertyType == "string")
335     {
336         std::variant<std::string> v = std::get<std::string>(value);
337         setDbusValue(v);
338     }
339     else
340     {
341         throw std::invalid_argument("UnSpported Dbus Type");
342     }
343 }
344 
345 PropertyValue DBusHandler::getDbusPropertyVariant(
346     const char* objPath, const char* dbusProp, const char* dbusInterface) const
347 {
348     auto& bus = DBusHandler::getBus();
349     auto service = getService(objPath, dbusInterface);
350     auto method =
351         bus.new_method_call(service.c_str(), objPath, dbusProperties, "Get");
352     method.append(dbusInterface, dbusProp);
353     PropertyValue value{};
354     auto reply = bus.call(method);
355     reply.read(value);
356     return value;
357 }
358 
359 PropertyValue jsonEntryToDbusVal(std::string_view type,
360                                  const nlohmann::json& value)
361 {
362     PropertyValue propValue{};
363     if (type == "uint8_t")
364     {
365         propValue = static_cast<uint8_t>(value);
366     }
367     else if (type == "uint16_t")
368     {
369         propValue = static_cast<uint16_t>(value);
370     }
371     else if (type == "uint32_t")
372     {
373         propValue = static_cast<uint32_t>(value);
374     }
375     else if (type == "uint64_t")
376     {
377         propValue = static_cast<uint64_t>(value);
378     }
379     else if (type == "int16_t")
380     {
381         propValue = static_cast<int16_t>(value);
382     }
383     else if (type == "int32_t")
384     {
385         propValue = static_cast<int32_t>(value);
386     }
387     else if (type == "int64_t")
388     {
389         propValue = static_cast<int64_t>(value);
390     }
391     else if (type == "bool")
392     {
393         propValue = static_cast<bool>(value);
394     }
395     else if (type == "double")
396     {
397         propValue = static_cast<double>(value);
398     }
399     else if (type == "string")
400     {
401         propValue = static_cast<std::string>(value);
402     }
403     else
404     {
405         std::cerr << "Unknown D-Bus property type, TYPE=" << type << "\n";
406     }
407 
408     return propValue;
409 }
410 
411 uint16_t findStateEffecterId(const pldm_pdr* pdrRepo, uint16_t entityType,
412                              uint16_t entityInstance, uint16_t containerId,
413                              uint16_t stateSetId, bool localOrRemote)
414 {
415     uint8_t* pdrData = nullptr;
416     uint32_t pdrSize{};
417     const pldm_pdr_record* record{};
418     do
419     {
420         record = pldm_pdr_find_record_by_type(pdrRepo, PLDM_STATE_EFFECTER_PDR,
421                                               record, &pdrData, &pdrSize);
422         if (record && (localOrRemote ^ pldm_pdr_record_is_remote(record)))
423         {
424             auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrData);
425             auto compositeEffecterCount = pdr->composite_effecter_count;
426             auto possible_states_start = pdr->possible_states;
427 
428             for (auto effecters = 0x00; effecters < compositeEffecterCount;
429                  effecters++)
430             {
431                 auto possibleStates =
432                     reinterpret_cast<state_effecter_possible_states*>(
433                         possible_states_start);
434                 auto setId = possibleStates->state_set_id;
435                 auto possibleStateSize = possibleStates->possible_states_size;
436 
437                 if (entityType == pdr->entity_type &&
438                     entityInstance == pdr->entity_instance &&
439                     containerId == pdr->container_id && stateSetId == setId)
440                 {
441                     return pdr->effecter_id;
442                 }
443                 possible_states_start += possibleStateSize + sizeof(setId) +
444                                          sizeof(possibleStateSize);
445             }
446         }
447     } while (record);
448 
449     return PLDM_INVALID_EFFECTER_ID;
450 }
451 
452 int emitStateSensorEventSignal(uint8_t tid, uint16_t sensorId,
453                                uint8_t sensorOffset, uint8_t eventState,
454                                uint8_t previousEventState)
455 {
456     try
457     {
458         auto& bus = DBusHandler::getBus();
459         auto msg = bus.new_signal("/xyz/openbmc_project/pldm",
460                                   "xyz.openbmc_project.PLDM.Event",
461                                   "StateSensorEvent");
462         msg.append(tid, sensorId, sensorOffset, eventState, previousEventState);
463 
464         msg.signal_send();
465     }
466     catch (const std::exception& e)
467     {
468         std::cerr << "Error emitting pldm event signal:"
469                   << "ERROR=" << e.what() << "\n";
470         return PLDM_ERROR;
471     }
472 
473     return PLDM_SUCCESS;
474 }
475 
476 uint16_t findStateSensorId(const pldm_pdr* pdrRepo, uint8_t tid,
477                            uint16_t entityType, uint16_t entityInstance,
478                            uint16_t containerId, uint16_t stateSetId)
479 {
480     auto pdrs = findStateSensorPDR(tid, entityType, stateSetId, pdrRepo);
481     for (auto pdr : pdrs)
482     {
483         auto sensorPdr = reinterpret_cast<pldm_state_sensor_pdr*>(pdr.data());
484         auto compositeSensorCount = sensorPdr->composite_sensor_count;
485         auto possible_states_start = sensorPdr->possible_states;
486 
487         for (auto sensors = 0x00; sensors < compositeSensorCount; sensors++)
488         {
489             auto possibleStates =
490                 reinterpret_cast<state_sensor_possible_states*>(
491                     possible_states_start);
492             auto setId = possibleStates->state_set_id;
493             auto possibleStateSize = possibleStates->possible_states_size;
494             if (entityType == sensorPdr->entity_type &&
495                 entityInstance == sensorPdr->entity_instance &&
496                 stateSetId == setId && containerId == sensorPdr->container_id)
497             {
498                 return sensorPdr->sensor_id;
499             }
500             possible_states_start +=
501                 possibleStateSize + sizeof(setId) + sizeof(possibleStateSize);
502         }
503     }
504     return PLDM_INVALID_EFFECTER_ID;
505 }
506 
507 void printBuffer(bool isTx, const std::vector<uint8_t>& buffer)
508 {
509     if (!buffer.empty())
510     {
511         if (isTx)
512         {
513             std::cout << "Tx: ";
514         }
515         else
516         {
517             std::cout << "Rx: ";
518         }
519         std::ostringstream tempStream;
520         for (int byte : buffer)
521         {
522             tempStream << std::setfill('0') << std::setw(2) << std::hex << byte
523                        << " ";
524         }
525         std::cout << tempStream.str() << std::endl;
526     }
527 }
528 
529 std::string toString(const struct variable_field& var)
530 {
531     if (var.ptr == nullptr || !var.length)
532     {
533         return "";
534     }
535 
536     std::string str(reinterpret_cast<const char*>(var.ptr), var.length);
537     std::replace_if(
538         str.begin(), str.end(), [](const char& c) { return !isprint(c); }, ' ');
539     return str;
540 }
541 
542 std::vector<std::string> split(std::string_view srcStr, std::string_view delim,
543                                std::string_view trimStr)
544 {
545     std::vector<std::string> out;
546     size_t start;
547     size_t end = 0;
548 
549     while ((start = srcStr.find_first_not_of(delim, end)) != std::string::npos)
550     {
551         end = srcStr.find(delim, start);
552         std::string_view dstStr = srcStr.substr(start, end - start);
553         if (!trimStr.empty())
554         {
555             dstStr.remove_prefix(dstStr.find_first_not_of(trimStr));
556             dstStr.remove_suffix(dstStr.size() - 1 -
557                                  dstStr.find_last_not_of(trimStr));
558         }
559 
560         if (!dstStr.empty())
561         {
562             out.push_back(std::string(dstStr));
563         }
564     }
565 
566     return out;
567 }
568 
569 std::string getCurrentSystemTime()
570 {
571     using namespace std::chrono;
572     const time_point<system_clock> tp = system_clock::now();
573     std::time_t tt = system_clock::to_time_t(tp);
574     auto ms = duration_cast<microseconds>(tp.time_since_epoch()) -
575               duration_cast<seconds>(tp.time_since_epoch());
576 
577     std::stringstream ss;
578     ss << std::put_time(std::localtime(&tt), "%F %Z %T.")
579        << std::to_string(ms.count());
580     return ss.str();
581 }
582 
583 bool checkForFruPresence(const std::string& objPath)
584 {
585     bool isPresent = false;
586     static constexpr auto presentInterface =
587         "xyz.openbmc_project.Inventory.Item";
588     static constexpr auto presentProperty = "Present";
589     try
590     {
591         auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant(
592             objPath.c_str(), presentProperty, presentInterface);
593         isPresent = std::get<bool>(propVal);
594     }
595     catch (const sdbusplus::exception::SdBusError& e)
596     {
597         std::cerr << "Failed to check for FRU presence for " << objPath
598                   << " ERROR =" << e.what() << std::endl;
599     }
600     return isPresent;
601 }
602 
603 } // namespace utils
604 } // namespace pldm
605