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