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