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