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