1 #include <ipmid/api.h>
2 #include <openssl/evp.h>
3 #include <openssl/sha.h>
4 #include <sys/mman.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 
9 #include <boost/algorithm/string.hpp>
10 #include <boost/asio.hpp>
11 #include <boost/process/child.hpp>
12 #include <boost/uuid/random_generator.hpp>
13 #include <boost/uuid/uuid_io.hpp>
14 #include <chrono>
15 #include <commandutils.hpp>
16 #include <cstdint>
17 #include <filesystem>
18 #include <fstream>
19 #include <iostream>
20 #include <ipmid/api.hpp>
21 #include <ipmid/utils.hpp>
22 #include <map>
23 #include <random>
24 #include <sdbusplus/bus.hpp>
25 #include <sdbusplus/bus/match.hpp>
26 #include <sdbusplus/server/object.hpp>
27 #include <sdbusplus/timer.hpp>
28 #include <sstream>
29 #ifdef INTEL_PFR_ENABLED
30 #include <spiDev.hpp>
31 #endif
32 
33 namespace ipmi
34 {
35 namespace firmware
36 {
37 constexpr Cmd cmdGetFwVersionInfo = 0x20;
38 constexpr Cmd cmdFwGetRootCertData = 0x25;
39 constexpr Cmd cmdFwImageWriteData = 0x2c;
40 } // namespace firmware
41 } // namespace ipmi
42 
43 #ifdef INTEL_PFR_ENABLED
44 uint32_t imgLength = 0;
45 uint32_t imgType = 0;
46 bool block0Mapped = false;
47 static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
48 
49 static constexpr const char *versionIntf =
50     "xyz.openbmc_project.Software.Version";
51 
52 enum class FWDeviceIDTag : uint8_t
53 {
54     bmcActiveImage = 1,
55     bmcRecoveryImage,
56 };
57 
58 const static boost::container::flat_map<FWDeviceIDTag, const char *>
59     fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
60                     "/xyz/openbmc_project/software/bmc_active"},
61                    {FWDeviceIDTag::bmcRecoveryImage,
62                     "/xyz/openbmc_project/software/bmc_recovery"}};
63 
64 #endif
65 
66 static constexpr const char *secondaryFitImageStartAddr = "22480000";
67 static uint8_t getActiveBootImage(void);
68 static void register_netfn_firmware_functions() __attribute__((constructor));
69 
70 // oem return code for firmware update control
71 constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5;
72 constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80;
73 
74 static constexpr bool DEBUG = false;
75 
76 static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] =
77     "xyz.openbmc_project.fwupdate1.server";
78 
79 static constexpr char FW_UPDATE_SERVER_PATH[] =
80     "/xyz/openbmc_project/fwupdate1";
81 static constexpr char FW_UPDATE_SERVER_INFO_PATH[] =
82     "/xyz/openbmc_project/fwupdate1/info";
83 static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] =
84     "/xyz/openbmc_project/fwupdate1/info/bmc_active";
85 static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] =
86     "/xyz/openbmc_project/fwupdate1/info/bmc_backup";
87 
88 static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1";
89 static constexpr char FW_UPDATE_INFO_INTERFACE[] =
90     "xyz.openbmc_project.fwupdate1.fwinfo";
91 static constexpr char FW_UPDATE_SECURITY_INTERFACE[] =
92     "xyz.openbmc_project.fwupdate1.security";
93 
94 constexpr std::size_t operator""_MB(unsigned long long v)
95 {
96     return 1024u * 1024u * v;
97 }
98 static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB;
99 
100 static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin";
101 static bool local_download_is_active(void)
102 {
103     struct stat sb;
104     if (stat(FIRMWARE_BUFFER_FILE, &sb) < 0)
105         return false;
106     return true;
107 }
108 
109 class fw_update_status_cache
110 {
111   public:
112     enum
113     {
114         FW_STATE_INIT = 0,
115         FW_STATE_IDLE,
116         FW_STATE_DOWNLOAD,
117         FW_STATE_VERIFY,
118         FW_STATE_WRITE,
119         FW_STATE_READY,
120         FW_STATE_ERROR = 0x0f,
121         FW_STATE_AC_CYCLE_REQUIRED = 0x83,
122     };
123     uint8_t state()
124     {
125         if (DEBUG)
126             std::cerr << "fw-state: 0x" << std::hex << (int)_state << '\n';
127         if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
128             local_download_is_active())
129         {
130             _state = FW_STATE_DOWNLOAD;
131             _percent = 0;
132         }
133         return _state;
134     }
135     uint8_t percent()
136     {
137         return _percent;
138     }
139     std::string msg()
140     {
141         return _msg;
142     }
143     std::string get_software_obj_path()
144     {
145         return _software_obj_path;
146     }
147     void set_software_obj_path(std::string &obj_path)
148     {
149         _software_obj_path = obj_path;
150         _state = FW_STATE_WRITE;
151         _percent = 0;
152         _match = std::make_shared<sdbusplus::bus::match::match>(
153             *_bus,
154             sdbusplus::bus::match::rules::propertiesChanged(
155                 _software_obj_path,
156                 "xyz.openbmc_project.Software.ActivationProgress"),
157             [&](sdbusplus::message::message &msg) {
158                 if (DEBUG)
159                     std::cerr << "propertiesChanged lambda\n";
160                 std::map<std::string, ipmi::DbusVariant> props;
161                 std::vector<std::string> inval;
162                 std::string iface;
163                 msg.read(iface, props, inval);
164                 _parse_props(props);
165             });
166     }
167     uint8_t activation_timer_timeout()
168     {
169         std::cerr << "activation_timer_timout(): increase percentage...\n";
170         _percent = _percent + 5;
171         if (_percent >= 95)
172         {
173             /*changing the state to ready to update firmware utility */
174             _state = FW_STATE_READY;
175         }
176         std::cerr << " _percent = " << (int)_percent << "\n";
177         return _percent;
178     }
179     /* API for changing state to ERROR  */
180     void firmwareUpdateAbortState()
181     {
182         unlink(FIRMWARE_BUFFER_FILE);
183         // changing the state to error
184         _state = FW_STATE_ERROR;
185     }
186     void setDeferRestart(bool deferRestart)
187     {
188         _deferRestart = deferRestart;
189     }
190     void setInhibitDowngrade(bool inhibitDowngrade)
191     {
192         _inhibitDowngrade = inhibitDowngrade;
193     }
194     bool getDeferRestart()
195     {
196         return _deferRestart;
197     }
198     bool getInhibitDowngrade()
199     {
200         return _inhibitDowngrade;
201     }
202 
203   protected:
204     void _parse_props(std::map<std::string, ipmi::DbusVariant> &properties)
205     {
206         if (DEBUG)
207             std::cerr << "propertiesChanged (" << properties.size()
208                       << " elements)";
209         for (const auto &t : properties)
210         {
211             auto key = t.first;
212             auto value = t.second;
213             if (key == "state")
214             {
215                 auto state = std::get<std::string>(value);
216                 if (DEBUG)
217                     std::cerr << ", state=" << state;
218                 if (state == "INIT")
219                     _state = FW_STATE_INIT;
220                 else if (state == "IDLE")
221                     _state = FW_STATE_IDLE;
222                 else if (state == "DOWNLOAD")
223                     _state = FW_STATE_DOWNLOAD;
224                 else if (state == "VERIFY")
225                     _state = FW_STATE_VERIFY;
226                 else if (state == "WRITE")
227                     _state = FW_STATE_WRITE;
228                 else if (state == "READY")
229                     _state = FW_STATE_READY;
230                 else if (state == "ERROR")
231                     _state = FW_STATE_ERROR;
232                 else if (state == "AC_CYCLE_REQUIRED")
233                     _state = FW_STATE_AC_CYCLE_REQUIRED;
234                 else
235                 {
236                     _state = FW_STATE_ERROR;
237                     _msg = "internal error";
238                 }
239             }
240             else if (key == "percent")
241             {
242                 _percent = std::get<int32_t>(value);
243                 if (DEBUG)
244                     std::cerr << ", pct=" << (int)_percent;
245             }
246             else if (key == "msg")
247             {
248                 _msg = std::get<std::string>(value);
249                 if (DEBUG)
250                     std::cerr << ", msg='" << _msg << '\'';
251             }
252             else if (key == "Progress")
253             {
254                 _percent = std::get<uint8_t>(value);
255                 ;
256                 if (_percent == 100)
257                     _state = FW_STATE_READY;
258             }
259         }
260         if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
261             local_download_is_active())
262         {
263             _state = FW_STATE_DOWNLOAD;
264             _percent = 0;
265         }
266         if (DEBUG)
267             std::cerr << '\n';
268     }
269 
270     std::shared_ptr<sdbusplus::asio::connection> _bus;
271     std::shared_ptr<sdbusplus::bus::match::match> _match;
272     uint8_t _state = 0;
273     uint8_t _percent = 0;
274     bool _deferRestart = false;
275     bool _inhibitDowngrade = false;
276     std::string _msg;
277 
278   private:
279     std::string _software_obj_path;
280 };
281 
282 static fw_update_status_cache fw_update_status;
283 
284 static std::chrono::steady_clock::time_point fw_random_number_timestamp;
285 static constexpr int FW_RANDOM_NUMBER_LENGTH = 8;
286 static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30);
287 static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH];
288 
289 static ipmi_ret_t ipmi_firmware_get_fw_random_number(
290     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
291     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
292 {
293     std::random_device rd;
294     std::default_random_engine gen(rd());
295     std::uniform_int_distribution<> dist{0, 255};
296 
297     if (*data_len != 0)
298     {
299         *data_len = 0;
300         return IPMI_CC_REQ_DATA_LEN_INVALID;
301     }
302 
303     fw_random_number_timestamp = std::chrono::steady_clock::now();
304 
305     uint8_t *msg_reply = static_cast<uint8_t *>(response);
306     for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
307         fw_random_number[i] = msg_reply[i] = dist(gen);
308 
309     if (DEBUG)
310         std::cerr << "FW Rand Num: 0x" << std::hex << (int)msg_reply[0] << " 0x"
311                   << (int)msg_reply[1] << " 0x" << (int)msg_reply[2] << " 0x"
312                   << (int)msg_reply[3] << " 0x" << (int)msg_reply[4] << " 0x"
313                   << (int)msg_reply[5] << " 0x" << (int)msg_reply[6] << " 0x"
314                   << (int)msg_reply[7] << '\n';
315 
316     *data_len = FW_RANDOM_NUMBER_LENGTH;
317 
318     return IPMI_CC_OK;
319 }
320 
321 /** @brief Set Firmware Update Mode
322  *
323  *  This function sets BMC into firmware update mode
324  *  after validating Random number obtained from the Get
325  *  Firmware Update Random Number command
326  *
327  *  @parameter
328  *   -  randNum - Random number(token)
329  *  @returns IPMI completion code
330  **/
331 ipmi::RspType<> ipmiSetFirmwareUpdateMode(
332     std::array<uint8_t, FW_RANDOM_NUMBER_LENGTH> &randNum)
333 {
334     /* Firmware Update Random number is valid for 30 seconds only */
335     auto timeElapsed =
336         (std::chrono::steady_clock::now() - fw_random_number_timestamp);
337     if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
338             .count() > std::chrono::duration_cast<std::chrono::microseconds>(
339                            FW_RANDOM_NUMBER_TTL)
340                            .count())
341     {
342         phosphor::logging::log<phosphor::logging::level::INFO>(
343             "Firmware update random number expired.");
344         return ipmi::responseInvalidFieldRequest();
345     }
346 
347     /* Validate random number */
348     for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
349     {
350         if (fw_random_number[i] != randNum[i])
351         {
352             phosphor::logging::log<phosphor::logging::level::INFO>(
353                 "Invalid random number specified.");
354             return ipmi::responseInvalidFieldRequest();
355         }
356     }
357 
358     if (fw_update_status.state() != fw_update_status_cache::FW_STATE_IDLE
359         // TODO: Allowing FW_STATE_INIT here to let image activation available
360         // without being in FW_STATE_IDLE, need to fix/adjust the state machine
361         // to match xyz.openbmc_project.Software.BMC.Updater service activation
362         // mechanism at finer grain
363         && fw_update_status.state() != fw_update_status_cache::FW_STATE_INIT)
364     {
365         phosphor::logging::log<phosphor::logging::level::INFO>(
366             "Already firmware update is in progress.");
367         return ipmi::responseBusy();
368     }
369     // FIXME? c++ doesn't off an option for exclusive file creation
370     FILE *fp = fopen(FIRMWARE_BUFFER_FILE, "wx");
371     if (!fp)
372     {
373         phosphor::logging::log<phosphor::logging::level::INFO>(
374             "Unable to open file.");
375         return ipmi::responseUnspecifiedError();
376     }
377     fclose(fp);
378 
379     return ipmi::responseSuccess();
380 }
381 
382 /** @brief implements exit firmware update mode command
383  *  @param None
384  *
385  *  @returns IPMI completion code
386  */
387 ipmi::RspType<> ipmiFirmwareExitFwUpdateMode()
388 {
389 
390     if (DEBUG)
391     {
392         std::cerr << "Exit FW update mode \n";
393     }
394     switch (fw_update_status.state())
395     {
396         case fw_update_status_cache::FW_STATE_INIT:
397         case fw_update_status_cache::FW_STATE_IDLE:
398             return ipmi::responseInvalidFieldRequest();
399             break;
400         case fw_update_status_cache::FW_STATE_DOWNLOAD:
401         case fw_update_status_cache::FW_STATE_VERIFY:
402             break;
403         case fw_update_status_cache::FW_STATE_WRITE:
404             break;
405         case fw_update_status_cache::FW_STATE_READY:
406         case fw_update_status_cache::FW_STATE_ERROR:
407             break;
408         case fw_update_status_cache::FW_STATE_AC_CYCLE_REQUIRED:
409             return ipmi::responseInvalidFieldRequest();
410             break;
411     }
412     fw_update_status.firmwareUpdateAbortState();
413     return ipmi::responseSuccess();
414 }
415 
416 static void post_transfer_complete_handler(
417     std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher);
418 static bool request_start_firmware_update(const std::string &uri)
419 {
420     if (DEBUG)
421         std::cerr << "request start firmware update()\n";
422 
423     // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
424     // the code gets to this point, the file should be transferred start the
425     // request (creating a new file in /tmp/images causes the update manager to
426     // check if it is ready for activation)
427     static std::unique_ptr<sdbusplus::bus::match::match> fw_update_matcher;
428     post_transfer_complete_handler(fw_update_matcher);
429     std::filesystem::rename(
430         uri, "/tmp/images/" +
431                  boost::uuids::to_string(boost::uuids::random_generator()()));
432     return true;
433 }
434 
435 class transfer_hash_check
436 {
437   public:
438     enum hash_check
439     {
440         CHECK_NOT_REQUESTED = 0,
441         CHECK_REQUESTED,
442         CHECK_PASSED_SHA2,
443         CHECK_RESVD1,
444         CHECK_FAILED_SHA2 = 0xe2,
445         CHECK_RESVD2 = 0xe3,
446     };
447 
448   protected:
449     EVP_MD_CTX *_ctx;
450     std::vector<uint8_t> _expected;
451     enum hash_check _check;
452     bool _started;
453 
454   public:
455     transfer_hash_check() : _check(CHECK_NOT_REQUESTED), _started(false)
456     {
457     }
458     ~transfer_hash_check()
459     {
460         if (_ctx)
461         {
462             EVP_MD_CTX_destroy(_ctx);
463             _ctx = NULL;
464         }
465     }
466     void init(const std::vector<uint8_t> &expected)
467     {
468         _expected = expected;
469         _check = CHECK_REQUESTED;
470         _ctx = EVP_MD_CTX_create();
471         EVP_DigestInit(_ctx, EVP_sha256());
472     }
473     void hash(const std::vector<uint8_t> &data)
474     {
475         if (!_started)
476             _started = true;
477         EVP_DigestUpdate(_ctx, data.data(), data.size());
478     }
479     void clear()
480     {
481         // if not started, nothing to clear
482         if (_started)
483         {
484             if (_ctx)
485                 EVP_MD_CTX_destroy(_ctx);
486             if (_check != CHECK_NOT_REQUESTED)
487                 _check = CHECK_REQUESTED;
488             _ctx = EVP_MD_CTX_create();
489             EVP_DigestInit(_ctx, EVP_sha256());
490         }
491     }
492     enum hash_check check()
493     {
494         if (_check == CHECK_REQUESTED)
495         {
496             unsigned int len;
497             std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
498             EVP_DigestFinal(_ctx, digest.data(), &len);
499             if (digest == _expected)
500             {
501                 if (DEBUG)
502                     std::cerr << "transfer sha2 check passed\n";
503                 _check = CHECK_PASSED_SHA2;
504             }
505             else
506             {
507                 if (DEBUG)
508                     std::cerr << "transfer sha2 check failed\n";
509                 _check = CHECK_FAILED_SHA2;
510             }
511         }
512         return _check;
513     }
514     uint8_t status() const
515     {
516         return static_cast<uint8_t>(_check);
517     }
518 };
519 
520 std::shared_ptr<transfer_hash_check> xfer_hash_check;
521 
522 static void activate_image(const char *obj_path)
523 {
524     // If flag is false  means to reboot
525     if (fw_update_status.getDeferRestart() == false)
526     {
527 
528         if (DEBUG)
529         {
530             std::cerr << "activateImage()...\n";
531             std::cerr << "obj_path = " << obj_path << "\n";
532         }
533         phosphor::logging::log<phosphor::logging::level::INFO>(
534             "activating Image: ",
535             phosphor::logging::entry("OBJPATH =%s", obj_path));
536         std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
537         bus->async_method_call(
538             [](const boost::system::error_code ec) {
539                 if (ec)
540                 {
541                     phosphor::logging::log<phosphor::logging::level::ERR>(
542                         "async_method_call error: activate_image failed");
543                     return;
544                 }
545             },
546             "xyz.openbmc_project.Software.BMC.Updater", obj_path,
547             "org.freedesktop.DBus.Properties", "Set",
548             "xyz.openbmc_project.Software.Activation", "RequestedActivation",
549             std::variant<std::string>("xyz.openbmc_project.Software.Activation."
550                                       "RequestedActivations.Active"));
551     }
552     else
553     {
554         phosphor::logging::log<phosphor::logging::level::INFO>(
555             "Firmware image activation is deferred.");
556     }
557 }
558 
559 static void post_transfer_complete_handler(
560     std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher)
561 {
562     // Setup timer for watching signal
563     static phosphor::Timer timer(
564         [&fw_update_matcher]() { fw_update_matcher = nullptr; });
565 
566     static phosphor::Timer activation_status_timer([]() {
567         if (fw_update_status.activation_timer_timeout() >= 95)
568         {
569             activation_status_timer.stop();
570         }
571     });
572 
573     timer.start(std::chrono::microseconds(5000000), false);
574 
575     // callback function for capturing signal
576     auto callback = [&fw_update_matcher](sdbusplus::message::message &m) {
577         if (DEBUG)
578             std::cerr << "[complete] Match fired\n";
579         bool flag = false;
580 
581         std::vector<std::pair<
582             std::string,
583             std::vector<std::pair<std::string, std::variant<std::string>>>>>
584             interfaces_properties;
585 
586         sdbusplus::message::object_path obj_path;
587 
588         try
589         {
590             m.read(obj_path, interfaces_properties); // Read in the object path
591                                                      // that was just created
592         }
593         catch (std::exception &e)
594         {
595             std::cerr
596                 << "[complete] Failed at post_transfer_complete-handler : "
597                 << e.what() << "\n";
598         }
599         // constructing response message
600         if (DEBUG)
601             std::cerr << "[complete] obj path = " << obj_path.str << "\n";
602         for (auto &interface : interfaces_properties)
603         {
604             if (DEBUG)
605                 std::cerr << "[complete] interface = " << interface.first
606                           << "\n";
607 
608             if (interface.first == "xyz.openbmc_project.Software.Activation")
609             {
610                 // cancel timer only when
611                 // xyz.openbmc_project.Software.Activation interface is
612                 // added
613 
614                 if (DEBUG)
615                     std::cerr << "[complete] Attempt to cancel timer...\n";
616                 try
617                 {
618                     timer.stop();
619                     activation_status_timer.start(
620                         std::chrono::microseconds(3000000), true);
621                 }
622                 catch (std::exception &e)
623                 {
624                     std::cerr << "[complete] cancel timer error: " << e.what()
625                               << "\n";
626                 }
627 
628                 fw_update_status.set_software_obj_path(obj_path.str);
629                 activate_image(obj_path.str.c_str());
630                 if (DEBUG)
631                     std::cerr << "[complete] returned from activeImage()\n";
632 
633                 fw_update_matcher = nullptr;
634             }
635         }
636     };
637 
638     // Adding matcher
639     fw_update_matcher = std::make_unique<sdbusplus::bus::match::match>(
640         *getSdBus(),
641         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
642         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
643         callback);
644 }
645 
646 class MappedFile
647 {
648   public:
649     MappedFile(const std::string &fname) : addr(nullptr), fsize(0)
650     {
651         std::error_code ec;
652         size_t sz = std::filesystem::file_size(fname, ec);
653         int fd = open(fname.c_str(), O_RDONLY);
654         if (!ec || fd < 0)
655         {
656             return;
657         }
658         void *tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
659         close(fd);
660         if (tmp == MAP_FAILED)
661         {
662             return;
663         }
664         addr = tmp;
665         fsize = sz;
666     }
667 
668     ~MappedFile()
669     {
670         if (addr)
671         {
672             munmap(addr, fsize);
673         }
674     }
675     const uint8_t *data() const
676     {
677         return static_cast<const uint8_t *>(addr);
678     }
679     size_t size() const
680     {
681         return fsize;
682     }
683 
684   private:
685     size_t fsize;
686     void *addr;
687 };
688 
689 static int transfer_from_file(const std::string &uri, bool move = true)
690 {
691     std::error_code ec;
692     if (DEBUG)
693         std::cerr << "transfer_from_file(" << uri << ")\n";
694     if (move)
695     {
696         std::filesystem::rename(uri, FIRMWARE_BUFFER_FILE, ec);
697     }
698     else
699     {
700         std::filesystem::copy(uri, FIRMWARE_BUFFER_FILE,
701                               std::filesystem::copy_options::overwrite_existing,
702                               ec);
703     }
704     if (xfer_hash_check)
705     {
706         MappedFile mappedfw(uri);
707         xfer_hash_check->hash(
708             {mappedfw.data(), mappedfw.data() + mappedfw.size()});
709     }
710     if (ec.value())
711     {
712         std::cerr << "cp/mv returns: " << ec.message() << "(" << ec.value()
713                   << ")\n";
714     }
715     return ec.value();
716 }
717 
718 template <typename... ArgTypes>
719 static int executeCmd(const char *path, ArgTypes &&... tArgs)
720 {
721     boost::process::child execProg(path, const_cast<char *>(tArgs)...);
722     execProg.wait();
723     return execProg.exit_code();
724 }
725 
726 constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl";
727 constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt";
728 constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img";
729 constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev";
730 constexpr size_t fwPathMaxLength = 255;
731 static int transfer_from_usb(const std::string &uri)
732 {
733     int ret, sysret;
734     char fwpath[fwPathMaxLength];
735     if (DEBUG)
736         std::cerr << "transfer_from_usb(" << uri << ")\n";
737     ret = executeCmd(USB_CTRL_PATH, "mount", FWUPDATE_USB_VOL_IMG,
738                      FWUPDATE_MOUNT_POINT);
739     if (ret)
740     {
741         return ret;
742     }
743 
744     std::string usb_path = std::string(FWUPDATE_MOUNT_POINT) + "/" + uri;
745     ret = transfer_from_file(usb_path, false);
746 
747     executeCmd(USB_CTRL_PATH, "cleanup", FWUPDATE_USB_VOL_IMG,
748                FWUPDATE_MOUNT_POINT);
749     return ret;
750 }
751 
752 static bool transfer_firmware_from_uri(const std::string &uri)
753 {
754     static constexpr char FW_URI_FILE[] = "file://";
755     static constexpr char FW_URI_USB[] = "usb://";
756     if (DEBUG)
757         std::cerr << "transfer_firmware_from_uri(" << uri << ")\n";
758     if (boost::algorithm::starts_with(uri, FW_URI_FILE))
759     {
760         std::string fname = uri.substr(sizeof(FW_URI_FILE) - 1);
761         if (fname != FIRMWARE_BUFFER_FILE)
762         {
763             return 0 == transfer_from_file(fname);
764         }
765         return true;
766     }
767     if (boost::algorithm::starts_with(uri, FW_URI_USB))
768     {
769         std::string fname = uri.substr(sizeof(FW_URI_USB) - 1);
770         return 0 == transfer_from_usb(fname);
771     }
772     return false;
773 }
774 
775 /* Get USB-mass-storage device status: inserted => true, ejected => false */
776 static int usb_get_status()
777 {
778     static constexpr char usb_gadget_base[] = "/sys/kernel/config/usb_gadget/";
779     auto usb_device =
780         std::filesystem::path(usb_gadget_base) / FWUPDATE_USB_DEV_NAME;
781     std::error_code ec;
782     return std::filesystem::exists(usb_device, ec) && !ec;
783 }
784 
785 /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
786 static int usb_attach_device()
787 {
788     if (usb_get_status())
789     {
790         return 1;
791     }
792     int ret =
793         executeCmd(USB_CTRL_PATH, "setup", FWUPDATE_USB_VOL_IMG,
794                    std::to_string(FIRMWARE_BUFFER_MAX_SIZE / 1_MB).c_str());
795     if (!ret)
796     {
797         ret = executeCmd(USB_CTRL_PATH, "insert", FWUPDATE_USB_DEV_NAME,
798                          FWUPDATE_USB_VOL_IMG);
799     }
800     return ret;
801 }
802 
803 /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
804 static int usb_detach_device()
805 {
806     if (!usb_get_status())
807     {
808         return 1;
809     }
810     return executeCmd(USB_CTRL_PATH, "eject", FWUPDATE_USB_DEV_NAME);
811 }
812 
813 constexpr uint8_t controls_init = 0x00;
814 constexpr uint8_t controls_transfer_started = 0x01;
815 constexpr uint8_t controls_transfer_completed = 0x02;
816 constexpr uint8_t controls_transfer_aborted = 0x04;
817 constexpr uint8_t controls_usb_attached = 0x08;
818 
819 struct fw_update_control_request
820 {
821     enum knob
822     {
823         CTRL_GET = 0,
824         CTRL_XFER_START,
825         CTRL_XFER_COMPLETE,
826         CTRL_XFER_ABORT,
827         CTRL_SET_FILENAME,
828         CTRL_USB_ATTACH,
829         CTRL_USB_DETACH,
830     } __attribute__((packed));
831     enum knob control;
832     uint8_t nlen;
833     char filename[fwPathMaxLength];
834 } __attribute__((packed));
835 
836 static ipmi_ret_t ipmi_firmware_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
837                                         ipmi_request_t request,
838                                         ipmi_response_t response,
839                                         ipmi_data_len_t data_len,
840                                         ipmi_context_t context)
841 {
842     static std::string fw_xfer_uri;
843 
844     if (DEBUG)
845         std::cerr << "FW update control\n";
846     *data_len = 0;
847 
848     static uint8_t controls = controls_init;
849     ipmi_ret_t rc = IPMI_CC_OK;
850     auto ctrl_req = reinterpret_cast<fw_update_control_request *>(request);
851     auto ctrl_resp = reinterpret_cast<uint8_t *>(response);
852 
853     if (usb_get_status())
854     {
855         controls |= controls_usb_attached;
856     }
857     else
858     {
859         controls &= ~controls_usb_attached;
860     }
861 
862     switch (ctrl_req->control)
863     {
864         case fw_update_control_request::CTRL_GET:
865             break;
866         case fw_update_control_request::CTRL_XFER_START:
867         {
868             controls |= controls_transfer_started;
869             // reset buffer to empty (truncate file)
870             std::ofstream out(FIRMWARE_BUFFER_FILE,
871                               std::ofstream::binary | std::ofstream::trunc);
872             fw_xfer_uri = std::string("file://") + FIRMWARE_BUFFER_FILE;
873             if (xfer_hash_check)
874             {
875                 xfer_hash_check->clear();
876             }
877 #ifdef INTEL_PFR_ENABLED
878             imgLength = 0;
879             imgType = 0;
880             block0Mapped = false;
881 #endif
882             if (DEBUG)
883                 std::cerr << "transfer start\n";
884         }
885         break;
886         case fw_update_control_request::CTRL_XFER_COMPLETE:
887         {
888             if (usb_get_status())
889             {
890                 rc = IPMI_CC_REQ_INVALID_PHASE;
891             }
892             // finish transfer based on URI
893             if (!transfer_firmware_from_uri(fw_xfer_uri))
894             {
895                 rc = IPMI_CC_UNSPECIFIED_ERROR;
896                 break;
897             }
898             // transfer complete
899             if (xfer_hash_check)
900             {
901                 if (transfer_hash_check::CHECK_PASSED_SHA2 !=
902                     xfer_hash_check->check())
903                 {
904                     if (DEBUG)
905                         std::cerr << "xfer_hash_check returns not "
906                                      "CHECK_PASSED_SHA2\n";
907                     rc = IPMI_CC_UNSPECIFIED_ERROR;
908                     break;
909                 }
910             }
911             // start the request
912             if (!request_start_firmware_update(FIRMWARE_BUFFER_FILE))
913             {
914                 if (DEBUG)
915                     std::cerr
916                         << "request_start_firmware_update returns failure\n";
917                 rc = IPMI_CC_UNSPECIFIED_ERROR;
918             }
919             if (rc == IPMI_CC_OK)
920             {
921                 controls |= controls_transfer_completed;
922             }
923         }
924         break;
925         case fw_update_control_request::CTRL_XFER_ABORT:
926             if (DEBUG)
927                 std::cerr << "send abort request\n";
928             if (usb_get_status())
929             {
930                 if (0 != usb_detach_device())
931                 {
932                     rc = IPMI_CC_USB_ATTACH_FAIL;
933                 }
934             }
935             fw_update_status.firmwareUpdateAbortState();
936             controls |= controls_transfer_aborted;
937             break;
938         case fw_update_control_request::CTRL_SET_FILENAME:
939             fw_xfer_uri.clear();
940             fw_xfer_uri.insert(0, ctrl_req->filename, ctrl_req->nlen);
941             break;
942         case fw_update_control_request::CTRL_USB_ATTACH:
943             if (usb_get_status())
944             {
945                 rc = IPMI_CC_INVALID_FIELD_REQUEST;
946             }
947             else if (0 != usb_attach_device())
948             {
949                 rc = IPMI_CC_USB_ATTACH_FAIL;
950             }
951             else
952             {
953                 rc = IPMI_CC_OK;
954             }
955             break;
956         case fw_update_control_request::CTRL_USB_DETACH:
957             if (!usb_get_status())
958             {
959                 rc = IPMI_CC_INVALID_FIELD_REQUEST;
960             }
961             if (0 != usb_detach_device())
962             {
963                 rc = IPMI_CC_USB_ATTACH_FAIL;
964             }
965             else
966             {
967                 rc = IPMI_CC_OK;
968             }
969             break;
970         default:
971             if (DEBUG)
972                 std::cerr << "control byte " << std::hex << ctrl_req->control
973                           << " unknown\n";
974             rc = IPMI_CC_INVALID_FIELD_REQUEST;
975             break;
976     }
977 
978     if (rc == IPMI_CC_OK)
979     {
980         *ctrl_resp = controls;
981         *data_len = sizeof(*ctrl_resp);
982     }
983 
984     return rc;
985 }
986 
987 #ifdef INTEL_PFR_ENABLED
988 using fwVersionInfoType = std::tuple<uint8_t,   // ID Tag
989                                      uint8_t,   // Major Version Number
990                                      uint8_t,   // Minor Version Number
991                                      uint32_t,  // Build Number
992                                      uint32_t,  // Build Timestamp
993                                      uint32_t>; // Update Timestamp
994 ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
995 {
996     // Byte 1 - Count (N) Number of devices data is being returned for.
997     // Bytes  2:16 - Device firmare information(fwVersionInfoType)
998     // Bytes - 17:(15xN) - Repeat of 2 through 16
999 
1000     std::vector<fwVersionInfoType> fwVerInfoList;
1001     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
1002     for (const auto &fwDev : fwVersionIdMap)
1003     {
1004         std::string verStr;
1005         try
1006         {
1007             auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
1008 
1009             ipmi::Value result = ipmi::getDbusProperty(
1010                 *busp, service, fwDev.second, versionIntf, "Version");
1011             verStr = std::get<std::string>(result);
1012         }
1013         catch (const std::exception &e)
1014         {
1015             phosphor::logging::log<phosphor::logging::level::INFO>(
1016                 "Failed to fetch Version property",
1017                 phosphor::logging::entry("ERROR=%s", e.what()),
1018                 phosphor::logging::entry("PATH=%s", fwDev.second),
1019                 phosphor::logging::entry("INTERFACE=%s", versionIntf));
1020             continue;
1021         }
1022 
1023         if (verStr.empty())
1024         {
1025             phosphor::logging::log<phosphor::logging::level::INFO>(
1026                 "Version is empty.",
1027                 phosphor::logging::entry("PATH=%s", fwDev.second),
1028                 phosphor::logging::entry("INTERFACE=%s", versionIntf));
1029             continue;
1030         }
1031 
1032         // BMC Version format: <major>.<minor>-<build bum>-<build hash>
1033         std::vector<std::string> splitVer;
1034         boost::split(splitVer, verStr, boost::is_any_of(".-"));
1035         if (splitVer.size() < 3)
1036         {
1037             phosphor::logging::log<phosphor::logging::level::INFO>(
1038                 "Invalid Version format.",
1039                 phosphor::logging::entry("Version=%s", verStr.c_str()),
1040                 phosphor::logging::entry("PATH=%s", fwDev.second));
1041             continue;
1042         }
1043 
1044         uint8_t majorNum = 0;
1045         uint8_t minorNum = 0;
1046         uint32_t buildNum = 0;
1047         try
1048         {
1049             majorNum = std::stoul(splitVer[0], nullptr, 16);
1050             minorNum = std::stoul(splitVer[1], nullptr, 16);
1051             buildNum = std::stoul(splitVer[2], nullptr, 16);
1052         }
1053         catch (const std::exception &e)
1054         {
1055             phosphor::logging::log<phosphor::logging::level::INFO>(
1056                 "Failed to convert stoul.",
1057                 phosphor::logging::entry("ERROR=%s", e.what()));
1058             continue;
1059         }
1060 
1061         // Build Timestamp - Not supported.
1062         // Update Timestamp - TODO: Need to check with CPLD team.
1063         fwVerInfoList.emplace_back(
1064             fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
1065                               minorNum, buildNum, 0, 0));
1066     }
1067 
1068     return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
1069 }
1070 #endif
1071 
1072 struct fw_security_revision_info
1073 {
1074     uint8_t id_tag;
1075     uint16_t sec_rev;
1076 } __attribute__((packed));
1077 
1078 static ipmi_ret_t ipmi_firmware_get_fw_security_revision(
1079     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1080     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1081 {
1082     if (DEBUG)
1083         std::cerr << "Get FW security revision info\n";
1084 
1085     // Byte 1 - Count (N) Number of devices data is being returned for.
1086     // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image
1087     //                 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR
1088     //                 Image
1089     // Byte 3 - Major Version Number
1090     // Byte 4 - Minor Version Number
1091     // Bytes 5:8 - Build Number
1092     // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL
1093     // timestamp
1094     // Bytes 13:16 - Update Timestamp
1095     // Bytes - 17:(15xN) - Repeat of 2 through 16
1096 
1097     uint8_t count = 0;
1098     auto ret_count = reinterpret_cast<uint8_t *>(response);
1099     auto info =
1100         reinterpret_cast<struct fw_security_revision_info *>(ret_count + 1);
1101 
1102     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1103     for (uint8_t id_tag = 1; id_tag < 6; id_tag++)
1104     {
1105         const char *fw_path;
1106         switch (id_tag)
1107         {
1108             case 1:
1109                 fw_path = FW_UPDATE_ACTIVE_INFO_PATH;
1110                 break;
1111             case 2:
1112                 fw_path = FW_UPDATE_BACKUP_INFO_PATH;
1113                 break;
1114             case 3:
1115             case 4:
1116             case 5:
1117                 continue; // skip for now
1118                 break;
1119         }
1120         auto method =
1121             bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
1122                                  "org.freedesktop.DBus.Properties", "GetAll");
1123         method.append(FW_UPDATE_INFO_INTERFACE, "security_version");
1124         ipmi::DbusVariant sec_rev;
1125         try
1126         {
1127             auto reply = bus->call(method);
1128 
1129             if (reply.is_method_error())
1130                 continue;
1131 
1132             reply.read(sec_rev);
1133         }
1134         catch (sdbusplus::exception::SdBusError &e)
1135         {
1136             std::cerr << "SDBus Error: " << e.what();
1137             return IPMI_CC_UNSPECIFIED_ERROR;
1138         }
1139 
1140         info->id_tag = id_tag;
1141         info->sec_rev = std::get<int>(sec_rev);
1142         count++;
1143         info++;
1144     }
1145     *ret_count = count;
1146 
1147     // Status code.
1148     ipmi_ret_t rc = IPMI_CC_OK;
1149     *data_len = sizeof(count) + count * sizeof(*info);
1150 
1151     return rc;
1152 }
1153 
1154 struct fw_channel_size
1155 {
1156     uint8_t channel_id;
1157     uint32_t channel_size;
1158 } __attribute__((packed));
1159 
1160 enum
1161 {
1162     CHANNEL_RESVD = 0,
1163     CHANNEL_KCS,
1164     CHANNEL_RMCP_PLUS,
1165     CHANNEL_USB_DATA,
1166     CHANNEL_USB_MASS_STORAGE,
1167 } channel_transfer_type;
1168 
1169 static constexpr uint8_t channelListSize = 2;
1170 /** @brief implements Maximum Firmware Transfer size command
1171  *  @parameter
1172  *   -  none
1173  *  @returns IPMI completion code plus response data
1174  *   - count - channel count
1175  *   - channelList - channel list information
1176  */
1177 ipmi::RspType<uint8_t, // channel count
1178               std::array<std::tuple<uint8_t, uint32_t>,
1179                          channelListSize> // channel
1180                                           // list
1181               >
1182     ipmiFirmwareMaxTransferSize()
1183 {
1184     constexpr uint8_t KCSMaxBufSize = 128;
1185     constexpr uint32_t RMCPPLUSMaxBufSize = 50 * 1024;
1186     if (DEBUG)
1187         std::cerr << "Get FW max transfer size\n";
1188     // Byte 1 - Count (N) Number of devices data is being returned for.
1189     // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+,
1190     //                 03 – usb data, 04 – usb mass storage
1191     // Byte 3-6 - transfer size (little endian)
1192     // Bytes - 7:(5xN) - Repeat of 2 through 6
1193     constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1194         channelList = {{{CHANNEL_KCS, KCSMaxBufSize},
1195                         {CHANNEL_RMCP_PLUS, RMCPPLUSMaxBufSize}}};
1196     return ipmi::responseSuccess(channelListSize, channelList);
1197 }
1198 
1199 enum
1200 {
1201     EXEC_CTX_RESVD = 0,
1202     EXEC_CTX_FULL_LINUX = 0x10,
1203     EXEC_CTX_SAFE_MODE_LINUX = 0x11,
1204 } bmc_execution_context;
1205 
1206 struct fw_execution_context
1207 {
1208     uint8_t context;
1209     uint8_t image_selection;
1210 } __attribute__((packed));
1211 
1212 static ipmi_ret_t ipmi_firmware_get_fw_execution_context(
1213     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1214     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1215 {
1216     if (DEBUG)
1217         std::cerr << "Get FW execution context\n";
1218 
1219     // Byte 1 - execution context
1220     //          0x10 - full linux stack, 0x11 - safe-mode linux stack
1221     // Byte 2 - current image selection
1222     //          1 - primary, 2 - secondary
1223 
1224     auto info = reinterpret_cast<struct fw_execution_context *>(response);
1225     info->context = EXEC_CTX_FULL_LINUX;
1226 
1227     info->image_selection = getActiveBootImage();
1228 
1229     // Status code.
1230     ipmi_ret_t rc = IPMI_CC_OK;
1231     *data_len = sizeof(*info);
1232 
1233     return rc;
1234 }
1235 
1236 uint8_t getActiveBootImage(void)
1237 {
1238     // 0x01 -  primaryImage
1239     constexpr uint8_t primaryImage = 0x01;
1240     // 0x02 -  secondaryImage
1241     constexpr uint8_t secondaryImage = 0x02;
1242     uint8_t bootImage = primaryImage;
1243 
1244     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1245     auto method = bus->new_method_call(
1246         "xyz.openbmc_project.U_Boot.Environment.Manager",
1247         "/xyz/openbmc_project/u_boot/environment/mgr",
1248         "xyz.openbmc_project.U_Boot.Environment.Manager", "Read");
1249     method.append("bootcmd");
1250     std::string value;
1251     try
1252     {
1253         auto reply = bus->call(method);
1254         reply.read(value);
1255     }
1256     catch (sdbusplus::exception::SdBusError &e)
1257     {
1258         std::cerr << "SDBus Error: " << e.what();
1259         return IPMI_CC_UNSPECIFIED_ERROR;
1260     }
1261     /* cheking for secondary FitImage Address 22480000  */
1262     if (value.find(secondaryFitImageStartAddr) != std::string::npos)
1263     {
1264         bootImage = secondaryImage;
1265     }
1266     else
1267     {
1268         bootImage = primaryImage;
1269     }
1270 
1271     return bootImage;
1272 }
1273 /** @brief implements firmware get status command
1274  *  @parameter
1275  *   -  none
1276  *  @returns IPMI completion code plus response data
1277  *   - status     -  processing status
1278  *   - percentage -  percentage completion
1279  *   - check      -  channel integrity check status
1280  **/
1281 ipmi::RspType<uint8_t, // status
1282               uint8_t, // percentage
1283               uint8_t  // check
1284               >
1285     ipmiFrmwareGetStatus()
1286 
1287 {
1288     if (DEBUG)
1289         std::cerr << "Get FW update status\n";
1290     // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1291     //                  5=ready, f=error, 83=ac cycle required)
1292     // Byte 2 - percent
1293     // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1294     uint8_t status = fw_update_status.state();
1295     uint8_t percent = fw_update_status.percent();
1296     uint8_t check = xfer_hash_check ? xfer_hash_check->status() : 0;
1297 
1298     // Status code.
1299     return ipmi::responseSuccess(status, percent, check);
1300 }
1301 
1302 static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0);
1303 static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1);
1304 static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2);
1305 static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3);
1306 struct fw_update_options_request
1307 {
1308     uint8_t mask;
1309     uint8_t options;
1310 } __attribute__((packed));
1311 
1312 uint32_t fw_update_options = 0;
1313 static ipmi_ret_t ipmi_firmware_update_options(
1314     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1315     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1316 {
1317     if (DEBUG)
1318         std::cerr << "Get/set FW update options\n";
1319 
1320     // request:
1321     // Byte 1 - mask
1322     // Byte 2 - options
1323     // Byte 3-34 - optional integrity check expected value
1324     // response:
1325     // Byte 1 - set options
1326 
1327     auto fw_options =
1328         reinterpret_cast<struct fw_update_options_request *>(request);
1329 
1330     const char *path = FW_UPDATE_SERVER_INFO_PATH;
1331     const char *iface = FW_UPDATE_SECURITY_INTERFACE;
1332     if ((fw_options->mask & FW_UPDATE_OPTIONS_NO_DOWNREV) &&
1333         (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) !=
1334             (fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV))
1335     {
1336         if (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV)
1337         {
1338             fw_update_options |= FW_UPDATE_OPTIONS_NO_DOWNREV;
1339             /*setting flag to flase for deferring downgrade support*/
1340             fw_update_status.setInhibitDowngrade(true);
1341         }
1342         else
1343         {
1344             fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV;
1345             /*setting flag to true for downgrade support*/
1346             fw_update_status.setInhibitDowngrade(false);
1347         }
1348     }
1349     if ((fw_options->mask & FW_UPDATE_OPTIONS_DEFER_RESTART) &&
1350         (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) !=
1351             (fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART))
1352     {
1353         if (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART)
1354         {
1355             fw_update_options |= FW_UPDATE_OPTIONS_DEFER_RESTART;
1356             /* setting flag to true to stop image activation */
1357             fw_update_status.setDeferRestart(true);
1358         }
1359         else
1360         {
1361             /* setting flag to false for image activation */
1362             fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART;
1363             fw_update_status.setDeferRestart(false);
1364         }
1365     }
1366     if (fw_options->mask & FW_UPDATE_OPTIONS_SHA2_CHECK)
1367     {
1368         auto hash_size = EVP_MD_size(EVP_sha256());
1369         if (fw_options->options & FW_UPDATE_OPTIONS_SHA2_CHECK)
1370         {
1371             if (*data_len != (sizeof(*fw_options) + hash_size))
1372             {
1373                 *data_len = 0;
1374                 return IPMI_CC_REQ_DATA_LEN_INVALID;
1375             }
1376             xfer_hash_check = std::make_shared<transfer_hash_check>();
1377             auto exp_hash = reinterpret_cast<uint8_t *>(fw_options + 1);
1378             xfer_hash_check->init({exp_hash, exp_hash + hash_size});
1379             fw_update_options |= FW_UPDATE_OPTIONS_SHA2_CHECK;
1380         }
1381         else
1382         {
1383             fw_update_options &= ~FW_UPDATE_OPTIONS_SHA2_CHECK;
1384             // delete the xfer_hash_check object
1385             xfer_hash_check.reset();
1386         }
1387     }
1388     auto options_rsp = reinterpret_cast<uint8_t *>(response);
1389     *options_rsp = fw_update_options;
1390 
1391     if (DEBUG)
1392         std::cerr << "current fw_update_options = " << std::hex
1393                   << fw_update_options << '\n';
1394     // Status code.
1395     *data_len = sizeof(*options_rsp);
1396     return IPMI_CC_OK;
1397 }
1398 
1399 struct fw_cert_info
1400 {
1401     uint16_t cert_len;
1402     uint64_t serial;
1403     uint8_t subject_len;
1404     char subject[255];
1405 } __attribute__((packed));
1406 
1407 static ipmi_ret_t ipmi_firmware_get_root_cert_info(
1408     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1409     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1410 {
1411     if (DEBUG)
1412         std::cerr << "Get FW root cert info\n";
1413 
1414     // request:
1415     // Byte 1 - certificate ID: request which certificate (ignored)
1416 
1417     // response:
1418     // Byte 1-2  - certificate length (little endian)
1419     // Byte 3-10 - serial number (little endian)
1420     // Byte 11   - subject length
1421     // Byte 12-N - subject data
1422 
1423     auto cert_info = reinterpret_cast<struct fw_cert_info *>(response);
1424     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1425     auto method = bus->new_method_call(
1426         FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH,
1427         "org.freedesktop.DBus.Properties", "GetAll");
1428     method.append(FW_UPDATE_SECURITY_INTERFACE);
1429     std::string subject;
1430     uint64_t serial;
1431     std::string cert;
1432     try
1433     {
1434         auto reply = bus->call(method);
1435 
1436         std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
1437         reply.read(properties);
1438 
1439         for (const auto &t : properties)
1440         {
1441             auto key = t.first;
1442             auto value = t.second;
1443             if (key == "certificate_subject")
1444             {
1445                 subject = std::get<std::string>(value);
1446             }
1447             else if (key == "cetificate_serial")
1448             {
1449                 serial = std::get<uint64_t>(value);
1450             }
1451             else if (key == "certificate")
1452             {
1453                 cert = std::get<std::string>(value);
1454             }
1455         }
1456     }
1457     catch (sdbusplus::exception::SdBusError &e)
1458     {
1459         std::cerr << "SDBus Error: " << e.what();
1460         return IPMI_CC_UNSPECIFIED_ERROR;
1461     }
1462 
1463     cert_info->cert_len = cert.size();
1464     cert_info->serial = serial;
1465     // truncate subject so it fits in the 255-byte array (if necessary)
1466     if (subject.size() > sizeof(cert_info->subject))
1467         subject.resize(sizeof(cert_info->subject));
1468     cert_info->subject_len = subject.size();
1469     std::copy(subject.begin(), subject.end(), cert_info->subject);
1470 
1471     // Status code.
1472     ipmi_ret_t rc = IPMI_CC_OK;
1473     // make sure to account for the *actual* size of the subject
1474     *data_len = sizeof(*cert_info) - sizeof(cert_info->subject) +
1475                 cert_info->subject_len;
1476 
1477     return rc;
1478 }
1479 
1480 #ifdef INTEL_PFR_ENABLED
1481 enum class FwGetRootCertDataTag : uint8_t
1482 {
1483     activeRootKey = 1,
1484     recoveryRootKey,
1485     activeCSK,
1486     recoveryCSK,
1487 };
1488 
1489 static constexpr char *bmcActivePfmMTDDev = "/dev/mtd/pfm";
1490 static constexpr char *bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
1491 static constexpr size_t pfmBaseOffsetInImage = 0x400;
1492 static constexpr size_t rootkeyOffsetInPfm = 0xA0;
1493 static constexpr size_t cskKeyOffsetInPfm = 0x124;
1494 static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
1495 static constexpr size_t certKeyLen = 96;
1496 static constexpr size_t cskSignatureLen = 96;
1497 
1498 ipmi::RspType<std::array<uint8_t, certKeyLen>,
1499               std::optional<std::array<uint8_t, cskSignatureLen>>>
1500     ipmiGetFwRootCertData(uint8_t certId)
1501 {
1502     size_t certKeyOffset = 0;
1503     size_t cskSigOffset = 0;
1504     std::string mtdDev;
1505 
1506     switch (static_cast<FwGetRootCertDataTag>(certId))
1507     {
1508         case FwGetRootCertDataTag::activeRootKey:
1509         {
1510             mtdDev = bmcActivePfmMTDDev;
1511             certKeyOffset = rootkeyOffsetInPfm;
1512             break;
1513         }
1514         case FwGetRootCertDataTag::recoveryRootKey:
1515         {
1516             mtdDev = bmcRecoveryImgMTDDev;
1517             certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1518             break;
1519         }
1520         case FwGetRootCertDataTag::activeCSK:
1521         {
1522             mtdDev = bmcActivePfmMTDDev;
1523             certKeyOffset = cskKeyOffsetInPfm;
1524             cskSigOffset = cskSignatureOffsetInPfm;
1525             break;
1526         }
1527         case FwGetRootCertDataTag::recoveryCSK:
1528         {
1529             mtdDev = bmcRecoveryImgMTDDev;
1530             certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1531             cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1532             break;
1533         }
1534         default:
1535         {
1536             return ipmi::responseInvalidFieldRequest();
1537         }
1538     }
1539 
1540     std::array<uint8_t, certKeyLen> certKey = {0};
1541 
1542     try
1543     {
1544         SPIDev spiDev(mtdDev);
1545         spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1546 
1547         if (cskSigOffset)
1548         {
1549             std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1550             spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1551                                cskSignature.data());
1552             return ipmi::responseSuccess(certKey, cskSignature);
1553         }
1554     }
1555     catch (const std::exception &e)
1556     {
1557         phosphor::logging::log<phosphor::logging::level::ERR>(
1558             "Exception caught in ipmiGetFwRootCertData",
1559             phosphor::logging::entry("MSG=%s", e.what()));
1560         return ipmi::responseUnspecifiedError();
1561     }
1562 
1563     return ipmi::responseSuccess(certKey, std::nullopt);
1564 }
1565 #endif
1566 
1567 ipmi::RspType<uint32_t>
1568     ipmiFwImageWriteData(const std::vector<uint8_t> &writeData)
1569 {
1570     const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1571     size_t writeDataLen = writeData.size();
1572 
1573     if (!writeDataLen)
1574     {
1575         return ipmi::responseReqDataLenInvalid();
1576     }
1577 
1578     if (fw_update_status.state() != fw_update_status_cache::FW_STATE_DOWNLOAD)
1579     {
1580         phosphor::logging::log<phosphor::logging::level::DEBUG>(
1581             "Invalid firmware update state.");
1582         return ipmi::response(ccCmdNotSupportedInPresentState);
1583     }
1584 
1585     std::ofstream out(FIRMWARE_BUFFER_FILE,
1586                       std::ofstream::binary | std::ofstream::app);
1587     if (!out)
1588     {
1589         phosphor::logging::log<phosphor::logging::level::DEBUG>(
1590             "Error while opening file.");
1591         return ipmi::responseUnspecifiedError();
1592     }
1593 
1594     uint64_t fileDataLen = out.tellp();
1595 
1596     if ((fileDataLen + writeDataLen) > FIRMWARE_BUFFER_MAX_SIZE)
1597     {
1598         phosphor::logging::log<phosphor::logging::level::DEBUG>(
1599             "Firmware image size exceeds the limit");
1600         return ipmi::responseInvalidFieldRequest();
1601     }
1602 
1603     const char *data = reinterpret_cast<const char *>(writeData.data());
1604     out.write(data, writeDataLen);
1605     out.close();
1606 
1607     if (xfer_hash_check)
1608     {
1609         xfer_hash_check->hash(writeData);
1610     }
1611 
1612 #ifdef INTEL_PFR_ENABLED
1613     /* PFR image block 0 - As defined in HAS */
1614     struct PFRImageBlock0
1615     {
1616         uint32_t tag;
1617         uint32_t pcLength;
1618         uint32_t pcType;
1619         uint32_t reserved1;
1620         uint8_t hash256[32];
1621         uint8_t hash384[48];
1622         uint8_t reserved2[32];
1623     } __attribute__((packed));
1624 
1625     /* Get the PFR block 0 data and read the uploaded image
1626      * information( Image type, length etc) */
1627     if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1628         (!block0Mapped))
1629     {
1630         struct PFRImageBlock0 block0Data = {0};
1631 
1632         std::ifstream inFile(FIRMWARE_BUFFER_FILE,
1633                              std::ios::binary | std::ios::in);
1634         inFile.read(reinterpret_cast<char *>(&block0Data), sizeof(block0Data));
1635         inFile.close();
1636 
1637         uint32_t magicNum = block0Data.tag;
1638 
1639         /* Validate the magic number */
1640         if (magicNum != perBlock0MagicNum)
1641         {
1642             phosphor::logging::log<phosphor::logging::level::DEBUG>(
1643                 "PFR image magic number not matched");
1644             return ipmi::responseInvalidFieldRequest();
1645         }
1646         // Note:imgLength, imgType and block0Mapped are in global scope, as
1647         // these are used in cascaded updates.
1648         imgLength = block0Data.pcLength;
1649         imgType = block0Data.pcType;
1650         block0Mapped = true;
1651     }
1652 #endif // end of INTEL_PFR_ENABLED
1653     return ipmi::responseSuccess(writeDataLen);
1654 }
1655 
1656 struct intc_app_get_buffer_size_resp
1657 {
1658     uint8_t kcs_size;
1659     uint8_t ipmb_size;
1660 } __attribute__((packed));
1661 
1662 static constexpr int KCS_MAX_BUFFER_SIZE = 63;
1663 static constexpr int IPMB_MAX_BUFFER_SIZE = 128;
1664 static ipmi_ret_t ipmi_intel_app_get_buffer_size(
1665     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1666     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1667 {
1668     auto msg_reply =
1669         reinterpret_cast<intc_app_get_buffer_size_resp *>(response);
1670     // for now this is hard coded; really this number is dependent on
1671     // the BMC kcs driver as well as the host kcs driver....
1672     // we can't know the latter.
1673     msg_reply->kcs_size = KCS_MAX_BUFFER_SIZE / 4;
1674     msg_reply->ipmb_size = IPMB_MAX_BUFFER_SIZE / 4;
1675     *data_len = sizeof(*msg_reply);
1676 
1677     return IPMI_CC_OK;
1678 }
1679 
1680 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20;
1681 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21;
1682 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22;
1683 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23;
1684 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24;
1685 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26;
1686 static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27;
1687 static constexpr ipmi_cmd_t cmdFirmwareExitFirmwareUpdateMode = 0x28;
1688 static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29;
1689 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a;
1690 static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b;
1691 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d;
1692 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0;
1693 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0;
1694 
1695 static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30;
1696 static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66;
1697 
1698 static void register_netfn_firmware_functions()
1699 {
1700     // guarantee that we start with an already timed out timestamp
1701     fw_random_number_timestamp =
1702         std::chrono::steady_clock::now() - FW_RANDOM_NUMBER_TTL;
1703 
1704     unlink(FIRMWARE_BUFFER_FILE);
1705 
1706     // <Get BT Interface Capabilities>
1707     if (DEBUG)
1708         std::cerr << "Registering firmware update commands\n";
1709 
1710 #ifdef INTEL_PFR_ENABLED
1711     // Following commands are supported only for PFR enabled platforms
1712     // CMD:0x20 - Get Firmware Version Information
1713 
1714     // get firmware version information
1715     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1716                           ipmi::firmware::cmdGetFwVersionInfo,
1717                           ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
1718 #endif
1719     // get firmware security version information
1720     ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO,
1721                            NULL, ipmi_firmware_get_fw_security_revision,
1722                            PRIVILEGE_ADMIN);
1723 
1724     // get channel information (max transfer sizes)
1725     ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1726                           IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO,
1727                           ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
1728 
1729     // get bmc execution context
1730     ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_BMC_EXEC_CTX, NULL,
1731                            ipmi_firmware_get_fw_execution_context,
1732                            PRIVILEGE_ADMIN);
1733 
1734     // get root certificate information
1735     ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO,
1736                            NULL, ipmi_firmware_get_root_cert_info,
1737                            PRIVILEGE_ADMIN);
1738 #ifdef INTEL_PFR_ENABLED
1739     // get root certificate data
1740     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1741                           ipmi::firmware::cmdFwGetRootCertData,
1742                           ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1743 #endif
1744 
1745     // generate bmc fw update random number (for enter fw tranfer mode)
1746     ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM,
1747                            NULL, ipmi_firmware_get_fw_random_number,
1748                            PRIVILEGE_ADMIN);
1749 
1750     // Set Firmware Update Mode(0x27)
1751     ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1752                           IPMI_CMD_FW_SET_FW_UPDATE_MODE,
1753                           ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
1754 
1755     // exit firmware update mode
1756     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1757                           cmdFirmwareExitFirmwareUpdateMode,
1758                           ipmi::Privilege::Admin, ipmiFirmwareExitFwUpdateMode);
1759 
1760     // firmware control mechanism (set filename, usb, etc.)
1761     ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_UPDATE_CONTROL, NULL,
1762                            ipmi_firmware_control, PRIVILEGE_ADMIN);
1763 
1764     // get firmware update status
1765     ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1766                           IPMI_CMD_FW_GET_STATUS, ipmi::Privilege::Admin,
1767                           ipmiFrmwareGetStatus);
1768     // set firmware update options (no downgrade, etc.)
1769     ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS,
1770                            NULL, ipmi_firmware_update_options, PRIVILEGE_ADMIN);
1771 
1772     // write image data
1773     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1774                           ipmi::firmware::cmdFwImageWriteData,
1775                           ipmi::Privilege::Admin, ipmiFwImageWriteData);
1776 
1777     // get buffer size is used by fw update (exclusively?)
1778     ipmi_register_callback(NETFUN_INTC_APP, IPMI_CMD_INTC_GET_BUFFER_SIZE, NULL,
1779                            ipmi_intel_app_get_buffer_size, PRIVILEGE_USER);
1780     return;
1781 }
1782