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