xref: /openbmc/pldm/oem/ibm/libpldmresponder/inband_code_update.cpp (revision 42afe90f51efee3d18de7940b41d5b6cc062b525)
1 #include "inband_code_update.hpp"
2 
3 #include "libpldmresponder/pdr.hpp"
4 #include "oem_ibm_handler.hpp"
5 #include "xyz/openbmc_project/Common/error.hpp"
6 
7 #include <arpa/inet.h>
8 #include <libpldm/entity.h>
9 
10 #include <nlohmann/json.hpp>
11 #include <phosphor-logging/lg2.hpp>
12 #include <sdbusplus/server.hpp>
13 #include <xyz/openbmc_project/Association/common.hpp>
14 #include <xyz/openbmc_project/Dump/NewDump/server.hpp>
15 
16 #include <exception>
17 #include <fstream>
18 
19 using Association = sdbusplus::common::xyz::openbmc_project::Association;
20 
21 PHOSPHOR_LOG2_USING;
22 
23 namespace pldm
24 {
25 using namespace utils;
26 
27 namespace responder
28 {
29 using namespace oem_ibm_platform;
30 using namespace oem_ibm_bios;
31 
32 /** @brief Directory where the lid files without a header are stored */
33 auto lidDirPath = fs::path(LID_STAGING_DIR) / "lid";
34 
35 /** @brief Directory where the image files are stored as they are built */
36 auto imageDirPath = fs::path(LID_STAGING_DIR) / "image";
37 
38 /** @brief Directory where the code update tarball files are stored */
39 auto updateDirPath = fs::path(LID_STAGING_DIR) / "update";
40 
41 /** @brief The file name of the code update tarball */
42 constexpr auto tarImageName = "image.tar";
43 
44 /** @brief The file name of the hostfw image */
45 constexpr auto hostfwImageName = "image-hostfw";
46 
47 /** @brief The path to the code update tarball file */
48 auto tarImagePath = fs::path(imageDirPath) / tarImageName;
49 
50 /** @brief The path to the hostfw image */
51 auto hostfwImagePath = fs::path(imageDirPath) / hostfwImageName;
52 
53 /** @brief The path to the tarball file expected by the phosphor software
54  *         manager */
55 auto updateImagePath = fs::path("/tmp/images") / tarImageName;
56 
57 /** @brief Current boot side */
58 constexpr auto bootSideAttrName = "fw_boot_side_current";
59 
60 /** @brief Next boot side */
61 constexpr auto bootNextSideAttrName = "fw_boot_side";
62 
fetchCurrentBootSide()63 std::string CodeUpdate::fetchCurrentBootSide()
64 {
65     return currBootSide;
66 }
67 
fetchNextBootSide()68 std::string CodeUpdate::fetchNextBootSide()
69 {
70     return nextBootSide;
71 }
72 
setCurrentBootSide(const std::string & currSide)73 int CodeUpdate::setCurrentBootSide(const std::string& currSide)
74 {
75     currBootSide = currSide;
76     return PLDM_SUCCESS;
77 }
78 
setNextBootSide(const std::string & nextSide)79 int CodeUpdate::setNextBootSide(const std::string& nextSide)
80 {
81     info("setNextBootSide, nextSide={NXT_SIDE}", "NXT_SIDE", nextSide);
82     pldm_boot_side_data pldmBootSideData = readBootSideFile();
83     currBootSide =
84         (pldmBootSideData.current_boot_side == "Perm" ? Pside : Tside);
85     nextBootSide = nextSide;
86     pldmBootSideData.next_boot_side = (nextSide == Pside ? "Perm" : "Temp");
87     std::string objPath{};
88     if (nextBootSide == currBootSide)
89     {
90         info(
91             "Current bootside is same as next boot side, setting priority of running version 0");
92         objPath = runningVersion;
93     }
94     else
95     {
96         info(
97             "Current bootside is not same as next boot side, setting priority of non running version 0");
98         objPath = nonRunningVersion;
99     }
100     if (objPath.empty())
101     {
102         error("no nonRunningVersion present");
103         return PLDM_PLATFORM_INVALID_STATE_VALUE;
104     }
105 
106     try
107     {
108         auto priorityPropValue = dBusIntf->getDbusPropertyVariant(
109             objPath.c_str(), "Priority", redundancyIntf);
110         const auto& priorityValue = std::get<uint8_t>(priorityPropValue);
111         if (priorityValue == 0)
112         {
113             // Requested next boot side is already set
114             return PLDM_SUCCESS;
115         }
116     }
117     catch (const std::exception& e)
118     {
119         // Alternate side may not be present due to a failed code update
120         error("Alternate side may not be present due to a failed code update. "
121               "ERROR: {ERR}",
122               "ERR", e);
123         return PLDM_PLATFORM_INVALID_STATE_VALUE;
124     }
125 
126     pldm::utils::DBusMapping dbusMapping{objPath, redundancyIntf, "Priority",
127                                          "uint8_t"};
128     uint8_t val = 0;
129     pldm::utils::PropertyValue value = static_cast<uint8_t>(val);
130     try
131     {
132         dBusIntf->setDbusProperty(dbusMapping, value);
133     }
134     catch (const std::exception& e)
135     {
136         error("Failed to set the next boot side to {PATH} , error - {ERROR}",
137               "PATH", objPath, "ERROR", e);
138         return PLDM_ERROR;
139     }
140     writeBootSideFile(pldmBootSideData);
141     return PLDM_SUCCESS;
142 }
143 
setRequestedApplyTime()144 int CodeUpdate::setRequestedApplyTime()
145 {
146     int rc = PLDM_SUCCESS;
147     pldm::utils::PropertyValue value =
148         "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
149     DBusMapping dbusMapping;
150     dbusMapping.objectPath = "/xyz/openbmc_project/software/apply_time";
151     dbusMapping.interface = "xyz.openbmc_project.Software.ApplyTime";
152     dbusMapping.propertyName = "RequestedApplyTime";
153     dbusMapping.propertyType = "string";
154     try
155     {
156         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
157     }
158     catch (const std::exception& e)
159     {
160         error(
161             "Failed to set property '{PROPERTY}' at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}",
162             "PATH", dbusMapping.objectPath, "INTERFACE", dbusMapping.interface,
163             "PROPERTY", dbusMapping.propertyName, "ERROR", e);
164         rc = PLDM_ERROR;
165     }
166     return rc;
167 }
168 
setRequestedActivation()169 int CodeUpdate::setRequestedActivation()
170 {
171     int rc = PLDM_SUCCESS;
172     pldm::utils::PropertyValue value =
173         "xyz.openbmc_project.Software.Activation.RequestedActivations.Active";
174     DBusMapping dbusMapping;
175     dbusMapping.objectPath = newImageId;
176     dbusMapping.interface = "xyz.openbmc_project.Software.Activation";
177     dbusMapping.propertyName = "RequestedActivation";
178     dbusMapping.propertyType = "string";
179     try
180     {
181         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
182     }
183     catch (const std::exception& e)
184     {
185         error(
186             "Failed to set property {PROPERTY} at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}",
187             "PATH", dbusMapping.objectPath, "INTERFACE", dbusMapping.interface,
188             "PROPERTY", dbusMapping.propertyName, "ERROR", e);
189         rc = PLDM_ERROR;
190     }
191     return rc;
192 }
193 
setVersions()194 void CodeUpdate::setVersions()
195 {
196     PendingAttributesList biosAttrList;
197     static constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
198     static constexpr auto functionalObjPath =
199         "/xyz/openbmc_project/software/functional";
200     static constexpr auto activeObjPath =
201         "/xyz/openbmc_project/software/active";
202     static constexpr auto propIntf = "org.freedesktop.DBus.Properties";
203     static constexpr auto pathIntf = "xyz.openbmc_project.Common.FilePath";
204 
205     auto& bus = dBusIntf->getBus();
206     try
207     {
208         auto method = bus.new_method_call(mapperService, functionalObjPath,
209                                           propIntf, "Get");
210         method.append(Association::interface,
211                       Association::property_names::endpoints);
212         std::variant<std::vector<std::string>> paths;
213 
214         auto reply = bus.call(method, dbusTimeout);
215         reply.read(paths);
216 
217         runningVersion = std::get<std::vector<std::string>>(paths)[0];
218         auto runningPathPropValue = dBusIntf->getDbusPropertyVariant(
219             runningVersion.c_str(), "Path", pathIntf);
220         const auto& runningPath = std::get<std::string>(runningPathPropValue);
221 
222         auto method1 =
223             bus.new_method_call(mapperService, activeObjPath, propIntf, "Get");
224         method1.append(Association::interface,
225                        Association::property_names::endpoints);
226 
227         auto reply1 = bus.call(method1, dbusTimeout);
228         reply1.read(paths);
229         for (const auto& path : std::get<std::vector<std::string>>(paths))
230         {
231             if (path != runningVersion)
232             {
233                 nonRunningVersion = path;
234                 break;
235             }
236         }
237         if (!fs::exists(bootSideDirPath))
238         {
239             pldm_boot_side_data pldmBootSideData;
240             std::string nextBootSideBiosValue = "Temp";
241             auto attributeValue = getBiosAttrValue<std::string>("fw_boot_side");
242 
243             // We enter this path during Genesis boot/boot after Factory reset.
244             // PLDM waits for Entity manager to populate System Type. After
245             // receiving system Type from EM it populates the bios attributes
246             // specific to that system We do not have bios attributes populated
247             // when we reach here so setting it to default value of the
248             // attribute as mentioned in the json files.
249             if (attributeValue.has_value())
250             {
251                 nextBootSideBiosValue = attributeValue.value();
252             }
253             else
254             {
255                 info(
256                     "Boot side is not initialized yet, so setting default value(Temp). Request was ignored to set the Boot side to {SIDE}",
257                     "SIDE", nextBootSideBiosValue);
258                 nextBootSideBiosValue = "Temp";
259             }
260             pldmBootSideData.current_boot_side = nextBootSideBiosValue;
261             pldmBootSideData.next_boot_side = nextBootSideBiosValue;
262             pldmBootSideData.running_version_object = runningPath;
263 
264             writeBootSideFile(pldmBootSideData);
265             biosAttrList.emplace_back(std::make_pair(
266                 bootSideAttrName,
267                 std::make_tuple(EnumAttribute,
268                                 pldmBootSideData.current_boot_side)));
269             biosAttrList.push_back(std::make_pair(
270                 bootNextSideAttrName,
271                 std::make_tuple(EnumAttribute,
272                                 pldmBootSideData.next_boot_side)));
273             setBiosAttr(biosAttrList);
274         }
275         else
276         {
277             pldm_boot_side_data pldmBootSideData = readBootSideFile();
278             if (pldmBootSideData.running_version_object != runningPath)
279             {
280                 info(
281                     "BMC have booted with the new image runningPath={RUNN_PATH}",
282                     "RUNN_PATH", runningPath.c_str());
283                 info("Previous Image was: {RUNN_VERS}", "RUNN_VERS",
284                      pldmBootSideData.running_version_object);
285                 auto current_boot_side =
286                     (pldmBootSideData.current_boot_side == "Temp" ? "Perm"
287                                                                   : "Temp");
288                 pldmBootSideData.current_boot_side = current_boot_side;
289                 pldmBootSideData.next_boot_side = current_boot_side;
290                 pldmBootSideData.running_version_object = runningPath;
291                 writeBootSideFile(pldmBootSideData);
292                 biosAttrList.emplace_back(std::make_pair(
293                     bootSideAttrName,
294                     std::make_tuple(EnumAttribute,
295                                     pldmBootSideData.current_boot_side)));
296                 biosAttrList.push_back(std::make_pair(
297                     bootNextSideAttrName,
298                     std::make_tuple(EnumAttribute,
299                                     pldmBootSideData.next_boot_side)));
300                 setBiosAttr(biosAttrList);
301             }
302             else
303             {
304                 info(
305                     "BMC have booted with the previous image runningPath={RUNN_PATH}",
306                     "RUNN_PATH", pldmBootSideData.running_version_object);
307                 pldm_boot_side_data pldmBootSideData = readBootSideFile();
308                 pldmBootSideData.next_boot_side =
309                     pldmBootSideData.current_boot_side;
310                 writeBootSideFile(pldmBootSideData);
311                 biosAttrList.emplace_back(std::make_pair(
312                     bootSideAttrName,
313                     std::make_tuple(EnumAttribute,
314                                     pldmBootSideData.current_boot_side)));
315                 biosAttrList.push_back(std::make_pair(
316                     bootNextSideAttrName,
317                     std::make_tuple(EnumAttribute,
318                                     pldmBootSideData.next_boot_side)));
319                 setBiosAttr(biosAttrList);
320             }
321             currBootSide =
322                 (pldmBootSideData.current_boot_side == "Temp" ? Tside : Pside);
323             nextBootSide =
324                 (pldmBootSideData.next_boot_side == "Temp" ? Tside : Pside);
325         }
326     }
327     catch (const std::exception& e)
328     {
329         error(
330             "Failed to make a d-bus call to Object Mapper Association, error - {ERROR}",
331             "ERROR", e);
332         return;
333     }
334 
335     using namespace sdbusplus::bus::match::rules;
336     captureNextBootSideChange.push_back(
337         std::make_unique<sdbusplus::bus::match_t>(
338             pldm::utils::DBusHandler::getBus(),
339             propertiesChanged(runningVersion, redundancyIntf),
340             [this](sdbusplus::message_t& msg) {
341                 DbusChangedProps props;
342                 std::string iface;
343                 msg.read(iface, props);
344                 processPriorityChangeNotification(props);
345             }));
346     fwUpdateMatcher.push_back(std::make_unique<sdbusplus::bus::match_t>(
347         pldm::utils::DBusHandler::getBus(),
348         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
349         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
350         [this](sdbusplus::message_t& msg) {
351             DBusInterfaceAdded interfaces;
352             sdbusplus::message::object_path path;
353             msg.read(path, interfaces);
354 
355             for (auto& interface : interfaces)
356             {
357                 if (interface.first ==
358                     "xyz.openbmc_project.Software.Activation")
359                 {
360                     auto imageInterface =
361                         "xyz.openbmc_project.Software.Activation";
362                     auto imageObjPath = path.str.c_str();
363 
364                     try
365                     {
366                         auto propVal = dBusIntf->getDbusPropertyVariant(
367                             imageObjPath, "Activation", imageInterface);
368                         if (isCodeUpdateInProgress())
369                         {
370                             newImageId = path.str;
371                             if (!imageActivationMatch)
372                             {
373                                 imageActivationMatch = std::make_unique<
374                                     sdbusplus::bus::match_t>(
375                                     pldm::utils::DBusHandler::getBus(),
376                                     propertiesChanged(newImageId,
377                                                       "xyz.openbmc_project."
378                                                       "Software.Activation"),
379                                     [this](sdbusplus::message_t& msg) {
380                                         DbusChangedProps props;
381                                         std::string iface;
382                                         msg.read(iface, props);
383                                         const auto itr =
384                                             props.find("Activation");
385                                         if (itr != props.end())
386                                         {
387                                             PropertyValue value = itr->second;
388                                             auto propVal =
389                                                 std::get<std::string>(value);
390                                             if (propVal ==
391                                                 "xyz.openbmc_project.Software."
392                                                 "Activation.Activations.Active")
393                                             {
394                                                 CodeUpdateState state =
395                                                     CodeUpdateState::END;
396                                                 setCodeUpdateProgress(false);
397                                                 auto sensorId =
398                                                     getFirmwareUpdateSensor();
399                                                 sendStateSensorEvent(
400                                                     sensorId,
401                                                     PLDM_STATE_SENSOR_STATE, 0,
402                                                     uint8_t(state),
403                                                     uint8_t(CodeUpdateState::
404                                                                 START));
405                                                 newImageId.clear();
406                                             }
407                                             else if (propVal ==
408                                                          "xyz.openbmc_project."
409                                                          "Software.Activation."
410                                                          "Activations.Failed" ||
411                                                      propVal ==
412                                                          "xyz.openbmc_"
413                                                          "project.Software."
414                                                          "Activation."
415                                                          "Activations."
416                                                          "Invalid")
417                                             {
418                                                 CodeUpdateState state =
419                                                     CodeUpdateState::FAIL;
420                                                 setCodeUpdateProgress(false);
421                                                 auto sensorId =
422                                                     getFirmwareUpdateSensor();
423                                                 sendStateSensorEvent(
424                                                     sensorId,
425                                                     PLDM_STATE_SENSOR_STATE, 0,
426                                                     uint8_t(state),
427                                                     uint8_t(CodeUpdateState::
428                                                                 START));
429                                                 newImageId.clear();
430                                             }
431                                         }
432                                     });
433                             }
434                             auto rc = setRequestedActivation();
435                             if (rc != PLDM_SUCCESS)
436                             {
437                                 error("Could not set Requested Activation");
438                                 CodeUpdateState state = CodeUpdateState::FAIL;
439                                 setCodeUpdateProgress(false);
440                                 auto sensorId = getFirmwareUpdateSensor();
441                                 sendStateSensorEvent(
442                                     sensorId, PLDM_STATE_SENSOR_STATE, 0,
443                                     uint8_t(state),
444                                     uint8_t(CodeUpdateState::START));
445                             }
446                             break;
447                         }
448                         else
449                         {
450                             // Out of band update
451                             processRenameEvent();
452                         }
453                     }
454                     catch (const sdbusplus::exception_t& e)
455                     {
456                         error(
457                             "Failed to get activation status for interface '{INTERFACE}' and object path '{PATH}', error - {ERROR}",
458                             "ERROR", e, "INTERFACE", imageInterface, "PATH",
459                             imageObjPath);
460                     }
461                 }
462             }
463         }));
464 }
465 
processRenameEvent()466 void CodeUpdate::processRenameEvent()
467 {
468     info("Processing Rename Event");
469 
470     PendingAttributesList biosAttrList;
471     pldm_boot_side_data pldmBootSideData = readBootSideFile();
472     pldmBootSideData.current_boot_side = "Perm";
473     pldmBootSideData.next_boot_side = "Perm";
474 
475     currBootSide = Pside;
476     nextBootSide = Pside;
477 
478     auto sensorId = getBootSideRenameStateSensor();
479     info("Received sendor id for rename {ID}", "ID", sensorId);
480     sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
481                          PLDM_OEM_IBM_BOOT_SIDE_RENAME_STATE_RENAMED,
482                          PLDM_OEM_IBM_BOOT_SIDE_RENAME_STATE_NOT_RENAMED);
483     writeBootSideFile(pldmBootSideData);
484     biosAttrList.emplace_back(std::make_pair(
485         bootSideAttrName,
486         std::make_tuple(EnumAttribute, pldmBootSideData.current_boot_side)));
487     biosAttrList.push_back(std::make_pair(
488         bootNextSideAttrName,
489         std::make_tuple(EnumAttribute, pldmBootSideData.next_boot_side)));
490     setBiosAttr(biosAttrList);
491 }
492 
writeBootSideFile(const pldm_boot_side_data & pldmBootSideData)493 void CodeUpdate::writeBootSideFile(const pldm_boot_side_data& pldmBootSideData)
494 {
495     try
496     {
497         fs::create_directories(fs::path(bootSideDirPath).parent_path());
498         std::ofstream writeFile(bootSideDirPath, std::ios::out);
499         if (!writeFile.is_open())
500         {
501             error("Failed to open bootside file {FILE} for writing", "FILE",
502                   bootSideDirPath);
503             return;
504         }
505 
506         nlohmann::json data;
507         data["CurrentBootSide"] = pldmBootSideData.current_boot_side;
508         data["NextBootSide"] = pldmBootSideData.next_boot_side;
509         data["RunningObject"] = pldmBootSideData.running_version_object;
510 
511         try
512         {
513             writeFile << data.dump(4);
514         }
515         catch (const nlohmann::json::exception& e)
516         {
517             error("JSON serialization for BootSide failed: {ERROR}", "ERROR",
518                   e);
519             return;
520         }
521 
522         writeFile.close();
523     }
524     catch (const std::exception& e)
525     {
526         error("Error {ERROR] while writing bootside file: {FILE}", "ERROR", e,
527               "FILE", bootSideDirPath);
528     }
529 }
530 
readBootSideFile()531 pldm_boot_side_data CodeUpdate::readBootSideFile()
532 {
533     pldm_boot_side_data pldmBootSideDataRead{};
534 
535     std::ifstream readFile(bootSideDirPath, std::ios::in);
536 
537     if (!readFile)
538     {
539         error("Failed to read Bootside file");
540         return pldmBootSideDataRead;
541     }
542 
543     nlohmann::json jsonBootSideData;
544     readFile >> jsonBootSideData;
545 
546     pldm_boot_side_data data;
547     data.current_boot_side = jsonBootSideData.value("CurrentBootSide", "");
548     data.next_boot_side = jsonBootSideData.value("NextBootSide", "");
549     data.running_version_object = jsonBootSideData.value("RunningObject", "");
550 
551     readFile.close();
552 
553     return pldmBootSideDataRead;
554 }
555 
processPriorityChangeNotification(const DbusChangedProps & chProperties)556 void CodeUpdate::processPriorityChangeNotification(
557     const DbusChangedProps& chProperties)
558 {
559     error("Processing priority change notification");
560     static constexpr auto propName = "Priority";
561     const auto it = chProperties.find(propName);
562     if (it == chProperties.end())
563     {
564         return;
565     }
566     uint8_t newVal = std::get<uint8_t>(it->second);
567 
568     pldm_boot_side_data pldmBootSideData = readBootSideFile();
569     pldmBootSideData.next_boot_side =
570         (newVal == 0)
571             ? pldmBootSideData.current_boot_side
572             : ((pldmBootSideData.current_boot_side == "Temp") ? "Perm"
573                                                               : "Temp");
574     writeBootSideFile(pldmBootSideData);
575     nextBootSide = (pldmBootSideData.next_boot_side == "Temp" ? Tside : Pside);
576     std::string currNextBootSide;
577     auto attributeValue = getBiosAttrValue<std::string>(bootNextSideAttrName);
578     if (attributeValue.has_value())
579     {
580         currNextBootSide = attributeValue.value();
581     }
582 
583     if (currNextBootSide == nextBootSide)
584     {
585         return;
586     }
587     PendingAttributesList biosAttrList;
588     biosAttrList.push_back(std::make_pair(
589         bootNextSideAttrName,
590         std::make_tuple(EnumAttribute, pldmBootSideData.next_boot_side)));
591     setBiosAttr(biosAttrList);
592 }
593 
setOemPlatformHandler(pldm::responder::oem_platform::Handler * handler)594 void CodeUpdate::setOemPlatformHandler(
595     pldm::responder::oem_platform::Handler* handler)
596 {
597     oemPlatformHandler = handler;
598 }
599 
clearDirPath(const std::string & dirPath)600 void CodeUpdate::clearDirPath(const std::string& dirPath)
601 {
602     if (!fs::is_directory(dirPath))
603     {
604         error("The directory '{PATH}' does not exist", "PATH", dirPath);
605         return;
606     }
607     for (const auto& iter : fs::directory_iterator(dirPath))
608     {
609         fs::remove_all(iter);
610     }
611 }
612 
sendStateSensorEvent(uint16_t sensorId,enum sensor_event_class_states sensorEventClass,uint8_t sensorOffset,uint8_t eventState,uint8_t prevEventState)613 void CodeUpdate::sendStateSensorEvent(
614     uint16_t sensorId, enum sensor_event_class_states sensorEventClass,
615     uint8_t sensorOffset, uint8_t eventState, uint8_t prevEventState)
616 {
617     pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler =
618         dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>(
619             oemPlatformHandler);
620     oemIbmPlatformHandler->sendStateSensorEvent(
621         sensorId, sensorEventClass, sensorOffset, eventState, prevEventState);
622 }
623 
deleteImage()624 void CodeUpdate::deleteImage()
625 {
626     static constexpr auto UPDATER_SERVICE =
627         "xyz.openbmc_project.Software.BMC.Updater";
628     static constexpr auto SW_OBJ_PATH = "/xyz/openbmc_project/software";
629     static constexpr auto DELETE_INTF =
630         "xyz.openbmc_project.Collection.DeleteAll";
631 
632     auto& bus = dBusIntf->getBus();
633     try
634     {
635         auto method = bus.new_method_call(UPDATER_SERVICE, SW_OBJ_PATH,
636                                           DELETE_INTF, "DeleteAll");
637         bus.call_noreply(method, dbusTimeout);
638     }
639     catch (const std::exception& e)
640     {
641         error(
642             "Failed to delete image at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}",
643             "PATH", SW_OBJ_PATH, "INTERFACE", DELETE_INTF, "ERROR", e);
644         return;
645     }
646 }
647 
fetchBootSide(uint16_t entityInstance,CodeUpdate * codeUpdate)648 uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate)
649 {
650     uint8_t sensorOpState = tSideNum;
651     if (entityInstance == 0)
652     {
653         auto currSide = codeUpdate->fetchCurrentBootSide();
654         if (currSide == Pside)
655         {
656             sensorOpState = pSideNum;
657         }
658     }
659     else if (entityInstance == 1)
660     {
661         auto nextSide = codeUpdate->fetchNextBootSide();
662         if (nextSide == Pside)
663         {
664             sensorOpState = pSideNum;
665         }
666     }
667     else
668     {
669         sensorOpState = PLDM_SENSOR_UNKNOWN;
670     }
671 
672     return sensorOpState;
673 }
674 
setBootSide(uint16_t entityInstance,uint8_t currState,const std::vector<set_effecter_state_field> & stateField,CodeUpdate * codeUpdate)675 int setBootSide(uint16_t entityInstance, uint8_t currState,
676                 const std::vector<set_effecter_state_field>& stateField,
677                 CodeUpdate* codeUpdate)
678 {
679     int rc = PLDM_SUCCESS;
680     auto side = (stateField[currState].effecter_state == pSideNum) ? "P" : "T";
681 
682     if (entityInstance == 0)
683     {
684         rc = codeUpdate->setCurrentBootSide(side);
685     }
686     else if (entityInstance == 1)
687     {
688         rc = codeUpdate->setNextBootSide(side);
689     }
690     else
691     {
692         rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
693     }
694     return rc;
695 }
696 
697 template <typename... T>
executeCmd(const T &...t)698 int executeCmd(const T&... t)
699 {
700     std::stringstream cmd;
701     ((cmd << t << " "), ...) << std::endl;
702     FILE* pipe = popen(cmd.str().c_str(), "r");
703     if (!pipe)
704     {
705         throw std::runtime_error("popen() failed!");
706     }
707     int rc = pclose(pipe);
708     if (WEXITSTATUS(rc))
709     {
710         std::cerr << "Error executing: ";
711         ((std::cerr << " " << t), ...);
712         std::cerr << "\n";
713         return -1;
714     }
715 
716     return 0;
717 }
718 
processCodeUpdateLid(const std::string & filePath)719 int processCodeUpdateLid(const std::string& filePath)
720 {
721     struct LidHeader
722     {
723         uint16_t magicNumber;
724         uint16_t headerVersion;
725         uint32_t lidNumber;
726         uint32_t lidDate;
727         uint16_t lidTime;
728         uint16_t lidClass;
729         uint32_t lidCrc;
730         uint32_t lidSize;
731         uint32_t headerSize;
732     };
733     LidHeader header;
734 
735     std::ifstream ifs(filePath, std::ios::in | std::ios::binary);
736     if (!ifs)
737     {
738         error("Failed to opening file '{FILE}' ifstream", "PATH", filePath);
739         return PLDM_ERROR;
740     }
741     ifs.seekg(0);
742     ifs.read(reinterpret_cast<char*>(&header), sizeof(header));
743 
744     // File size should be the value of lid size minus the header size
745     auto fileSize = fs::file_size(filePath);
746     fileSize -= htonl(header.headerSize);
747     if (fileSize < htonl(header.lidSize))
748     {
749         // File is not completely written yet
750         ifs.close();
751         return PLDM_SUCCESS;
752     }
753 
754     constexpr auto magicNumber = 0x0222;
755     if (htons(header.magicNumber) != magicNumber)
756     {
757         error("Invalid magic number for file '{PATH}'", "PATH", filePath);
758         ifs.close();
759         return PLDM_ERROR;
760     }
761 
762     fs::create_directories(imageDirPath);
763     fs::create_directories(lidDirPath);
764 
765     constexpr auto bmcClass = 0x2000;
766     if (htons(header.lidClass) == bmcClass)
767     {
768         // Skip the header and concatenate the BMC LIDs into a tar file
769         std::ofstream ofs(tarImagePath,
770                           std::ios::out | std::ios::binary | std::ios::app);
771         ifs.seekg(htonl(header.headerSize));
772         ofs << ifs.rdbuf();
773         ofs.flush();
774         ofs.close();
775     }
776     else
777     {
778         std::stringstream lidFileName;
779         lidFileName << std::hex << htonl(header.lidNumber) << ".lid";
780         auto lidNoHeaderPath = fs::path(lidDirPath) / lidFileName.str();
781         std::ofstream ofs(lidNoHeaderPath,
782                           std::ios::out | std::ios::binary | std::ios::trunc);
783         ifs.seekg(htonl(header.headerSize));
784         ofs << ifs.rdbuf();
785         ofs.flush();
786         ofs.close();
787     }
788 
789     ifs.close();
790     fs::remove(filePath);
791     return PLDM_SUCCESS;
792 }
793 
assembleCodeUpdateImage()794 int CodeUpdate::assembleCodeUpdateImage()
795 {
796     pid_t pid = fork();
797 
798     if (pid == 0)
799     {
800         pid_t nextPid = fork();
801         if (nextPid == 0)
802         {
803             // Create the hostfw squashfs image from the LID files without
804             // header
805             auto rc = executeCmd("/usr/sbin/mksquashfs", lidDirPath.c_str(),
806                                  hostfwImagePath.c_str(), "-all-root",
807                                  "-no-recovery");
808             if (rc < 0)
809             {
810                 error("Error occurred during the mksqusquashfs call");
811                 setCodeUpdateProgress(false);
812                 auto sensorId = getFirmwareUpdateSensor();
813                 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
814                                      uint8_t(CodeUpdateState::FAIL),
815                                      uint8_t(CodeUpdateState::START));
816                 exit(EXIT_FAILURE);
817             }
818 
819             fs::create_directories(updateDirPath);
820 
821             // Extract the BMC tarball content
822             rc = executeCmd("/bin/tar", "-xf", tarImagePath.c_str(), "-C",
823                             updateDirPath);
824             if (rc < 0)
825             {
826                 setCodeUpdateProgress(false);
827                 auto sensorId = getFirmwareUpdateSensor();
828                 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
829                                      uint8_t(CodeUpdateState::FAIL),
830                                      uint8_t(CodeUpdateState::START));
831                 exit(EXIT_FAILURE);
832             }
833 
834             // Add the hostfw image to the directory where the contents were
835             // extracted
836             fs::copy_file(hostfwImagePath,
837                           fs::path(updateDirPath) / hostfwImageName,
838                           fs::copy_options::overwrite_existing);
839 
840             // Remove the tarball file, then re-generate it with so that the
841             // hostfw image becomes part of the tarball
842             fs::remove(tarImagePath);
843             rc = executeCmd("/bin/tar", "-cf", tarImagePath, ".", "-C",
844                             updateDirPath);
845             if (rc < 0)
846             {
847                 error("Error occurred during the generation of the tarball");
848                 setCodeUpdateProgress(false);
849                 auto sensorId = getFirmwareUpdateSensor();
850                 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
851                                      uint8_t(CodeUpdateState::FAIL),
852                                      uint8_t(CodeUpdateState::START));
853                 exit(EXIT_FAILURE);
854             }
855 
856             // Copy the tarball to the update directory to trigger the phosphor
857             // software manager to create a version interface
858             fs::copy_file(tarImagePath, updateImagePath,
859                           fs::copy_options::overwrite_existing);
860 
861             // Cleanup
862             fs::remove_all(updateDirPath);
863             fs::remove_all(lidDirPath);
864             fs::remove_all(imageDirPath);
865 
866             exit(EXIT_SUCCESS);
867         }
868         else if (nextPid < 0)
869         {
870             error("Failure occurred during fork, error number - {ERROR_NUM}",
871                   "ERROR_NUM", errno);
872             exit(EXIT_FAILURE);
873         }
874 
875         // Do nothing as parent. When parent exits, child will be reparented
876         // under init and be reaped properly.
877         exit(0);
878     }
879     else if (pid > 0)
880     {
881         int status;
882         if (waitpid(pid, &status, 0) < 0)
883         {
884             error("Error occurred during waitpid, error number - {ERROR_NUM}",
885                   "ERROR_NUM", errno);
886 
887             return PLDM_ERROR;
888         }
889         else if (WEXITSTATUS(status) != 0)
890         {
891             error(
892                 "Failed to execute the assembling of the image, status is {IMG_STATUS}",
893                 "IMG_STATUS", status);
894             return PLDM_ERROR;
895         }
896     }
897     else
898     {
899         error("Error occurred during fork, error number - {ERROR_NUM}}",
900               "ERROR_NUM", errno);
901         return PLDM_ERROR;
902     }
903 
904     return PLDM_SUCCESS;
905 }
906 
907 } // namespace responder
908 } // namespace pldm
909