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