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