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(uint8_t certId)
860 {
861     size_t certKeyOffset = 0;
862     size_t cskSigOffset = 0;
863     std::string mtdDev;
864 
865     switch (static_cast<FwGetRootCertDataTag>(certId))
866     {
867         case FwGetRootCertDataTag::activeRootKey:
868         {
869             mtdDev = bmcActivePfmMTDDev;
870             certKeyOffset = rootkeyOffsetInPfm;
871             break;
872         }
873         case FwGetRootCertDataTag::recoveryRootKey:
874         {
875             mtdDev = bmcRecoveryImgMTDDev;
876             certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
877             break;
878         }
879         case FwGetRootCertDataTag::activeCSK:
880         {
881             mtdDev = bmcActivePfmMTDDev;
882             certKeyOffset = cskKeyOffsetInPfm;
883             cskSigOffset = cskSignatureOffsetInPfm;
884             break;
885         }
886         case FwGetRootCertDataTag::recoveryCSK:
887         {
888             mtdDev = bmcRecoveryImgMTDDev;
889             certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
890             cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
891             break;
892         }
893         default:
894         {
895             return ipmi::responseInvalidFieldRequest();
896         }
897     }
898 
899     std::array<uint8_t, certKeyLen> certKey = {0};
900 
901     try
902     {
903         SPIDev spiDev(mtdDev);
904         spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
905 
906         if (cskSigOffset)
907         {
908             std::array<uint8_t, cskSignatureLen> cskSignature = {0};
909             spiDev.spiReadData(cskSigOffset, cskSignatureLen,
910                                cskSignature.data());
911             return ipmi::responseSuccess(certKey, cskSignature);
912         }
913     }
914     catch (const std::exception &e)
915     {
916         phosphor::logging::log<phosphor::logging::level::ERR>(
917             "Exception caught in ipmiGetFwRootCertData",
918             phosphor::logging::entry("MSG=%s", e.what()));
919         return ipmi::responseUnspecifiedError();
920     }
921 
922     return ipmi::responseSuccess(certKey, std::nullopt);
923 }
924 #endif // INTEL_PFR_ENABLED
925 
926 static constexpr uint8_t channelListSize = 3;
927 /** @brief implements Maximum Firmware Transfer size command
928  *  @parameter
929  *   -  none
930  *  @returns IPMI completion code plus response data
931  *   - count - channel count
932  *   - channelList - channel list information
933  */
934 ipmi::RspType<uint8_t, // channel count
935               std::array<std::tuple<uint8_t, uint32_t>,
936                          channelListSize> // Channel List
937               >
938     ipmiFirmwareMaxTransferSize()
939 {
940     constexpr size_t kcsMaxBufSize = 128;
941     constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
942     constexpr size_t ipmbMaxBufSize = 4 * 1024;
943     // Byte 1 - Count (N) Number of devices data is being returned for.
944     // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
945     // Byte 3-6 - transfer size (little endian)
946     // Bytes - 7:(5xN) - Repeat of 2 through 6
947     constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
948         channelList = {
949             {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
950              {static_cast<uint8_t>(ChannelIdTag::ipmb), ipmbMaxBufSize},
951              {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
952               rmcpPlusMaxBufSize}}};
953 
954     return ipmi::responseSuccess(channelListSize, channelList);
955 }
956 
957 ipmi::RspType<uint8_t, uint8_t>
958     ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
959 {
960     // Byte 1 - Current execution context
961     //          0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
962     // Byte 2 - Partition pointer
963     //          0x01 - primary, 0x02 - secondary
964     uint8_t partitionPtr = getActiveBootImage(ctx);
965 
966     return ipmi::responseSuccess(
967         static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
968 }
969 
970 /** @brief Get Firmware Update Random Number
971  *
972  *  This function generate the random number used for
973  *  setting the firmware update mode as authentication key.
974  *
975  *  @parameter : None
976  *  @returns IPMI completion code along with
977  *   - random number
978  **/
979 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
980     ipmiGetFwUpdateRandomNumber()
981 {
982     phosphor::logging::log<phosphor::logging::level::INFO>(
983         "Generate FW update random number");
984     std::random_device rd;
985     std::default_random_engine gen(rd());
986     std::uniform_int_distribution<> dist{0, 255};
987 
988     fwRandomNumGenTs = std::chrono::steady_clock::now();
989 
990     for (int i = 0; i < fwRandomNumLength; i++)
991     {
992         fwRandomNum[i] = dist(gen);
993     }
994 
995     return ipmi::responseSuccess(fwRandomNum);
996 }
997 
998 /** @brief Set Firmware Update Mode
999  *
1000  *  This function sets BMC into firmware update mode
1001  *  after validating Random number obtained from the Get
1002  *  Firmware Update Random Number command
1003  *
1004  *  @parameter
1005  *   -  randNum - Random number(token)
1006  *  @returns IPMI completion code
1007  **/
1008 ipmi::RspType<>
1009     ipmiSetFirmwareUpdateMode(std::array<uint8_t, fwRandomNumLength> &randNum)
1010 {
1011     phosphor::logging::log<phosphor::logging::level::INFO>(
1012         "Start FW update mode");
1013     /* Firmware Update Random number is valid for 30 seconds only */
1014     auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1015     if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1016             .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1017                            fwRandomNumExpirySeconds)
1018                            .count())
1019     {
1020         phosphor::logging::log<phosphor::logging::level::INFO>(
1021             "Firmware update random number expired.");
1022         return ipmi::responseInvalidFieldRequest();
1023     }
1024 
1025     /* Validate random number */
1026     for (int i = 0; i < fwRandomNumLength; i++)
1027     {
1028         if (fwRandomNum[i] != randNum[i])
1029         {
1030             phosphor::logging::log<phosphor::logging::level::INFO>(
1031                 "Invalid random number specified.");
1032             return ipmi::responseInvalidFieldRequest();
1033         }
1034     }
1035 
1036     try
1037     {
1038         if (getFirmwareUpdateMode())
1039         {
1040             phosphor::logging::log<phosphor::logging::level::INFO>(
1041                 "Already firmware update is in progress.");
1042             return ipmi::responseBusy();
1043         }
1044     }
1045     catch (const std::exception &e)
1046     {
1047         return ipmi::responseUnspecifiedError();
1048     }
1049 
1050     // FIXME? c++ doesn't off an option for exclusive file creation
1051     FILE *fp = fopen(firmwareBufferFile, "wx");
1052     if (!fp)
1053     {
1054         phosphor::logging::log<phosphor::logging::level::INFO>(
1055             "Unable to open file.");
1056         return ipmi::responseUnspecifiedError();
1057     }
1058     fclose(fp);
1059 
1060     try
1061     {
1062         setFirmwareUpdateMode(true);
1063     }
1064     catch (const std::exception &e)
1065     {
1066         unlink(firmwareBufferFile);
1067         return ipmi::responseUnspecifiedError();
1068     }
1069 
1070     return ipmi::responseSuccess();
1071 }
1072 
1073 /** @brief implements exit firmware update mode command
1074  *  @param None
1075  *
1076  *  @returns IPMI completion code
1077  */
1078 ipmi::RspType<> ipmiExitFirmwareUpdateMode()
1079 {
1080     phosphor::logging::log<phosphor::logging::level::INFO>(
1081         "Exit FW update mode");
1082     switch (fwUpdateStatus.getState())
1083     {
1084         case FwUpdateStatusCache::fwStateInit:
1085         case FwUpdateStatusCache::fwStateIdle:
1086             return ipmi::responseInvalidFieldRequest();
1087             break;
1088         case FwUpdateStatusCache::fwStateDownload:
1089         case FwUpdateStatusCache::fwStateVerify:
1090             break;
1091         case FwUpdateStatusCache::fwStateProgram:
1092             break;
1093         case FwUpdateStatusCache::fwStateUpdateSuccess:
1094         case FwUpdateStatusCache::fwStateError:
1095             break;
1096         case FwUpdateStatusCache::fwStateAcCycleRequired:
1097             return ipmi::responseInvalidFieldRequest();
1098             break;
1099     }
1100     fwUpdateStatus.firmwareUpdateAbortState();
1101 
1102     try
1103     {
1104         setFirmwareUpdateMode(false);
1105     }
1106     catch (const std::exception &e)
1107     {
1108         return ipmi::responseUnspecifiedError();
1109     }
1110 
1111     return ipmi::responseSuccess();
1112 }
1113 
1114 /** @brief implements Get/Set Firmware Update Control
1115  *  @parameter
1116  *   - Byte 1: Control Byte
1117  *   - Byte 2: Firmware filename length (Optional)
1118  *   - Byte 3:N: Firmware filename data (Optional)
1119  *  @returns IPMI completion code plus response data
1120  *   - Byte 2: Current control status
1121  **/
1122 ipmi::RspType<bool, bool, bool, bool, uint4_t>
1123     ipmiGetSetFirmwareUpdateControl(const uint8_t controlReq,
1124                                     const std::optional<std::string> &fileName)
1125 {
1126     static std::string fwXferUriPath;
1127     static bool imageTransferStarted = false;
1128     static bool imageTransferCompleted = false;
1129     static bool imageTransferAborted = false;
1130 
1131     if ((controlReq !=
1132          static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
1133         (fileName))
1134     {
1135         phosphor::logging::log<phosphor::logging::level::ERR>(
1136             "Invalid request field (Filename).");
1137         return ipmi::responseInvalidFieldRequest();
1138     }
1139 
1140     static bool usbAttached = getUsbStatus();
1141 
1142     switch (static_cast<FwUpdateCtrlReq>(controlReq))
1143     {
1144         case FwUpdateCtrlReq::getCurrentControlStatus:
1145             phosphor::logging::log<phosphor::logging::level::INFO>(
1146                 "ipmiGetSetFirmwareUpdateControl: Get status");
1147             break;
1148         case FwUpdateCtrlReq::imageTransferStart:
1149         {
1150             phosphor::logging::log<phosphor::logging::level::INFO>(
1151                 "ipmiGetSetFirmwareUpdateControl: Set transfer start");
1152             imageTransferStarted = true;
1153             // reset buffer to empty (truncate file)
1154             std::ofstream out(firmwareBufferFile,
1155                               std::ofstream::binary | std::ofstream::trunc);
1156             fwXferUriPath = std::string("file://") + firmwareBufferFile;
1157             if (xferHashCheck)
1158             {
1159                 xferHashCheck->clear();
1160             }
1161             // Setting state to download
1162             fwUpdateStatus.setState(
1163                 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
1164 #ifdef INTEL_PFR_ENABLED
1165             imgLength = 0;
1166             imgType = 0;
1167             block0Mapped = false;
1168 #endif
1169         }
1170         break;
1171         case FwUpdateCtrlReq::imageTransferComplete:
1172         {
1173             phosphor::logging::log<phosphor::logging::level::INFO>(
1174                 "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
1175             if (usbAttached)
1176             {
1177                 phosphor::logging::log<phosphor::logging::level::ERR>(
1178                     "USB should be detached to perform this operation.");
1179                 return ipmi::responseNotSupportedInPresentState();
1180             }
1181             // finish transfer based on URI
1182             if (!transferFirmwareFromUri(fwXferUriPath))
1183             {
1184                 phosphor::logging::log<phosphor::logging::level::ERR>(
1185                     "transferFirmwareFromUri failed.");
1186                 return ipmi::responseUnspecifiedError();
1187             }
1188             // transfer complete
1189             if (xferHashCheck)
1190             {
1191                 if (TransferHashCheck::HashCheck::sha2Success !=
1192                     xferHashCheck->verify())
1193                 {
1194                     phosphor::logging::log<phosphor::logging::level::ERR>(
1195                         "xferHashCheck failed.");
1196                     return ipmi::responseUnspecifiedError();
1197                 }
1198             }
1199             // Set state to verify and start the update
1200             fwUpdateStatus.setState(
1201                 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
1202             // start the request
1203             if (!startFirmwareUpdate(firmwareBufferFile))
1204             {
1205                 phosphor::logging::log<phosphor::logging::level::ERR>(
1206                     "startFirmwareUpdate failed.");
1207                 return ipmi::responseUnspecifiedError();
1208             }
1209             imageTransferCompleted = true;
1210         }
1211         break;
1212         case FwUpdateCtrlReq::imageTransferAbort:
1213             phosphor::logging::log<phosphor::logging::level::INFO>(
1214                 "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
1215             if (usbAttached)
1216             {
1217                 if (detachUsbDevice())
1218                 {
1219                     phosphor::logging::log<phosphor::logging::level::ERR>(
1220                         "Detach USB device failed.");
1221                     return ipmi::responseUsbAttachOrDetachFailed();
1222                 }
1223                 usbAttached = false;
1224             }
1225             // During abort request reset the state to Init by cleaning update
1226             // file.
1227             fwUpdateStatus.firmwareUpdateAbortState();
1228             imageTransferAborted = true;
1229             break;
1230         case FwUpdateCtrlReq::setFirmwareFilename:
1231             phosphor::logging::log<phosphor::logging::level::INFO>(
1232                 "ipmiGetSetFirmwareUpdateControl: Set filename.");
1233             if (!fileName || ((*fileName).length() == 0))
1234             {
1235                 phosphor::logging::log<phosphor::logging::level::ERR>(
1236                     "Invalid Filename specified.");
1237                 return ipmi::responseInvalidFieldRequest();
1238             }
1239 
1240             fwXferUriPath = *fileName;
1241             break;
1242         case FwUpdateCtrlReq::attachUsbDevice:
1243             phosphor::logging::log<phosphor::logging::level::INFO>(
1244                 "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
1245             if (usbAttached)
1246             {
1247                 phosphor::logging::log<phosphor::logging::level::ERR>(
1248                     "USB device is already attached.");
1249                 return ipmi::responseInvalidFieldRequest();
1250             }
1251             if (attachUsbDevice())
1252             {
1253                 phosphor::logging::log<phosphor::logging::level::ERR>(
1254                     "Attach USB device failed.");
1255                 return ipmi::responseUsbAttachOrDetachFailed();
1256             }
1257             usbAttached = true;
1258             break;
1259         case FwUpdateCtrlReq::detachUsbDevice:
1260             phosphor::logging::log<phosphor::logging::level::INFO>(
1261                 "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
1262             if (!usbAttached)
1263             {
1264                 phosphor::logging::log<phosphor::logging::level::ERR>(
1265                     "USB device is not attached.");
1266                 return ipmi::responseInvalidFieldRequest();
1267             }
1268             if (detachUsbDevice())
1269             {
1270                 phosphor::logging::log<phosphor::logging::level::ERR>(
1271                     "Detach USB device failed.");
1272                 return ipmi::responseUsbAttachOrDetachFailed();
1273             }
1274             usbAttached = false;
1275             break;
1276         default:
1277             phosphor::logging::log<phosphor::logging::level::ERR>(
1278                 "Invalid control option specified.");
1279             return ipmi::responseInvalidFieldRequest();
1280     }
1281 
1282     return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
1283                                  imageTransferAborted, usbAttached, uint4_t(0));
1284 }
1285 
1286 /** @brief implements firmware get status command
1287  *  @parameter
1288  *   -  none
1289  *  @returns IPMI completion code plus response data
1290  *   - status     -  processing status
1291  *   - percentage -  percentage completion
1292  *   - check      -  channel integrity check status
1293  **/
1294 ipmi::RspType<uint8_t, // status
1295               uint8_t, // percentage
1296               uint8_t  // check
1297               >
1298     ipmiGetFirmwareUpdateStatus()
1299 
1300 {
1301     // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1302     //                  5=ready, f=error, 83=ac cycle required)
1303     // Byte 2 - percent
1304     // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1305     uint8_t status = fwUpdateStatus.getState();
1306     uint8_t percent = fwUpdateStatus.percent();
1307     uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
1308 
1309     // Status code.
1310     return ipmi::responseSuccess(status, percent, check);
1311 }
1312 
1313 ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
1314     bool noDowngradeMask, bool deferRestartMask, bool sha2CheckMask,
1315     uint5_t reserved1, bool noDowngrade, bool deferRestart, bool sha2Check,
1316     uint5_t reserved2, std::optional<std::vector<uint8_t>> integrityCheckVal)
1317 {
1318     phosphor::logging::log<phosphor::logging::level::INFO>(
1319         "Set firmware update options.");
1320     bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
1321     bool deferRestartState = fwUpdateStatus.getDeferRestart();
1322     bool sha2CheckState = xferHashCheck ? true : false;
1323 
1324     if (noDowngradeMask && (noDowngradeState != noDowngrade))
1325     {
1326         fwUpdateStatus.setInhibitDowngrade(noDowngrade);
1327         noDowngradeState = noDowngrade;
1328     }
1329     if (deferRestartMask && (deferRestartState != deferRestart))
1330     {
1331         fwUpdateStatus.setDeferRestart(deferRestart);
1332         deferRestartState = deferRestart;
1333     }
1334     if (sha2CheckMask)
1335     {
1336         if (sha2Check)
1337         {
1338             auto hashSize = EVP_MD_size(EVP_sha256());
1339             if ((*integrityCheckVal).size() != hashSize)
1340             {
1341                 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1342                     "Invalid size of Hash specified.");
1343                 return ipmi::responseInvalidFieldRequest();
1344             }
1345             xferHashCheck = std::make_shared<TransferHashCheck>();
1346             xferHashCheck->init(*integrityCheckVal);
1347         }
1348         else
1349         {
1350             // delete the xferHashCheck object
1351             xferHashCheck.reset();
1352         }
1353         sha2CheckState = sha2CheckMask;
1354     }
1355     return ipmi::responseSuccess(noDowngradeState, deferRestartState,
1356                                  sha2CheckState, reserved1);
1357 }
1358 
1359 ipmi::RspType<uint32_t>
1360     ipmiFwImageWriteData(const std::vector<uint8_t> &writeData)
1361 {
1362     const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1363     size_t writeDataLen = writeData.size();
1364 
1365     if (!writeDataLen)
1366     {
1367         return ipmi::responseReqDataLenInvalid();
1368     }
1369 
1370     if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
1371     {
1372         phosphor::logging::log<phosphor::logging::level::ERR>(
1373             "Invalid firmware update state.");
1374         return ipmi::response(ccCmdNotSupportedInPresentState);
1375     }
1376 
1377     std::ofstream out(firmwareBufferFile,
1378                       std::ofstream::binary | std::ofstream::app);
1379     if (!out)
1380     {
1381         phosphor::logging::log<phosphor::logging::level::DEBUG>(
1382             "Error while opening file.");
1383         return ipmi::responseUnspecifiedError();
1384     }
1385 
1386     uint64_t fileDataLen = out.tellp();
1387 
1388     if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
1389     {
1390         phosphor::logging::log<phosphor::logging::level::DEBUG>(
1391             "Firmware image size exceeds the limit");
1392         return ipmi::responseInvalidFieldRequest();
1393     }
1394 
1395     const char *data = reinterpret_cast<const char *>(writeData.data());
1396     out.write(data, writeDataLen);
1397     out.close();
1398 
1399     if (xferHashCheck)
1400     {
1401         xferHashCheck->hash(writeData);
1402     }
1403 
1404 #ifdef INTEL_PFR_ENABLED
1405     /* PFR image block 0 - As defined in HAS */
1406     struct PFRImageBlock0
1407     {
1408         uint32_t tag;
1409         uint32_t pcLength;
1410         uint32_t pcType;
1411         uint32_t reserved1;
1412         uint8_t hash256[32];
1413         uint8_t hash384[48];
1414         uint8_t reserved2[32];
1415     } __attribute__((packed));
1416 
1417     /* Get the PFR block 0 data and read the uploaded image
1418      * information( Image type, length etc) */
1419     if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1420         (!block0Mapped))
1421     {
1422         struct PFRImageBlock0 block0Data = {0};
1423 
1424         std::ifstream inFile(firmwareBufferFile,
1425                              std::ios::binary | std::ios::in);
1426         inFile.read(reinterpret_cast<char *>(&block0Data), sizeof(block0Data));
1427         inFile.close();
1428 
1429         uint32_t magicNum = block0Data.tag;
1430 
1431         /* Validate the magic number */
1432         if (magicNum != perBlock0MagicNum)
1433         {
1434             phosphor::logging::log<phosphor::logging::level::DEBUG>(
1435                 "PFR image magic number not matched");
1436             return ipmi::responseInvalidFieldRequest();
1437         }
1438         // Note:imgLength, imgType and block0Mapped are in global scope, as
1439         // these are used in cascaded updates.
1440         imgLength = block0Data.pcLength;
1441         imgType = block0Data.pcType;
1442         block0Mapped = true;
1443     }
1444 #endif // end of INTEL_PFR_ENABLED
1445     return ipmi::responseSuccess(writeDataLen);
1446 }
1447 
1448 static void registerFirmwareFunctions()
1449 {
1450     // guarantee that we start with an already timed out timestamp
1451     fwRandomNumGenTs =
1452         std::chrono::steady_clock::now() - fwRandomNumExpirySeconds;
1453     fwUpdateStatus.setState(
1454         static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
1455 
1456     unlink(firmwareBufferFile);
1457 
1458 #ifdef INTEL_PFR_ENABLED
1459     // Following commands are supported only for PFR enabled platforms
1460     // CMD:0x20 - Get Firmware Version Information
1461     // CMD:0x21 - Get Firmware Security Version Information
1462     // CMD:0x25 - Get Root Certificate Data
1463 
1464     // get firmware version information
1465     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1466                           ipmi::firmware::cmdGetFwVersionInfo,
1467                           ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
1468 
1469     // get firmware security version information
1470     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1471                           ipmi::firmware::cmdGetFwSecurityVersionInfo,
1472                           ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
1473 
1474     // get root certificate data
1475     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1476                           ipmi::firmware::cmdFwGetRootCertData,
1477                           ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1478 #endif
1479 
1480     // get firmware update channel information (max transfer sizes)
1481     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1482                           ipmi::firmware::cmdGetFwUpdateChannelInfo,
1483                           ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
1484 
1485     // get bmc execution context
1486     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1487                           ipmi::firmware::cmdGetBmcExecutionContext,
1488                           ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
1489 
1490     // Get Firmware Update Random number
1491     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1492                           ipmi::firmware::cmdGetFwUpdateRandomNumber,
1493                           ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
1494 
1495     // Set Firmware Update Mode
1496     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1497                           ipmi::firmware::cmdSetFirmwareUpdateMode,
1498                           ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
1499 
1500     // Exit Firmware Update Mode
1501     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1502                           ipmi::firmware::cmdExitFirmwareUpdateMode,
1503                           ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
1504 
1505     // Get/Set Firmware Update Control
1506     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1507                           ipmi::firmware::cmdGetSetFwUpdateControl,
1508                           ipmi::Privilege::Admin,
1509                           ipmiGetSetFirmwareUpdateControl);
1510 
1511     // Get Firmware Update Status
1512     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1513                           ipmi::firmware::cmdGetFirmwareUpdateStatus,
1514                           ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
1515 
1516     // Set Firmware Update Options
1517     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1518                           ipmi::firmware::cmdSetFirmwareUpdateOptions,
1519                           ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
1520     // write image data
1521     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1522                           ipmi::firmware::cmdFwImageWriteData,
1523                           ipmi::Privilege::Admin, ipmiFwImageWriteData);
1524     return;
1525 }
1526