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