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 
66 static inline auto responseUsbAttachOrDetachFailed()
67 {
68     return response(ccUsbAttachOrDetachFailed);
69 }
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     (0, 0), (0, 0)};
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 
166 constexpr std::size_t operator""_MB(unsigned long long v)
167 {
168     return 1024u * 1024u * v;
169 }
170 static constexpr size_t maxFirmwareImageSize = 32_MB;
171 
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:
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 
215     ~TransferHashCheck() {}
216 
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 
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 
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 
268     uint8_t status() const
269     {
270         return static_cast<uint8_t>(check);
271     }
272 };
273 
274 class MappedFile
275 {
276   public:
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 
300     ~MappedFile()
301     {
302         if (addr)
303         {
304             munmap(addr, fsize);
305         }
306     }
307     const uint8_t* data() const
308     {
309         return static_cast<const uint8_t*>(addr);
310     }
311     size_t size() const
312     {
313         return fsize;
314     }
315 
316   private:
317     size_t fsize;
318     void* addr;
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     };
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     }
345     void resetStatusCache()
346     {
347         unlink(firmwareBufferFile);
348     }
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     }
369     uint8_t percent()
370     {
371         return progressPercent;
372     }
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     }
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  */
422     void firmwareUpdateAbortState()
423     {
424         unlink(firmwareBufferFile);
425         // changing the state to error
426         fwUpdateState = fwStateError;
427     }
428     void setDeferRestart(bool deferRestart)
429     {
430         deferRestartState = deferRestart;
431     }
432     void setInhibitDowngrade(bool inhibitDowngrade)
433     {
434         inhibitDowngradeState = inhibitDowngrade;
435     }
436     bool getDeferRestart()
437     {
438         return deferRestartState;
439     }
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 
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 
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 
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  **/
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 
561 static void postTransferCompleteHandler(
562     std::unique_ptr<sdbusplus::bus::match_t>& fwUpdateMatchSignal)
563 {
564     // Setup timer for watching signal
565     static phosphor::Timer timer(
566         [&fwUpdateMatchSignal]() { fwUpdateMatchSignal = nullptr; });
567 
568     static phosphor::Timer activationStatusTimer([]() {
569         if (fwUpdateStatus.activationTimerTimeout() > 95)
570         {
571             activationStatusTimer.stop();
572             fwUpdateStatus.setState(
573                 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
574         }
575     });
576 
577     timer.start(std::chrono::microseconds(5000000), false);
578 
579     // callback function for capturing signal
580     auto callback = [&](sdbusplus::message_t& m) {
581         bool flag = false;
582 
583         std::vector<
584             std::pair<std::string,
585                       std::vector<std::pair<std::string, ipmi::DbusVariant>>>>
586             intfPropsPair;
587         sdbusplus::message::object_path objPath;
588 
589         try
590         {
591             m.read(objPath, intfPropsPair); // Read in the object path
592                                             // that was just created
593         }
594         catch (const std::exception& e)
595         {
596             phosphor::logging::log<phosphor::logging::level::ERR>(
597                 "Exception caught in reading created object path.");
598             return;
599         }
600         // constructing response message
601         phosphor::logging::log<phosphor::logging::level::INFO>(
602             "New Interface Added.",
603             phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
604         for (auto& interface : intfPropsPair)
605         {
606             if (interface.first == "xyz.openbmc_project.Software.Activation")
607             {
608                 // There are chances of getting two signals for
609                 // InterfacesAdded. So cross check and discrad second instance.
610                 if (fwUpdateMatchSignal == nullptr)
611                 {
612                     return;
613                 }
614                 // Found our interface, disable callbacks
615                 fwUpdateMatchSignal = nullptr;
616 
617                 phosphor::logging::log<phosphor::logging::level::INFO>(
618                     "Start activationStatusTimer for status.");
619                 try
620                 {
621                     timer.stop();
622                     activationStatusTimer.start(
623                         std::chrono::microseconds(3000000), true);
624                 }
625                 catch (const std::exception& e)
626                 {
627                     phosphor::logging::log<phosphor::logging::level::ERR>(
628                         "Exception caught in start activationStatusTimer.",
629                         phosphor::logging::entry("ERROR=%s", e.what()));
630                 }
631 
632                 fwUpdateStatus.updateActivationPercent(objPath.str);
633                 activateImage(objPath.str);
634             }
635         }
636     };
637 
638     // Adding matcher
639     fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match_t>(
640         *getSdBus(),
641         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
642         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
643         callback);
644 }
645 static bool startFirmwareUpdate(const std::string& uri)
646 {
647     // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
648     // the code gets to this point, the file should be transferred start the
649     // request (creating a new file in /tmp/images causes the update manager to
650     // check if it is ready for activation)
651     static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatchSignal;
652     postTransferCompleteHandler(fwUpdateMatchSignal);
653     std::filesystem::rename(
654         uri, "/tmp/images/" +
655                  boost::uuids::to_string(boost::uuids::random_generator()()));
656     return true;
657 }
658 
659 static bool transferImageFromFile(const std::string& uri, bool move = true)
660 {
661     std::error_code ec;
662     phosphor::logging::log<phosphor::logging::level::INFO>(
663         "Transfer Image From File.",
664         phosphor::logging::entry("URI=%s", uri.c_str()));
665     if (move)
666     {
667         std::filesystem::rename(uri, firmwareBufferFile, ec);
668     }
669     else
670     {
671         std::filesystem::copy(uri, firmwareBufferFile,
672                               std::filesystem::copy_options::overwrite_existing,
673                               ec);
674     }
675     if (xferHashCheck)
676     {
677         MappedFile mappedfw(uri);
678         if (!xferHashCheck->hash(
679                 {mappedfw.data(), mappedfw.data() + mappedfw.size()}))
680         {
681             phosphor::logging::log<phosphor::logging::level::ERR>(
682                 "transferImageFromFile: xferHashCheck->hash failed.");
683             return false;
684         }
685     }
686     if (ec.value())
687     {
688         phosphor::logging::log<phosphor::logging::level::ERR>(
689             "Image copy failed.");
690         return false;
691     }
692 
693     return true;
694 }
695 
696 template <typename... ArgTypes>
697 static int executeCmd(const char* path, ArgTypes&&... tArgs)
698 {
699     boost::process::child execProg(path, const_cast<char*>(tArgs)...);
700     execProg.wait();
701     return execProg.exit_code();
702 }
703 
704 static bool transferImageFromUsb(const std::string& uri)
705 {
706     bool ret = false;
707     char fwpath[fwPathMaxLength];
708 
709     phosphor::logging::log<phosphor::logging::level::INFO>(
710         "Transfer Image From USB.",
711         phosphor::logging::entry("URI=%s", uri.c_str()));
712 
713     if (executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
714                    fwUpdateMountPoint) == 0)
715     {
716         std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
717         ret = transferImageFromFile(usb_path, false);
718 
719         executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage,
720                    fwUpdateMountPoint);
721     }
722 
723     return ret;
724 }
725 
726 static bool transferFirmwareFromUri(const std::string& uri)
727 {
728     static constexpr char fwUriFile[] = "file://";
729     static constexpr char fwUriUsb[] = "usb://";
730     phosphor::logging::log<phosphor::logging::level::INFO>(
731         "Transfer Image From URI.",
732         phosphor::logging::entry("URI=%s", uri.c_str()));
733     if (boost::algorithm::starts_with(uri, fwUriFile))
734     {
735         std::string fname = uri.substr(sizeof(fwUriFile) - 1);
736         if (fname != firmwareBufferFile)
737         {
738             return transferImageFromFile(fname);
739         }
740         return true;
741     }
742     if (boost::algorithm::starts_with(uri, fwUriUsb))
743     {
744         std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
745         return transferImageFromUsb(fname);
746     }
747     return false;
748 }
749 
750 /* Get USB-mass-storage device status: inserted => true, ejected => false */
751 static bool getUsbStatus()
752 {
753     std::filesystem::path usbDevPath =
754         std::filesystem::path("/sys/kernel/config/usb_gadget") /
755         fwUpdateUSBDevName;
756     return (std::filesystem::exists(usbDevPath) ? true : false);
757 }
758 
759 /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
760 static int attachUsbDevice()
761 {
762     if (getUsbStatus())
763     {
764         return 1;
765     }
766     int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
767                          std::to_string(maxFirmwareImageSize / 1_MB).c_str());
768     if (!ret)
769     {
770         ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
771                          fwUpdateUsbVolImage);
772     }
773     return ret;
774 }
775 
776 /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
777 static int detachUsbDevice()
778 {
779     if (!getUsbStatus())
780     {
781         return 1;
782     }
783     return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
784 }
785 static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
786 {
787     constexpr uint8_t undefinedImage = 0x00;
788     constexpr uint8_t primaryImage = 0x01;
789     constexpr uint8_t secondaryImage = 0x02;
790     constexpr const char* secondaryFitImageStartAddr = "22480000";
791 
792     uint8_t bootImage = primaryImage;
793     boost::system::error_code ec;
794     std::string value = ctx->bus->yield_method_call<std::string>(
795         ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
796         "/xyz/openbmc_project/u_boot/environment/mgr",
797         "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
798     if (ec)
799     {
800         phosphor::logging::log<phosphor::logging::level::ERR>(
801             "Failed to read the bootcmd value");
802         /* don't fail, just give back undefined until it is ready */
803         bootImage = undefinedImage;
804     }
805 
806     /* cheking for secondary FitImage Address 22480000  */
807     else if (value.find(secondaryFitImageStartAddr) != std::string::npos)
808     {
809         bootImage = secondaryImage;
810     }
811     else
812     {
813         bootImage = primaryImage;
814     }
815 
816     return bootImage;
817 }
818 
819 #ifdef INTEL_PFR_ENABLED
820 using fwVersionInfoType = std::tuple<uint8_t,   // ID Tag
821                                      uint8_t,   // Major Version Number
822                                      uint8_t,   // Minor Version Number
823                                      uint32_t,  // Build Number
824                                      uint32_t,  // Build Timestamp
825                                      uint32_t>; // Update Timestamp
826 ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
827 {
828     // Byte 1 - Count (N) Number of devices data is being returned for.
829     // Bytes  2:16 - Device firmare information(fwVersionInfoType)
830     // Bytes - 17:(15xN) - Repeat of 2 through 16
831 
832     std::vector<fwVersionInfoType> fwVerInfoList;
833     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
834     for (const auto& fwDev : fwVersionIdMap)
835     {
836         std::string verStr;
837         try
838         {
839             auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
840 
841             ipmi::Value result = ipmi::getDbusProperty(
842                 *busp, service, fwDev.second, versionIntf, "Version");
843             verStr = std::get<std::string>(result);
844         }
845         catch (const std::exception& e)
846         {
847             phosphor::logging::log<phosphor::logging::level::INFO>(
848                 "Failed to fetch Version property",
849                 phosphor::logging::entry("ERROR=%s", e.what()),
850                 phosphor::logging::entry("PATH=%s", fwDev.second),
851                 phosphor::logging::entry("INTERFACE=%s", versionIntf));
852             continue;
853         }
854 
855         if (verStr.empty())
856         {
857             phosphor::logging::log<phosphor::logging::level::INFO>(
858                 "Version is empty.",
859                 phosphor::logging::entry("PATH=%s", fwDev.second),
860                 phosphor::logging::entry("INTERFACE=%s", versionIntf));
861             continue;
862         }
863 
864         uint8_t majorNum = 0;
865         uint8_t minorNum = 0;
866         uint32_t buildNum = 0;
867         try
868         {
869             std::optional<ipmi::MetaRevision> rev =
870                 ipmi::convertIntelVersion(verStr);
871             if (rev.has_value())
872             {
873                 ipmi::MetaRevision revision = rev.value();
874                 majorNum = revision.major % 10 + (revision.major / 10) * 16;
875                 minorNum = (revision.minor > 99 ? 99 : revision.minor);
876                 minorNum = minorNum % 10 + (minorNum / 10) * 16;
877                 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
878                 hash = bswap_32(hash);
879                 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
880             }
881             else
882             {
883                 std::vector<std::string> splitVer;
884                 boost::split(splitVer, verStr, boost::is_any_of(".-"));
885                 if (splitVer.size() < 3)
886                 {
887                     phosphor::logging::log<phosphor::logging::level::INFO>(
888                         "Invalid Version format.",
889                         phosphor::logging::entry("Version=%s", verStr.c_str()),
890                         phosphor::logging::entry("PATH=%s", fwDev.second));
891                     continue;
892                 }
893                 majorNum = std::stoul(splitVer[0], nullptr, 16);
894                 minorNum = std::stoul(splitVer[1], nullptr, 16);
895                 buildNum = std::stoul(splitVer[2], nullptr, 16);
896             }
897             // Build Timestamp - Not supported.
898             // Update Timestamp - TODO: Need to check with CPLD team.
899             fwVerInfoList.emplace_back(
900                 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
901                                   minorNum, buildNum, 0, 0));
902         }
903         catch (const std::exception& e)
904         {
905             phosphor::logging::log<phosphor::logging::level::INFO>(
906                 "Failed to convert stoul.",
907                 phosphor::logging::entry("ERROR=%s", e.what()));
908             continue;
909         }
910     }
911 
912     return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
913 }
914 
915 std::array<uint8_t, imageCount> getSecurityVersionInfo(const char* mtdDevBuf,
916                                                        size_t svnVerOffsetInPfm,
917                                                        size_t bkcVerOffsetInPfm)
918 {
919     constexpr size_t bufLength = 1;
920     std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp;
921     constexpr uint8_t svnIndexValue = 0x00;
922     constexpr uint8_t bkcIndexValue = 0x01;
923     constexpr uint8_t tempIndexValue = 0x00;
924     try
925     {
926         SPIDev spiDev(mtdDevBuf);
927         spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data());
928         fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue);
929         spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data());
930         fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue);
931     }
932     catch (const std::exception& e)
933     {
934         phosphor::logging::log<phosphor::logging::level::ERR>(
935             "Exception caught in getSecurityVersionInfo",
936             phosphor::logging::entry("MSG=%s", e.what()));
937         fwSecurityVersionBuf = {0, 0};
938     }
939 
940     return fwSecurityVersionBuf;
941 }
942 
943 ipmi::RspType<
944     uint8_t,                         // device ID
945     uint8_t,                         // Active Image Value
946     std::array<uint8_t, imageCount>, // Security version for Active Image
947     uint8_t,                         // recovery Image Value
948     std::array<uint8_t, imageCount>> // Security version for Recovery Image
949     ipmiGetFwSecurityVersionInfo()
950 {
951     static bool cacheFlag = false;
952     constexpr std::array<const char*, imageCount> mtdDevBuf = {
953         bmcActivePfmMTDDev, bmcRecoveryImgMTDDev};
954 
955     // To avoid multiple reading from SPI device
956     if (!cacheFlag)
957     {
958         imgFwSecurityVersion[0] = getSecurityVersionInfo(
959             mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm);
960         imgFwSecurityVersion[1] = getSecurityVersionInfo(
961             mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm);
962         cacheFlag = true;
963     }
964 
965     constexpr uint8_t ActivePfmMTDDev = 0x00;
966     constexpr uint8_t RecoveryImgMTDDev = 0x01;
967 
968     return ipmi::responseSuccess(
969         imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage),
970         imgFwSecurityVersion[ActivePfmMTDDev],
971         static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage),
972         imgFwSecurityVersion[RecoveryImgMTDDev]);
973 }
974 
975 ipmi::RspType<std::array<uint8_t, certKeyLen>,
976               std::optional<std::array<uint8_t, cskSignatureLen>>>
977     ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId)
978 {
979     bool isIPMBChannel = false;
980 
981     if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
982     {
983         return ipmi::responseUnspecifiedError();
984     }
985     if (isIPMBChannel)
986     {
987         phosphor::logging::log<phosphor::logging::level::INFO>(
988             "Command not supported. Failed to get root certificate data.");
989         return ipmi::responseCommandNotAvailable();
990     }
991 
992     size_t certKeyOffset = 0;
993     size_t cskSigOffset = 0;
994     std::string mtdDev;
995 
996     switch (static_cast<FwGetRootCertDataTag>(certId))
997     {
998         case FwGetRootCertDataTag::activeRootKey:
999         {
1000             mtdDev = bmcActivePfmMTDDev;
1001             certKeyOffset = rootkeyOffsetInPfm;
1002             break;
1003         }
1004         case FwGetRootCertDataTag::recoveryRootKey:
1005         {
1006             mtdDev = bmcRecoveryImgMTDDev;
1007             certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1008             break;
1009         }
1010         case FwGetRootCertDataTag::activeCSK:
1011         {
1012             mtdDev = bmcActivePfmMTDDev;
1013             certKeyOffset = cskKeyOffsetInPfm;
1014             cskSigOffset = cskSignatureOffsetInPfm;
1015             break;
1016         }
1017         case FwGetRootCertDataTag::recoveryCSK:
1018         {
1019             mtdDev = bmcRecoveryImgMTDDev;
1020             certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1021             cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1022             break;
1023         }
1024         default:
1025         {
1026             return ipmi::responseInvalidFieldRequest();
1027         }
1028     }
1029 
1030     std::array<uint8_t, certKeyLen> certKey = {0};
1031 
1032     try
1033     {
1034         SPIDev spiDev(mtdDev);
1035         spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1036 
1037         if (cskSigOffset)
1038         {
1039             std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1040             spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1041                                cskSignature.data());
1042             return ipmi::responseSuccess(certKey, cskSignature);
1043         }
1044     }
1045     catch (const std::exception& e)
1046     {
1047         phosphor::logging::log<phosphor::logging::level::ERR>(
1048             "Exception caught in ipmiGetFwRootCertData",
1049             phosphor::logging::entry("MSG=%s", e.what()));
1050         return ipmi::responseUnspecifiedError();
1051     }
1052 
1053     return ipmi::responseSuccess(certKey, std::nullopt);
1054 }
1055 #endif // INTEL_PFR_ENABLED
1056 
1057 static constexpr uint8_t channelListSize = 3;
1058 /** @brief implements Maximum Firmware Transfer size command
1059  *  @parameter
1060  *   -  none
1061  *  @returns IPMI completion code plus response data
1062  *   - count - channel count
1063  *   - channelList - channel list information
1064  */
1065 ipmi::RspType<uint8_t,                    // channel count
1066               std::array<std::tuple<uint8_t, uint32_t>,
1067                          channelListSize> // Channel List
1068               >
1069     ipmiFirmwareMaxTransferSize()
1070 {
1071     constexpr size_t kcsMaxBufSize = 128;
1072     constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
1073     // Byte 1 - Count (N) Number of devices data is being returned for.
1074     // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
1075     // Byte 3-6 - transfer size (little endian)
1076     // Bytes - 7:(5xN) - Repeat of 2 through 6
1077     constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1078         channelList = {
1079             {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
1080              {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
1081               rmcpPlusMaxBufSize}}};
1082 
1083     return ipmi::responseSuccess(channelListSize, channelList);
1084 }
1085 
1086 ipmi::RspType<uint8_t, uint8_t>
1087     ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
1088 {
1089     // Byte 1 - Current execution context
1090     //          0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
1091     // Byte 2 - Partition pointer
1092     //          0x01 - primary, 0x02 - secondary
1093     uint8_t partitionPtr = getActiveBootImage(ctx);
1094 
1095     return ipmi::responseSuccess(
1096         static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
1097 }
1098 /** @brief Get Firmware Update Random Number
1099  *
1100  *  This function generate the random number used for
1101  *  setting the firmware update mode as authentication key.
1102  *
1103  * @param[in] ctx - context of current session
1104  *  @returns IPMI completion code along with
1105  *   - random number
1106  **/
1107 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
1108     ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx)
1109 {
1110     phosphor::logging::log<phosphor::logging::level::INFO>(
1111         "Generate FW update random number");
1112     bool isIPMBChannel = false;
1113 
1114     if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1115     {
1116         return ipmi::responseUnspecifiedError();
1117     }
1118     if (isIPMBChannel)
1119     {
1120         phosphor::logging::log<phosphor::logging::level::INFO>(
1121             "Channel not supported. Failed to fetch FW update random number");
1122         return ipmi::responseCommandNotAvailable();
1123     }
1124     std::random_device rd;
1125     std::default_random_engine gen(rd());
1126     std::uniform_int_distribution<> dist{0, 255};
1127 
1128     fwRandomNumGenTs = std::chrono::steady_clock::now();
1129 
1130     for (int i = 0; i < fwRandomNumLength; i++)
1131     {
1132         fwRandomNum[i] = dist(gen);
1133     }
1134 
1135     return ipmi::responseSuccess(fwRandomNum);
1136 }
1137 
1138 /** @brief Set Firmware Update Mode
1139  *
1140  *  This function sets BMC into firmware update mode
1141  *  after validating Random number obtained from the Get
1142  *  Firmware Update Random Number command
1143  *
1144  * @param[in] ctx - context of current session
1145  * @parameter randNum - Random number(token)
1146  * @returns IPMI completion code
1147  **/
1148 ipmi::RspType<>
1149     ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx,
1150                               std::array<uint8_t, fwRandomNumLength>& randNum)
1151 {
1152     phosphor::logging::log<phosphor::logging::level::INFO>(
1153         "Start FW update mode");
1154 
1155     bool isIPMBChannel = false;
1156 
1157     if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1158     {
1159         return ipmi::responseUnspecifiedError();
1160     }
1161     if (isIPMBChannel)
1162     {
1163         phosphor::logging::log<phosphor::logging::level::INFO>(
1164             "Channel not supported. Failed to set FW update mode");
1165         return ipmi::responseCommandNotAvailable();
1166     }
1167     /* Firmware Update Random number is valid for 30 seconds only */
1168     auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1169     if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1170             .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1171                            fwRandomNumExpirySeconds)
1172                            .count())
1173     {
1174         phosphor::logging::log<phosphor::logging::level::INFO>(
1175             "Firmware update random number expired.");
1176         return ipmi::responseInvalidFieldRequest();
1177     }
1178 
1179     /* Validate random number */
1180     for (int i = 0; i < fwRandomNumLength; i++)
1181     {
1182         if (fwRandomNum[i] != randNum[i])
1183         {
1184             phosphor::logging::log<phosphor::logging::level::INFO>(
1185                 "Invalid random number specified.");
1186             return ipmi::responseInvalidFieldRequest();
1187         }
1188     }
1189 
1190     try
1191     {
1192         if (getFirmwareUpdateMode())
1193         {
1194             phosphor::logging::log<phosphor::logging::level::INFO>(
1195                 "Already firmware update is in progress.");
1196             return ipmi::responseBusy();
1197         }
1198     }
1199     catch (const std::exception& e)
1200     {
1201         return ipmi::responseUnspecifiedError();
1202     }
1203 
1204     // FIXME? c++ doesn't off an option for exclusive file creation
1205     FILE* fp = fopen(firmwareBufferFile, "wx");
1206     if (!fp)
1207     {
1208         phosphor::logging::log<phosphor::logging::level::INFO>(
1209             "Unable to open file.");
1210         return ipmi::responseUnspecifiedError();
1211     }
1212     fclose(fp);
1213 
1214     try
1215     {
1216         setFirmwareUpdateMode(true);
1217     }
1218     catch (const std::exception& e)
1219     {
1220         unlink(firmwareBufferFile);
1221         return ipmi::responseUnspecifiedError();
1222     }
1223 
1224     return ipmi::responseSuccess();
1225 }
1226 
1227 /** @brief implements exit firmware update mode command
1228  *  @param None
1229  *
1230  *  @returns IPMI completion code
1231  */
1232 ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx)
1233 {
1234     phosphor::logging::log<phosphor::logging::level::INFO>(
1235         "Exit FW update mode");
1236     bool isIPMBChannel = false;
1237 
1238     if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1239     {
1240         return ipmi::responseUnspecifiedError();
1241     }
1242     if (isIPMBChannel)
1243     {
1244         phosphor::logging::log<phosphor::logging::level::INFO>(
1245             "Command not supported. Failed to exit firmware update mode");
1246         return ipmi::responseCommandNotAvailable();
1247     }
1248 
1249     switch (fwUpdateStatus.getState())
1250     {
1251         case FwUpdateStatusCache::fwStateInit:
1252         case FwUpdateStatusCache::fwStateIdle:
1253             return ipmi::responseInvalidFieldRequest();
1254             break;
1255         case FwUpdateStatusCache::fwStateDownload:
1256         case FwUpdateStatusCache::fwStateVerify:
1257             break;
1258         case FwUpdateStatusCache::fwStateProgram:
1259             break;
1260         case FwUpdateStatusCache::fwStateUpdateSuccess:
1261         case FwUpdateStatusCache::fwStateError:
1262             break;
1263         case FwUpdateStatusCache::fwStateAcCycleRequired:
1264             return ipmi::responseInvalidFieldRequest();
1265             break;
1266     }
1267     fwUpdateStatus.firmwareUpdateAbortState();
1268 
1269     try
1270     {
1271         setFirmwareUpdateMode(false);
1272     }
1273     catch (const std::exception& e)
1274     {
1275         return ipmi::responseUnspecifiedError();
1276     }
1277 
1278     return ipmi::responseSuccess();
1279 }
1280 
1281 /** @brief implements Get/Set Firmware Update Control
1282  *  @param[in] ctx - context of current session
1283  *  @parameter
1284  *   - Byte 1: Control Byte
1285  *   - Byte 2: Firmware filename length (Optional)
1286  *   - Byte 3:N: Firmware filename data (Optional)
1287  *  @returns IPMI completion code plus response data
1288  *   - Byte 2: Current control status
1289  **/
1290 ipmi::RspType<bool, bool, bool, bool, uint4_t>
1291     ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr& ctx,
1292                                     const uint8_t controlReq,
1293                                     const std::optional<std::string>& fileName)
1294 {
1295     bool isIPMBChannel = false;
1296 
1297     if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1298     {
1299         return ipmi::responseUnspecifiedError();
1300     }
1301     if (isIPMBChannel)
1302     {
1303         phosphor::logging::log<phosphor::logging::level::INFO>(
1304             "Channel not supported. Failed to get or set FW update control");
1305         return ipmi::responseCommandNotAvailable();
1306     }
1307 
1308     static std::string fwXferUriPath;
1309     static bool imageTransferStarted = false;
1310     static bool imageTransferCompleted = false;
1311     static bool imageTransferAborted = false;
1312 
1313     if ((controlReq !=
1314          static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
1315         (fileName))
1316     {
1317         phosphor::logging::log<phosphor::logging::level::ERR>(
1318             "Invalid request field (Filename).");
1319         return ipmi::responseInvalidFieldRequest();
1320     }
1321 
1322     static bool usbAttached = getUsbStatus();
1323 
1324     switch (static_cast<FwUpdateCtrlReq>(controlReq))
1325     {
1326         case FwUpdateCtrlReq::getCurrentControlStatus:
1327             phosphor::logging::log<phosphor::logging::level::INFO>(
1328                 "ipmiGetSetFirmwareUpdateControl: Get status");
1329             break;
1330         case FwUpdateCtrlReq::imageTransferStart:
1331         {
1332             phosphor::logging::log<phosphor::logging::level::INFO>(
1333                 "ipmiGetSetFirmwareUpdateControl: Set transfer start");
1334             imageTransferStarted = true;
1335             // reset buffer to empty (truncate file)
1336             std::ofstream out(firmwareBufferFile,
1337                               std::ofstream::binary | std::ofstream::trunc);
1338             fwXferUriPath = std::string("file://") + firmwareBufferFile;
1339             if (xferHashCheck)
1340             {
1341                 if (!xferHashCheck->clear())
1342                 {
1343                     phosphor::logging::log<phosphor::logging::level::ERR>(
1344                         "clear() for xferHashCheck failed");
1345                     return ipmi::responseUnspecifiedError();
1346                 }
1347             }
1348             // Setting state to download
1349             fwUpdateStatus.setState(
1350                 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
1351 #ifdef INTEL_PFR_ENABLED
1352             imgLength = 0;
1353             imgType = 0;
1354             block0Mapped = false;
1355 #endif
1356         }
1357         break;
1358         case FwUpdateCtrlReq::imageTransferComplete:
1359         {
1360             if (!imageTransferStarted)
1361             {
1362                 phosphor::logging::log<phosphor::logging::level::ERR>(
1363                     "transferFirmwareUpdate not started.");
1364                 return ipmi::responseNotSupportedInPresentState();
1365             }
1366             phosphor::logging::log<phosphor::logging::level::INFO>(
1367                 "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
1368             if (usbAttached)
1369             {
1370                 phosphor::logging::log<phosphor::logging::level::ERR>(
1371                     "USB should be detached to perform this operation.");
1372                 return ipmi::responseNotSupportedInPresentState();
1373             }
1374             // finish transfer based on URI
1375             if (!transferFirmwareFromUri(fwXferUriPath))
1376             {
1377                 phosphor::logging::log<phosphor::logging::level::ERR>(
1378                     "transferFirmwareFromUri failed.");
1379                 return ipmi::responseUnspecifiedError();
1380             }
1381             // transfer complete
1382             if (xferHashCheck)
1383             {
1384                 if (TransferHashCheck::HashCheck::sha2Success !=
1385                     xferHashCheck->verify())
1386                 {
1387                     phosphor::logging::log<phosphor::logging::level::ERR>(
1388                         "xferHashCheck failed.");
1389                     return ipmi::responseUnspecifiedError();
1390                 }
1391             }
1392             // Set state to verify and start the update
1393             fwUpdateStatus.setState(
1394                 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
1395             // start the request
1396             if (!startFirmwareUpdate(firmwareBufferFile))
1397             {
1398                 phosphor::logging::log<phosphor::logging::level::ERR>(
1399                     "startFirmwareUpdate failed.");
1400                 return ipmi::responseUnspecifiedError();
1401             }
1402             imageTransferCompleted = true;
1403         }
1404         break;
1405         case FwUpdateCtrlReq::imageTransferAbort:
1406             phosphor::logging::log<phosphor::logging::level::INFO>(
1407                 "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
1408             if (usbAttached)
1409             {
1410                 if (detachUsbDevice())
1411                 {
1412                     phosphor::logging::log<phosphor::logging::level::ERR>(
1413                         "Detach USB device failed.");
1414                     return ipmi::responseUsbAttachOrDetachFailed();
1415                 }
1416                 usbAttached = false;
1417             }
1418             // During abort request reset the state to Init by cleaning update
1419             // file.
1420             fwUpdateStatus.firmwareUpdateAbortState();
1421             imageTransferAborted = true;
1422             break;
1423         case FwUpdateCtrlReq::setFirmwareFilename:
1424             phosphor::logging::log<phosphor::logging::level::INFO>(
1425                 "ipmiGetSetFirmwareUpdateControl: Set filename.");
1426             if (!fileName || ((*fileName).length() == 0))
1427             {
1428                 phosphor::logging::log<phosphor::logging::level::ERR>(
1429                     "Invalid Filename specified.");
1430                 return ipmi::responseInvalidFieldRequest();
1431             }
1432 
1433             fwXferUriPath = *fileName;
1434             break;
1435         case FwUpdateCtrlReq::attachUsbDevice:
1436             phosphor::logging::log<phosphor::logging::level::INFO>(
1437                 "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
1438             if (usbAttached)
1439             {
1440                 phosphor::logging::log<phosphor::logging::level::ERR>(
1441                     "USB device is already attached.");
1442                 return ipmi::responseInvalidFieldRequest();
1443             }
1444             if (attachUsbDevice())
1445             {
1446                 phosphor::logging::log<phosphor::logging::level::ERR>(
1447                     "Attach USB device failed.");
1448                 return ipmi::responseUsbAttachOrDetachFailed();
1449             }
1450             usbAttached = true;
1451             break;
1452         case FwUpdateCtrlReq::detachUsbDevice:
1453             phosphor::logging::log<phosphor::logging::level::INFO>(
1454                 "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
1455             if (!usbAttached)
1456             {
1457                 phosphor::logging::log<phosphor::logging::level::ERR>(
1458                     "USB device is not attached.");
1459                 return ipmi::responseInvalidFieldRequest();
1460             }
1461             if (detachUsbDevice())
1462             {
1463                 phosphor::logging::log<phosphor::logging::level::ERR>(
1464                     "Detach USB device failed.");
1465                 return ipmi::responseUsbAttachOrDetachFailed();
1466             }
1467             usbAttached = false;
1468             break;
1469         default:
1470             phosphor::logging::log<phosphor::logging::level::ERR>(
1471                 "Invalid control option specified.");
1472             return ipmi::responseInvalidFieldRequest();
1473     }
1474 
1475     return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
1476                                  imageTransferAborted, usbAttached, uint4_t(0));
1477 }
1478 
1479 /** @brief implements firmware get status command
1480  *  @parameter
1481  *   -  none
1482  *  @returns IPMI completion code plus response data
1483  *   - status     -  processing status
1484  *   - percentage -  percentage completion
1485  *   - check      -  channel integrity check status
1486  **/
1487 ipmi::RspType<uint8_t, // status
1488               uint8_t, // percentage
1489               uint8_t  // check
1490               >
1491     ipmiGetFirmwareUpdateStatus()
1492 
1493 {
1494     // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1495     //                  5=ready, f=error, 83=ac cycle required)
1496     // Byte 2 - percent
1497     // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1498     uint8_t status = fwUpdateStatus.getState();
1499     uint8_t percent = fwUpdateStatus.percent();
1500     uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
1501 
1502     // Status code.
1503     return ipmi::responseSuccess(status, percent, check);
1504 }
1505 
1506 ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
1507     const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask,
1508     bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart,
1509     bool sha2Check, uint5_t reserved2,
1510     std::optional<std::vector<uint8_t>> integrityCheckVal)
1511 {
1512     phosphor::logging::log<phosphor::logging::level::INFO>(
1513         "Set firmware update options.");
1514     bool isIPMBChannel = false;
1515 
1516     if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1517     {
1518         return ipmi::responseUnspecifiedError();
1519     }
1520     if (isIPMBChannel)
1521     {
1522         phosphor::logging::log<phosphor::logging::level::INFO>(
1523             "Channel not supported. Failed to set firmware update options");
1524         return ipmi::responseCommandNotAvailable();
1525     }
1526     bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
1527     bool deferRestartState = fwUpdateStatus.getDeferRestart();
1528     bool sha2CheckState = xferHashCheck ? true : false;
1529 
1530     if (noDowngradeMask && (noDowngradeState != noDowngrade))
1531     {
1532         fwUpdateStatus.setInhibitDowngrade(noDowngrade);
1533         noDowngradeState = noDowngrade;
1534     }
1535     if (deferRestartMask && (deferRestartState != deferRestart))
1536     {
1537         fwUpdateStatus.setDeferRestart(deferRestart);
1538         deferRestartState = deferRestart;
1539     }
1540     if (sha2CheckMask)
1541     {
1542         if (sha2Check)
1543         {
1544             auto hashSize = EVP_MD_size(EVP_sha256());
1545             if ((*integrityCheckVal).size() != hashSize)
1546             {
1547                 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1548                     "Invalid size of Hash specified.");
1549                 return ipmi::responseInvalidFieldRequest();
1550             }
1551 
1552             try
1553             {
1554                 xferHashCheck =
1555                     std::make_unique<TransferHashCheck>(*integrityCheckVal);
1556             }
1557             catch (const std::exception& ex)
1558             {
1559                 phosphor::logging::log<phosphor::logging::level::ERR>(
1560                     ex.what());
1561                 return ipmi::responseUnspecifiedError();
1562             }
1563         }
1564         else
1565         {
1566             // delete the xferHashCheck object
1567             xferHashCheck.reset();
1568         }
1569         sha2CheckState = sha2CheckMask;
1570     }
1571     return ipmi::responseSuccess(noDowngradeState, deferRestartState,
1572                                  sha2CheckState, reserved1);
1573 }
1574 
1575 ipmi::RspType<uint32_t>
1576     ipmiFwImageWriteData(const std::vector<uint8_t>& writeData)
1577 {
1578     const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1579     size_t writeDataLen = writeData.size();
1580 
1581     if (!writeDataLen)
1582     {
1583         return ipmi::responseReqDataLenInvalid();
1584     }
1585 
1586     if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
1587     {
1588         phosphor::logging::log<phosphor::logging::level::ERR>(
1589             "Invalid firmware update state.");
1590         return ipmi::response(ccCmdNotSupportedInPresentState);
1591     }
1592 
1593     std::ofstream out(firmwareBufferFile,
1594                       std::ofstream::binary | std::ofstream::app);
1595     if (!out)
1596     {
1597         phosphor::logging::log<phosphor::logging::level::DEBUG>(
1598             "Error while opening file.");
1599         return ipmi::responseUnspecifiedError();
1600     }
1601 
1602     uint64_t fileDataLen = out.tellp();
1603 
1604     if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
1605     {
1606         phosphor::logging::log<phosphor::logging::level::DEBUG>(
1607             "Firmware image size exceeds the limit");
1608         return ipmi::responseInvalidFieldRequest();
1609     }
1610 
1611     const char* data = reinterpret_cast<const char*>(writeData.data());
1612     out.write(data, writeDataLen);
1613     out.close();
1614 
1615     if (xferHashCheck)
1616     {
1617         if (!xferHashCheck->hash(writeData))
1618         {
1619             phosphor::logging::log<phosphor::logging::level::ERR>(
1620                 "ipmiFwImageWriteData: xferHashCheck->hash failed.");
1621             return ipmi::responseUnspecifiedError();
1622         }
1623     }
1624 
1625 #ifdef INTEL_PFR_ENABLED
1626     /* PFR image block 0 - As defined in HAS */
1627     struct PFRImageBlock0
1628     {
1629         uint32_t tag;
1630         uint32_t pcLength;
1631         uint32_t pcType;
1632         uint32_t reserved1;
1633         uint8_t hash256[32];
1634         uint8_t hash384[48];
1635         uint8_t reserved2[32];
1636     } __attribute__((packed));
1637 
1638     /* Get the PFR block 0 data and read the uploaded image
1639      * information( Image type, length etc) */
1640     if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1641         (!block0Mapped))
1642     {
1643         struct PFRImageBlock0 block0Data = {0};
1644 
1645         std::ifstream inFile(firmwareBufferFile,
1646                              std::ios::binary | std::ios::in);
1647         inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data));
1648         inFile.close();
1649 
1650         uint32_t magicNum = block0Data.tag;
1651 
1652         /* Validate the magic number */
1653         if (magicNum != perBlock0MagicNum)
1654         {
1655             phosphor::logging::log<phosphor::logging::level::DEBUG>(
1656                 "PFR image magic number not matched");
1657             return ipmi::responseInvalidFieldRequest();
1658         }
1659         // Note:imgLength, imgType and block0Mapped are in global scope, as
1660         // these are used in cascaded updates.
1661         imgLength = block0Data.pcLength;
1662         imgType = block0Data.pcType;
1663         block0Mapped = true;
1664     }
1665 #endif // end of INTEL_PFR_ENABLED
1666     return ipmi::responseSuccess(writeDataLen);
1667 }
1668 
1669 static void registerFirmwareFunctions()
1670 {
1671     // guarantee that we start with an already timed out timestamp
1672     fwRandomNumGenTs = std::chrono::steady_clock::now() -
1673                        fwRandomNumExpirySeconds;
1674     fwUpdateStatus.setState(
1675         static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
1676 
1677     unlink(firmwareBufferFile);
1678 
1679 #ifdef INTEL_PFR_ENABLED
1680     // Following commands are supported only for PFR enabled platforms
1681     // CMD:0x20 - Get Firmware Version Information
1682     // CMD:0x21 - Get Firmware Security Version Information
1683     // CMD:0x25 - Get Root Certificate Data
1684 
1685     // get firmware version information
1686     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1687                           ipmi::firmware::cmdGetFwVersionInfo,
1688                           ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
1689 
1690     // get firmware security version information
1691     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1692                           ipmi::firmware::cmdGetFwSecurityVersionInfo,
1693                           ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
1694 
1695     // get root certificate data
1696     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1697                           ipmi::firmware::cmdFwGetRootCertData,
1698                           ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1699 #endif
1700 
1701     // get firmware update channel information (max transfer sizes)
1702     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1703                           ipmi::firmware::cmdGetFwUpdateChannelInfo,
1704                           ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
1705 
1706     // get bmc execution context
1707     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1708                           ipmi::firmware::cmdGetBmcExecutionContext,
1709                           ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
1710 
1711     // Get Firmware Update Random number
1712     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1713                           ipmi::firmware::cmdGetFwUpdateRandomNumber,
1714                           ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
1715 
1716     // Set Firmware Update Mode
1717     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1718                           ipmi::firmware::cmdSetFirmwareUpdateMode,
1719                           ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
1720 
1721     // Exit Firmware Update Mode
1722     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1723                           ipmi::firmware::cmdExitFirmwareUpdateMode,
1724                           ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
1725 
1726     // Get/Set Firmware Update Control
1727     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1728                           ipmi::firmware::cmdGetSetFwUpdateControl,
1729                           ipmi::Privilege::Admin,
1730                           ipmiGetSetFirmwareUpdateControl);
1731 
1732     // Get Firmware Update Status
1733     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1734                           ipmi::firmware::cmdGetFirmwareUpdateStatus,
1735                           ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
1736 
1737     // Set Firmware Update Options
1738     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1739                           ipmi::firmware::cmdSetFirmwareUpdateOptions,
1740                           ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
1741     // write image data
1742     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1743                           ipmi::firmware::cmdFwImageWriteData,
1744                           ipmi::Privilege::Admin, ipmiFwImageWriteData);
1745     return;
1746 }
1747