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