xref: /openbmc/pldm/oem/ibm/libpldmresponder/inband_code_update.cpp (revision e26d13fc2589e480aea0d648500bdc15e5a177fd)
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 
28 /** @brief Directory where the lid files without a header are stored */
29 auto lidDirPath = fs::path(LID_STAGING_DIR) / "lid";
30 
31 /** @brief Directory where the image files are stored as they are built */
32 auto imageDirPath = fs::path(LID_STAGING_DIR) / "image";
33 
34 /** @brief Directory where the code update tarball files are stored */
35 auto updateDirPath = fs::path(LID_STAGING_DIR) / "update";
36 
37 /** @brief The file name of the code update tarball */
38 constexpr auto tarImageName = "image.tar";
39 
40 /** @brief The file name of the hostfw image */
41 constexpr auto hostfwImageName = "image-hostfw";
42 
43 /** @brief The filename of the file where bootside data will be saved */
44 constexpr auto bootSideFileName = "bootSide";
45 
46 /** @brief The path to the code update tarball file */
47 auto tarImagePath = fs::path(imageDirPath) / tarImageName;
48 
49 /** @brief The path to the hostfw image */
50 auto hostfwImagePath = fs::path(imageDirPath) / hostfwImageName;
51 
52 /** @brief The path to the tarball file expected by the phosphor software
53  *         manager */
54 auto updateImagePath = fs::path("/tmp/images") / tarImageName;
55 
56 /** @brief Current boot side */
57 constexpr auto bootSideAttrName = "fw_boot_side_current";
58 
59 /** @brief Next boot side */
60 constexpr auto bootNextSideAttrName = "fw_boot_side";
61 
62 /** @brief The filepath of file where bootside data will be saved */
63 auto bootSideDirPath = fs::path("/var/lib/pldm/") / bootSideFileName;
64 
fetchCurrentBootSide()65 std::string CodeUpdate::fetchCurrentBootSide()
66 {
67     return currBootSide;
68 }
69 
fetchNextBootSide()70 std::string CodeUpdate::fetchNextBootSide()
71 {
72     return nextBootSide;
73 }
74 
setCurrentBootSide(const std::string & currSide)75 int CodeUpdate::setCurrentBootSide(const std::string& currSide)
76 {
77     currBootSide = currSide;
78     return PLDM_SUCCESS;
79 }
80 
setNextBootSide(const std::string & nextSide)81 int CodeUpdate::setNextBootSide(const std::string& nextSide)
82 {
83     info("setNextBootSide, nextSide={NXT_SIDE}", "NXT_SIDE", nextSide);
84     pldm_boot_side_data pldmBootSideData = readBootSideFile();
85     currBootSide =
86         (pldmBootSideData.current_boot_side == "Perm" ? Pside : Tside);
87     nextBootSide = nextSide;
88     pldmBootSideData.next_boot_side = (nextSide == Pside ? "Perm" : "Temp");
89     std::string objPath{};
90     if (nextBootSide == currBootSide)
91     {
92         info(
93             "Current bootside is same as next boot side, setting priority of running version 0");
94         objPath = runningVersion;
95     }
96     else
97     {
98         info(
99             "Current bootside is not same as next boot side, setting priority of non running version 0");
100         objPath = nonRunningVersion;
101     }
102     if (objPath.empty())
103     {
104         error("no nonRunningVersion present");
105         return PLDM_PLATFORM_INVALID_STATE_VALUE;
106     }
107 
108     try
109     {
110         auto priorityPropValue = dBusIntf->getDbusPropertyVariant(
111             objPath.c_str(), "Priority", redundancyIntf);
112         const auto& priorityValue = std::get<uint8_t>(priorityPropValue);
113         if (priorityValue == 0)
114         {
115             // Requested next boot side is already set
116             return PLDM_SUCCESS;
117         }
118     }
119     catch (const std::exception& e)
120     {
121         // Alternate side may not be present due to a failed code update
122         error("Alternate side may not be present due to a failed code update. "
123               "ERROR: {ERR}",
124               "ERR", e);
125         return PLDM_PLATFORM_INVALID_STATE_VALUE;
126     }
127 
128     pldm::utils::DBusMapping dbusMapping{objPath, redundancyIntf, "Priority",
129                                          "uint8_t"};
130     uint8_t val = 0;
131     pldm::utils::PropertyValue value = static_cast<uint8_t>(val);
132     try
133     {
134         dBusIntf->setDbusProperty(dbusMapping, value);
135     }
136     catch (const std::exception& e)
137     {
138         error("Failed to set the next boot side to {PATH} , error - {ERROR}",
139               "PATH", objPath, "ERROR", e);
140         return PLDM_ERROR;
141     }
142     writeBootSideFile(pldmBootSideData);
143     return PLDM_SUCCESS;
144 }
145 
setRequestedApplyTime()146 int CodeUpdate::setRequestedApplyTime()
147 {
148     int rc = PLDM_SUCCESS;
149     pldm::utils::PropertyValue value =
150         "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
151     DBusMapping dbusMapping;
152     dbusMapping.objectPath = "/xyz/openbmc_project/software/apply_time";
153     dbusMapping.interface = "xyz.openbmc_project.Software.ApplyTime";
154     dbusMapping.propertyName = "RequestedApplyTime";
155     dbusMapping.propertyType = "string";
156     try
157     {
158         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
159     }
160     catch (const std::exception& e)
161     {
162         error(
163             "Failed to set property '{PROPERTY}' at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}",
164             "PATH", dbusMapping.objectPath, "INTERFACE", dbusMapping.interface,
165             "PROPERTY", dbusMapping.propertyName, "ERROR", e);
166         rc = PLDM_ERROR;
167     }
168     return rc;
169 }
170 
setRequestedActivation()171 int CodeUpdate::setRequestedActivation()
172 {
173     int rc = PLDM_SUCCESS;
174     pldm::utils::PropertyValue value =
175         "xyz.openbmc_project.Software.Activation.RequestedActivations.Active";
176     DBusMapping dbusMapping;
177     dbusMapping.objectPath = newImageId;
178     dbusMapping.interface = "xyz.openbmc_project.Software.Activation";
179     dbusMapping.propertyName = "RequestedActivation";
180     dbusMapping.propertyType = "string";
181     try
182     {
183         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
184     }
185     catch (const std::exception& e)
186     {
187         error(
188             "Failed to set property {PROPERTY} at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}",
189             "PATH", dbusMapping.objectPath, "INTERFACE", dbusMapping.interface,
190             "PROPERTY", dbusMapping.propertyName, "ERROR", e);
191         rc = PLDM_ERROR;
192     }
193     return rc;
194 }
195 
setVersions()196 void CodeUpdate::setVersions()
197 {
198     PendingAttributesList biosAttrList;
199     static constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
200     static constexpr auto functionalObjPath =
201         "/xyz/openbmc_project/software/functional";
202     static constexpr auto activeObjPath =
203         "/xyz/openbmc_project/software/active";
204     static constexpr auto propIntf = "org.freedesktop.DBus.Properties";
205 
206     auto& bus = dBusIntf->getBus();
207     try
208     {
209         auto method = bus.new_method_call(mapperService, functionalObjPath,
210                                           propIntf, "Get");
211         method.append("xyz.openbmc_project.Association", "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 
219         auto method1 =
220             bus.new_method_call(mapperService, activeObjPath, propIntf, "Get");
221         method1.append("xyz.openbmc_project.Association", "endpoints");
222 
223         auto reply1 = bus.call(method1, dbusTimeout);
224         reply1.read(paths);
225         for (const auto& path : std::get<std::vector<std::string>>(paths))
226         {
227             if (path != runningVersion)
228             {
229                 nonRunningVersion = path;
230                 break;
231             }
232         }
233         if (!fs::exists(bootSideDirPath))
234         {
235             pldm_boot_side_data pldmBootSideData;
236             std::string nextBootSideBiosValue = "Temp";
237             auto attributeValue = getBiosAttrValue<std::string>("fw_boot_side");
238 
239             // We enter this path during Genesis boot/boot after Factory reset.
240             // PLDM waits for Entity manager to populate System Type. After
241             // receiving system Type from EM it populates the bios attributes
242             // specific to that system We do not have bios attributes populated
243             // when we reach here so setting it to default value of the
244             // attribute as mentioned in the json files.
245             if (attributeValue.has_value())
246             {
247                 nextBootSideBiosValue = attributeValue.value();
248             }
249             else
250             {
251                 info(
252                     "Boot side is not initialized yet, so setting default value");
253                 nextBootSideBiosValue = "Temp";
254             }
255             pldmBootSideData.current_boot_side = nextBootSideBiosValue;
256             pldmBootSideData.next_boot_side = nextBootSideBiosValue;
257             pldmBootSideData.running_version_object = runningVersion;
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 != runningVersion)
274             {
275                 info(
276                     "BMC have booted with the new image runningPath={RUNN_PATH}",
277                     "RUNN_PATH", runningVersion.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 = runningVersion;
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(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.string());
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.string(), 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     static constexpr auto propName = "Priority";
555     const auto it = chProperties.find(propName);
556     if (it == chProperties.end())
557     {
558         return;
559     }
560     uint8_t newVal = std::get<uint8_t>(it->second);
561     nextBootSide = (newVal == 0) ? currBootSide
562                                  : ((currBootSide == Tside) ? Pside : Tside);
563 }
564 
setOemPlatformHandler(pldm::responder::oem_platform::Handler * handler)565 void CodeUpdate::setOemPlatformHandler(
566     pldm::responder::oem_platform::Handler* handler)
567 {
568     oemPlatformHandler = handler;
569 }
570 
clearDirPath(const std::string & dirPath)571 void CodeUpdate::clearDirPath(const std::string& dirPath)
572 {
573     if (!fs::is_directory(dirPath))
574     {
575         error("The directory '{PATH}' does not exist", "PATH", dirPath);
576         return;
577     }
578     for (const auto& iter : fs::directory_iterator(dirPath))
579     {
580         fs::remove_all(iter);
581     }
582 }
583 
sendStateSensorEvent(uint16_t sensorId,enum sensor_event_class_states sensorEventClass,uint8_t sensorOffset,uint8_t eventState,uint8_t prevEventState)584 void CodeUpdate::sendStateSensorEvent(
585     uint16_t sensorId, enum sensor_event_class_states sensorEventClass,
586     uint8_t sensorOffset, uint8_t eventState, uint8_t prevEventState)
587 {
588     pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler =
589         dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>(
590             oemPlatformHandler);
591     oemIbmPlatformHandler->sendStateSensorEvent(
592         sensorId, sensorEventClass, sensorOffset, eventState, prevEventState);
593 }
594 
deleteImage()595 void CodeUpdate::deleteImage()
596 {
597     static constexpr auto UPDATER_SERVICE =
598         "xyz.openbmc_project.Software.BMC.Updater";
599     static constexpr auto SW_OBJ_PATH = "/xyz/openbmc_project/software";
600     static constexpr auto DELETE_INTF =
601         "xyz.openbmc_project.Collection.DeleteAll";
602 
603     auto& bus = dBusIntf->getBus();
604     try
605     {
606         auto method = bus.new_method_call(UPDATER_SERVICE, SW_OBJ_PATH,
607                                           DELETE_INTF, "DeleteAll");
608         bus.call_noreply(method, dbusTimeout);
609     }
610     catch (const std::exception& e)
611     {
612         error(
613             "Failed to delete image at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}",
614             "PATH", SW_OBJ_PATH, "INTERFACE", DELETE_INTF, "ERROR", e);
615         return;
616     }
617 }
618 
fetchBootSide(uint16_t entityInstance,CodeUpdate * codeUpdate)619 uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate)
620 {
621     uint8_t sensorOpState = tSideNum;
622     if (entityInstance == 0)
623     {
624         auto currSide = codeUpdate->fetchCurrentBootSide();
625         if (currSide == Pside)
626         {
627             sensorOpState = pSideNum;
628         }
629     }
630     else if (entityInstance == 1)
631     {
632         auto nextSide = codeUpdate->fetchNextBootSide();
633         if (nextSide == Pside)
634         {
635             sensorOpState = pSideNum;
636         }
637     }
638     else
639     {
640         sensorOpState = PLDM_SENSOR_UNKNOWN;
641     }
642 
643     return sensorOpState;
644 }
645 
setBootSide(uint16_t entityInstance,uint8_t currState,const std::vector<set_effecter_state_field> & stateField,CodeUpdate * codeUpdate)646 int setBootSide(uint16_t entityInstance, uint8_t currState,
647                 const std::vector<set_effecter_state_field>& stateField,
648                 CodeUpdate* codeUpdate)
649 {
650     int rc = PLDM_SUCCESS;
651     auto side = (stateField[currState].effecter_state == pSideNum) ? "P" : "T";
652 
653     if (entityInstance == 0)
654     {
655         rc = codeUpdate->setCurrentBootSide(side);
656     }
657     else if (entityInstance == 1)
658     {
659         rc = codeUpdate->setNextBootSide(side);
660     }
661     else
662     {
663         rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
664     }
665     return rc;
666 }
667 
668 template <typename... T>
executeCmd(const T &...t)669 int executeCmd(const T&... t)
670 {
671     std::stringstream cmd;
672     ((cmd << t << " "), ...) << std::endl;
673     FILE* pipe = popen(cmd.str().c_str(), "r");
674     if (!pipe)
675     {
676         throw std::runtime_error("popen() failed!");
677     }
678     int rc = pclose(pipe);
679     if (WEXITSTATUS(rc))
680     {
681         std::cerr << "Error executing: ";
682         ((std::cerr << " " << t), ...);
683         std::cerr << "\n";
684         return -1;
685     }
686 
687     return 0;
688 }
689 
processCodeUpdateLid(const std::string & filePath)690 int processCodeUpdateLid(const std::string& filePath)
691 {
692     struct LidHeader
693     {
694         uint16_t magicNumber;
695         uint16_t headerVersion;
696         uint32_t lidNumber;
697         uint32_t lidDate;
698         uint16_t lidTime;
699         uint16_t lidClass;
700         uint32_t lidCrc;
701         uint32_t lidSize;
702         uint32_t headerSize;
703     };
704     LidHeader header;
705 
706     std::ifstream ifs(filePath, std::ios::in | std::ios::binary);
707     if (!ifs)
708     {
709         error("Failed to opening file '{FILE}' ifstream", "PATH", filePath);
710         return PLDM_ERROR;
711     }
712     ifs.seekg(0);
713     ifs.read(reinterpret_cast<char*>(&header), sizeof(header));
714 
715     // File size should be the value of lid size minus the header size
716     auto fileSize = fs::file_size(filePath);
717     fileSize -= htonl(header.headerSize);
718     if (fileSize < htonl(header.lidSize))
719     {
720         // File is not completely written yet
721         ifs.close();
722         return PLDM_SUCCESS;
723     }
724 
725     constexpr auto magicNumber = 0x0222;
726     if (htons(header.magicNumber) != magicNumber)
727     {
728         error("Invalid magic number for file '{PATH}'", "PATH", filePath);
729         ifs.close();
730         return PLDM_ERROR;
731     }
732 
733     fs::create_directories(imageDirPath);
734     fs::create_directories(lidDirPath);
735 
736     constexpr auto bmcClass = 0x2000;
737     if (htons(header.lidClass) == bmcClass)
738     {
739         // Skip the header and concatenate the BMC LIDs into a tar file
740         std::ofstream ofs(tarImagePath,
741                           std::ios::out | std::ios::binary | std::ios::app);
742         ifs.seekg(htonl(header.headerSize));
743         ofs << ifs.rdbuf();
744         ofs.flush();
745         ofs.close();
746     }
747     else
748     {
749         std::stringstream lidFileName;
750         lidFileName << std::hex << htonl(header.lidNumber) << ".lid";
751         auto lidNoHeaderPath = fs::path(lidDirPath) / lidFileName.str();
752         std::ofstream ofs(lidNoHeaderPath,
753                           std::ios::out | std::ios::binary | std::ios::trunc);
754         ifs.seekg(htonl(header.headerSize));
755         ofs << ifs.rdbuf();
756         ofs.flush();
757         ofs.close();
758     }
759 
760     ifs.close();
761     fs::remove(filePath);
762     return PLDM_SUCCESS;
763 }
764 
assembleCodeUpdateImage()765 int CodeUpdate::assembleCodeUpdateImage()
766 {
767     pid_t pid = fork();
768 
769     if (pid == 0)
770     {
771         pid_t nextPid = fork();
772         if (nextPid == 0)
773         {
774             // Create the hostfw squashfs image from the LID files without
775             // header
776             auto rc = executeCmd("/usr/sbin/mksquashfs", lidDirPath.c_str(),
777                                  hostfwImagePath.c_str(), "-all-root",
778                                  "-no-recovery");
779             if (rc < 0)
780             {
781                 error("Error occurred during the mksqusquashfs call");
782                 setCodeUpdateProgress(false);
783                 auto sensorId = getFirmwareUpdateSensor();
784                 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
785                                      uint8_t(CodeUpdateState::FAIL),
786                                      uint8_t(CodeUpdateState::START));
787                 exit(EXIT_FAILURE);
788             }
789 
790             fs::create_directories(updateDirPath);
791 
792             // Extract the BMC tarball content
793             rc = executeCmd("/bin/tar", "-xf", tarImagePath.c_str(), "-C",
794                             updateDirPath);
795             if (rc < 0)
796             {
797                 setCodeUpdateProgress(false);
798                 auto sensorId = getFirmwareUpdateSensor();
799                 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
800                                      uint8_t(CodeUpdateState::FAIL),
801                                      uint8_t(CodeUpdateState::START));
802                 exit(EXIT_FAILURE);
803             }
804 
805             // Add the hostfw image to the directory where the contents were
806             // extracted
807             fs::copy_file(hostfwImagePath,
808                           fs::path(updateDirPath) / hostfwImageName,
809                           fs::copy_options::overwrite_existing);
810 
811             // Remove the tarball file, then re-generate it with so that the
812             // hostfw image becomes part of the tarball
813             fs::remove(tarImagePath);
814             rc = executeCmd("/bin/tar", "-cf", tarImagePath, ".", "-C",
815                             updateDirPath);
816             if (rc < 0)
817             {
818                 error("Error occurred during the generation of the tarball");
819                 setCodeUpdateProgress(false);
820                 auto sensorId = getFirmwareUpdateSensor();
821                 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0,
822                                      uint8_t(CodeUpdateState::FAIL),
823                                      uint8_t(CodeUpdateState::START));
824                 exit(EXIT_FAILURE);
825             }
826 
827             // Copy the tarball to the update directory to trigger the phosphor
828             // software manager to create a version interface
829             fs::copy_file(tarImagePath, updateImagePath,
830                           fs::copy_options::overwrite_existing);
831 
832             // Cleanup
833             fs::remove_all(updateDirPath);
834             fs::remove_all(lidDirPath);
835             fs::remove_all(imageDirPath);
836 
837             exit(EXIT_SUCCESS);
838         }
839         else if (nextPid < 0)
840         {
841             error("Failure occurred during fork, error number - {ERROR_NUM}",
842                   "ERROR_NUM", errno);
843             exit(EXIT_FAILURE);
844         }
845 
846         // Do nothing as parent. When parent exits, child will be reparented
847         // under init and be reaped properly.
848         exit(0);
849     }
850     else if (pid > 0)
851     {
852         int status;
853         if (waitpid(pid, &status, 0) < 0)
854         {
855             error("Error occurred during waitpid, error number - {ERROR_NUM}",
856                   "ERROR_NUM", errno);
857 
858             return PLDM_ERROR;
859         }
860         else if (WEXITSTATUS(status) != 0)
861         {
862             error(
863                 "Failed to execute the assembling of the image, status is {IMG_STATUS}",
864                 "IMG_STATUS", status);
865             return PLDM_ERROR;
866         }
867     }
868     else
869     {
870         error("Error occurred during fork, error number - {ERROR_NUM}}",
871               "ERROR_NUM", errno);
872         return PLDM_ERROR;
873     }
874 
875     return PLDM_SUCCESS;
876 }
877 
878 } // namespace responder
879 } // namespace pldm
880