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