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