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