xref: /openbmc/pldm/oem/ibm/libpldmresponder/oem_ibm_handler.hpp (revision fdf6da79ab3df7b06e4b52f2353dc2e55252c6de)
1 #pragma once
2 
3 #include "collect_slot_vpd.hpp"
4 #include "common/utils.hpp"
5 #include "file_table.hpp"
6 #include "inband_code_update.hpp"
7 #include "libpldmresponder/bios_config.hpp"
8 #include "libpldmresponder/oem_handler.hpp"
9 #include "libpldmresponder/pdr_utils.hpp"
10 #include "libpldmresponder/platform.hpp"
11 #include "requester/handler.hpp"
12 
13 #include <libpldm/entity.h>
14 #include <libpldm/oem/ibm/state_set.h>
15 #include <libpldm/platform.h>
16 #include <libpldm/state_set.h>
17 
18 #include <sdbusplus/bus/match.hpp>
19 #include <sdeventplus/event.hpp>
20 #include <sdeventplus/utility/timer.hpp>
21 #include <xyz/openbmc_project/State/Host/client.hpp>
22 
23 typedef ibm_oem_pldm_state_set_firmware_update_state_values CodeUpdateState;
24 
25 using HostState = sdbusplus::common::xyz::openbmc_project::state::Host;
26 
27 namespace pldm
28 {
29 namespace responder
30 {
31 using ObjectPath = std::string;
32 using AssociatedEntityMap = std::map<ObjectPath, pldm_entity>;
33 using namespace pldm::bios;
34 using namespace pldm::utils;
35 
36 namespace oem_ibm_platform
37 {
38 constexpr uint16_t ENTITY_INSTANCE_0 = 0;
39 constexpr uint16_t ENTITY_INSTANCE_1 = 1;
40 
41 constexpr uint32_t BMC_PDR_START_RANGE = 0x00000000;
42 constexpr uint32_t BMC_PDR_END_RANGE = 0x00FFFFFF;
43 constexpr uint32_t HOST_PDR_START_RANGE = 0x01000000;
44 constexpr uint32_t HOST_PDR_END_RANGE = 0x01FFFFFF;
45 
46 const pldm::pdr::TerminusID HYPERVISOR_TID = 208;
47 
48 static constexpr uint8_t HEARTBEAT_TIMEOUT_DELTA = 10;
49 
50 enum SetEventReceiverCount
51 {
52     SET_EVENT_RECEIVER_SENT = 0x2,
53 };
54 
55 class Handler : public oem_platform::Handler
56 {
57   public:
Handler(const pldm::utils::DBusHandler * dBusIntf,pldm::responder::CodeUpdate * codeUpdate,pldm::responder::SlotHandler * slotHandler,int mctp_fd,uint8_t mctp_eid,pldm::InstanceIdDb & instanceIdDb,sdeventplus::Event & event,pldm::requester::Handler<pldm::requester::Request> * handler)58     Handler(const pldm::utils::DBusHandler* dBusIntf,
59             pldm::responder::CodeUpdate* codeUpdate,
60             pldm::responder::SlotHandler* slotHandler, int mctp_fd,
61             uint8_t mctp_eid, pldm::InstanceIdDb& instanceIdDb,
62             sdeventplus::Event& event,
63             pldm::requester::Handler<pldm::requester::Request>* handler) :
64         oem_platform::Handler(dBusIntf), codeUpdate(codeUpdate),
65         slotHandler(slotHandler), platformHandler(nullptr), mctp_fd(mctp_fd),
66         mctp_eid(mctp_eid), instanceIdDb(instanceIdDb), event(event),
67         handler(handler),
68         timer(event, std::bind(std::mem_fn(&Handler::setSurvTimer), this,
69                                HYPERVISOR_TID, false)),
70         hostTransitioningToOff(true)
71     {
72         codeUpdate->setVersions();
73         setEventReceiverCnt = 0;
74 
75         using namespace sdbusplus::bus::match::rules;
76         hostOffMatch = std::make_unique<sdbusplus::bus::match_t>(
77             pldm::utils::DBusHandler::getBus(),
78             propertiesChanged("/xyz/openbmc_project/state/host0",
79                               HostState::interface),
80             [this](sdbusplus::message_t& msg) {
81                 pldm::utils::DbusChangedProps props{};
82                 std::string intf;
83                 msg.read(intf, props);
84                 const auto itr =
85                     props.find(HostState::property_names::current_host_state);
86                 if (itr != props.end())
87                 {
88                     pldm::utils::PropertyValue value = itr->second;
89                     auto propVal = std::get<std::string>(value);
90                     if (propVal ==
91                         "xyz.openbmc_project.State.Host.HostState.Off")
92                     {
93                         hostOff = true;
94                         setEventReceiverCnt = 0;
95                         disableWatchDogTimer();
96                         startStopTimer(false);
97                         pldm::filetable::clearFileTable();
98                     }
99                     else if (propVal ==
100                              "xyz.openbmc_project.State.Host.HostState.Running")
101                     {
102                         hostOff = false;
103                         hostTransitioningToOff = false;
104                     }
105                     else if (
106                         propVal ==
107                         "xyz.openbmc_project.State.Host.HostState.TransitioningToOff")
108                     {
109                         hostTransitioningToOff = true;
110                     }
111                 }
112             });
113 
114         powerStateOffMatch = std::make_unique<sdbusplus::bus::match_t>(
115             pldm::utils::DBusHandler::getBus(),
116             propertiesChanged("/xyz/openbmc_project/state/chassis0",
117                               "xyz.openbmc_project.State.Chassis"),
118             [this](sdbusplus::message_t& msg) {
119                 pldm::utils::DbusChangedProps props{};
120                 std::string intf;
121                 msg.read(intf, props);
122                 const auto itr = props.find("CurrentPowerState");
123                 if (itr != props.end())
124                 {
125                     pldm::utils::PropertyValue value = itr->second;
126                     auto propVal = std::get<std::string>(value);
127                     if (propVal ==
128                         "xyz.openbmc_project.State.Chassis.PowerState.Off")
129                     {
130                         handleBootTypesAtChassisOff();
131                         static constexpr auto searchpath =
132                             "/xyz/openbmc_project/inventory/system/chassis/motherboard";
133                         int depth = 0;
134                         std::vector<std::string> powerInterface = {
135                             "xyz.openbmc_project.State.Decorator.PowerState"};
136                         pldm::utils::GetSubTreeResponse response =
137                             pldm::utils::DBusHandler().getSubtree(
138                                 searchpath, depth, powerInterface);
139                         for (const auto& [objPath, serviceMap] : response)
140                         {
141                             pldm::utils::DBusMapping dbusMapping{
142                                 objPath,
143                                 "xyz.openbmc_project.State.Decorator.PowerState",
144                                 "PowerState", "string"};
145                             value =
146                                 "xyz.openbmc_project.State.Decorator.PowerState.State.Off";
147                             try
148                             {
149                                 pldm::utils::DBusHandler().setDbusProperty(
150                                     dbusMapping, value);
151                             }
152                             catch (const std::exception& e)
153                             {
154                                 error(
155                                     "Unable to set the slot power state to Off error - {ERROR}",
156                                     "ERROR", e);
157                             }
158                         }
159                     }
160                 }
161             });
162 
163         updateBIOSMatch = std::make_unique<sdbusplus::bus::match_t>(
164             pldm::utils::DBusHandler::getBus(),
165             propertiesChanged("/xyz/openbmc_project/bios_config/manager",
166                               "xyz.openbmc_project.BIOSConfig.Manager"),
167             [codeUpdate](sdbusplus::message_t& msg) {
168                 constexpr auto propertyName = "PendingAttributes";
169                 using Value =
170                     std::variant<std::string, PendingAttributes, BaseBIOSTable>;
171                 using Properties = std::map<pldm::utils::DbusProp, Value>;
172                 Properties props{};
173                 std::string intf;
174                 msg.read(intf, props);
175                 auto valPropMap = props.find(propertyName);
176                 if (valPropMap == props.end())
177                 {
178                     return;
179                 }
180 
181                 PendingAttributes pendingAttributes =
182                     std::get<PendingAttributes>(valPropMap->second);
183                 for (auto it : pendingAttributes)
184                 {
185                     if (it.first == "fw_boot_side")
186                     {
187                         auto& [attributeType, attributevalue] = it.second;
188                         std::string nextBootSideAttr =
189                             std::get<std::string>(attributevalue);
190                         std::string nextBootSide =
191                             (nextBootSideAttr == "Perm" ? Pside : Tside);
192                         codeUpdate->setNextBootSide(nextBootSide);
193                     }
194                 }
195             });
196 
197         platformSAIMatch = std::make_unique<sdbusplus::bus::match_t>(
198             pldm::utils::DBusHandler::getBus(),
199             propertiesChanged(
200                 "/xyz/openbmc_project/led/groups/partition_system_attention_indicator",
201                 "xyz.openbmc_project.Led.Group"),
202             [this](sdbusplus::message_t& msg) {
203                 pldm::utils::DbusChangedProps props{};
204                 std::string intf;
205                 msg.read(intf, props);
206                 const auto itr = props.find("Asserted");
207                 if (itr != props.end())
208                 {
209                     processSAIUpdate();
210                 }
211             });
212 
213         partitionSAIMatch = std::make_unique<sdbusplus::bus::match_t>(
214             pldm::utils::DBusHandler::getBus(),
215             propertiesChanged(
216                 "/xyz/openbmc_project/led/groups/platform_system_attention_indicator",
217                 "xyz.openbmc_project.Led.Group"),
218             [this](sdbusplus::message_t& msg) {
219                 pldm::utils::DbusChangedProps props{};
220                 std::string intf;
221                 msg.read(intf, props);
222                 const auto itr = props.find("Asserted");
223                 if (itr != props.end())
224                 {
225                     processSAIUpdate();
226                 }
227             });
228     }
229 
230     int getOemStateSensorReadingsHandler(
231         pldm::pdr::EntityType entityType,
232         pldm::pdr::EntityInstance entityInstance,
233         pldm::pdr::ContainerID containerId, pldm::pdr::StateSetId stateSetId,
234         pldm::pdr::CompositeCount compSensorCnt, uint16_t sensorId,
235         std::vector<get_sensor_state_field>& stateField);
236 
237     int oemSetStateEffecterStatesHandler(
238         uint16_t entityType, uint16_t entityInstance, uint16_t stateSetId,
239         uint8_t compEffecterCnt,
240         std::vector<set_effecter_state_field>& stateField, uint16_t effecterId);
241 
242     /** @brief Method to set the platform handler in the
243      *         oem_ibm_handler class
244      *  @param[in] handler - pointer to PLDM platform handler
245      */
246     void setPlatformHandler(pldm::responder::platform::Handler* handler);
247 
248     /** @brief Method to fetch the effecter ID of the code update PDRs
249      *
250      * @return platformHandler->getNextEffecterId() - returns the
251      *             effecter ID from the platform handler
252      */
getNextEffecterId()253     virtual uint16_t getNextEffecterId()
254     {
255         return platformHandler->getNextEffecterId();
256     }
257 
258     /** @brief Method to fetch the sensor ID of the code update PDRs
259      *
260      * @return platformHandler->getNextSensorId() - returns the
261      *             Sensor ID from the platform handler
262      */
getNextSensorId()263     virtual uint16_t getNextSensorId()
264     {
265         return platformHandler->getNextSensorId();
266     }
267 
268     /** @brief Get std::map associated with the entity
269      *         key: object path
270      *         value: pldm_entity
271      *
272      *  @return std::map<ObjectPath, pldm_entity>
273      */
getAssociateEntityMap()274     virtual const AssociatedEntityMap& getAssociateEntityMap()
275     {
276         return platformHandler->getAssociateEntityMap();
277     }
278 
279     /** @brief Method to Generate the OEM PDRs
280      *
281      * @param[in] repo - instance of concrete implementation of Repo
282      */
283     void buildOEMPDR(pdr_utils::Repo& repo);
284 
285     /** @brief Method to send code update event to host
286      * @param[in] sensorId - sendor ID
287      * @param[in] sensorEventClass - event class of sensor
288      * @param[in] sensorOffset - sensor offset
289      * @param[in] eventState - new code update event state
290      * @param[in] prevEventState - previous code update event state
291      * @return none
292      */
293     void sendStateSensorEvent(uint16_t sensorId,
294                               enum sensor_event_class_states sensorEventClass,
295                               uint8_t sensorOffset, uint8_t eventState,
296                               uint8_t prevEventState);
297 
298     /** @brief Method to send encoded request msg of code update event to host
299      *  @param[in] requestMsg - encoded request msg
300      *  @param[in] instanceId - instance id of the message
301      *  @return PLDM status code
302      */
303     int sendEventToHost(std::vector<uint8_t>& requestMsg, uint8_t instanceId);
304 
305     /** @brief _processEndUpdate processes the actual work that needs
306      *  to be carried out after EndUpdate effecter is set. This is done async
307      *  after sending response for EndUpdate set effecter
308      *  @param[in] source - sdeventplus event source
309      */
310     void _processEndUpdate(sdeventplus::source::EventBase& source);
311 
312     /** @brief _processStartUpdate processes the actual work that needs
313      *  to be carried out after StartUpdate effecter is set. This is done async
314      *  after sending response for StartUpdate set effecter
315      *  @param[in] source - sdeventplus event source
316      */
317     void _processStartUpdate(sdeventplus::source::EventBase& source);
318 
319     /** @brief _processSystemReboot processes the actual work that needs to be
320      *  carried out after the System Power State effecter is set to reboot
321      *  the system
322      *  @param[in] source - sdeventplus event source
323      */
324     void _processSystemReboot(sdeventplus::source::EventBase& source);
325 
326     /*keeps track how many times setEventReceiver is sent */
countSetEventReceiver()327     void countSetEventReceiver()
328     {
329         setEventReceiverCnt++;
330     }
331 
332     /* disables watchdog if running and Host is up */
333     void checkAndDisableWatchDog();
334 
335     /** @brief To check if the watchdog app is running
336      *
337      *  @return the running status of watchdog app
338      */
339     bool watchDogRunning();
340 
341     /** @brief Method to reset the Watchdog timer on receiving platform Event
342      *  Message for heartbeat elapsed time from Hostboot
343      */
344     void resetWatchDogTimer();
345 
346     /** @brief To disable to the watchdog timer on host poweron completion*/
347     void disableWatchDogTimer();
348 
349     /** @brief to check the BMC state*/
350     int checkBMCState();
351 
352     /** @brief update the dbus object paths */
353     void updateOemDbusPaths(std::string& dbusPath);
354 
355     /** @brief Method to fetch the last BMC record from the PDR repo
356      *
357      * @param[in] repo - pointer to BMC's primary PDR repo
358      *
359      * @return the last BMC record from the repo
360      */
361     const pldm_pdr_record* fetchLastBMCRecord(const pldm_pdr* repo);
362 
363     /** @brief Method to check if the record handle passed is in remote PDR
364      *         record handle range
365      *
366      *  @param[in] record_handle - record handle of the PDR
367      *
368      *  @return true if record handle passed is in host PDR record handle range
369      */
370     bool checkRecordHandleInRange(const uint32_t& record_handle);
371 
372     /** *brief Method to call the setEventReceiver command*/
373     void processSetEventReceiver();
374 
375     /** @brief Method to call the setEventReceiver through the platform
376      *   handler
377      */
setEventReceiver()378     virtual void setEventReceiver()
379     {
380         platformHandler->setEventReceiver();
381     }
382 
383     /** @brief Method to Enable/Disable timer to see if remote terminus sends
384      *  the surveillance ping and logs informational error if remote terminus
385      *  fails to send the surveillance pings
386      *
387      * @param[in] tid - TID of the remote terminus
388      * @param[in] value - true or false, to indicate if the timer is
389      *                    running or not
390      */
391     void setSurvTimer(uint8_t tid, bool value);
392 
393     /** @brief To handle the boot types bios attributes at power on*/
394     void handleBootTypesAtPowerOn();
395 
396     /** @brief To handle the boot types bios attributes at shutdown*/
397     void handleBootTypesAtChassisOff();
398 
399     /** @brief To set the boot types bios attributes based on the RestartCause
400      *  of host
401      *
402      *  @param[in] RestartCause - Host restart cause
403      */
404     void setBootTypesBiosAttr(const std::string& restartCause);
405 
406     /** @brief To turn off Real SAI effecter*/
407     void turnOffRealSAIEffecter();
408 
409     /** @brief Fetch Real SAI status based on the partition SAI and platform SAI
410      *  sensor states. Real SAI is turned on if any of the partition or platform
411      *  SAI turned on else Real SAI is turned off
412      *  @return Real SAI sensor state PLDM_SENSOR_WARNING/PLDM_SENSOR_NORMAL
413      */
414     uint8_t fetchRealSAIStatus();
415 
416     /** @brief Method to process virtual platform/partition SAI update*/
417     void processSAIUpdate();
418 
419     ~Handler() = default;
420 
421     pldm::responder::CodeUpdate* codeUpdate; //!< pointer to CodeUpdate object
422 
423     pldm::responder::SlotHandler*
424         slotHandler; //!< pointer to SlotHandler object
425 
426     pldm::responder::platform::Handler*
427         platformHandler; //!< pointer to PLDM platform handler
428 
429     /** @brief fd of MCTP communications socket */
430     int mctp_fd;
431 
432     /** @brief MCTP EID of host firmware */
433     uint8_t mctp_eid;
434 
435     /** @brief reference to an InstanceIdDb object, used to obtain a PLDM
436      * instance id. */
437     pldm::InstanceIdDb& instanceIdDb;
438     /** @brief sdeventplus event source */
439     std::unique_ptr<sdeventplus::source::Defer> assembleImageEvent;
440     std::unique_ptr<sdeventplus::source::Defer> startUpdateEvent;
441     std::unique_ptr<sdeventplus::source::Defer> systemRebootEvent;
442 
443     /** @brief Effecterid to dbus object path map
444      */
445     std::unordered_map<uint16_t, std::string> effecterIdToDbusMap;
446 
447     /** @brief reference of main event loop of pldmd, primarily used to schedule
448      *  work
449      */
450     sdeventplus::Event& event;
451 
452   private:
453     /** @brief Method to reset or stop the surveillance timer
454      *
455      * @param[in] value - true or false, to indicate if the timer
456      *                    should be reset or turned off
457      */
458     void startStopTimer(bool value);
459 
460     /** @brief D-Bus property changed signal match for CurrentPowerState*/
461     std::unique_ptr<sdbusplus::bus::match_t> chassisOffMatch;
462 
463     /** @brief PLDM request handler */
464     pldm::requester::Handler<pldm::requester::Request>* handler;
465 
466     /** @brief D-Bus property changed signal match */
467     std::unique_ptr<sdbusplus::bus::match_t> updateBIOSMatch;
468 
469     /** @brief D-Bus property changed signal match */
470     std::unique_ptr<sdbusplus::bus::match_t> hostOffMatch;
471 
472     /** @brief D-Bus property changed signal match */
473     std::unique_ptr<sdbusplus::bus::match_t> powerStateOffMatch;
474 
475     /** @brief Timer used for monitoring surveillance pings from host */
476     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer;
477 
478     /** @brief D-Bus Interface added signal match for virtual platform SAI */
479     std::unique_ptr<sdbusplus::bus::match_t> platformSAIMatch;
480 
481     /** @brief D-Bus Interface added signal match for virtual partition SAI */
482     std::unique_ptr<sdbusplus::bus::match_t> partitionSAIMatch;
483 
484     /** @brief Real SAI sensor id*/
485     uint16_t realSAISensorId;
486 
487     bool hostOff = true;
488 
489     bool hostTransitioningToOff;
490 
491     int setEventReceiverCnt = 0;
492 };
493 
494 /** @brief Method to encode code update event msg
495  *  @param[in] eventType - type of event
496  *  @param[in] eventDataVec - vector of event data to be sent to host
497  *  @param[in/out] requestMsg - request msg to be encoded
498  *  @param[in] instanceId - instance ID
499  *  @return PLDM status code
500  */
501 int encodeEventMsg(uint8_t eventType, const std::vector<uint8_t>& eventDataVec,
502                    std::vector<uint8_t>& requestMsg, uint8_t instanceId);
503 
504 } // namespace oem_ibm_platform
505 
506 namespace oem_ibm_bios
507 {
508 /** @brief The file where bootside data will be saved */
509 constexpr auto bootSideDirPath = "/var/lib/pldm/bootSide";
510 
511 class Handler : public oem_bios::Handler
512 {
513   public:
Handler()514     Handler() {}
515 
processOEMBaseBiosTable(const BaseBIOSTable & biosTable)516     void processOEMBaseBiosTable(const BaseBIOSTable& biosTable)
517     {
518         for (const auto& [attrName, biostabObj] : biosTable)
519         {
520             // The additional check to see if /var/lib/pldm/bootSide file exists
521             // is added to make sure we are doing the fw_boot_side setting after
522             // the base bios table is initialised.
523             if ((attrName == "fw_boot_side") && fs::exists(bootSideDirPath))
524             {
525                 PendingAttributesList biosAttrList;
526 
527                 std::string nextBootSide =
528                     std::get<std::string>(std::get<5>(biostabObj));
529 
530                 std::string currNextBootSide;
531                 auto attributeValue =
532                     getBiosAttrValue<std::string>("fw_boot_side");
533 
534                 if (attributeValue.has_value())
535                 {
536                     currNextBootSide = attributeValue.value();
537                 }
538                 else
539                 {
540                     info(
541                         "Boot side is not initialized yet, so setting default value");
542                     currNextBootSide = "Temp";
543                 }
544 
545                 if (currNextBootSide != nextBootSide)
546                 {
547                     biosAttrList.emplace_back(std::make_pair(
548                         attrName,
549                         std::make_tuple(EnumAttribute, nextBootSide)));
550                     setBiosAttr(biosAttrList);
551                 }
552             }
553         }
554     }
555 };
556 
557 } // namespace oem_ibm_bios
558 
559 } // namespace responder
560 
561 } // namespace pldm
562