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