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