xref: /openbmc/intel-ipmi-oem/src/firmware-update.cpp (revision ab5c0e5763a6e25c7d44758b5522a5d4e0cb6728)
1  #include <byteswap.h>
2  #include <ipmid/api.h>
3  #include <openssl/evp.h>
4  #include <openssl/sha.h>
5  #include <sys/mman.h>
6  #include <sys/stat.h>
7  #include <sys/types.h>
8  #include <unistd.h>
9  
10  #include <appcommands.hpp>
11  #include <boost/algorithm/string.hpp>
12  #include <boost/container/flat_map.hpp>
13  #include <boost/process/child.hpp>
14  #include <boost/uuid/random_generator.hpp>
15  #include <boost/uuid/uuid_io.hpp>
16  #include <commandutils.hpp>
17  #include <ipmid/api.hpp>
18  #include <ipmid/utils.hpp>
19  #include <phosphor-logging/log.hpp>
20  #include <sdbusplus/bus.hpp>
21  #include <sdbusplus/bus/match.hpp>
22  #include <sdbusplus/server/object.hpp>
23  #include <sdbusplus/timer.hpp>
24  #include <types.hpp>
25  
26  #include <chrono>
27  #include <cstdint>
28  #include <filesystem>
29  #include <fstream>
30  #include <iostream>
31  #include <map>
32  #include <random>
33  #ifdef INTEL_PFR_ENABLED
34  #include <spiDev.hpp>
35  #endif
36  
37  static constexpr int openSslSuccess = 1;
38  static constexpr bool DEBUG = true;
39  static void registerFirmwareFunctions() __attribute__((constructor));
40  
41  namespace ipmi
42  {
43  namespace firmware
44  {
45  constexpr Cmd cmdGetFwVersionInfo = 0x20;
46  constexpr Cmd cmdGetFwSecurityVersionInfo = 0x21;
47  constexpr Cmd cmdGetFwUpdateChannelInfo = 0x22;
48  constexpr Cmd cmdGetBmcExecutionContext = 0x23;
49  constexpr Cmd cmdFwGetRootCertData = 0x25;
50  constexpr Cmd cmdGetFwUpdateRandomNumber = 0x26;
51  constexpr Cmd cmdSetFirmwareUpdateMode = 0x27;
52  constexpr Cmd cmdExitFirmwareUpdateMode = 0x28;
53  constexpr Cmd cmdGetSetFwUpdateControl = 0x29;
54  constexpr Cmd cmdGetFirmwareUpdateStatus = 0x2A;
55  constexpr Cmd cmdSetFirmwareUpdateOptions = 0x2B;
56  constexpr Cmd cmdFwImageWriteData = 0x2c;
57  } // namespace firmware
58  } // namespace ipmi
59  
60  namespace ipmi
61  {
62  // Custom completion codes
63  constexpr Cc ccUsbAttachOrDetachFailed = 0x80;
64  constexpr Cc ccNotSupportedInPresentState = 0xD5;
65  
responseUsbAttachOrDetachFailed()66  static inline auto responseUsbAttachOrDetachFailed()
67  {
68      return response(ccUsbAttachOrDetachFailed);
69  }
responseNotSupportedInPresentState()70  static inline auto responseNotSupportedInPresentState()
71  {
72      return response(ccNotSupportedInPresentState);
73  }
74  } // namespace ipmi
75  
76  static constexpr size_t imageCount = 2;
77  std::array<std::array<uint8_t, imageCount>, imageCount> imgFwSecurityVersion =
78      {};
79  static constexpr size_t svnActiveVerOffsetInPfm = 0x404;
80  static constexpr size_t bkcActiveVerOffsetInPfm = 0x405;
81  static constexpr size_t svnRecoveryVerOffsetInPfm = 0x804;
82  static constexpr size_t bkcRecoveryVerOffsetInPfm = 0x805;
83  static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC";
84  static constexpr const char* bmcStatePath = "/xyz/openbmc_project/state/bmc0";
85  static constexpr const char* bmcStateReady =
86      "xyz.openbmc_project.State.BMC.BMCState.Ready";
87  static constexpr const char* bmcStateUpdateInProgress =
88      "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
89  
90  static constexpr char firmwareBufferFile[] = "/tmp/fw-download.bin";
91  static std::chrono::steady_clock::time_point fwRandomNumGenTs;
92  static constexpr auto fwRandomNumExpirySeconds = std::chrono::seconds(30);
93  static constexpr size_t fwRandomNumLength = 8;
94  static std::array<uint8_t, fwRandomNumLength> fwRandomNum;
95  constexpr char usbCtrlPath[] = "/usr/bin/usb-ctrl";
96  constexpr char fwUpdateMountPoint[] = "/tmp/usb-fwupd.mnt";
97  constexpr char fwUpdateUsbVolImage[] = "/tmp/usb-fwupd.img";
98  constexpr char fwUpdateUSBDevName[] = "fw-usb-mass-storage-dev";
99  constexpr size_t fwPathMaxLength = 255;
100  
101  #ifdef INTEL_PFR_ENABLED
102  uint32_t imgLength = 0;
103  uint32_t imgType = 0;
104  bool block0Mapped = false;
105  static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
106  
107  static constexpr const char* bmcActivePfmMTDDev = "/dev/mtd/pfm";
108  static constexpr const char* bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
109  static constexpr size_t pfmBaseOffsetInImage = 0x400;
110  static constexpr size_t rootkeyOffsetInPfm = 0xA0;
111  static constexpr size_t cskKeyOffsetInPfm = 0x124;
112  static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
113  static constexpr size_t certKeyLen = 96;
114  static constexpr size_t cskSignatureLen = 96;
115  
116  static constexpr const char* versionIntf =
117      "xyz.openbmc_project.Software.Version";
118  
119  enum class FwGetRootCertDataTag : uint8_t
120  {
121      activeRootKey = 1,
122      recoveryRootKey,
123      activeCSK,
124      recoveryCSK,
125  };
126  
127  enum class FWDeviceIDTag : uint8_t
128  {
129      bmcActiveImage = 1,
130      bmcRecoveryImage,
131  };
132  
133  const static boost::container::flat_map<FWDeviceIDTag, const char*>
134      fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
135                      "/xyz/openbmc_project/software/bmc_active"},
136                     {FWDeviceIDTag::bmcRecoveryImage,
137                      "/xyz/openbmc_project/software/bmc_recovery"}};
138  #endif // INTEL_PFR_ENABLED
139  
140  enum class ChannelIdTag : uint8_t
141  {
142      reserved = 0,
143      kcs = 1,
144      ipmb = 2,
145      rmcpPlus = 3
146  };
147  
148  enum class BmcExecutionContext : uint8_t
149  {
150      reserved = 0,
151      linuxOs = 0x10,
152      bootLoader = 0x11,
153  };
154  
155  enum class FwUpdateCtrlReq : uint8_t
156  {
157      getCurrentControlStatus = 0x00,
158      imageTransferStart = 0x01,
159      imageTransferComplete = 0x02,
160      imageTransferAbort = 0x03,
161      setFirmwareFilename = 0x04,
162      attachUsbDevice = 0x05,
163      detachUsbDevice = 0x06
164  };
165  
operator ""_MB(unsigned long long v)166  constexpr std::size_t operator""_MB(unsigned long long v)
167  {
168      return 1024u * 1024u * v;
169  }
170  static constexpr size_t maxFirmwareImageSize = 37_MB;
171  
localDownloadInProgress(void)172  static bool localDownloadInProgress(void)
173  {
174      struct stat sb;
175      if (stat(firmwareBufferFile, &sb) < 0)
176      {
177          return false;
178      }
179      return true;
180  }
181  
182  class TransferHashCheck
183  {
184    public:
185      enum class HashCheck : uint8_t
186      {
187          notRequested = 0,
188          requested,
189          sha2Success,
190          sha2Failed = 0xe2,
191      };
192  
193    protected:
194      std::unique_ptr<EVP_MD_CTX, std::function<void(EVP_MD_CTX*)>> ctx;
195      std::vector<uint8_t> expectedHash;
196      HashCheck check;
197  
198    public:
TransferHashCheck(const std::vector<uint8_t> & expected)199      TransferHashCheck(const std::vector<uint8_t>& expected) :
200          ctx(EVP_MD_CTX_new(), &EVP_MD_CTX_free), expectedHash(expected)
201      {
202          if (!ctx)
203          {
204              throw std::runtime_error("Unable to allocate for ctx.");
205          }
206  
207          if (EVP_DigestInit(ctx.get(), EVP_sha256()) != openSslSuccess)
208          {
209              throw std::runtime_error("Unable to allocate for ctx.");
210          }
211  
212          check = HashCheck::requested;
213      }
214  
~TransferHashCheck()215      ~TransferHashCheck() {}
216  
hash(const std::vector<uint8_t> & data)217      bool hash(const std::vector<uint8_t>& data)
218      {
219          if (EVP_DigestUpdate(ctx.get(), data.data(), data.size()) !=
220              openSslSuccess)
221          {
222              return false;
223          }
224  
225          return true;
226      }
227  
clear()228      bool clear()
229      {
230          /*
231           *   EVP_DigestInit() always uses the default digest implementation and
232           * calls EVP_MD_CTX_reset().
233           */
234          if (EVP_DigestInit(ctx.get(), EVP_sha256()) != openSslSuccess)
235          {
236              return false;
237          }
238  
239          return true;
240      }
241  
verify()242      enum HashCheck verify()
243      {
244          unsigned int len = 0;
245          std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
246  
247          check = HashCheck::sha2Failed;
248  
249          if (EVP_DigestFinal(ctx.get(), digest.data(), &len) == openSslSuccess)
250          {
251              if (digest == expectedHash)
252              {
253                  phosphor::logging::log<phosphor::logging::level::INFO>(
254                      "Transfer sha2 verify passed.");
255                  check = HashCheck::sha2Success;
256              }
257              else
258              {
259                  phosphor::logging::log<phosphor::logging::level::ERR>(
260                      "Transfer sha2 verify failed.");
261                  check = HashCheck::sha2Failed;
262              }
263          }
264  
265          return check;
266      }
267  
status() const268      uint8_t status() const
269      {
270          return static_cast<uint8_t>(check);
271      }
272  };
273  
274  class MappedFile
275  {
276    public:
MappedFile(const std::string & fname)277      MappedFile(const std::string& fname) : addr(nullptr), fsize(0)
278      {
279          std::error_code ec;
280          size_t sz = std::filesystem::file_size(fname, ec);
281          if (!ec)
282          {
283              return;
284          }
285          int fd = open(fname.c_str(), O_RDONLY);
286          if (fd < 0)
287          {
288              return;
289          }
290          void* tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
291          close(fd);
292          if (tmp == MAP_FAILED)
293          {
294              return;
295          }
296          addr = tmp;
297          fsize = sz;
298      }
299  
~MappedFile()300      ~MappedFile()
301      {
302          if (addr)
303          {
304              munmap(addr, fsize);
305          }
306      }
data() const307      const uint8_t* data() const
308      {
309          return static_cast<const uint8_t*>(addr);
310      }
size() const311      size_t size() const
312      {
313          return fsize;
314      }
315  
316    private:
317      void* addr;
318      size_t fsize;
319  };
320  
321  class FwUpdateStatusCache
322  {
323    public:
324      enum
325      {
326          fwStateInit = 0,
327          fwStateIdle,
328          fwStateDownload,
329          fwStateVerify,
330          fwStateProgram,
331          fwStateUpdateSuccess,
332          fwStateError = 0x0f,
333          fwStateAcCycleRequired = 0x83,
334      };
getState()335      uint8_t getState()
336      {
337          if ((fwUpdateState == fwStateIdle || fwUpdateState == fwStateInit) &&
338              localDownloadInProgress())
339          {
340              fwUpdateState = fwStateDownload;
341              progressPercent = 0;
342          }
343          return fwUpdateState;
344      }
resetStatusCache()345      void resetStatusCache()
346      {
347          unlink(firmwareBufferFile);
348      }
setState(const uint8_t state)349      void setState(const uint8_t state)
350      {
351          switch (state)
352          {
353              case fwStateInit:
354              case fwStateIdle:
355              case fwStateError:
356                  resetStatusCache();
357                  break;
358              case fwStateDownload:
359              case fwStateVerify:
360              case fwStateProgram:
361              case fwStateUpdateSuccess:
362                  break;
363              default:
364                  // Error
365                  break;
366          }
367          fwUpdateState = state;
368      }
percent()369      uint8_t percent()
370      {
371          return progressPercent;
372      }
updateActivationPercent(const std::string & objPath)373      void updateActivationPercent(const std::string& objPath)
374      {
375          std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
376          fwUpdateState = fwStateProgram;
377          progressPercent = 0;
378          match = std::make_shared<sdbusplus::bus::match_t>(
379              *busp,
380              sdbusplus::bus::match::rules::propertiesChanged(
381                  objPath, "xyz.openbmc_project.Software.ActivationProgress"),
382              [&](sdbusplus::message_t& msg) {
383                  std::map<std::string, ipmi::DbusVariant> props;
384                  std::vector<std::string> inVal;
385                  std::string iface;
386                  try
387                  {
388                      msg.read(iface, props, inVal);
389                  }
390                  catch (const std::exception& e)
391                  {
392                      phosphor::logging::log<phosphor::logging::level::ERR>(
393                          "Exception caught in get ActivationProgress");
394                      return;
395                  }
396  
397                  auto it = props.find("Progress");
398                  if (it != props.end())
399                  {
400                      progressPercent = std::get<uint8_t>(it->second);
401                      if (progressPercent == 100)
402                      {
403                          fwUpdateState = fwStateUpdateSuccess;
404                      }
405                  }
406              });
407      }
activationTimerTimeout()408      uint8_t activationTimerTimeout()
409      {
410          phosphor::logging::log<phosphor::logging::level::INFO>(
411              "activationTimerTimeout: Increase percentage...",
412              phosphor::logging::entry("PERCENT:%d", progressPercent));
413          progressPercent = progressPercent + 5;
414          if (progressPercent > 95)
415          {
416              /*changing the state to ready to update firmware utility */
417              fwUpdateState = fwStateUpdateSuccess;
418          }
419          return progressPercent;
420      }
421      /* API for changing state to ERROR  */
firmwareUpdateAbortState()422      void firmwareUpdateAbortState()
423      {
424          unlink(firmwareBufferFile);
425          // changing the state to error
426          fwUpdateState = fwStateError;
427      }
setDeferRestart(bool deferRestart)428      void setDeferRestart(bool deferRestart)
429      {
430          deferRestartState = deferRestart;
431      }
setInhibitDowngrade(bool inhibitDowngrade)432      void setInhibitDowngrade(bool inhibitDowngrade)
433      {
434          inhibitDowngradeState = inhibitDowngrade;
435      }
getDeferRestart()436      bool getDeferRestart()
437      {
438          return deferRestartState;
439      }
getInhibitDowngrade()440      bool getInhibitDowngrade()
441      {
442          return inhibitDowngradeState;
443      }
444  
445    protected:
446      std::shared_ptr<sdbusplus::asio::connection> busp;
447      std::shared_ptr<sdbusplus::bus::match_t> match;
448      uint8_t fwUpdateState = 0;
449      uint8_t progressPercent = 0;
450      bool deferRestartState = false;
451      bool inhibitDowngradeState = false;
452  };
453  
454  static FwUpdateStatusCache fwUpdateStatus;
455  std::unique_ptr<TransferHashCheck> xferHashCheck;
456  
activateImage(const std::string & objPath)457  static void activateImage(const std::string& objPath)
458  {
459      // If flag is false  means to reboot
460      if (fwUpdateStatus.getDeferRestart() == false)
461      {
462          phosphor::logging::log<phosphor::logging::level::INFO>(
463              "activating Image: ",
464              phosphor::logging::entry("OBJPATH =%s", objPath.c_str()));
465          std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
466          bus->async_method_call(
467              [](const boost::system::error_code ec) {
468                  if (ec)
469                  {
470                      phosphor::logging::log<phosphor::logging::level::ERR>(
471                          "async_method_call error: activateImage failed");
472                      return;
473                  }
474              },
475              "xyz.openbmc_project.Software.BMC.Updater", objPath,
476              "org.freedesktop.DBus.Properties", "Set",
477              "xyz.openbmc_project.Software.Activation", "RequestedActivation",
478              ipmi::DbusVariant("xyz.openbmc_project.Software.Activation."
479                                "RequestedActivations.Active"));
480      }
481      else
482      {
483          phosphor::logging::log<phosphor::logging::level::INFO>(
484              "Firmware image activation is deferred.");
485      }
486      fwUpdateStatus.setState(
487          static_cast<uint8_t>(FwUpdateStatusCache::fwStateUpdateSuccess));
488  }
489  
getFirmwareUpdateMode()490  static bool getFirmwareUpdateMode()
491  {
492      std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
493      try
494      {
495          auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
496          ipmi::Value state = ipmi::getDbusProperty(
497              *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState");
498          std::string bmcState = std::get<std::string>(state);
499          return (bmcState == bmcStateUpdateInProgress);
500      }
501      catch (const std::exception& e)
502      {
503          phosphor::logging::log<phosphor::logging::level::ERR>(
504              "Exception caught while getting BMC state.",
505              phosphor::logging::entry("EXCEPTION=%s", e.what()));
506          throw;
507      }
508  }
509  
setFirmwareUpdateMode(const bool mode)510  static void setFirmwareUpdateMode(const bool mode)
511  {
512      std::string bmcState(bmcStateReady);
513      if (mode)
514      {
515          bmcState = bmcStateUpdateInProgress;
516      }
517  
518      std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
519      try
520      {
521          auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
522          ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf,
523                                "CurrentBMCState", bmcState);
524      }
525      catch (const std::exception& e)
526      {
527          phosphor::logging::log<phosphor::logging::level::ERR>(
528              "Exception caught while setting BMC state.",
529              phosphor::logging::entry("EXCEPTION=%s", e.what()));
530          throw;
531      }
532  }
533  
534  /** @brief check if channel IPMB
535   *
536   *  This function checks if the command is from IPMB
537   *
538   * @param[in] ctx - context of current session.
539   *  @returns true if the medium is IPMB else return true.
540   **/
checkIPMBChannel(const ipmi::Context::ptr & ctx,bool & isIPMBChannel)541  ipmi::Cc checkIPMBChannel(const ipmi::Context::ptr& ctx, bool& isIPMBChannel)
542  {
543      ipmi::ChannelInfo chInfo;
544  
545      if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
546      {
547          phosphor::logging::log<phosphor::logging::level::ERR>(
548              "Failed to get Channel Info",
549              phosphor::logging::entry("CHANNEL=%d", ctx->channel));
550          return ipmi::ccUnspecifiedError;
551      }
552  
553      if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
554          ipmi::EChannelMediumType::ipmb)
555      {
556          isIPMBChannel = true;
557      }
558      return ipmi::ccSuccess;
559  }
560  
postTransferCompleteHandler(std::unique_ptr<sdbusplus::bus::match_t> & fwUpdateMatchSignal)561  static void postTransferCompleteHandler(
562      std::unique_ptr<sdbusplus::bus::match_t>& fwUpdateMatchSignal)
563  {
564      // Setup timer for watching signal
565      static sdbusplus::Timer timer([&fwUpdateMatchSignal]() {
566          fwUpdateMatchSignal = nullptr;
567      });
568  
569      static sdbusplus::Timer activationStatusTimer([]() {
570          if (fwUpdateStatus.activationTimerTimeout() > 95)
571          {
572              activationStatusTimer.stop();
573              fwUpdateStatus.setState(
574                  static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
575          }
576      });
577  
578      timer.start(std::chrono::microseconds(5000000), false);
579  
580      // callback function for capturing signal
581      auto callback = [&](sdbusplus::message_t& m) {
582          std::vector<
583              std::pair<std::string,
584                        std::vector<std::pair<std::string, ipmi::DbusVariant>>>>
585              intfPropsPair;
586          sdbusplus::message::object_path objPath;
587  
588          try
589          {
590              m.read(objPath, intfPropsPair); // Read in the object path
591                                              // that was just created
592          }
593          catch (const std::exception& e)
594          {
595              phosphor::logging::log<phosphor::logging::level::ERR>(
596                  "Exception caught in reading created object path.");
597              return;
598          }
599          // constructing response message
600          phosphor::logging::log<phosphor::logging::level::INFO>(
601              "New Interface Added.",
602              phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
603          for (auto& interface : intfPropsPair)
604          {
605              if (interface.first == "xyz.openbmc_project.Software.Activation")
606              {
607                  // There are chances of getting two signals for
608                  // InterfacesAdded. So cross check and discrad second instance.
609                  if (fwUpdateMatchSignal == nullptr)
610                  {
611                      return;
612                  }
613                  // Found our interface, disable callbacks
614                  fwUpdateMatchSignal = nullptr;
615  
616                  phosphor::logging::log<phosphor::logging::level::INFO>(
617                      "Start activationStatusTimer for status.");
618                  try
619                  {
620                      timer.stop();
621                      activationStatusTimer.start(
622                          std::chrono::microseconds(3000000), true);
623                  }
624                  catch (const std::exception& e)
625                  {
626                      phosphor::logging::log<phosphor::logging::level::ERR>(
627                          "Exception caught in start activationStatusTimer.",
628                          phosphor::logging::entry("ERROR=%s", e.what()));
629                  }
630  
631                  fwUpdateStatus.updateActivationPercent(objPath.str);
632                  activateImage(objPath.str);
633              }
634          }
635      };
636  
637      // Adding matcher
638      fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match_t>(
639          *getSdBus(),
640          "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
641          "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
642          callback);
643  }
startFirmwareUpdate(const std::string & uri)644  static bool startFirmwareUpdate(const std::string& uri)
645  {
646      // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
647      // the code gets to this point, the file should be transferred start the
648      // request (creating a new file in /tmp/images causes the update manager to
649      // check if it is ready for activation)
650      static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatchSignal;
651      postTransferCompleteHandler(fwUpdateMatchSignal);
652      std::string randomFname =
653          "/tmp/images/" +
654          boost::uuids::to_string(boost::uuids::random_generator()());
655      std::error_code fsError{};
656      std::filesystem::rename(uri, randomFname, fsError);
657      if (fsError)
658      {
659          // The source and destination may not be in the same
660          // filesystem.
661          std::filesystem::copy(uri, randomFname, fsError);
662          if (fsError)
663          {
664              phosphor::logging::log<phosphor::logging::level::ERR>(
665                  "Unable to move/copy image to destination directory.",
666                  phosphor::logging::entry("ERROR=%s",
667                                           fsError.message().c_str()));
668              return false;
669          }
670          std::filesystem::remove(uri);
671      }
672      return true;
673  }
674  
transferImageFromFile(const std::string & uri,bool move=true)675  static bool transferImageFromFile(const std::string& uri, bool move = true)
676  {
677      std::error_code ec;
678      phosphor::logging::log<phosphor::logging::level::INFO>(
679          "Transfer Image From File.",
680          phosphor::logging::entry("URI=%s", uri.c_str()));
681      if (move)
682      {
683          std::filesystem::rename(uri, firmwareBufferFile, ec);
684      }
685      else
686      {
687          std::filesystem::copy(uri, firmwareBufferFile,
688                                std::filesystem::copy_options::overwrite_existing,
689                                ec);
690      }
691      if (xferHashCheck)
692      {
693          MappedFile mappedfw(uri);
694          if (!xferHashCheck->hash(
695                  {mappedfw.data(), mappedfw.data() + mappedfw.size()}))
696          {
697              phosphor::logging::log<phosphor::logging::level::ERR>(
698                  "transferImageFromFile: xferHashCheck->hash failed.");
699              return false;
700          }
701      }
702      if (ec.value())
703      {
704          phosphor::logging::log<phosphor::logging::level::ERR>(
705              "Image copy failed.");
706          return false;
707      }
708  
709      return true;
710  }
711  
712  template <typename... ArgTypes>
executeCmd(const char * path,ArgTypes &&...tArgs)713  static int executeCmd(const char* path, ArgTypes&&... tArgs)
714  {
715      boost::process::child execProg(path, const_cast<char*>(tArgs)...);
716      execProg.wait();
717      return execProg.exit_code();
718  }
719  
transferImageFromUsb(const std::string & uri)720  static bool transferImageFromUsb(const std::string& uri)
721  {
722      bool ret = false;
723  
724      phosphor::logging::log<phosphor::logging::level::INFO>(
725          "Transfer Image From USB.",
726          phosphor::logging::entry("URI=%s", uri.c_str()));
727  
728      if (executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
729                     fwUpdateMountPoint) == 0)
730      {
731          std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
732          ret = transferImageFromFile(usb_path, false);
733  
734          executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage,
735                     fwUpdateMountPoint);
736      }
737  
738      return ret;
739  }
740  
transferFirmwareFromUri(const std::string & uri)741  static bool transferFirmwareFromUri(const std::string& uri)
742  {
743      static constexpr char fwUriFile[] = "file://";
744      static constexpr char fwUriUsb[] = "usb://";
745      phosphor::logging::log<phosphor::logging::level::INFO>(
746          "Transfer Image From URI.",
747          phosphor::logging::entry("URI=%s", uri.c_str()));
748      if (boost::algorithm::starts_with(uri, fwUriFile))
749      {
750          std::string fname = uri.substr(sizeof(fwUriFile) - 1);
751          if (fname != firmwareBufferFile)
752          {
753              return transferImageFromFile(fname);
754          }
755          return true;
756      }
757      if (boost::algorithm::starts_with(uri, fwUriUsb))
758      {
759          std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
760          return transferImageFromUsb(fname);
761      }
762      return false;
763  }
764  
765  /* Get USB-mass-storage device status: inserted => true, ejected => false */
getUsbStatus()766  static bool getUsbStatus()
767  {
768      std::filesystem::path usbDevPath =
769          std::filesystem::path("/sys/kernel/config/usb_gadget") /
770          fwUpdateUSBDevName;
771      return (std::filesystem::exists(usbDevPath) ? true : false);
772  }
773  
774  /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
attachUsbDevice()775  static int attachUsbDevice()
776  {
777      if (getUsbStatus())
778      {
779          return 1;
780      }
781      int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
782                           std::to_string(maxFirmwareImageSize / 1_MB).c_str());
783      if (!ret)
784      {
785          ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
786                           fwUpdateUsbVolImage);
787      }
788      return ret;
789  }
790  
791  /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
detachUsbDevice()792  static int detachUsbDevice()
793  {
794      if (!getUsbStatus())
795      {
796          return 1;
797      }
798      return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
799  }
getActiveBootImage(ipmi::Context::ptr ctx)800  static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
801  {
802      constexpr uint8_t undefinedImage = 0x00;
803      constexpr uint8_t primaryImage = 0x01;
804      constexpr uint8_t secondaryImage = 0x02;
805      constexpr const char* secondaryFitImageStartAddr = "22480000";
806  
807      uint8_t bootImage = primaryImage;
808      boost::system::error_code ec;
809      std::string value = ctx->bus->yield_method_call<std::string>(
810          ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
811          "/xyz/openbmc_project/u_boot/environment/mgr",
812          "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
813      if (ec)
814      {
815          phosphor::logging::log<phosphor::logging::level::ERR>(
816              "Failed to read the bootcmd value");
817          /* don't fail, just give back undefined until it is ready */
818          bootImage = undefinedImage;
819      }
820  
821      /* cheking for secondary FitImage Address 22480000  */
822      else if (value.find(secondaryFitImageStartAddr) != std::string::npos)
823      {
824          bootImage = secondaryImage;
825      }
826      else
827      {
828          bootImage = primaryImage;
829      }
830  
831      return bootImage;
832  }
833  
834  #ifdef INTEL_PFR_ENABLED
835  using fwVersionInfoType =
836      std::tuple<uint8_t,   // ID Tag
837                 uint8_t,   // Major Version Number
838                 uint8_t,   // Minor Version Number
839                 uint32_t,  // Build Number
840                 uint32_t,  // Build Timestamp
841                 uint32_t>; // Update Timestamp
ipmiGetFwVersionInfo()842  ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
843  {
844      // Byte 1 - Count (N) Number of devices data is being returned for.
845      // Bytes  2:16 - Device firmare information(fwVersionInfoType)
846      // Bytes - 17:(15xN) - Repeat of 2 through 16
847  
848      std::vector<fwVersionInfoType> fwVerInfoList;
849      std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
850      for (const auto& fwDev : fwVersionIdMap)
851      {
852          std::string verStr;
853          try
854          {
855              auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
856  
857              ipmi::Value result = ipmi::getDbusProperty(
858                  *busp, service, fwDev.second, versionIntf, "Version");
859              verStr = std::get<std::string>(result);
860          }
861          catch (const std::exception& e)
862          {
863              phosphor::logging::log<phosphor::logging::level::INFO>(
864                  "Failed to fetch Version property",
865                  phosphor::logging::entry("ERROR=%s", e.what()),
866                  phosphor::logging::entry("PATH=%s", fwDev.second),
867                  phosphor::logging::entry("INTERFACE=%s", versionIntf));
868              continue;
869          }
870  
871          if (verStr.empty())
872          {
873              phosphor::logging::log<phosphor::logging::level::INFO>(
874                  "Version is empty.",
875                  phosphor::logging::entry("PATH=%s", fwDev.second),
876                  phosphor::logging::entry("INTERFACE=%s", versionIntf));
877              continue;
878          }
879  
880          uint8_t majorNum = 0;
881          uint8_t minorNum = 0;
882          uint32_t buildNum = 0;
883          try
884          {
885              std::optional<ipmi::MetaRevision> rev =
886                  ipmi::convertIntelVersion(verStr);
887              if (rev.has_value())
888              {
889                  ipmi::MetaRevision revision = rev.value();
890                  majorNum = revision.major % 10 + (revision.major / 10) * 16;
891                  minorNum = (revision.minor > 99 ? 99 : revision.minor);
892                  minorNum = minorNum % 10 + (minorNum / 10) * 16;
893                  uint32_t hash = std::stoul(revision.metaHash, 0, 16);
894                  hash = bswap_32(hash);
895                  buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
896              }
897              else
898              {
899                  std::vector<std::string> splitVer;
900                  boost::split(splitVer, verStr, boost::is_any_of(".-"));
901                  if (splitVer.size() < 3)
902                  {
903                      phosphor::logging::log<phosphor::logging::level::INFO>(
904                          "Invalid Version format.",
905                          phosphor::logging::entry("Version=%s", verStr.c_str()),
906                          phosphor::logging::entry("PATH=%s", fwDev.second));
907                      continue;
908                  }
909                  majorNum = std::stoul(splitVer[0], nullptr, 16);
910                  minorNum = std::stoul(splitVer[1], nullptr, 16);
911                  buildNum = std::stoul(splitVer[2], nullptr, 16);
912              }
913              // Build Timestamp - Not supported.
914              // Update Timestamp - TODO: Need to check with CPLD team.
915              fwVerInfoList.emplace_back(
916                  fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
917                                    minorNum, buildNum, 0, 0));
918          }
919          catch (const std::exception& e)
920          {
921              phosphor::logging::log<phosphor::logging::level::INFO>(
922                  "Failed to convert stoul.",
923                  phosphor::logging::entry("ERROR=%s", e.what()));
924              continue;
925          }
926      }
927  
928      return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
929  }
930  
getSecurityVersionInfo(const char * mtdDevBuf,size_t svnVerOffsetInPfm,size_t bkcVerOffsetInPfm)931  std::array<uint8_t, imageCount> getSecurityVersionInfo(
932      const char* mtdDevBuf, size_t svnVerOffsetInPfm, size_t bkcVerOffsetInPfm)
933  {
934      constexpr size_t bufLength = 1;
935      std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp;
936      constexpr uint8_t svnIndexValue = 0x00;
937      constexpr uint8_t bkcIndexValue = 0x01;
938      constexpr uint8_t tempIndexValue = 0x00;
939      try
940      {
941          SPIDev spiDev(mtdDevBuf);
942          spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data());
943          fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue);
944          spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data());
945          fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue);
946      }
947      catch (const std::exception& e)
948      {
949          phosphor::logging::log<phosphor::logging::level::ERR>(
950              "Exception caught in getSecurityVersionInfo",
951              phosphor::logging::entry("MSG=%s", e.what()));
952          fwSecurityVersionBuf = {0, 0};
953      }
954  
955      return fwSecurityVersionBuf;
956  }
957  
958  ipmi::RspType<
959      uint8_t,                         // device ID
960      uint8_t,                         // Active Image Value
961      std::array<uint8_t, imageCount>, // Security version for Active Image
962      uint8_t,                         // recovery Image Value
963      std::array<uint8_t, imageCount>> // Security version for Recovery Image
ipmiGetFwSecurityVersionInfo()964      ipmiGetFwSecurityVersionInfo()
965  {
966      static bool cacheFlag = false;
967      constexpr std::array<const char*, imageCount> mtdDevBuf = {
968          bmcActivePfmMTDDev, bmcRecoveryImgMTDDev};
969  
970      // To avoid multiple reading from SPI device
971      if (!cacheFlag)
972      {
973          imgFwSecurityVersion[0] = getSecurityVersionInfo(
974              mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm);
975          imgFwSecurityVersion[1] = getSecurityVersionInfo(
976              mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm);
977          cacheFlag = true;
978      }
979  
980      constexpr uint8_t ActivePfmMTDDev = 0x00;
981      constexpr uint8_t RecoveryImgMTDDev = 0x01;
982  
983      return ipmi::responseSuccess(
984          imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage),
985          imgFwSecurityVersion[ActivePfmMTDDev],
986          static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage),
987          imgFwSecurityVersion[RecoveryImgMTDDev]);
988  }
989  
990  ipmi::RspType<std::array<uint8_t, certKeyLen>,
991                std::optional<std::array<uint8_t, cskSignatureLen>>>
ipmiGetFwRootCertData(const ipmi::Context::ptr & ctx,uint8_t certId)992      ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId)
993  {
994      bool isIPMBChannel = false;
995  
996      if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
997      {
998          return ipmi::responseUnspecifiedError();
999      }
1000      if (isIPMBChannel)
1001      {
1002          phosphor::logging::log<phosphor::logging::level::INFO>(
1003              "Command not supported. Failed to get root certificate data.");
1004          return ipmi::responseCommandNotAvailable();
1005      }
1006  
1007      size_t certKeyOffset = 0;
1008      size_t cskSigOffset = 0;
1009      std::string mtdDev;
1010  
1011      switch (static_cast<FwGetRootCertDataTag>(certId))
1012      {
1013          case FwGetRootCertDataTag::activeRootKey:
1014          {
1015              mtdDev = bmcActivePfmMTDDev;
1016              certKeyOffset = rootkeyOffsetInPfm;
1017              break;
1018          }
1019          case FwGetRootCertDataTag::recoveryRootKey:
1020          {
1021              mtdDev = bmcRecoveryImgMTDDev;
1022              certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1023              break;
1024          }
1025          case FwGetRootCertDataTag::activeCSK:
1026          {
1027              mtdDev = bmcActivePfmMTDDev;
1028              certKeyOffset = cskKeyOffsetInPfm;
1029              cskSigOffset = cskSignatureOffsetInPfm;
1030              break;
1031          }
1032          case FwGetRootCertDataTag::recoveryCSK:
1033          {
1034              mtdDev = bmcRecoveryImgMTDDev;
1035              certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1036              cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1037              break;
1038          }
1039          default:
1040          {
1041              return ipmi::responseInvalidFieldRequest();
1042          }
1043      }
1044  
1045      std::array<uint8_t, certKeyLen> certKey = {0};
1046  
1047      try
1048      {
1049          SPIDev spiDev(mtdDev);
1050          spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1051  
1052          if (cskSigOffset)
1053          {
1054              std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1055              spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1056                                 cskSignature.data());
1057              return ipmi::responseSuccess(certKey, cskSignature);
1058          }
1059      }
1060      catch (const std::exception& e)
1061      {
1062          phosphor::logging::log<phosphor::logging::level::ERR>(
1063              "Exception caught in ipmiGetFwRootCertData",
1064              phosphor::logging::entry("MSG=%s", e.what()));
1065          return ipmi::responseUnspecifiedError();
1066      }
1067  
1068      return ipmi::responseSuccess(certKey, std::nullopt);
1069  }
1070  #endif // INTEL_PFR_ENABLED
1071  
1072  static constexpr uint8_t channelListSize = 3;
1073  /** @brief implements Maximum Firmware Transfer size command
1074   *  @parameter
1075   *   -  none
1076   *  @returns IPMI completion code plus response data
1077   *   - count - channel count
1078   *   - channelList - channel list information
1079   */
1080  ipmi::RspType<uint8_t,                    // channel count
1081                std::array<std::tuple<uint8_t, uint32_t>,
1082                           channelListSize> // Channel List
1083                >
ipmiFirmwareMaxTransferSize()1084      ipmiFirmwareMaxTransferSize()
1085  {
1086      constexpr size_t kcsMaxBufSize = 128;
1087      constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
1088      // Byte 1 - Count (N) Number of devices data is being returned for.
1089      // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
1090      // Byte 3-6 - transfer size (little endian)
1091      // Bytes - 7:(5xN) - Repeat of 2 through 6
1092      constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1093          channelList = {
1094              {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
1095               {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
1096                rmcpPlusMaxBufSize}}};
1097  
1098      return ipmi::responseSuccess(channelListSize, channelList);
1099  }
1100  
1101  ipmi::RspType<uint8_t, uint8_t>
ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)1102      ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
1103  {
1104      // Byte 1 - Current execution context
1105      //          0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
1106      // Byte 2 - Partition pointer
1107      //          0x01 - primary, 0x02 - secondary
1108      uint8_t partitionPtr = getActiveBootImage(ctx);
1109  
1110      return ipmi::responseSuccess(
1111          static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
1112  }
1113  /** @brief Get Firmware Update Random Number
1114   *
1115   *  This function generate the random number used for
1116   *  setting the firmware update mode as authentication key.
1117   *
1118   * @param[in] ctx - context of current session
1119   *  @returns IPMI completion code along with
1120   *   - random number
1121   **/
1122  ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr & ctx)1123      ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx)
1124  {
1125      phosphor::logging::log<phosphor::logging::level::INFO>(
1126          "Generate FW update random number");
1127      bool isIPMBChannel = false;
1128  
1129      if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1130      {
1131          return ipmi::responseUnspecifiedError();
1132      }
1133      if (isIPMBChannel)
1134      {
1135          phosphor::logging::log<phosphor::logging::level::INFO>(
1136              "Channel not supported. Failed to fetch FW update random number");
1137          return ipmi::responseCommandNotAvailable();
1138      }
1139      std::random_device rd;
1140      std::default_random_engine gen(rd());
1141      std::uniform_int_distribution<> dist{0, 255};
1142  
1143      fwRandomNumGenTs = std::chrono::steady_clock::now();
1144  
1145      for (size_t i = 0; i < fwRandomNumLength; i++)
1146      {
1147          fwRandomNum[i] = dist(gen);
1148      }
1149  
1150      return ipmi::responseSuccess(fwRandomNum);
1151  }
1152  
1153  /** @brief Set Firmware Update Mode
1154   *
1155   *  This function sets BMC into firmware update mode
1156   *  after validating Random number obtained from the Get
1157   *  Firmware Update Random Number command
1158   *
1159   * @param[in] ctx - context of current session
1160   * @parameter randNum - Random number(token)
1161   * @returns IPMI completion code
1162   **/
1163  ipmi::RspType<>
ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr & ctx,std::array<uint8_t,fwRandomNumLength> & randNum)1164      ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx,
1165                                std::array<uint8_t, fwRandomNumLength>& randNum)
1166  {
1167      phosphor::logging::log<phosphor::logging::level::INFO>(
1168          "Start FW update mode");
1169  
1170      bool isIPMBChannel = false;
1171  
1172      if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1173      {
1174          return ipmi::responseUnspecifiedError();
1175      }
1176      if (isIPMBChannel)
1177      {
1178          phosphor::logging::log<phosphor::logging::level::INFO>(
1179              "Channel not supported. Failed to set FW update mode");
1180          return ipmi::responseCommandNotAvailable();
1181      }
1182      /* Firmware Update Random number is valid for 30 seconds only */
1183      auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1184      if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1185              .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1186                             fwRandomNumExpirySeconds)
1187                             .count())
1188      {
1189          phosphor::logging::log<phosphor::logging::level::INFO>(
1190              "Firmware update random number expired.");
1191          return ipmi::responseInvalidFieldRequest();
1192      }
1193  
1194      /* Validate random number */
1195      for (size_t i = 0; i < fwRandomNumLength; i++)
1196      {
1197          if (fwRandomNum[i] != randNum[i])
1198          {
1199              phosphor::logging::log<phosphor::logging::level::INFO>(
1200                  "Invalid random number specified.");
1201              return ipmi::responseInvalidFieldRequest();
1202          }
1203      }
1204  
1205      try
1206      {
1207          if (getFirmwareUpdateMode())
1208          {
1209              phosphor::logging::log<phosphor::logging::level::INFO>(
1210                  "Already firmware update is in progress.");
1211              return ipmi::responseBusy();
1212          }
1213      }
1214      catch (const std::exception& e)
1215      {
1216          return ipmi::responseUnspecifiedError();
1217      }
1218  
1219      // FIXME? c++ doesn't off an option for exclusive file creation
1220      FILE* fp = fopen(firmwareBufferFile, "wx");
1221      if (!fp)
1222      {
1223          phosphor::logging::log<phosphor::logging::level::INFO>(
1224              "Unable to open file.");
1225          return ipmi::responseUnspecifiedError();
1226      }
1227      fclose(fp);
1228  
1229      try
1230      {
1231          setFirmwareUpdateMode(true);
1232      }
1233      catch (const std::exception& e)
1234      {
1235          unlink(firmwareBufferFile);
1236          return ipmi::responseUnspecifiedError();
1237      }
1238  
1239      return ipmi::responseSuccess();
1240  }
1241  
1242  /** @brief implements exit firmware update mode command
1243   *  @param None
1244   *
1245   *  @returns IPMI completion code
1246   */
ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr & ctx)1247  ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx)
1248  {
1249      phosphor::logging::log<phosphor::logging::level::INFO>(
1250          "Exit FW update mode");
1251      bool isIPMBChannel = false;
1252  
1253      if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1254      {
1255          return ipmi::responseUnspecifiedError();
1256      }
1257      if (isIPMBChannel)
1258      {
1259          phosphor::logging::log<phosphor::logging::level::INFO>(
1260              "Command not supported. Failed to exit firmware update mode");
1261          return ipmi::responseCommandNotAvailable();
1262      }
1263  
1264      switch (fwUpdateStatus.getState())
1265      {
1266          case FwUpdateStatusCache::fwStateInit:
1267          case FwUpdateStatusCache::fwStateIdle:
1268              return ipmi::responseInvalidFieldRequest();
1269              break;
1270          case FwUpdateStatusCache::fwStateDownload:
1271          case FwUpdateStatusCache::fwStateVerify:
1272              break;
1273          case FwUpdateStatusCache::fwStateProgram:
1274              break;
1275          case FwUpdateStatusCache::fwStateUpdateSuccess:
1276          case FwUpdateStatusCache::fwStateError:
1277              break;
1278          case FwUpdateStatusCache::fwStateAcCycleRequired:
1279              return ipmi::responseInvalidFieldRequest();
1280              break;
1281      }
1282      fwUpdateStatus.firmwareUpdateAbortState();
1283  
1284      try
1285      {
1286          setFirmwareUpdateMode(false);
1287      }
1288      catch (const std::exception& e)
1289      {
1290          return ipmi::responseUnspecifiedError();
1291      }
1292  
1293      return ipmi::responseSuccess();
1294  }
1295  
1296  /** @brief implements Get/Set Firmware Update Control
1297   *  @param[in] ctx - context of current session
1298   *  @parameter
1299   *   - Byte 1: Control Byte
1300   *   - Byte 2: Firmware filename length (Optional)
1301   *   - Byte 3:N: Firmware filename data (Optional)
1302   *  @returns IPMI completion code plus response data
1303   *   - Byte 2: Current control status
1304   **/
ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr & ctx,const uint8_t controlReq,const std::optional<std::string> & fileName)1305  ipmi::RspType<bool, bool, bool, bool, uint4_t> ipmiGetSetFirmwareUpdateControl(
1306      const ipmi::Context::ptr& ctx, const uint8_t controlReq,
1307      const std::optional<std::string>& fileName)
1308  {
1309      bool isIPMBChannel = false;
1310  
1311      if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1312      {
1313          return ipmi::responseUnspecifiedError();
1314      }
1315      if (isIPMBChannel)
1316      {
1317          phosphor::logging::log<phosphor::logging::level::INFO>(
1318              "Channel not supported. Failed to get or set FW update control");
1319          return ipmi::responseCommandNotAvailable();
1320      }
1321  
1322      static std::string fwXferUriPath;
1323      static bool imageTransferStarted = false;
1324      static bool imageTransferCompleted = false;
1325      static bool imageTransferAborted = false;
1326  
1327      if ((controlReq !=
1328           static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
1329          (fileName))
1330      {
1331          phosphor::logging::log<phosphor::logging::level::ERR>(
1332              "Invalid request field (Filename).");
1333          return ipmi::responseInvalidFieldRequest();
1334      }
1335  
1336      static bool usbAttached = getUsbStatus();
1337  
1338      switch (static_cast<FwUpdateCtrlReq>(controlReq))
1339      {
1340          case FwUpdateCtrlReq::getCurrentControlStatus:
1341              phosphor::logging::log<phosphor::logging::level::INFO>(
1342                  "ipmiGetSetFirmwareUpdateControl: Get status");
1343              break;
1344          case FwUpdateCtrlReq::imageTransferStart:
1345          {
1346              phosphor::logging::log<phosphor::logging::level::INFO>(
1347                  "ipmiGetSetFirmwareUpdateControl: Set transfer start");
1348              imageTransferStarted = true;
1349              // reset buffer to empty (truncate file)
1350              std::ofstream out(firmwareBufferFile,
1351                                std::ofstream::binary | std::ofstream::trunc);
1352              fwXferUriPath = std::string("file://") + firmwareBufferFile;
1353              if (xferHashCheck)
1354              {
1355                  if (!xferHashCheck->clear())
1356                  {
1357                      phosphor::logging::log<phosphor::logging::level::ERR>(
1358                          "clear() for xferHashCheck failed");
1359                      return ipmi::responseUnspecifiedError();
1360                  }
1361              }
1362              // Setting state to download
1363              fwUpdateStatus.setState(
1364                  static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
1365  #ifdef INTEL_PFR_ENABLED
1366              imgLength = 0;
1367              imgType = 0;
1368              block0Mapped = false;
1369  #endif
1370          }
1371          break;
1372          case FwUpdateCtrlReq::imageTransferComplete:
1373          {
1374              if (!imageTransferStarted)
1375              {
1376                  phosphor::logging::log<phosphor::logging::level::ERR>(
1377                      "transferFirmwareUpdate not started.");
1378                  return ipmi::responseNotSupportedInPresentState();
1379              }
1380              phosphor::logging::log<phosphor::logging::level::INFO>(
1381                  "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
1382              if (usbAttached)
1383              {
1384                  phosphor::logging::log<phosphor::logging::level::ERR>(
1385                      "USB should be detached to perform this operation.");
1386                  return ipmi::responseNotSupportedInPresentState();
1387              }
1388              // finish transfer based on URI
1389              if (!transferFirmwareFromUri(fwXferUriPath))
1390              {
1391                  phosphor::logging::log<phosphor::logging::level::ERR>(
1392                      "transferFirmwareFromUri failed.");
1393                  return ipmi::responseUnspecifiedError();
1394              }
1395              // transfer complete
1396              if (xferHashCheck)
1397              {
1398                  if (TransferHashCheck::HashCheck::sha2Success !=
1399                      xferHashCheck->verify())
1400                  {
1401                      phosphor::logging::log<phosphor::logging::level::ERR>(
1402                          "xferHashCheck failed.");
1403                      return ipmi::responseUnspecifiedError();
1404                  }
1405              }
1406              // Set state to verify and start the update
1407              fwUpdateStatus.setState(
1408                  static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
1409              // start the request
1410              if (!startFirmwareUpdate(firmwareBufferFile))
1411              {
1412                  phosphor::logging::log<phosphor::logging::level::ERR>(
1413                      "startFirmwareUpdate failed.");
1414                  return ipmi::responseUnspecifiedError();
1415              }
1416              imageTransferCompleted = true;
1417          }
1418          break;
1419          case FwUpdateCtrlReq::imageTransferAbort:
1420              phosphor::logging::log<phosphor::logging::level::INFO>(
1421                  "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
1422              if (usbAttached)
1423              {
1424                  if (detachUsbDevice())
1425                  {
1426                      phosphor::logging::log<phosphor::logging::level::ERR>(
1427                          "Detach USB device failed.");
1428                      return ipmi::responseUsbAttachOrDetachFailed();
1429                  }
1430                  usbAttached = false;
1431              }
1432              // During abort request reset the state to Init by cleaning update
1433              // file.
1434              fwUpdateStatus.firmwareUpdateAbortState();
1435              imageTransferAborted = true;
1436              break;
1437          case FwUpdateCtrlReq::setFirmwareFilename:
1438              phosphor::logging::log<phosphor::logging::level::INFO>(
1439                  "ipmiGetSetFirmwareUpdateControl: Set filename.");
1440              if (!fileName || ((*fileName).length() == 0))
1441              {
1442                  phosphor::logging::log<phosphor::logging::level::ERR>(
1443                      "Invalid Filename specified.");
1444                  return ipmi::responseInvalidFieldRequest();
1445              }
1446  
1447              fwXferUriPath = *fileName;
1448              break;
1449          case FwUpdateCtrlReq::attachUsbDevice:
1450              phosphor::logging::log<phosphor::logging::level::INFO>(
1451                  "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
1452              if (usbAttached)
1453              {
1454                  phosphor::logging::log<phosphor::logging::level::ERR>(
1455                      "USB device is already attached.");
1456                  return ipmi::responseInvalidFieldRequest();
1457              }
1458              if (attachUsbDevice())
1459              {
1460                  phosphor::logging::log<phosphor::logging::level::ERR>(
1461                      "Attach USB device failed.");
1462                  return ipmi::responseUsbAttachOrDetachFailed();
1463              }
1464              usbAttached = true;
1465              break;
1466          case FwUpdateCtrlReq::detachUsbDevice:
1467              phosphor::logging::log<phosphor::logging::level::INFO>(
1468                  "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
1469              if (!usbAttached)
1470              {
1471                  phosphor::logging::log<phosphor::logging::level::ERR>(
1472                      "USB device is not attached.");
1473                  return ipmi::responseInvalidFieldRequest();
1474              }
1475              if (detachUsbDevice())
1476              {
1477                  phosphor::logging::log<phosphor::logging::level::ERR>(
1478                      "Detach USB device failed.");
1479                  return ipmi::responseUsbAttachOrDetachFailed();
1480              }
1481              usbAttached = false;
1482              break;
1483          default:
1484              phosphor::logging::log<phosphor::logging::level::ERR>(
1485                  "Invalid control option specified.");
1486              return ipmi::responseInvalidFieldRequest();
1487      }
1488  
1489      return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
1490                                   imageTransferAborted, usbAttached, uint4_t(0));
1491  }
1492  
1493  /** @brief implements firmware get status command
1494   *  @parameter
1495   *   -  none
1496   *  @returns IPMI completion code plus response data
1497   *   - status     -  processing status
1498   *   - percentage -  percentage completion
1499   *   - check      -  channel integrity check status
1500   **/
1501  ipmi::RspType<uint8_t, // status
1502                uint8_t, // percentage
1503                uint8_t  // check
1504                >
ipmiGetFirmwareUpdateStatus()1505      ipmiGetFirmwareUpdateStatus()
1506  
1507  {
1508      // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1509      //                  5=ready, f=error, 83=ac cycle required)
1510      // Byte 2 - percent
1511      // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1512      uint8_t status = fwUpdateStatus.getState();
1513      uint8_t percent = fwUpdateStatus.percent();
1514      uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
1515  
1516      // Status code.
1517      return ipmi::responseSuccess(status, percent, check);
1518  }
1519  
ipmiSetFirmwareUpdateOptions(const ipmi::Context::ptr & ctx,bool noDowngradeMask,bool deferRestartMask,bool sha2CheckMask,uint5_t reserved1,bool noDowngrade,bool deferRestart,bool sha2Check,uint5_t reserved2,std::optional<std::vector<uint8_t>> integrityCheckVal)1520  ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
1521      const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask,
1522      bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart,
1523      bool sha2Check, uint5_t reserved2,
1524      std::optional<std::vector<uint8_t>> integrityCheckVal)
1525  {
1526      phosphor::logging::log<phosphor::logging::level::INFO>(
1527          "Set firmware update options.");
1528      bool isIPMBChannel = false;
1529  
1530      if (reserved1 || reserved2)
1531      {
1532          return ipmi::responseInvalidFieldRequest();
1533      }
1534      if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1535      {
1536          return ipmi::responseUnspecifiedError();
1537      }
1538      if (isIPMBChannel)
1539      {
1540          phosphor::logging::log<phosphor::logging::level::INFO>(
1541              "Channel not supported. Failed to set firmware update options");
1542          return ipmi::responseCommandNotAvailable();
1543      }
1544      bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
1545      bool deferRestartState = fwUpdateStatus.getDeferRestart();
1546      bool sha2CheckState = xferHashCheck ? true : false;
1547  
1548      if (noDowngradeMask && (noDowngradeState != noDowngrade))
1549      {
1550          fwUpdateStatus.setInhibitDowngrade(noDowngrade);
1551          noDowngradeState = noDowngrade;
1552      }
1553      if (deferRestartMask && (deferRestartState != deferRestart))
1554      {
1555          fwUpdateStatus.setDeferRestart(deferRestart);
1556          deferRestartState = deferRestart;
1557      }
1558      if (sha2CheckMask)
1559      {
1560          if (sha2Check)
1561          {
1562              size_t hashSize = EVP_MD_size(EVP_sha256());
1563              if ((*integrityCheckVal).size() != hashSize)
1564              {
1565                  phosphor::logging::log<phosphor::logging::level::DEBUG>(
1566                      "Invalid size of Hash specified.");
1567                  return ipmi::responseInvalidFieldRequest();
1568              }
1569  
1570              try
1571              {
1572                  xferHashCheck =
1573                      std::make_unique<TransferHashCheck>(*integrityCheckVal);
1574              }
1575              catch (const std::exception& ex)
1576              {
1577                  phosphor::logging::log<phosphor::logging::level::ERR>(
1578                      ex.what());
1579                  return ipmi::responseUnspecifiedError();
1580              }
1581          }
1582          else
1583          {
1584              // delete the xferHashCheck object
1585              xferHashCheck.reset();
1586          }
1587          sha2CheckState = sha2CheckMask;
1588      }
1589      return ipmi::responseSuccess(noDowngradeState, deferRestartState,
1590                                   sha2CheckState, uint5_t{});
1591  }
1592  
1593  ipmi::RspType<uint32_t>
ipmiFwImageWriteData(const std::vector<uint8_t> & writeData)1594      ipmiFwImageWriteData(const std::vector<uint8_t>& writeData)
1595  {
1596      const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1597      size_t writeDataLen = writeData.size();
1598  
1599      if (!writeDataLen)
1600      {
1601          return ipmi::responseReqDataLenInvalid();
1602      }
1603  
1604      if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
1605      {
1606          phosphor::logging::log<phosphor::logging::level::ERR>(
1607              "Invalid firmware update state.");
1608          return ipmi::response(ccCmdNotSupportedInPresentState);
1609      }
1610  
1611      std::ofstream out(firmwareBufferFile,
1612                        std::ofstream::binary | std::ofstream::app);
1613      if (!out)
1614      {
1615          phosphor::logging::log<phosphor::logging::level::DEBUG>(
1616              "Error while opening file.");
1617          return ipmi::responseUnspecifiedError();
1618      }
1619  
1620      uint64_t fileDataLen = out.tellp();
1621  
1622      if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
1623      {
1624          phosphor::logging::log<phosphor::logging::level::DEBUG>(
1625              "Firmware image size exceeds the limit");
1626          return ipmi::responseInvalidFieldRequest();
1627      }
1628  
1629      const char* data = reinterpret_cast<const char*>(writeData.data());
1630      out.write(data, writeDataLen);
1631      out.close();
1632  
1633      if (xferHashCheck)
1634      {
1635          if (!xferHashCheck->hash(writeData))
1636          {
1637              phosphor::logging::log<phosphor::logging::level::ERR>(
1638                  "ipmiFwImageWriteData: xferHashCheck->hash failed.");
1639              return ipmi::responseUnspecifiedError();
1640          }
1641      }
1642  
1643  #ifdef INTEL_PFR_ENABLED
1644      /* PFR image block 0 - As defined in HAS */
1645      struct PFRImageBlock0
1646      {
1647          uint32_t tag;
1648          uint32_t pcLength;
1649          uint32_t pcType;
1650          uint32_t reserved1;
1651          uint8_t hash256[32];
1652          uint8_t hash384[48];
1653          uint8_t reserved2[32];
1654      } __attribute__((packed));
1655  
1656      /* Get the PFR block 0 data and read the uploaded image
1657       * information( Image type, length etc) */
1658      if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1659          (!block0Mapped))
1660      {
1661          PFRImageBlock0 block0Data{};
1662  
1663          std::ifstream inFile(firmwareBufferFile,
1664                               std::ios::binary | std::ios::in);
1665          inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data));
1666          inFile.close();
1667  
1668          uint32_t magicNum = block0Data.tag;
1669  
1670          /* Validate the magic number */
1671          if (magicNum != perBlock0MagicNum)
1672          {
1673              phosphor::logging::log<phosphor::logging::level::DEBUG>(
1674                  "PFR image magic number not matched");
1675              return ipmi::responseInvalidFieldRequest();
1676          }
1677          // Note:imgLength, imgType and block0Mapped are in global scope, as
1678          // these are used in cascaded updates.
1679          imgLength = block0Data.pcLength;
1680          imgType = block0Data.pcType;
1681          block0Mapped = true;
1682      }
1683  #endif // end of INTEL_PFR_ENABLED
1684      return ipmi::responseSuccess(writeDataLen);
1685  }
1686  
registerFirmwareFunctions()1687  static void registerFirmwareFunctions()
1688  {
1689      // guarantee that we start with an already timed out timestamp
1690      fwRandomNumGenTs =
1691          std::chrono::steady_clock::now() - fwRandomNumExpirySeconds;
1692      fwUpdateStatus.setState(
1693          static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
1694  
1695      unlink(firmwareBufferFile);
1696  
1697  #ifdef INTEL_PFR_ENABLED
1698      // Following commands are supported only for PFR enabled platforms
1699      // CMD:0x20 - Get Firmware Version Information
1700      // CMD:0x21 - Get Firmware Security Version Information
1701      // CMD:0x25 - Get Root Certificate Data
1702  
1703      // get firmware version information
1704      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1705                            ipmi::firmware::cmdGetFwVersionInfo,
1706                            ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
1707  
1708      // get firmware security version information
1709      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1710                            ipmi::firmware::cmdGetFwSecurityVersionInfo,
1711                            ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
1712  
1713      // get root certificate data
1714      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1715                            ipmi::firmware::cmdFwGetRootCertData,
1716                            ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1717  #endif
1718  
1719      // get firmware update channel information (max transfer sizes)
1720      ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1721                            ipmi::firmware::cmdGetFwUpdateChannelInfo,
1722                            ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
1723  
1724      // get bmc execution context
1725      ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1726                            ipmi::firmware::cmdGetBmcExecutionContext,
1727                            ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
1728  
1729      // Get Firmware Update Random number
1730      ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1731                            ipmi::firmware::cmdGetFwUpdateRandomNumber,
1732                            ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
1733  
1734      // Set Firmware Update Mode
1735      ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1736                            ipmi::firmware::cmdSetFirmwareUpdateMode,
1737                            ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
1738  
1739      // Exit Firmware Update Mode
1740      ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1741                            ipmi::firmware::cmdExitFirmwareUpdateMode,
1742                            ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
1743  
1744      // Get/Set Firmware Update Control
1745      ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1746                            ipmi::firmware::cmdGetSetFwUpdateControl,
1747                            ipmi::Privilege::Admin,
1748                            ipmiGetSetFirmwareUpdateControl);
1749  
1750      // Get Firmware Update Status
1751      ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1752                            ipmi::firmware::cmdGetFirmwareUpdateStatus,
1753                            ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
1754  
1755      // Set Firmware Update Options
1756      ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1757                            ipmi::firmware::cmdSetFirmwareUpdateOptions,
1758                            ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
1759      // write image data
1760      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1761                            ipmi::firmware::cmdFwImageWriteData,
1762                            ipmi::Privilege::Admin, ipmiFwImageWriteData);
1763      return;
1764  }
1765