1 #include "collect_slot_vpd.hpp"
2 
3 #include "oem_ibm_handler.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 PHOSPHOR_LOG2_USING;
8 
9 namespace pldm
10 {
11 namespace responder
12 {
13 using namespace oem_ibm_platform;
14 void SlotHandler::timeOutHandler()
15 {
16     info(
17         "Timer expired waiting for Event from Inventory on following pldm_entity: [ {ENTITY_TYP}, {ENTITY_NUM}, {ENTITY_ID} ]",
18         "ENTITY_TYP",
19         static_cast<unsigned>(currentOnGoingSlotEntity.entity_type),
20         "ENTITY_NUM",
21         static_cast<unsigned>(currentOnGoingSlotEntity.entity_instance_num),
22         "ENTITY_ID",
23         static_cast<unsigned>(currentOnGoingSlotEntity.entity_container_id));
24     // Disable the timer
25     timer.setEnabled(false);
26 
27     // obtain the sensor Id
28     auto sensorId = pldm::utils::findStateSensorId(
29         pdrRepo, 0, PLDM_ENTITY_SLOT,
30         currentOnGoingSlotEntity.entity_instance_num,
31         currentOnGoingSlotEntity.entity_container_id,
32         PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE);
33 
34     // send the sensor event to host with error state
35     sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
36                          PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_ERROR,
37                          PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_UNKOWN);
38 }
39 
40 void SlotHandler::enableSlot(uint16_t effecterId,
41                              const AssociatedEntityMap& fruAssociationMap,
42                              uint8_t stateFileValue)
43 
44 {
45     info("CM: slot enable effecter id: {EFFECTER_ID}", "EFFECTER_ID",
46          effecterId);
47     const pldm_entity entity = getEntityIDfromEffecterID(effecterId);
48 
49     for (const auto& [key, value] : fruAssociationMap)
50     {
51         if (entity.entity_instance_num == value.entity_instance_num &&
52             entity.entity_type == value.entity_type &&
53             entity.entity_container_id == value.entity_container_id)
54         {
55             this->currentOnGoingSlotEntity.entity_type = value.entity_type;
56             this->currentOnGoingSlotEntity.entity_instance_num =
57                 value.entity_instance_num;
58             this->currentOnGoingSlotEntity.entity_container_id =
59                 value.entity_container_id;
60             processSlotOperations(key, value, stateFileValue);
61         }
62     }
63 }
64 
65 void SlotHandler::processSlotOperations(const std::string& slotObjectPath,
66                                         const pldm_entity& entity,
67                                         uint8_t stateFieldValue)
68 {
69     info("CM: processing the slot operations, SlotObject: {SLOT_OBJ}",
70          "SLOT_OBJ", slotObjectPath);
71 
72     std::string adapterObjPath;
73     try
74     {
75         // get the adapter dbus object path from the slot dbus object path
76         adapterObjPath = getAdapterObjPath(slotObjectPath).value();
77     }
78     catch (const std::bad_optional_access& e)
79     {
80         error("Failed to get the adapter dbus object ERROR={ERROR}", "ERROR",
81               e);
82         return;
83     }
84 
85     info(
86         "CM: Found an adapter under the slot, adapter object:{ADAPTER_OBJ_PATH}",
87         "ADAPTER_OBJ_PATH", adapterObjPath);
88     // create a presence match for the adpter present property
89     createPresenceMatch(adapterObjPath, entity, stateFieldValue);
90 
91     // call the VPD Manager to collect/remove VPD objects
92     callVPDManager(adapterObjPath, stateFieldValue);
93 
94     // start the 1 min timer
95     timer.restart(std::chrono::seconds(60));
96 }
97 
98 void SlotHandler::callVPDManager(const std::string& adapterObjPath,
99                                  uint8_t stateFieldValue)
100 {
101     static constexpr auto VPDObjPath = "/com/ibm/VPD/Manager";
102     static constexpr auto VPDInterface = "com.ibm.VPD.Manager";
103     auto& bus = pldm::utils::DBusHandler::getBus();
104     try
105     {
106         auto service =
107             pldm::utils::DBusHandler().getService(VPDObjPath, VPDInterface);
108         if (stateFieldValue == PLDM_OEM_IBM_PCIE_SLOT_EFFECTER_ADD)
109         {
110             auto method = bus.new_method_call(service.c_str(), VPDObjPath,
111                                               VPDInterface, "CollectFRUVPD");
112             method.append(
113                 static_cast<sdbusplus::message::object_path>(adapterObjPath));
114             bus.call_noreply(method, dbusTimeout);
115         }
116         else if (stateFieldValue == PLDM_OEM_IBM_PCIE_SLOT_EFFECTER_REMOVE ||
117                  stateFieldValue == PLDM_OEM_IBM_PCIE_SLOT_EFFECTER_REPLACE)
118         {
119             auto method = bus.new_method_call(service.c_str(), VPDObjPath,
120                                               VPDInterface, "deleteFRUVPD");
121             method.append(
122                 static_cast<sdbusplus::message::object_path>(adapterObjPath));
123             bus.call_noreply(method, dbusTimeout);
124         }
125     }
126     catch (const std::exception& e)
127     {
128         error(
129             "failed to make a d-bus call to VPD Manager , Operation = {STATE_FILED_VAL}, ERROR={ERROR}",
130             "STATE_FILED_VAL", (unsigned)stateFieldValue, "ERROR", e);
131     }
132 }
133 
134 std::optional<std::string>
135     SlotHandler::getAdapterObjPath(const std::string& slotObjPath)
136 {
137     static constexpr auto searchpath = "/xyz/openbmc_project/inventory";
138     int depth = 0;
139     std::vector<std::string> pcieAdapterInterface = {
140         "xyz.openbmc_project.Inventory.Item.PCIeDevice"};
141     pldm::utils::GetSubTreeResponse response =
142         pldm::utils::DBusHandler().getSubtree(searchpath, depth,
143                                               pcieAdapterInterface);
144     for (const auto& [objPath, serviceMap] : response)
145     {
146         // An adapter is a child of a PCIe Slot Object
147         if (objPath.contains(slotObjPath))
148         {
149             // Found the Adapter under the slot
150             return objPath;
151         }
152     }
153     return std::nullopt;
154 }
155 
156 void SlotHandler::createPresenceMatch(const std::string& adapterObjectPath,
157                                       const pldm_entity& entity,
158                                       uint8_t stateFieldValue)
159 {
160     fruPresenceMatch = std::make_unique<sdbusplus::bus::match_t>(
161         pldm::utils::DBusHandler::getBus(),
162         propertiesChanged(adapterObjectPath,
163                           "xyz.openbmc_project.Inventory.Item"),
164         [this, adapterObjectPath, stateFieldValue,
165          entity](sdbusplus::message::message& msg) {
166             pldm::utils::DbusChangedProps props{};
167             std::string intf;
168             msg.read(intf, props);
169             const auto itr = props.find("Present");
170             if (itr != props.end())
171             {
172                 bool value = std::get<bool>(itr->second);
173                 // Present Property is found
174                 this->processPresentPropertyChange(value, stateFieldValue,
175                                                    entity);
176             }
177         });
178 }
179 
180 void SlotHandler::processPresentPropertyChange(
181     bool presentValue, uint8_t stateFiledvalue, const pldm_entity& entity)
182 {
183     // irrespective of true->false or false->true change, we should stop the
184     // timer
185     timer.setEnabled(false);
186 
187     // remove the prence match so that it does not monitor the change
188     // even after we captured the property changed signal
189     fruPresenceMatch = nullptr;
190 
191     // obtain the sensor id attached with this slot
192     auto sensorId = pldm::utils::findStateSensorId(
193         pdrRepo, 0, PLDM_ENTITY_SLOT, entity.entity_instance_num,
194         entity.entity_container_id, PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE);
195 
196     uint8_t sensorOpState = PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_UNKOWN;
197     if (presentValue)
198     {
199         sensorOpState = PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_ENABLED;
200     }
201     else
202     {
203         if (stateFiledvalue == PLDM_OEM_IBM_PCIE_SLOT_EFFECTER_REPLACE)
204         {
205             sensorOpState = PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_UNKOWN;
206         }
207         else
208         {
209             sensorOpState = PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_DISABLED;
210         }
211     }
212     info(
213         "CM: processing the property change from VPD Present value and sensor opState: {CURR_VAL} and {SENSOR_OP_STATE}",
214         "CURR_VAL", presentValue, "SENSOR_OP_STATE", (unsigned)sensorOpState);
215     // set the sensor state based on the stateFieldValue
216     this->sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
217                                sensorOpState,
218                                PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_UNKOWN);
219 }
220 
221 pldm_entity SlotHandler::getEntityIDfromEffecterID(uint16_t effecterId)
222 {
223     pldm_entity parentFruEntity{};
224     uint8_t* pdrData = nullptr;
225     uint32_t pdrSize{};
226     const pldm_pdr_record* record{};
227     do
228     {
229         record = pldm_pdr_find_record_by_type(pdrRepo, PLDM_STATE_EFFECTER_PDR,
230                                               record, &pdrData, &pdrSize);
231         if (record && !pldm_pdr_record_is_remote(record))
232         {
233             auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrData);
234             auto compositeEffecterCount = pdr->composite_effecter_count;
235             auto possible_states_start = pdr->possible_states;
236 
237             for (auto effecters = 0x00; effecters < compositeEffecterCount;
238                  effecters++)
239             {
240                 auto possibleStates =
241                     reinterpret_cast<state_effecter_possible_states*>(
242                         possible_states_start);
243 
244                 if (possibleStates->state_set_id ==
245                         PLDM_OEM_IBM_PCIE_SLOT_EFFECTER_STATE &&
246                     effecterId == pdr->effecter_id)
247                 {
248                     parentFruEntity.entity_type = pdr->entity_type;
249                     parentFruEntity.entity_instance_num = pdr->entity_instance;
250                     parentFruEntity.entity_container_id = pdr->container_id;
251 
252                     return parentFruEntity;
253                 }
254             }
255         }
256     } while (record);
257 
258     return parentFruEntity;
259 }
260 
261 uint8_t SlotHandler::fetchSlotSensorState(const std::string& slotObjectPath)
262 {
263     std::string adapterObjPath;
264     uint8_t sensorOpState = PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_UNKOWN;
265 
266     try
267     {
268         // get the adapter dbus object path from the slot dbus object path
269         adapterObjPath = getAdapterObjPath(slotObjectPath).value();
270     }
271     catch (const std::bad_optional_access& e)
272     {
273         error(
274             "Failed to get the adapterObjectPath from slotObjectPath : {SLOT_OBJ_PATH} with error ERROR={ERROR}",
275             "SLOT_OBJ_PATH", slotObjectPath, "ERROR", e);
276         return PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_UNKOWN;
277     }
278 
279     if (fetchSensorStateFromDbus(adapterObjPath))
280     {
281         sensorOpState = PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_ENABLED;
282     }
283     else
284     {
285         sensorOpState = PLDM_OEM_IBM_PCIE_SLOT_SENSOR_STATE_DISABLED;
286     }
287     return sensorOpState;
288 }
289 
290 void SlotHandler::setOemPlatformHandler(
291     pldm::responder::oem_platform::Handler* handler)
292 {
293     oemPlatformHandler = handler;
294 }
295 
296 void SlotHandler::sendStateSensorEvent(
297     uint16_t sensorId, enum sensor_event_class_states sensorEventClass,
298     uint8_t sensorOffset, uint8_t eventState, uint8_t prevEventState)
299 {
300     pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler =
301         dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>(
302             oemPlatformHandler);
303     if (oemIbmPlatformHandler)
304     {
305         oemIbmPlatformHandler->sendStateSensorEvent(
306             sensorId, sensorEventClass, sensorOffset, eventState,
307             prevEventState);
308     }
309 }
310 
311 bool SlotHandler::fetchSensorStateFromDbus(const std::string& adapterObjectPath)
312 {
313     static constexpr auto ItemInterface = "xyz.openbmc_project.Inventory.Item";
314 
315     try
316     {
317         auto presentProperty =
318             pldm::utils::DBusHandler().getDbusPropertyVariant(
319                 adapterObjectPath.c_str(), "Present", ItemInterface);
320         return std::get<bool>(presentProperty);
321     }
322     catch (const std::exception& e)
323     {
324         error(
325             "failed to make a d-bus call to Inventory manager from adapterObjectPath : {ADAPTER_OBJ_PATH} with error ERROR={ERROR}",
326             "ADAPTER_OBJ_PATH", adapterObjectPath, "ERROR", e);
327     }
328 
329     return false;
330 }
331 
332 } // namespace responder
333 } // namespace pldm
334