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