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