blob: 99402e885cbdbb4268e2b72ef2ee67418d279820 [file] [log] [blame]
Vernon Mauery52ce6622019-05-22 09:19:46 -07001#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>
anil kumar appana6c7d9382019-05-31 14:33:13 +000020#include <ipmid/api.hpp>
Vernon Mauery52ce6622019-05-22 09:19:46 -070021#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
James Feistbeaa2eb2019-08-22 10:59:24 -070029static constexpr const char *secondaryFitImageStartAddr = "22480000";
anil kumar appana31f88872019-08-02 15:16:27 +000030static uint8_t getActiveBootImage(void);
Vernon Mauery52ce6622019-05-22 09:19:46 -070031static void register_netfn_firmware_functions() __attribute__((constructor));
32
33// oem return code for firmware update control
34constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5;
35constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80;
36
Vernon Mauery52ce6622019-05-22 09:19:46 -070037static constexpr bool DEBUG = false;
38
39static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] =
40 "xyz.openbmc_project.fwupdate1.server";
41
42static constexpr char FW_UPDATE_SERVER_PATH[] =
43 "/xyz/openbmc_project/fwupdate1";
44static constexpr char FW_UPDATE_SERVER_INFO_PATH[] =
45 "/xyz/openbmc_project/fwupdate1/info";
46static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] =
47 "/xyz/openbmc_project/fwupdate1/info/bmc_active";
48static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] =
49 "/xyz/openbmc_project/fwupdate1/info/bmc_backup";
50
51static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1";
52static constexpr char FW_UPDATE_INFO_INTERFACE[] =
53 "xyz.openbmc_project.fwupdate1.fwinfo";
54static constexpr char FW_UPDATE_SECURITY_INTERFACE[] =
55 "xyz.openbmc_project.fwupdate1.security";
56
57constexpr std::size_t operator""_MB(unsigned long long v)
58{
59 return 1024u * 1024u * v;
60}
61static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB;
62
63static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin";
64static 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
72class 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 };
Vernon Mauery52ce6622019-05-22 09:19:46 -070086 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 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700106 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>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700116 *_bus,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700117 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;
anil kumar appana31f88872019-08-02 15:16:27 +0000134 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";
Vernon Mauery52ce6622019-05-22 09:19:46 -0700140 return _percent;
141 }
anil kumar appana31f88872019-08-02 15:16:27 +0000142 /* 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
Vernon Mauery52ce6622019-05-22 09:19:46 -0700166 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 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700178 auto state = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700179 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 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700205 _percent = std::get<int32_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700206 if (DEBUG)
207 std::cerr << ", pct=" << (int)_percent;
208 }
209 else if (key == "msg")
210 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700211 _msg = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700212 if (DEBUG)
213 std::cerr << ", msg='" << _msg << '\'';
214 }
215 else if (key == "Progress")
216 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700217 _percent = std::get<uint8_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700218 ;
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 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700232
Vernon Mauery15419dd2019-05-24 09:40:30 -0700233 std::shared_ptr<sdbusplus::asio::connection> _bus;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700234 std::shared_ptr<sdbusplus::bus::match::match> _match;
235 uint8_t _state = 0;
236 uint8_t _percent = 0;
anil kumar appana31f88872019-08-02 15:16:27 +0000237 bool _deferRestart = false;
238 bool _inhibitDowngrade = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700239 std::string _msg;
240
241 private:
242 std::string _software_obj_path;
243};
244
245static fw_update_status_cache fw_update_status;
246
247static std::chrono::steady_clock::time_point fw_random_number_timestamp;
248static constexpr int FW_RANDOM_NUMBER_LENGTH = 8;
249static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30);
250static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH];
251
252static 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
284static 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
353static 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:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700369 case fw_update_status_cache::FW_STATE_VERIFY:
370 break;
371 case fw_update_status_cache::FW_STATE_WRITE:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700372 break;
373 case fw_update_status_cache::FW_STATE_READY:
374 case fw_update_status_cache::FW_STATE_ERROR:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700375 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 {
anil kumar appana31f88872019-08-02 15:16:27 +0000382 fw_update_status.firmwareUpdateAbortState();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700383 }
384
385 return rc;
386}
387
388static void post_transfer_complete_handler(
389 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher);
390static bool request_start_firmware_update(const std::string &uri)
391{
392 if (DEBUG)
393 std::cerr << "request start firmware update()\n";
394
Vernon Mauery52ce6622019-05-22 09:19:46 -0700395 // 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()()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700404 return true;
405}
406
407class 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
492std::shared_ptr<transfer_hash_check> xfer_hash_check;
493
Vernon Mauery52ce6622019-05-22 09:19:46 -0700494static void activate_image(const char *obj_path)
495{
anil kumar appana31f88872019-08-02 15:16:27 +0000496 // If flag is false means to reboot
497 if (fw_update_status.getDeferRestart() == false)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700498 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700499
anil kumar appana31f88872019-08-02 15:16:27 +0000500 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"));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700523 }
anil kumar appana31f88872019-08-02 15:16:27 +0000524 else
Vernon Mauery52ce6622019-05-22 09:19:46 -0700525 {
anil kumar appana31f88872019-08-02 15:16:27 +0000526 phosphor::logging::log<phosphor::logging::level::INFO>(
527 "Firmware image activation is deferred.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700528 }
529}
530
531static 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,
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700555 std::vector<std::pair<std::string, std::variant<std::string>>>>>
Vernon Mauery52ce6622019-05-22 09:19:46 -0700556 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>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700612 *getSdBus(),
Vernon Mauery52ce6622019-05-22 09:19:46 -0700613 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
614 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
615 callback);
616}
Vernon Mauery52ce6622019-05-22 09:19:46 -0700617
618class 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
661static 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
690template <typename... ArgTypes>
691static 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
698constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl";
699constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt";
700constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img";
701constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev";
702constexpr size_t fwPathMaxLength = 255;
703static 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
724static 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 */
748static 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 */
758static 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 */
776static 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
785constexpr uint8_t controls_init = 0x00;
786constexpr uint8_t controls_transfer_started = 0x01;
787constexpr uint8_t controls_transfer_completed = 0x02;
788constexpr uint8_t controls_transfer_aborted = 0x04;
789constexpr uint8_t controls_usb_attached = 0x08;
790
791struct 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
808static 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 }
anil kumar appana31f88872019-08-02 15:16:27 +0000902 fw_update_status.firmwareUpdateAbortState();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700903 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
954struct 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
964static 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 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001004 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001005 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001006 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001007 "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 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001012 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001013
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 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001035 auto strver = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001036 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 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001049 build_time = std::get<int32_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001050 }
1051 else if (key == "update_time")
1052 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001053 update_time = std::get<int32_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001054 }
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
1075struct fw_security_revision_info
1076{
1077 uint8_t id_tag;
1078 uint16_t sec_rev;
1079} __attribute__((packed));
1080
1081static 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
Vernon Mauery15419dd2019-05-24 09:40:30 -07001105 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001106 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 =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001124 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001125 "org.freedesktop.DBus.Properties", "GetAll");
1126 method.append(FW_UPDATE_INFO_INTERFACE, "security_version");
1127 ipmi::DbusVariant sec_rev;
1128 try
1129 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001130 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001131
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;
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001144 info->sec_rev = std::get<int>(sec_rev);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001145 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
1157struct fw_channel_size
1158{
1159 uint8_t channel_id;
1160 uint32_t channel_size;
1161} __attribute__((packed));
1162
1163enum
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
anil kumar appana159547c2019-05-31 16:08:34 +00001172static 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 */
1180ipmi::RspType<uint8_t, // channel count
1181 std::array<std::tuple<uint8_t, uint32_t>,
1182 channelListSize> // channel
1183 // list
1184 >
1185 ipmiFirmwareMaxTransferSize()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001186{
anil kumar appana159547c2019-05-31 16:08:34 +00001187 constexpr uint8_t KCSMaxBufSize = 128;
1188 constexpr uint32_t RMCPPLUSMaxBufSize = 50 * 1024;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001189 if (DEBUG)
1190 std::cerr << "Get FW max transfer size\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -07001191 // 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
anil kumar appana159547c2019-05-31 16:08:34 +00001196 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);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001200}
1201
1202enum
1203{
1204 EXEC_CTX_RESVD = 0,
1205 EXEC_CTX_FULL_LINUX = 0x10,
1206 EXEC_CTX_SAFE_MODE_LINUX = 0x11,
1207} bmc_execution_context;
1208
1209struct fw_execution_context
1210{
1211 uint8_t context;
1212 uint8_t image_selection;
1213} __attribute__((packed));
1214
1215static 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);
anil kumar appana31f88872019-08-02 15:16:27 +00001228 info->context = EXEC_CTX_FULL_LINUX;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001229
anil kumar appana31f88872019-08-02 15:16:27 +00001230 info->image_selection = getActiveBootImage();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001231
1232 // Status code.
1233 ipmi_ret_t rc = IPMI_CC_OK;
1234 *data_len = sizeof(*info);
1235
1236 return rc;
1237}
1238
anil kumar appana31f88872019-08-02 15:16:27 +00001239uint8_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}
anil kumar appana6c7d9382019-05-31 14:33:13 +00001276/** @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 **/
1284ipmi::RspType<uint8_t, // status
1285 uint8_t, // percentage
1286 uint8_t // check
1287 >
1288 ipmiFrmwareGetStatus()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001289
Vernon Mauery52ce6622019-05-22 09:19:46 -07001290{
1291 if (DEBUG)
1292 std::cerr << "Get FW update status\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -07001293 // 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)
anil kumar appana6c7d9382019-05-31 14:33:13 +00001297 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;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001300
1301 // Status code.
anil kumar appana6c7d9382019-05-31 14:33:13 +00001302 return ipmi::responseSuccess(status, percent, check);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001303}
1304
1305static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0);
1306static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1);
1307static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2);
1308static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3);
1309struct fw_update_options_request
1310{
1311 uint8_t mask;
1312 uint8_t options;
1313} __attribute__((packed));
1314
Vernon Mauery52ce6622019-05-22 09:19:46 -07001315uint32_t fw_update_options = 0;
1316static 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;
anil kumar appana31f88872019-08-02 15:16:27 +00001342 /*setting flag to flase for deferring downgrade support*/
1343 fw_update_status.setInhibitDowngrade(true);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001344 }
1345 else
1346 {
1347 fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV;
anil kumar appana31f88872019-08-02 15:16:27 +00001348 /*setting flag to true for downgrade support*/
1349 fw_update_status.setInhibitDowngrade(false);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001350 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001351 }
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;
anil kumar appana31f88872019-08-02 15:16:27 +00001359 /* setting flag to true to stop image activation */
1360 fw_update_status.setDeferRestart(true);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001361 }
1362 else
1363 {
anil kumar appana31f88872019-08-02 15:16:27 +00001364 /* setting flag to false for image activation */
Vernon Mauery52ce6622019-05-22 09:19:46 -07001365 fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART;
anil kumar appana31f88872019-08-02 15:16:27 +00001366 fw_update_status.setDeferRestart(false);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001367 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001368 }
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
1402struct 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
1410static 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);
Vernon Mauery15419dd2019-05-24 09:40:30 -07001427 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1428 auto method = bus->new_method_call(
Vernon Mauery52ce6622019-05-22 09:19:46 -07001429 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 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001437 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001438
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 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001448 subject = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001449 }
1450 else if (key == "cetificate_serial")
1451 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001452 serial = std::get<uint64_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001453 }
1454 else if (key == "certificate")
1455 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001456 cert = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001457 }
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
1483struct fw_cert_data_req
1484{
1485 uint8_t cert_id;
1486 uint16_t offset;
1487 uint16_t count;
1488} __attribute__((packed));
1489
1490static 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);
Vernon Mauery15419dd2019-05-24 09:40:30 -07001509 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1510 auto method = bus->new_method_call(
Vernon Mauery52ce6622019-05-22 09:19:46 -07001511 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 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001517 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001518 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 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001525 auto cert_data = std::get<std::string>(cert);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001526
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
1547static 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
1583static constexpr char NOT_IMPLEMENTED[] = "NOT IMPLEMENTED";
1584
1585static 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
1606struct intc_app_get_buffer_size_resp
1607{
1608 uint8_t kcs_size;
1609 uint8_t ipmb_size;
1610} __attribute__((packed));
1611
1612static constexpr int KCS_MAX_BUFFER_SIZE = 63;
1613static constexpr int IPMB_MAX_BUFFER_SIZE = 128;
1614static 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
1630static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20;
1631static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21;
1632static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22;
1633static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23;
1634static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24;
1635static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_DATA = 0x25;
1636static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26;
1637static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27;
1638static constexpr ipmi_cmd_t IPMI_CMD_FW_EXIT_FW_UPDATE_MODE = 0x28;
1639static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29;
1640static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a;
1641static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b;
1642static constexpr ipmi_cmd_t IPMI_CMD_FW_IMAGE_WRITE = 0x2c;
1643static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d;
1644static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0;
1645static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0;
1646
1647static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30;
1648static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66;
1649
1650static 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)
anil kumar appana159547c2019-05-31 16:08:34 +00001673 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1674 IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO,
1675 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001676
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
anil kumar appana6c7d9382019-05-31 14:33:13 +00001712 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1713 IPMI_CMD_FW_GET_STATUS, ipmi::Privilege::Admin,
1714 ipmiFrmwareGetStatus);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001715 // 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}