blob: 27ce945c2e893e5aa0579ab7180d250c697ff8e6 [file] [log] [blame]
Sampa Misraaea5dde2020-08-31 08:33:47 -05001#include "inband_code_update.hpp"
2
Sagar Srinivas78a225a2020-08-27 00:52:20 -05003#include "libpldm/entity.h"
4
5#include "libpldmresponder/pdr.hpp"
Sampa Misraaea5dde2020-08-31 08:33:47 -05006#include "oem_ibm_handler.hpp"
7#include "xyz/openbmc_project/Common/error.hpp"
8
Adriana Kobylak727f7382020-09-01 14:38:25 -05009#include <arpa/inet.h>
10
Sampa Misraaea5dde2020-08-31 08:33:47 -050011#include <sdbusplus/server.hpp>
12#include <xyz/openbmc_project/Dump/NewDump/server.hpp>
13
14#include <exception>
Adriana Kobylak727f7382020-09-01 14:38:25 -050015#include <fstream>
Sampa Misraaea5dde2020-08-31 08:33:47 -050016namespace pldm
17{
Sampa Misraaea5dde2020-08-31 08:33:47 -050018namespace responder
19{
20using namespace oem_ibm_platform;
21
Adriana Kobylakfa810d72020-10-16 16:27:28 -050022/** @brief Directory where the lid files without a header are stored */
23auto lidDirPath = fs::path(LID_STAGING_DIR) / "lid";
24
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060025/** @brief Directory where the image files are stored as they are built */
26auto imageDirPath = fs::path(LID_STAGING_DIR) / "image";
27
Adriana Kobylak837fb472020-10-16 16:53:42 -050028/** @brief Directory where the code update tarball files are stored */
29auto updateDirPath = fs::path(LID_STAGING_DIR) / "update";
30
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060031/** @brief The file name of the code update tarball */
32constexpr auto tarImageName = "image.tar";
33
Adriana Kobylak837fb472020-10-16 16:53:42 -050034/** @brief The file name of the hostfw image */
Adriana Kobylak131327e2021-03-10 18:45:24 +000035constexpr auto hostfwImageName = "image-hostfw";
Adriana Kobylak837fb472020-10-16 16:53:42 -050036
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060037/** @brief The path to the code update tarball file */
38auto tarImagePath = fs::path(imageDirPath) / tarImageName;
39
Adriana Kobylak837fb472020-10-16 16:53:42 -050040/** @brief The path to the hostfw image */
41auto hostfwImagePath = fs::path(imageDirPath) / hostfwImageName;
42
43/** @brief The path to the tarball file expected by the phosphor software
44 * manager */
45auto updateImagePath = fs::path("/tmp/images") / tarImageName;
46
Sampa Misraaea5dde2020-08-31 08:33:47 -050047std::string CodeUpdate::fetchCurrentBootSide()
48{
49 return currBootSide;
50}
51
52std::string CodeUpdate::fetchNextBootSide()
53{
54 return nextBootSide;
55}
56
57int CodeUpdate::setCurrentBootSide(const std::string& currSide)
58{
59 currBootSide = currSide;
60 return PLDM_SUCCESS;
61}
62
63int CodeUpdate::setNextBootSide(const std::string& nextSide)
64{
65 nextBootSide = nextSide;
66 std::string objPath{};
67 if (nextBootSide == currBootSide)
68 {
69 objPath = runningVersion;
70 }
71 else
72 {
73 objPath = nonRunningVersion;
74 }
75 if (objPath.empty())
76 {
77 std::cerr << "no nonRunningVersion present \n";
78 return PLDM_PLATFORM_INVALID_STATE_VALUE;
79 }
80
81 pldm::utils::DBusMapping dbusMapping{objPath, redundancyIntf, "Priority",
82 "uint8_t"};
83 uint8_t val = 0;
84 pldm::utils::PropertyValue value = static_cast<uint8_t>(val);
85 try
86 {
87 dBusIntf->setDbusProperty(dbusMapping, value);
88 }
89 catch (const std::exception& e)
90 {
91 std::cerr << "failed to set the next boot side to " << objPath.c_str()
92 << " ERROR=" << e.what() << "\n";
93 return PLDM_ERROR;
94 }
95 return PLDM_SUCCESS;
96}
97
Sagar Srinivascfdbca72020-09-22 10:03:35 -050098int CodeUpdate::setRequestedApplyTime()
99{
100 int rc = PLDM_SUCCESS;
101 pldm::utils::PropertyValue value =
102 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
103 DBusMapping dbusMapping;
104 dbusMapping.objectPath = "/xyz/openbmc_project/software/apply_time";
105 dbusMapping.interface = "xyz.openbmc_project.Software.ApplyTime";
106 dbusMapping.propertyName = "RequestedApplyTime";
107 dbusMapping.propertyType = "string";
108 try
109 {
110 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
111 }
112 catch (const std::exception& e)
113 {
114 std::cerr << "Failed To set RequestedApplyTime property "
115 << "ERROR=" << e.what() << std::endl;
116 rc = PLDM_ERROR;
117 }
118 return rc;
119}
120
121int CodeUpdate::setRequestedActivation()
122{
123 int rc = PLDM_SUCCESS;
124 pldm::utils::PropertyValue value =
125 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active";
126 DBusMapping dbusMapping;
127 dbusMapping.objectPath = newImageId;
128 dbusMapping.interface = "xyz.openbmc_project.Software.Activation";
129 dbusMapping.propertyName = "RequestedActivation";
130 dbusMapping.propertyType = "string";
131 try
132 {
133 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
134 }
135 catch (const std::exception& e)
136 {
137 std::cerr << "Failed To set RequestedActivation property"
138 << "ERROR=" << e.what() << std::endl;
139 rc = PLDM_ERROR;
140 }
Sagar Srinivascfdbca72020-09-22 10:03:35 -0500141 return rc;
142}
143
Sampa Misraaea5dde2020-08-31 08:33:47 -0500144void CodeUpdate::setVersions()
145{
146 static constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
147 static constexpr auto functionalObjPath =
148 "/xyz/openbmc_project/software/functional";
149 static constexpr auto activeObjPath =
150 "/xyz/openbmc_project/software/active";
151 static constexpr auto propIntf = "org.freedesktop.DBus.Properties";
152
153 auto& bus = dBusIntf->getBus();
Sampa Misraaea5dde2020-08-31 08:33:47 -0500154 try
155 {
156 auto method = bus.new_method_call(mapperService, functionalObjPath,
157 propIntf, "Get");
158 method.append("xyz.openbmc_project.Association", "endpoints");
159 std::variant<std::vector<std::string>> paths;
160
161 auto reply = bus.call(method);
162 reply.read(paths);
163
164 runningVersion = std::get<std::vector<std::string>>(paths)[0];
165
166 auto method1 =
167 bus.new_method_call(mapperService, activeObjPath, propIntf, "Get");
168 method1.append("xyz.openbmc_project.Association", "endpoints");
169
170 auto reply1 = bus.call(method1);
171 reply1.read(paths);
172 for (const auto& path : std::get<std::vector<std::string>>(paths))
173 {
174 if (path != runningVersion)
175 {
176 nonRunningVersion = path;
177 break;
178 }
179 }
180 }
181 catch (const std::exception& e)
182 {
183 std::cerr << "failed to make a d-bus call to Object Mapper "
184 "Association, ERROR="
185 << e.what() << "\n";
186 return;
187 }
188
189 using namespace sdbusplus::bus::match::rules;
190 captureNextBootSideChange.push_back(
191 std::make_unique<sdbusplus::bus::match::match>(
192 pldm::utils::DBusHandler::getBus(),
193 propertiesChanged(runningVersion, redundancyIntf),
194 [this](sdbusplus::message::message& msg) {
195 DbusChangedProps props;
196 std::string iface;
197 msg.read(iface, props);
198 processPriorityChangeNotification(props);
199 }));
Sagar Srinivas9a64b4a2021-02-09 07:55:38 -0600200 fwUpdateMatcher.push_back(std::make_unique<sdbusplus::bus::match::match>(
Sampa Misraaea5dde2020-08-31 08:33:47 -0500201 pldm::utils::DBusHandler::getBus(),
202 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
203 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
204 [this](sdbusplus::message::message& msg) {
205 DBusInterfaceAdded interfaces;
206 sdbusplus::message::object_path path;
207 msg.read(path, interfaces);
Sagar Srinivas9a64b4a2021-02-09 07:55:38 -0600208
Sampa Misraaea5dde2020-08-31 08:33:47 -0500209 for (auto& interface : interfaces)
210 {
211 if (interface.first ==
212 "xyz.openbmc_project.Software.Activation")
213 {
Sampa Misra3a0e3b92020-10-21 05:58:00 -0500214 auto imageInterface =
215 "xyz.openbmc_project.Software.Activation";
216 auto imageObjPath = path.str.c_str();
Sagar Srinivas9a64b4a2021-02-09 07:55:38 -0600217
Sampa Misra3a0e3b92020-10-21 05:58:00 -0500218 try
219 {
220 auto propVal = dBusIntf->getDbusPropertyVariant(
221 imageObjPath, "Activation", imageInterface);
222 const auto& imageProp = std::get<std::string>(propVal);
223 if (imageProp == "xyz.openbmc_project.Software."
224 "Activation.Activations.Ready" &&
225 isCodeUpdateInProgress())
226 {
227 newImageId = path.str;
Sagar Srinivas9a64b4a2021-02-09 07:55:38 -0600228 if (!imageActivationMatch)
229 {
230 imageActivationMatch = std::make_unique<
231 sdbusplus::bus::match::match>(
232 pldm::utils::DBusHandler::getBus(),
233 propertiesChanged(newImageId,
234 "xyz.openbmc_project."
235 "Software.Activation"),
236 [this](sdbusplus::message::message& msg) {
237 DbusChangedProps props;
238 std::string iface;
239 msg.read(iface, props);
240 const auto itr =
241 props.find("Activation");
242 if (itr != props.end())
243 {
244 PropertyValue value = itr->second;
245 auto propVal =
246 std::get<std::string>(value);
247 if (propVal ==
248 "xyz.openbmc_project.Software."
249 "Activation.Activations.Active")
250 {
251 CodeUpdateState state =
252 CodeUpdateState::END;
253 setCodeUpdateProgress(false);
254 auto sensorId =
255 getFirmwareUpdateSensor();
256 sendStateSensorEvent(
257 sensorId,
258 PLDM_STATE_SENSOR_STATE, 0,
259 uint8_t(state),
260 uint8_t(CodeUpdateState::
261 START));
262 newImageId.clear();
263 }
264 else if (propVal ==
265 "xyz.openbmc_project."
266 "Software.Activation."
267 "Activations.Failed" ||
268 propVal ==
269 "xyz.openbmc_"
270 "project.Software."
271 "Activation."
272 "Activations."
273 "Invalid")
274 {
275 CodeUpdateState state =
276 CodeUpdateState::FAIL;
277 setCodeUpdateProgress(false);
278 auto sensorId =
279 getFirmwareUpdateSensor();
280 sendStateSensorEvent(
281 sensorId,
282 PLDM_STATE_SENSOR_STATE, 0,
283 uint8_t(state),
284 uint8_t(CodeUpdateState::
285 START));
286 newImageId.clear();
287 }
288 }
289 });
290 }
Sampa Misra3a0e3b92020-10-21 05:58:00 -0500291 auto rc = setRequestedActivation();
Sampa Misra3a0e3b92020-10-21 05:58:00 -0500292 if (rc != PLDM_SUCCESS)
293 {
Sagar Srinivas9a64b4a2021-02-09 07:55:38 -0600294 CodeUpdateState state = CodeUpdateState::FAIL;
295 setCodeUpdateProgress(false);
296 auto sensorId = getFirmwareUpdateSensor();
297 sendStateSensorEvent(
298 sensorId, PLDM_STATE_SENSOR_STATE, 0,
299 uint8_t(state),
300 uint8_t(CodeUpdateState::START));
Sampa Misra3a0e3b92020-10-21 05:58:00 -0500301 std::cerr
302 << "could not set RequestedActivation \n";
303 }
Sampa Misra3a0e3b92020-10-21 05:58:00 -0500304 break;
305 }
306 }
307 catch (const sdbusplus::exception::SdBusError& e)
308 {
309 std::cerr << "Error in getting Activation status \n";
310 }
Sampa Misraaea5dde2020-08-31 08:33:47 -0500311 }
312 }
Sagar Srinivas9a64b4a2021-02-09 07:55:38 -0600313 }));
Sampa Misraaea5dde2020-08-31 08:33:47 -0500314}
315
316void CodeUpdate::processPriorityChangeNotification(
317 const DbusChangedProps& chProperties)
318{
319 static constexpr auto propName = "Priority";
320 const auto it = chProperties.find(propName);
321 if (it == chProperties.end())
322 {
323 return;
324 }
325 uint8_t newVal = std::get<uint8_t>(it->second);
326 nextBootSide = (newVal == 0) ? currBootSide
327 : ((currBootSide == Tside) ? Pside : Tside);
328}
329
330void CodeUpdate::setOemPlatformHandler(
331 pldm::responder::oem_platform::Handler* handler)
332{
333 oemPlatformHandler = handler;
334}
335
Varsha Kaverappa3ca29df2020-09-27 12:39:22 -0500336void CodeUpdate::clearDirPath(const std::string& dirPath)
337{
338 for (auto& path : fs::directory_iterator(dirPath.c_str()))
339 {
340 fs::remove_all(path);
341 }
342 return;
343}
344
Sampa Misra3a0e3b92020-10-21 05:58:00 -0500345void CodeUpdate::sendStateSensorEvent(
346 uint16_t sensorId, enum sensor_event_class_states sensorEventClass,
347 uint8_t sensorOffset, uint8_t eventState, uint8_t prevEventState)
348{
349 pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler =
350 dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>(
351 oemPlatformHandler);
352 oemIbmPlatformHandler->sendStateSensorEvent(
353 sensorId, sensorEventClass, sensorOffset, eventState, prevEventState);
354}
355
356void CodeUpdate::deleteImage()
357{
358 static constexpr auto UPDATER_SERVICE =
359 "xyz.openbmc_project.Software.BMC.Updater";
360 static constexpr auto SW_OBJ_PATH = "/xyz/openbmc_project/software";
361 static constexpr auto DELETE_INTF =
362 "xyz.openbmc_project.Collection.DeleteAll";
363
364 auto& bus = dBusIntf->getBus();
365 try
366 {
367 auto method = bus.new_method_call(UPDATER_SERVICE, SW_OBJ_PATH,
368 DELETE_INTF, "DeleteAll");
369 bus.call_noreply(method);
370 }
371 catch (const std::exception& e)
372 {
373 std::cerr << "Failed to delete image, ERROR=" << e.what() << "\n";
374 return;
375 }
376}
377
Sampa Misraaea5dde2020-08-31 08:33:47 -0500378uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate)
379{
380 uint8_t sensorOpState = tSideNum;
Sampa Misraaea5dde2020-08-31 08:33:47 -0500381 if (entityInstance == 0)
382 {
383 auto currSide = codeUpdate->fetchCurrentBootSide();
384 if (currSide == Pside)
385 {
386 sensorOpState = pSideNum;
387 }
388 }
389 else if (entityInstance == 1)
390 {
391 auto nextSide = codeUpdate->fetchNextBootSide();
392 if (nextSide == Pside)
393 {
394 sensorOpState = pSideNum;
395 }
396 }
397 else
398 {
399 sensorOpState = PLDM_SENSOR_UNKNOWN;
400 }
401
402 return sensorOpState;
403}
404
405int setBootSide(uint16_t entityInstance, uint8_t currState,
406 const std::vector<set_effecter_state_field>& stateField,
407 CodeUpdate* codeUpdate)
408{
409 int rc = PLDM_SUCCESS;
410 auto side = (stateField[currState].effecter_state == pSideNum) ? "P" : "T";
411
412 if (entityInstance == 0)
413 {
414 rc = codeUpdate->setCurrentBootSide(side);
415 }
416 else if (entityInstance == 1)
417 {
418 rc = codeUpdate->setNextBootSide(side);
419 }
420 else
421 {
422 rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
423 }
424 return rc;
425}
426
Adriana Kobylak837fb472020-10-16 16:53:42 -0500427template <typename... T>
428int executeCmd(T const&... t)
429{
430 std::stringstream cmd;
431 ((cmd << t << " "), ...) << std::endl;
432 FILE* pipe = popen(cmd.str().c_str(), "r");
433 if (!pipe)
434 {
435 throw std::runtime_error("popen() failed!");
436 }
437 int rc = pclose(pipe);
438 if (WEXITSTATUS(rc))
439 {
440 std::cerr << "Error executing: ";
441 ((std::cerr << " " << t), ...);
442 std::cerr << "\n";
443 return -1;
444 }
445
446 return 0;
447}
448
Adriana Kobylak727f7382020-09-01 14:38:25 -0500449int processCodeUpdateLid(const std::string& filePath)
450{
451 struct LidHeader
452 {
453 uint16_t magicNumber;
Adriana Kobylak86d14182020-10-16 16:11:08 -0500454 uint16_t headerVersion;
455 uint32_t lidNumber;
456 uint32_t lidDate;
457 uint16_t lidTime;
458 uint16_t lidClass;
459 uint32_t lidCrc;
460 uint32_t lidSize;
461 uint32_t headerSize;
Adriana Kobylak727f7382020-09-01 14:38:25 -0500462 };
463 LidHeader header;
464
465 std::ifstream ifs(filePath, std::ios::in | std::ios::binary);
466 if (!ifs)
467 {
468 std::cerr << "ifstream open error: " << filePath << "\n";
469 return PLDM_ERROR;
470 }
471 ifs.seekg(0);
472 ifs.read(reinterpret_cast<char*>(&header), sizeof(header));
Adriana Kobylak727f7382020-09-01 14:38:25 -0500473
Adriana Kobylak86d14182020-10-16 16:11:08 -0500474 // File size should be the value of lid size minus the header size
475 auto fileSize = fs::file_size(filePath);
476 fileSize -= htonl(header.headerSize);
477 if (fileSize < htonl(header.lidSize))
478 {
479 // File is not completely written yet
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500480 ifs.close();
Adriana Kobylak86d14182020-10-16 16:11:08 -0500481 return PLDM_SUCCESS;
482 }
483
Adriana Kobylak727f7382020-09-01 14:38:25 -0500484 constexpr auto magicNumber = 0x0222;
485 if (htons(header.magicNumber) != magicNumber)
486 {
487 std::cerr << "Invalid magic number: " << filePath << "\n";
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500488 ifs.close();
Adriana Kobylak727f7382020-09-01 14:38:25 -0500489 return PLDM_ERROR;
490 }
491
Adriana Kobylaka1f158c2020-11-09 12:47:29 -0600492 fs::create_directories(imageDirPath);
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500493 fs::create_directories(lidDirPath);
494
Adriana Kobylaka1f158c2020-11-09 12:47:29 -0600495 constexpr auto bmcClass = 0x2000;
496 if (htons(header.lidClass) == bmcClass)
497 {
498 // Skip the header and concatenate the BMC LIDs into a tar file
499 std::ofstream ofs(tarImagePath,
500 std::ios::out | std::ios::binary | std::ios::app);
501 ifs.seekg(htonl(header.headerSize));
502 ofs << ifs.rdbuf();
503 ofs.flush();
504 ofs.close();
505 }
506 else
507 {
508 std::stringstream lidFileName;
509 lidFileName << std::hex << htonl(header.lidNumber) << ".lid";
510 auto lidNoHeaderPath = fs::path(lidDirPath) / lidFileName.str();
511 std::ofstream ofs(lidNoHeaderPath,
512 std::ios::out | std::ios::binary | std::ios::trunc);
513 ifs.seekg(htonl(header.headerSize));
514 ofs << ifs.rdbuf();
515 ofs.flush();
516 ofs.close();
517 }
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500518
519 ifs.close();
520 fs::remove(filePath);
Adriana Kobylak727f7382020-09-01 14:38:25 -0500521 return PLDM_SUCCESS;
522}
523
Adriana Kobylak837fb472020-10-16 16:53:42 -0500524int assembleCodeUpdateImage()
525{
526 // Create the hostfw squashfs image from the LID files without header
527 auto rc = executeCmd("/usr/sbin/mksquashfs", lidDirPath.c_str(),
528 hostfwImagePath.c_str(), "-all-root", "-no-recovery");
529 if (rc < 0)
530 {
531 return PLDM_ERROR;
532 }
533
534 fs::create_directories(updateDirPath);
535
536 // Extract the BMC tarball content
537 rc = executeCmd("/bin/tar", "-xf", tarImagePath.c_str(), "-C",
538 updateDirPath);
539 if (rc < 0)
540 {
541 return PLDM_ERROR;
542 }
543
544 // Add the hostfw image to the directory where the contents were extracted
545 fs::copy_file(hostfwImagePath, fs::path(updateDirPath) / hostfwImageName,
546 fs::copy_options::overwrite_existing);
547
548 // Remove the tarball file, then re-generate it with so that the hostfw
549 // image becomes part of the tarball
550 fs::remove(tarImagePath);
551 rc = executeCmd("/bin/tar", "-cf", tarImagePath, ".", "-C", updateDirPath);
552 if (rc < 0)
553 {
554 return PLDM_ERROR;
555 }
556
557 // Copy the tarball to the update directory to trigger the phosphor software
558 // manager to create a version interface
559 fs::copy_file(tarImagePath, updateImagePath,
560 fs::copy_options::overwrite_existing);
561
562 // Cleanup
563 fs::remove_all(updateDirPath);
564 fs::remove_all(lidDirPath);
565 fs::remove_all(imageDirPath);
566
567 return PLDM_SUCCESS;
568}
569
Sampa Misraaea5dde2020-08-31 08:33:47 -0500570} // namespace responder
571} // namespace pldm