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