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