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