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