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