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