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