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