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