1 #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 <ipmid/api.hpp> 21 #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 29 static void register_netfn_firmware_functions() __attribute__((constructor)); 30 31 // oem return code for firmware update control 32 constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5; 33 constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80; 34 35 static constexpr bool DEBUG = false; 36 37 static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] = 38 "xyz.openbmc_project.fwupdate1.server"; 39 40 static constexpr char FW_UPDATE_SERVER_PATH[] = 41 "/xyz/openbmc_project/fwupdate1"; 42 static constexpr char FW_UPDATE_SERVER_INFO_PATH[] = 43 "/xyz/openbmc_project/fwupdate1/info"; 44 static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] = 45 "/xyz/openbmc_project/fwupdate1/info/bmc_active"; 46 static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] = 47 "/xyz/openbmc_project/fwupdate1/info/bmc_backup"; 48 49 static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1"; 50 static constexpr char FW_UPDATE_INFO_INTERFACE[] = 51 "xyz.openbmc_project.fwupdate1.fwinfo"; 52 static constexpr char FW_UPDATE_SECURITY_INTERFACE[] = 53 "xyz.openbmc_project.fwupdate1.security"; 54 55 constexpr std::size_t operator""_MB(unsigned long long v) 56 { 57 return 1024u * 1024u * v; 58 } 59 static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB; 60 61 static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin"; 62 static 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 70 class 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 }; 84 fw_update_status_cache() : _bus(getSdBus()) 85 { 86 _match = std::make_shared<sdbusplus::bus::match::match>( 87 *_bus, 88 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>( 133 *_bus, 134 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 { 167 auto state = std::get<std::string>(value); 168 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 { 194 _percent = std::get<int32_t>(value); 195 if (DEBUG) 196 std::cerr << ", pct=" << (int)_percent; 197 } 198 else if (key == "msg") 199 { 200 _msg = std::get<std::string>(value); 201 if (DEBUG) 202 std::cerr << ", msg='" << _msg << '\''; 203 } 204 else if (key == "Progress") 205 { 206 _percent = std::get<uint8_t>(value); 207 ; 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 224 auto method = _bus->new_method_call( 225 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 { 232 auto reply = _bus->call(method); 233 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 247 std::shared_ptr<sdbusplus::asio::connection> _bus; 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 257 static fw_update_status_cache fw_update_status; 258 259 static std::chrono::steady_clock::time_point fw_random_number_timestamp; 260 static constexpr int FW_RANDOM_NUMBER_LENGTH = 8; 261 static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30); 262 static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH]; 263 264 static 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 296 static 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 365 static 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 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 398 // attempt to reset the state machine -- this may fail 399 auto method = bus->new_method_call( 400 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, 401 "org.freedesktop.DBus.Properties", "Abort"); 402 try 403 { 404 auto reply = bus->call(method); 405 406 ipmi::DbusVariant retval; 407 reply.read(retval); 408 if (std::get<int>(retval) != 0) 409 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 421 static void post_transfer_complete_handler( 422 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher); 423 static 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 439 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 440 auto method = 441 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, 442 FW_UPDATE_INTERFACE, "start"); 443 if (DEBUG) 444 std::cerr << "fwupdate1.start: " << uri << '\n'; 445 method.append(uri); 446 try 447 { 448 auto reply = bus->call(method); 449 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 469 static bool request_abort_firmware_update() 470 { 471 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 472 auto method = 473 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, 474 FW_UPDATE_INTERFACE, "abort"); 475 try 476 { 477 auto reply = bus->call(method); 478 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 494 class 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 579 std::shared_ptr<transfer_hash_check> xfer_hash_check; 580 581 #ifdef UPDATER_ENABLED 582 static 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 } 589 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 590 auto method_call = bus->new_method_call( 591 "xyz.openbmc_project.Software.BMC.Updater", obj_path, 592 "org.freedesktop.DBus.Properties", "Set"); 593 method_call.append( 594 "xyz.openbmc_project.Software.Activation", "RequestedActivation", 595 std::variant<std::string>("xyz.openbmc_project.Software.Activation." 596 "RequestedActivations.Active")); 597 598 try 599 { 600 auto method_call_reply = bus->call(method_call); 601 } 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 610 static 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, 634 std::vector<std::pair<std::string, std::variant<std::string>>>>> 635 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>( 691 *getSdBus(), 692 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 693 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 694 callback); 695 } 696 #endif 697 698 class 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 741 static 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 770 template <typename... ArgTypes> 771 static 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 778 constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl"; 779 constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt"; 780 constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img"; 781 constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev"; 782 constexpr size_t fwPathMaxLength = 255; 783 static 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 804 static 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 */ 828 static 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 */ 838 static 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 */ 856 static 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 865 constexpr uint8_t controls_init = 0x00; 866 constexpr uint8_t controls_transfer_started = 0x01; 867 constexpr uint8_t controls_transfer_completed = 0x02; 868 constexpr uint8_t controls_transfer_aborted = 0x04; 869 constexpr uint8_t controls_usb_attached = 0x08; 870 871 struct 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 888 static 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 1040 struct 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 1050 static 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 } 1090 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1091 auto method = 1092 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path, 1093 "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 { 1098 auto reply = bus->call(method); 1099 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 { 1121 auto strver = std::get<std::string>(value); 1122 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 { 1135 build_time = std::get<int32_t>(value); 1136 } 1137 else if (key == "update_time") 1138 { 1139 update_time = std::get<int32_t>(value); 1140 } 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 1161 struct fw_security_revision_info 1162 { 1163 uint8_t id_tag; 1164 uint16_t sec_rev; 1165 } __attribute__((packed)); 1166 1167 static 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 1191 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1192 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 = 1210 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path, 1211 "org.freedesktop.DBus.Properties", "GetAll"); 1212 method.append(FW_UPDATE_INFO_INTERFACE, "security_version"); 1213 ipmi::DbusVariant sec_rev; 1214 try 1215 { 1216 auto reply = bus->call(method); 1217 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; 1230 info->sec_rev = std::get<int>(sec_rev); 1231 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 1243 struct fw_channel_size 1244 { 1245 uint8_t channel_id; 1246 uint32_t channel_size; 1247 } __attribute__((packed)); 1248 1249 enum 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 1258 static constexpr uint8_t channelListSize = 2; 1259 /** @brief implements Maximum Firmware Transfer size command 1260 * @parameter 1261 * - none 1262 * @returns IPMI completion code plus response data 1263 * - count - channel count 1264 * - channelList - channel list information 1265 */ 1266 ipmi::RspType<uint8_t, // channel count 1267 std::array<std::tuple<uint8_t, uint32_t>, 1268 channelListSize> // channel 1269 // list 1270 > 1271 ipmiFirmwareMaxTransferSize() 1272 { 1273 constexpr uint8_t KCSMaxBufSize = 128; 1274 constexpr uint32_t RMCPPLUSMaxBufSize = 50 * 1024; 1275 if (DEBUG) 1276 std::cerr << "Get FW max transfer size\n"; 1277 // Byte 1 - Count (N) Number of devices data is being returned for. 1278 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 1279 // 03 – usb data, 04 – usb mass storage 1280 // Byte 3-6 - transfer size (little endian) 1281 // Bytes - 7:(5xN) - Repeat of 2 through 6 1282 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize> 1283 channelList = {{{CHANNEL_KCS, KCSMaxBufSize}, 1284 {CHANNEL_RMCP_PLUS, RMCPPLUSMaxBufSize}}}; 1285 return ipmi::responseSuccess(channelListSize, channelList); 1286 } 1287 1288 enum 1289 { 1290 EXEC_CTX_RESVD = 0, 1291 EXEC_CTX_FULL_LINUX = 0x10, 1292 EXEC_CTX_SAFE_MODE_LINUX = 0x11, 1293 } bmc_execution_context; 1294 1295 struct fw_execution_context 1296 { 1297 uint8_t context; 1298 uint8_t image_selection; 1299 } __attribute__((packed)); 1300 1301 static ipmi_ret_t ipmi_firmware_get_fw_execution_context( 1302 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 1303 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) 1304 { 1305 if (DEBUG) 1306 std::cerr << "Get FW execution context\n"; 1307 1308 // Byte 1 - execution context 1309 // 0x10 - full linux stack, 0x11 - safe-mode linux stack 1310 // Byte 2 - current image selection 1311 // 1 - primary, 2 - secondary 1312 1313 auto info = reinterpret_cast<struct fw_execution_context *>(response); 1314 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1315 auto method = 1316 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, 1317 "org.freedesktop.DBus.Properties", "GetAll"); 1318 method.append(FW_UPDATE_SECURITY_INTERFACE); 1319 int active_img; 1320 bool safe_mode; 1321 std::string cert; 1322 try 1323 { 1324 auto reply = bus->call(method); 1325 1326 if (!reply.is_method_error()) 1327 { 1328 std::vector<std::pair<std::string, ipmi::DbusVariant>> properties; 1329 reply.read(properties); 1330 1331 for (const auto &t : properties) 1332 { 1333 auto key = t.first; 1334 auto value = t.second; 1335 if (key == "active_partition") 1336 { 1337 active_img = std::get<int>(value); 1338 } 1339 else if (key == "safe_mode") 1340 { 1341 safe_mode = std::get<bool>(value); 1342 } 1343 } 1344 } 1345 } 1346 catch (sdbusplus::exception::SdBusError &e) 1347 { 1348 std::cerr << "SDBus Error: " << e.what(); 1349 return IPMI_CC_UNSPECIFIED_ERROR; 1350 } 1351 1352 if (safe_mode) 1353 info->context = EXEC_CTX_SAFE_MODE_LINUX; 1354 else 1355 info->context = EXEC_CTX_FULL_LINUX; 1356 1357 info->image_selection = active_img; 1358 1359 // Status code. 1360 ipmi_ret_t rc = IPMI_CC_OK; 1361 *data_len = sizeof(*info); 1362 1363 return rc; 1364 } 1365 1366 /** @brief implements firmware get status command 1367 * @parameter 1368 * - none 1369 * @returns IPMI completion code plus response data 1370 * - status - processing status 1371 * - percentage - percentage completion 1372 * - check - channel integrity check status 1373 **/ 1374 ipmi::RspType<uint8_t, // status 1375 uint8_t, // percentage 1376 uint8_t // check 1377 > 1378 ipmiFrmwareGetStatus() 1379 1380 { 1381 if (DEBUG) 1382 std::cerr << "Get FW update status\n"; 1383 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write, 1384 // 5=ready, f=error, 83=ac cycle required) 1385 // Byte 2 - percent 1386 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail) 1387 uint8_t status = fw_update_status.state(); 1388 uint8_t percent = fw_update_status.percent(); 1389 uint8_t check = xfer_hash_check ? xfer_hash_check->status() : 0; 1390 1391 // Status code. 1392 return ipmi::responseSuccess(status, percent, check); 1393 } 1394 1395 static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0); 1396 static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1); 1397 static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2); 1398 static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3); 1399 struct fw_update_options_request 1400 { 1401 uint8_t mask; 1402 uint8_t options; 1403 } __attribute__((packed)); 1404 1405 bool fw_update_set_dbus_property(const std::string &path, 1406 const std::string &iface, 1407 const std::string &name, 1408 ipmi::DbusVariant value) 1409 { 1410 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1411 auto method = 1412 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, path.c_str(), 1413 "org.freedesktop.DBus.Properties", "Set"); 1414 method.append(iface, name, value); 1415 try 1416 { 1417 auto reply = bus->call(method); 1418 auto err = reply.is_method_error(); 1419 if (err) 1420 if (DEBUG) 1421 std::cerr << "failed to set prop " << path << '/' << iface 1422 << '.' << name << '\n'; 1423 return err; 1424 } 1425 catch (sdbusplus::exception::SdBusError &e) 1426 { 1427 std::cerr << "SDBus Error: " << e.what(); 1428 return false; 1429 } 1430 } 1431 1432 uint32_t fw_update_options = 0; 1433 static ipmi_ret_t ipmi_firmware_update_options( 1434 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 1435 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) 1436 { 1437 if (DEBUG) 1438 std::cerr << "Get/set FW update options\n"; 1439 1440 // request: 1441 // Byte 1 - mask 1442 // Byte 2 - options 1443 // Byte 3-34 - optional integrity check expected value 1444 // response: 1445 // Byte 1 - set options 1446 1447 auto fw_options = 1448 reinterpret_cast<struct fw_update_options_request *>(request); 1449 1450 const char *path = FW_UPDATE_SERVER_INFO_PATH; 1451 const char *iface = FW_UPDATE_SECURITY_INTERFACE; 1452 if ((fw_options->mask & FW_UPDATE_OPTIONS_NO_DOWNREV) && 1453 (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) != 1454 (fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV)) 1455 { 1456 if (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) 1457 { 1458 fw_update_options |= FW_UPDATE_OPTIONS_NO_DOWNREV; 1459 } 1460 else 1461 { 1462 fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV; 1463 // send dbus 1464 } 1465 const char *name = "inhibit_downgrade"; 1466 fw_update_set_dbus_property( 1467 path, iface, name, 1468 (bool)(!!(fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV))); 1469 } 1470 if ((fw_options->mask & FW_UPDATE_OPTIONS_DEFER_RESTART) && 1471 (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) != 1472 (fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART)) 1473 { 1474 if (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) 1475 { 1476 fw_update_options |= FW_UPDATE_OPTIONS_DEFER_RESTART; 1477 } 1478 else 1479 { 1480 fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART; 1481 } 1482 const char *name = "defer_restart"; 1483 fw_update_set_dbus_property( 1484 path, iface, name, 1485 (bool)(!!(fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART))); 1486 } 1487 if (fw_options->mask & FW_UPDATE_OPTIONS_SHA2_CHECK) 1488 { 1489 auto hash_size = EVP_MD_size(EVP_sha256()); 1490 if (fw_options->options & FW_UPDATE_OPTIONS_SHA2_CHECK) 1491 { 1492 if (*data_len != (sizeof(*fw_options) + hash_size)) 1493 { 1494 *data_len = 0; 1495 return IPMI_CC_REQ_DATA_LEN_INVALID; 1496 } 1497 xfer_hash_check = std::make_shared<transfer_hash_check>(); 1498 auto exp_hash = reinterpret_cast<uint8_t *>(fw_options + 1); 1499 xfer_hash_check->init({exp_hash, exp_hash + hash_size}); 1500 fw_update_options |= FW_UPDATE_OPTIONS_SHA2_CHECK; 1501 } 1502 else 1503 { 1504 fw_update_options &= ~FW_UPDATE_OPTIONS_SHA2_CHECK; 1505 // delete the xfer_hash_check object 1506 xfer_hash_check.reset(); 1507 } 1508 } 1509 auto options_rsp = reinterpret_cast<uint8_t *>(response); 1510 *options_rsp = fw_update_options; 1511 1512 if (DEBUG) 1513 std::cerr << "current fw_update_options = " << std::hex 1514 << fw_update_options << '\n'; 1515 // Status code. 1516 *data_len = sizeof(*options_rsp); 1517 return IPMI_CC_OK; 1518 } 1519 1520 struct fw_cert_info 1521 { 1522 uint16_t cert_len; 1523 uint64_t serial; 1524 uint8_t subject_len; 1525 char subject[255]; 1526 } __attribute__((packed)); 1527 1528 static ipmi_ret_t ipmi_firmware_get_root_cert_info( 1529 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 1530 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) 1531 { 1532 if (DEBUG) 1533 std::cerr << "Get FW root cert info\n"; 1534 1535 // request: 1536 // Byte 1 - certificate ID: request which certificate (ignored) 1537 1538 // response: 1539 // Byte 1-2 - certificate length (little endian) 1540 // Byte 3-10 - serial number (little endian) 1541 // Byte 11 - subject length 1542 // Byte 12-N - subject data 1543 1544 auto cert_info = reinterpret_cast<struct fw_cert_info *>(response); 1545 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1546 auto method = bus->new_method_call( 1547 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH, 1548 "org.freedesktop.DBus.Properties", "GetAll"); 1549 method.append(FW_UPDATE_SECURITY_INTERFACE); 1550 std::string subject; 1551 uint64_t serial; 1552 std::string cert; 1553 try 1554 { 1555 auto reply = bus->call(method); 1556 1557 std::vector<std::pair<std::string, ipmi::DbusVariant>> properties; 1558 reply.read(properties); 1559 1560 for (const auto &t : properties) 1561 { 1562 auto key = t.first; 1563 auto value = t.second; 1564 if (key == "certificate_subject") 1565 { 1566 subject = std::get<std::string>(value); 1567 } 1568 else if (key == "cetificate_serial") 1569 { 1570 serial = std::get<uint64_t>(value); 1571 } 1572 else if (key == "certificate") 1573 { 1574 cert = std::get<std::string>(value); 1575 } 1576 } 1577 } 1578 catch (sdbusplus::exception::SdBusError &e) 1579 { 1580 std::cerr << "SDBus Error: " << e.what(); 1581 return IPMI_CC_UNSPECIFIED_ERROR; 1582 } 1583 1584 cert_info->cert_len = cert.size(); 1585 cert_info->serial = serial; 1586 // truncate subject so it fits in the 255-byte array (if necessary) 1587 if (subject.size() > sizeof(cert_info->subject)) 1588 subject.resize(sizeof(cert_info->subject)); 1589 cert_info->subject_len = subject.size(); 1590 std::copy(subject.begin(), subject.end(), cert_info->subject); 1591 1592 // Status code. 1593 ipmi_ret_t rc = IPMI_CC_OK; 1594 // make sure to account for the *actual* size of the subject 1595 *data_len = sizeof(*cert_info) - sizeof(cert_info->subject) + 1596 cert_info->subject_len; 1597 1598 return rc; 1599 } 1600 1601 struct fw_cert_data_req 1602 { 1603 uint8_t cert_id; 1604 uint16_t offset; 1605 uint16_t count; 1606 } __attribute__((packed)); 1607 1608 static ipmi_ret_t ipmi_firmware_get_root_cert_data( 1609 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 1610 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) 1611 { 1612 if (DEBUG) 1613 std::cerr << "Get FW root cert data\n"; 1614 1615 // request: 1616 // Byte 1 - certificate ID: request which certificate (ignored) 1617 // Byte 2-3 - offset within cert to start at 1618 // Byte 4-5 - number of bytes to return 1619 1620 // response: 1621 // Byte 1-N - certificate data 1622 1623 if (*data_len != sizeof(fw_cert_data_req)) 1624 return IPMI_CC_REQ_DATA_LEN_INVALID; 1625 1626 auto cert_data_req = reinterpret_cast<struct fw_cert_data_req *>(request); 1627 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1628 auto method = bus->new_method_call( 1629 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH, 1630 "org.freedesktop.DBus.Properties", "Get"); 1631 method.append(FW_UPDATE_SECURITY_INTERFACE, "certificate"); 1632 ipmi::DbusVariant cert; 1633 try 1634 { 1635 auto reply = bus->call(method); 1636 reply.read(cert); 1637 } 1638 catch (sdbusplus::exception::SdBusError &e) 1639 { 1640 std::cerr << "SDBus Error: " << e.what(); 1641 return IPMI_CC_UNSPECIFIED_ERROR; 1642 } 1643 auto cert_data = std::get<std::string>(cert); 1644 1645 if (cert_data_req->offset >= cert_data.size()) 1646 { 1647 *data_len = 0; 1648 return IPMI_CC_INVALID_FIELD_REQUEST; 1649 } 1650 auto first = cert_data.begin() + cert_data_req->offset; 1651 auto last = first + cert_data_req->count; 1652 if (last > cert_data.end()) 1653 last = cert_data.end(); 1654 1655 auto data_out = reinterpret_cast<char *>(response); 1656 std::copy(first, last, data_out); 1657 1658 // Status code. 1659 ipmi_ret_t rc = IPMI_CC_OK; 1660 *data_len = (last - first); 1661 1662 return rc; 1663 } 1664 1665 static ipmi_ret_t ipmi_firmware_write_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1666 ipmi_request_t request, 1667 ipmi_response_t response, 1668 ipmi_data_len_t data_len, 1669 ipmi_context_t context) 1670 { 1671 if (DEBUG) 1672 std::cerr << "write fw data (" << *data_len << " bytes)\n"; 1673 1674 auto bytes_in = *data_len; 1675 *data_len = 0; 1676 if (fw_update_status.state() != fw_update_status_cache::FW_STATE_DOWNLOAD) 1677 return IPMI_CC_INVALID; 1678 1679 std::ofstream out(FIRMWARE_BUFFER_FILE, 1680 std::ofstream::binary | std::ofstream::app); 1681 if (!out) 1682 { 1683 return IPMI_CC_UNSPECIFIED_ERROR; 1684 } 1685 if (out.tellp() > FIRMWARE_BUFFER_MAX_SIZE) 1686 { 1687 return IPMI_CC_INVALID_FIELD_REQUEST; 1688 } 1689 auto data = reinterpret_cast<uint8_t *>(request); 1690 out.write(reinterpret_cast<char *>(data), bytes_in); 1691 out.close(); 1692 if (xfer_hash_check) 1693 { 1694 xfer_hash_check->hash({data, data + bytes_in}); 1695 } 1696 1697 // Status code. 1698 return IPMI_CC_OK; 1699 } 1700 1701 static constexpr char NOT_IMPLEMENTED[] = "NOT IMPLEMENTED"; 1702 1703 static ipmi_ret_t ipmi_firmware_wildcard_handler( 1704 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 1705 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) 1706 { 1707 if (DEBUG) 1708 std::cerr << "Handling stubbed Netfn:[0x" << std::hex << +netfn 1709 << "], Cmd:[0x" << std::hex << +cmd << "]\n"; 1710 1711 // Status code. 1712 ipmi_ret_t rc = IPMI_CC_OK; 1713 1714 *data_len = sizeof(NOT_IMPLEMENTED); 1715 1716 // Now pack actual response 1717 char *msg_reply = static_cast<char *>(response); 1718 std::copy(std::begin(NOT_IMPLEMENTED), std::end(NOT_IMPLEMENTED), 1719 msg_reply); 1720 1721 return rc; 1722 } 1723 1724 struct intc_app_get_buffer_size_resp 1725 { 1726 uint8_t kcs_size; 1727 uint8_t ipmb_size; 1728 } __attribute__((packed)); 1729 1730 static constexpr int KCS_MAX_BUFFER_SIZE = 63; 1731 static constexpr int IPMB_MAX_BUFFER_SIZE = 128; 1732 static ipmi_ret_t ipmi_intel_app_get_buffer_size( 1733 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 1734 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) 1735 { 1736 auto msg_reply = 1737 reinterpret_cast<intc_app_get_buffer_size_resp *>(response); 1738 // for now this is hard coded; really this number is dependent on 1739 // the BMC kcs driver as well as the host kcs driver.... 1740 // we can't know the latter. 1741 msg_reply->kcs_size = KCS_MAX_BUFFER_SIZE / 4; 1742 msg_reply->ipmb_size = IPMB_MAX_BUFFER_SIZE / 4; 1743 *data_len = sizeof(*msg_reply); 1744 1745 return IPMI_CC_OK; 1746 } 1747 1748 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20; 1749 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21; 1750 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22; 1751 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23; 1752 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24; 1753 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_DATA = 0x25; 1754 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26; 1755 static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27; 1756 static constexpr ipmi_cmd_t IPMI_CMD_FW_EXIT_FW_UPDATE_MODE = 0x28; 1757 static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29; 1758 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a; 1759 static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b; 1760 static constexpr ipmi_cmd_t IPMI_CMD_FW_IMAGE_WRITE = 0x2c; 1761 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d; 1762 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0; 1763 static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0; 1764 1765 static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30; 1766 static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66; 1767 1768 static void register_netfn_firmware_functions() 1769 { 1770 // guarantee that we start with an already timed out timestamp 1771 fw_random_number_timestamp = 1772 std::chrono::steady_clock::now() - FW_RANDOM_NUMBER_TTL; 1773 1774 unlink(FIRMWARE_BUFFER_FILE); 1775 1776 // <Get BT Interface Capabilities> 1777 if (DEBUG) 1778 std::cerr << "Registering firmware update commands\n"; 1779 1780 // get firmware version information 1781 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_VERSION_INFO, 1782 NULL, ipmi_firmware_get_fw_version_info, 1783 PRIVILEGE_ADMIN); 1784 1785 // get firmware security version information 1786 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO, 1787 NULL, ipmi_firmware_get_fw_security_revision, 1788 PRIVILEGE_ADMIN); 1789 1790 // get channel information (max transfer sizes) 1791 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE, 1792 IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO, 1793 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize); 1794 1795 // get bmc execution context 1796 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_BMC_EXEC_CTX, NULL, 1797 ipmi_firmware_get_fw_execution_context, 1798 PRIVILEGE_ADMIN); 1799 1800 // get root certificate information 1801 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO, 1802 NULL, ipmi_firmware_get_root_cert_info, 1803 PRIVILEGE_ADMIN); 1804 1805 // get root certificate data 1806 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_DATA, 1807 NULL, ipmi_firmware_get_root_cert_data, 1808 PRIVILEGE_ADMIN); 1809 1810 // generate bmc fw update random number (for enter fw tranfer mode) 1811 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM, 1812 NULL, ipmi_firmware_get_fw_random_number, 1813 PRIVILEGE_ADMIN); 1814 1815 // enter firmware update mode 1816 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_MODE, 1817 NULL, ipmi_firmware_enter_fw_transfer_mode, 1818 PRIVILEGE_ADMIN); 1819 1820 // exit firmware update mode 1821 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_EXIT_FW_UPDATE_MODE, 1822 NULL, ipmi_firmware_exit_fw_update_mode, 1823 PRIVILEGE_ADMIN); 1824 1825 // firmware control mechanism (set filename, usb, etc.) 1826 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_UPDATE_CONTROL, NULL, 1827 ipmi_firmware_control, PRIVILEGE_ADMIN); 1828 1829 // get firmware update status 1830 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE, 1831 IPMI_CMD_FW_GET_STATUS, ipmi::Privilege::Admin, 1832 ipmiFrmwareGetStatus); 1833 // set firmware update options (no downgrade, etc.) 1834 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS, 1835 NULL, ipmi_firmware_update_options, PRIVILEGE_ADMIN); 1836 1837 // write image data 1838 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_IMAGE_WRITE, NULL, 1839 ipmi_firmware_write_data, PRIVILEGE_ADMIN); 1840 1841 // get update timestamps 1842 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_TIMESTAMP, NULL, 1843 ipmi_firmware_wildcard_handler, PRIVILEGE_ADMIN); 1844 1845 // get error message (when in error state) 1846 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_UPDATE_ERR_MSG, 1847 NULL, ipmi_firmware_wildcard_handler, 1848 PRIVILEGE_ADMIN); 1849 1850 // get remote firmware information (PSU, HSBP, etc.) 1851 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_REMOTE_FW_INFO, 1852 NULL, ipmi_firmware_wildcard_handler, 1853 PRIVILEGE_ADMIN); 1854 1855 // <Wildcard Command> 1856 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_WILDCARD, NULL, 1857 ipmi_firmware_wildcard_handler, PRIVILEGE_ADMIN); 1858 1859 // get buffer size is used by fw update (exclusively?) 1860 ipmi_register_callback(NETFUN_INTC_APP, IPMI_CMD_INTC_GET_BUFFER_SIZE, NULL, 1861 ipmi_intel_app_get_buffer_size, PRIVILEGE_USER); 1862 return; 1863 } 1864