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