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/container/flat_map.hpp> 11 #include <boost/process/child.hpp> 12 #include <boost/uuid/random_generator.hpp> 13 #include <boost/uuid/uuid_io.hpp> 14 #include <commandutils.hpp> 15 #include <ipmid/api.hpp> 16 #include <ipmid/utils.hpp> 17 #include <phosphor-logging/log.hpp> 18 #include <sdbusplus/bus.hpp> 19 #include <sdbusplus/bus/match.hpp> 20 #include <sdbusplus/server/object.hpp> 21 #include <sdbusplus/timer.hpp> 22 #include <types.hpp> 23 24 #include <chrono> 25 #include <cstdint> 26 #include <filesystem> 27 #include <fstream> 28 #include <iostream> 29 #include <map> 30 #include <random> 31 #ifdef INTEL_PFR_ENABLED 32 #include <spiDev.hpp> 33 #endif 34 35 static constexpr bool DEBUG = true; 36 static void registerFirmwareFunctions() __attribute__((constructor)); 37 38 namespace ipmi 39 { 40 namespace firmware 41 { 42 constexpr Cmd cmdGetFwVersionInfo = 0x20; 43 constexpr Cmd cmdGetFwSecurityVersionInfo = 0x21; 44 constexpr Cmd cmdGetFwUpdateChannelInfo = 0x22; 45 constexpr Cmd cmdGetBmcExecutionContext = 0x23; 46 constexpr Cmd cmdFwGetRootCertData = 0x25; 47 constexpr Cmd cmdGetFwUpdateRandomNumber = 0x26; 48 constexpr Cmd cmdSetFirmwareUpdateMode = 0x27; 49 constexpr Cmd cmdExitFirmwareUpdateMode = 0x28; 50 constexpr Cmd cmdGetSetFwUpdateControl = 0x29; 51 constexpr Cmd cmdGetFirmwareUpdateStatus = 0x2A; 52 constexpr Cmd cmdSetFirmwareUpdateOptions = 0x2B; 53 constexpr Cmd cmdFwImageWriteData = 0x2c; 54 } // namespace firmware 55 } // namespace ipmi 56 57 namespace ipmi 58 { 59 // Custom completion codes 60 constexpr Cc ccUsbAttachOrDetachFailed = 0x80; 61 constexpr Cc ccNotSupportedInPresentState = 0xD5; 62 63 static inline auto responseUsbAttachOrDetachFailed() 64 { 65 return response(ccUsbAttachOrDetachFailed); 66 } 67 static inline auto responseNotSupportedInPresentState() 68 { 69 return response(ccNotSupportedInPresentState); 70 } 71 } // namespace ipmi 72 73 static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC"; 74 static constexpr const char* bmcStatePath = "/xyz/openbmc_project/state/bmc0"; 75 static constexpr const char* bmcStateReady = 76 "xyz.openbmc_project.State.BMC.BMCState.Ready"; 77 static constexpr const char* bmcStateUpdateInProgress = 78 "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress"; 79 80 static constexpr char firmwareBufferFile[] = "/tmp/fw-download.bin"; 81 static std::chrono::steady_clock::time_point fwRandomNumGenTs; 82 static constexpr auto fwRandomNumExpirySeconds = std::chrono::seconds(30); 83 static constexpr size_t fwRandomNumLength = 8; 84 static std::array<uint8_t, fwRandomNumLength> fwRandomNum; 85 constexpr char usbCtrlPath[] = "/usr/bin/usb-ctrl"; 86 constexpr char fwUpdateMountPoint[] = "/tmp/usb-fwupd.mnt"; 87 constexpr char fwUpdateUsbVolImage[] = "/tmp/usb-fwupd.img"; 88 constexpr char fwUpdateUSBDevName[] = "fw-usb-mass-storage-dev"; 89 constexpr size_t fwPathMaxLength = 255; 90 91 #ifdef INTEL_PFR_ENABLED 92 uint32_t imgLength = 0; 93 uint32_t imgType = 0; 94 bool block0Mapped = false; 95 static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19; 96 97 static constexpr const char* bmcActivePfmMTDDev = "/dev/mtd/pfm"; 98 static constexpr const char* bmcRecoveryImgMTDDev = "/dev/mtd/rc-image"; 99 static constexpr size_t pfmBaseOffsetInImage = 0x400; 100 static constexpr size_t rootkeyOffsetInPfm = 0xA0; 101 static constexpr size_t cskKeyOffsetInPfm = 0x124; 102 static constexpr size_t cskSignatureOffsetInPfm = 0x19c; 103 static constexpr size_t certKeyLen = 96; 104 static constexpr size_t cskSignatureLen = 96; 105 106 static constexpr const char* versionIntf = 107 "xyz.openbmc_project.Software.Version"; 108 109 enum class FwGetRootCertDataTag : uint8_t 110 { 111 activeRootKey = 1, 112 recoveryRootKey, 113 activeCSK, 114 recoveryCSK, 115 }; 116 117 enum class FWDeviceIDTag : uint8_t 118 { 119 bmcActiveImage = 1, 120 bmcRecoveryImage, 121 }; 122 123 const static boost::container::flat_map<FWDeviceIDTag, const char*> 124 fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage, 125 "/xyz/openbmc_project/software/bmc_active"}, 126 {FWDeviceIDTag::bmcRecoveryImage, 127 "/xyz/openbmc_project/software/bmc_recovery"}}; 128 #endif // INTEL_PFR_ENABLED 129 130 enum class ChannelIdTag : uint8_t 131 { 132 reserved = 0, 133 kcs = 1, 134 ipmb = 2, 135 rmcpPlus = 3 136 }; 137 138 enum class BmcExecutionContext : uint8_t 139 { 140 reserved = 0, 141 linuxOs = 0x10, 142 bootLoader = 0x11, 143 }; 144 145 enum class FwUpdateCtrlReq : uint8_t 146 { 147 getCurrentControlStatus = 0x00, 148 imageTransferStart = 0x01, 149 imageTransferComplete = 0x02, 150 imageTransferAbort = 0x03, 151 setFirmwareFilename = 0x04, 152 attachUsbDevice = 0x05, 153 detachUsbDevice = 0x06 154 }; 155 156 constexpr std::size_t operator""_MB(unsigned long long v) 157 { 158 return 1024u * 1024u * v; 159 } 160 static constexpr size_t maxFirmwareImageSize = 32_MB; 161 162 static bool localDownloadInProgress(void) 163 { 164 struct stat sb; 165 if (stat(firmwareBufferFile, &sb) < 0) 166 { 167 return false; 168 } 169 return true; 170 } 171 172 class TransferHashCheck 173 { 174 public: 175 enum class HashCheck : uint8_t 176 { 177 notRequested = 0, 178 requested, 179 sha2Success, 180 sha2Failed = 0xe2, 181 }; 182 183 protected: 184 EVP_MD_CTX* ctx; 185 std::vector<uint8_t> expectedHash; 186 enum HashCheck check; 187 bool started; 188 189 public: 190 TransferHashCheck() : check(HashCheck::notRequested), started(false) 191 {} 192 ~TransferHashCheck() 193 { 194 if (ctx) 195 { 196 EVP_MD_CTX_destroy(ctx); 197 ctx = NULL; 198 } 199 } 200 void init(const std::vector<uint8_t>& expected) 201 { 202 expectedHash = expected; 203 check = HashCheck::requested; 204 ctx = EVP_MD_CTX_create(); 205 EVP_DigestInit(ctx, EVP_sha256()); 206 } 207 void hash(const std::vector<uint8_t>& data) 208 { 209 if (!started) 210 { 211 started = true; 212 } 213 EVP_DigestUpdate(ctx, data.data(), data.size()); 214 } 215 void clear() 216 { 217 // if not started, nothing to clear 218 if (started) 219 { 220 if (ctx) 221 { 222 EVP_MD_CTX_destroy(ctx); 223 } 224 if (check != HashCheck::notRequested) 225 { 226 check = HashCheck::requested; 227 } 228 ctx = EVP_MD_CTX_create(); 229 EVP_DigestInit(ctx, EVP_sha256()); 230 } 231 } 232 enum HashCheck verify() 233 { 234 if (check == HashCheck::requested) 235 { 236 unsigned int len = 0; 237 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256())); 238 EVP_DigestFinal(ctx, digest.data(), &len); 239 if (digest == expectedHash) 240 { 241 phosphor::logging::log<phosphor::logging::level::INFO>( 242 "Transfer sha2 verify passed."); 243 check = HashCheck::sha2Success; 244 } 245 else 246 { 247 phosphor::logging::log<phosphor::logging::level::ERR>( 248 "Transfer sha2 verify failed."); 249 check = HashCheck::sha2Failed; 250 } 251 } 252 return check; 253 } 254 uint8_t status() const 255 { 256 return static_cast<uint8_t>(check); 257 } 258 }; 259 260 class MappedFile 261 { 262 public: 263 MappedFile(const std::string& fname) : addr(nullptr), fsize(0) 264 { 265 std::error_code ec; 266 size_t sz = std::filesystem::file_size(fname, ec); 267 int fd = open(fname.c_str(), O_RDONLY); 268 if (!ec || fd < 0) 269 { 270 return; 271 } 272 void* tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); 273 close(fd); 274 if (tmp == MAP_FAILED) 275 { 276 return; 277 } 278 addr = tmp; 279 fsize = sz; 280 } 281 282 ~MappedFile() 283 { 284 if (addr) 285 { 286 munmap(addr, fsize); 287 } 288 } 289 const uint8_t* data() const 290 { 291 return static_cast<const uint8_t*>(addr); 292 } 293 size_t size() const 294 { 295 return fsize; 296 } 297 298 private: 299 size_t fsize; 300 void* addr; 301 }; 302 303 class FwUpdateStatusCache 304 { 305 public: 306 enum 307 { 308 fwStateInit = 0, 309 fwStateIdle, 310 fwStateDownload, 311 fwStateVerify, 312 fwStateProgram, 313 fwStateUpdateSuccess, 314 fwStateError = 0x0f, 315 fwStateAcCycleRequired = 0x83, 316 }; 317 uint8_t getState() 318 { 319 if ((fwUpdateState == fwStateIdle || fwUpdateState == fwStateInit) && 320 localDownloadInProgress()) 321 { 322 fwUpdateState = fwStateDownload; 323 progressPercent = 0; 324 } 325 return fwUpdateState; 326 } 327 void resetStatusCache() 328 { 329 unlink(firmwareBufferFile); 330 } 331 void setState(const uint8_t state) 332 { 333 switch (state) 334 { 335 case fwStateInit: 336 case fwStateIdle: 337 case fwStateError: 338 resetStatusCache(); 339 break; 340 case fwStateDownload: 341 case fwStateVerify: 342 case fwStateProgram: 343 case fwStateUpdateSuccess: 344 break; 345 default: 346 // Error 347 break; 348 } 349 fwUpdateState = state; 350 } 351 uint8_t percent() 352 { 353 return progressPercent; 354 } 355 void updateActivationPercent(const std::string& objPath) 356 { 357 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 358 fwUpdateState = fwStateProgram; 359 progressPercent = 0; 360 match = std::make_shared<sdbusplus::bus::match::match>( 361 *busp, 362 sdbusplus::bus::match::rules::propertiesChanged( 363 objPath, "xyz.openbmc_project.Software.ActivationProgress"), 364 [&](sdbusplus::message::message& msg) { 365 std::map<std::string, ipmi::DbusVariant> props; 366 std::vector<std::string> inVal; 367 std::string iface; 368 try 369 { 370 msg.read(iface, props, inVal); 371 } 372 catch (const std::exception& e) 373 { 374 phosphor::logging::log<phosphor::logging::level::ERR>( 375 "Exception caught in get ActivationProgress"); 376 return; 377 } 378 379 auto it = props.find("Progress"); 380 if (it != props.end()) 381 { 382 progressPercent = std::get<uint8_t>(it->second); 383 if (progressPercent == 100) 384 { 385 fwUpdateState = fwStateUpdateSuccess; 386 } 387 } 388 }); 389 } 390 uint8_t activationTimerTimeout() 391 { 392 phosphor::logging::log<phosphor::logging::level::INFO>( 393 "activationTimerTimeout: Increase percentage...", 394 phosphor::logging::entry("PERCENT:%d", progressPercent)); 395 progressPercent = progressPercent + 5; 396 if (progressPercent > 95) 397 { 398 /*changing the state to ready to update firmware utility */ 399 fwUpdateState = fwStateUpdateSuccess; 400 } 401 return progressPercent; 402 } 403 /* API for changing state to ERROR */ 404 void firmwareUpdateAbortState() 405 { 406 unlink(firmwareBufferFile); 407 // changing the state to error 408 fwUpdateState = fwStateError; 409 } 410 void setDeferRestart(bool deferRestart) 411 { 412 deferRestartState = deferRestart; 413 } 414 void setInhibitDowngrade(bool inhibitDowngrade) 415 { 416 inhibitDowngradeState = inhibitDowngrade; 417 } 418 bool getDeferRestart() 419 { 420 return deferRestartState; 421 } 422 bool getInhibitDowngrade() 423 { 424 return inhibitDowngradeState; 425 } 426 427 protected: 428 std::shared_ptr<sdbusplus::asio::connection> busp; 429 std::shared_ptr<sdbusplus::bus::match::match> match; 430 uint8_t fwUpdateState = 0; 431 uint8_t progressPercent = 0; 432 bool deferRestartState = false; 433 bool inhibitDowngradeState = false; 434 }; 435 436 static FwUpdateStatusCache fwUpdateStatus; 437 std::shared_ptr<TransferHashCheck> xferHashCheck; 438 439 static void activateImage(const std::string& objPath) 440 { 441 // If flag is false means to reboot 442 if (fwUpdateStatus.getDeferRestart() == false) 443 { 444 phosphor::logging::log<phosphor::logging::level::INFO>( 445 "activating Image: ", 446 phosphor::logging::entry("OBJPATH =%s", objPath.c_str())); 447 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 448 bus->async_method_call( 449 [](const boost::system::error_code ec) { 450 if (ec) 451 { 452 phosphor::logging::log<phosphor::logging::level::ERR>( 453 "async_method_call error: activateImage failed"); 454 return; 455 } 456 }, 457 "xyz.openbmc_project.Software.BMC.Updater", objPath, 458 "org.freedesktop.DBus.Properties", "Set", 459 "xyz.openbmc_project.Software.Activation", "RequestedActivation", 460 std::variant<std::string>("xyz.openbmc_project.Software.Activation." 461 "RequestedActivations.Active")); 462 } 463 else 464 { 465 phosphor::logging::log<phosphor::logging::level::INFO>( 466 "Firmware image activation is deferred."); 467 } 468 fwUpdateStatus.setState( 469 static_cast<uint8_t>(FwUpdateStatusCache::fwStateUpdateSuccess)); 470 } 471 472 static bool getFirmwareUpdateMode() 473 { 474 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 475 try 476 { 477 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath); 478 ipmi::Value state = ipmi::getDbusProperty( 479 *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState"); 480 std::string bmcState = std::get<std::string>(state); 481 return (bmcState == bmcStateUpdateInProgress); 482 } 483 catch (const std::exception& e) 484 { 485 phosphor::logging::log<phosphor::logging::level::ERR>( 486 "Exception caught while getting BMC state.", 487 phosphor::logging::entry("EXCEPTION=%s", e.what())); 488 throw; 489 } 490 } 491 492 static void setFirmwareUpdateMode(const bool mode) 493 { 494 495 std::string bmcState(bmcStateReady); 496 if (mode) 497 { 498 bmcState = bmcStateUpdateInProgress; 499 } 500 501 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 502 try 503 { 504 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath); 505 ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf, 506 "CurrentBMCState", bmcState); 507 } 508 catch (const std::exception& e) 509 { 510 phosphor::logging::log<phosphor::logging::level::ERR>( 511 "Exception caught while setting BMC state.", 512 phosphor::logging::entry("EXCEPTION=%s", e.what())); 513 throw; 514 } 515 } 516 517 /** @brief check if channel IPMB 518 * 519 * This function checks if the command is from IPMB 520 * 521 * @param[in] ctx - context of current session. 522 * @returns true if the medium is IPMB else return true. 523 **/ 524 ipmi::Cc checkIPMBChannel(const ipmi::Context::ptr& ctx, bool& isIPMBChannel) 525 { 526 ipmi::ChannelInfo chInfo; 527 528 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess) 529 { 530 phosphor::logging::log<phosphor::logging::level::ERR>( 531 "Failed to get Channel Info", 532 phosphor::logging::entry("CHANNEL=%d", ctx->channel)); 533 return ipmi::ccUnspecifiedError; 534 } 535 536 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) == 537 ipmi::EChannelMediumType::ipmb) 538 { 539 isIPMBChannel = true; 540 } 541 return ipmi::ccSuccess; 542 } 543 544 static void postTransferCompleteHandler( 545 std::unique_ptr<sdbusplus::bus::match::match>& fwUpdateMatchSignal) 546 { 547 // Setup timer for watching signal 548 static phosphor::Timer timer( 549 [&fwUpdateMatchSignal]() { fwUpdateMatchSignal = nullptr; }); 550 551 static phosphor::Timer activationStatusTimer([]() { 552 if (fwUpdateStatus.activationTimerTimeout() > 95) 553 { 554 activationStatusTimer.stop(); 555 fwUpdateStatus.setState( 556 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify)); 557 } 558 }); 559 560 timer.start(std::chrono::microseconds(5000000), false); 561 562 // callback function for capturing signal 563 auto callback = [&](sdbusplus::message::message& m) { 564 bool flag = false; 565 566 std::vector<std::pair< 567 std::string, 568 std::vector<std::pair<std::string, std::variant<std::string>>>>> 569 intfPropsPair; 570 sdbusplus::message::object_path objPath; 571 572 try 573 { 574 m.read(objPath, intfPropsPair); // Read in the object path 575 // that was just created 576 } 577 catch (const std::exception& e) 578 { 579 phosphor::logging::log<phosphor::logging::level::ERR>( 580 "Exception caught in reading created object path."); 581 return; 582 } 583 // constructing response message 584 phosphor::logging::log<phosphor::logging::level::INFO>( 585 "New Interface Added.", 586 phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str())); 587 for (auto& interface : intfPropsPair) 588 { 589 if (interface.first == "xyz.openbmc_project.Software.Activation") 590 { 591 // There are chances of getting two signals for 592 // InterfacesAdded. So cross check and discrad second instance. 593 if (fwUpdateMatchSignal == nullptr) 594 { 595 return; 596 } 597 // Found our interface, disable callbacks 598 fwUpdateMatchSignal = nullptr; 599 600 phosphor::logging::log<phosphor::logging::level::INFO>( 601 "Start activationStatusTimer for status."); 602 try 603 { 604 timer.stop(); 605 activationStatusTimer.start( 606 std::chrono::microseconds(3000000), true); 607 } 608 catch (const std::exception& e) 609 { 610 phosphor::logging::log<phosphor::logging::level::ERR>( 611 "Exception caught in start activationStatusTimer.", 612 phosphor::logging::entry("ERROR=%s", e.what())); 613 } 614 615 fwUpdateStatus.updateActivationPercent(objPath.str); 616 activateImage(objPath.str); 617 } 618 } 619 }; 620 621 // Adding matcher 622 fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match::match>( 623 *getSdBus(), 624 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 625 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 626 callback); 627 } 628 static bool startFirmwareUpdate(const std::string& uri) 629 { 630 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time 631 // the code gets to this point, the file should be transferred start the 632 // request (creating a new file in /tmp/images causes the update manager to 633 // check if it is ready for activation) 634 static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatchSignal; 635 postTransferCompleteHandler(fwUpdateMatchSignal); 636 std::filesystem::rename( 637 uri, "/tmp/images/" + 638 boost::uuids::to_string(boost::uuids::random_generator()())); 639 return true; 640 } 641 642 static int transferImageFromFile(const std::string& uri, bool move = true) 643 { 644 std::error_code ec; 645 phosphor::logging::log<phosphor::logging::level::INFO>( 646 "Transfer Image From File.", 647 phosphor::logging::entry("URI=%s", uri.c_str())); 648 if (move) 649 { 650 std::filesystem::rename(uri, firmwareBufferFile, ec); 651 } 652 else 653 { 654 std::filesystem::copy(uri, firmwareBufferFile, 655 std::filesystem::copy_options::overwrite_existing, 656 ec); 657 } 658 if (xferHashCheck) 659 { 660 MappedFile mappedfw(uri); 661 xferHashCheck->hash( 662 {mappedfw.data(), mappedfw.data() + mappedfw.size()}); 663 } 664 if (ec.value()) 665 { 666 phosphor::logging::log<phosphor::logging::level::ERR>( 667 "Image copy failed."); 668 } 669 return ec.value(); 670 } 671 672 template <typename... ArgTypes> 673 static int executeCmd(const char* path, ArgTypes&&... tArgs) 674 { 675 boost::process::child execProg(path, const_cast<char*>(tArgs)...); 676 execProg.wait(); 677 return execProg.exit_code(); 678 } 679 680 static int transferImageFromUsb(const std::string& uri) 681 { 682 int ret, sysret; 683 char fwpath[fwPathMaxLength]; 684 phosphor::logging::log<phosphor::logging::level::INFO>( 685 "Transfer Image From USB.", 686 phosphor::logging::entry("URI=%s", uri.c_str())); 687 ret = executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage, 688 fwUpdateMountPoint); 689 if (ret) 690 { 691 return ret; 692 } 693 694 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri; 695 ret = transferImageFromFile(usb_path, false); 696 697 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage, fwUpdateMountPoint); 698 return ret; 699 } 700 701 static bool transferFirmwareFromUri(const std::string& uri) 702 { 703 static constexpr char fwUriFile[] = "file://"; 704 static constexpr char fwUriUsb[] = "usb://"; 705 phosphor::logging::log<phosphor::logging::level::INFO>( 706 "Transfer Image From URI.", 707 phosphor::logging::entry("URI=%s", uri.c_str())); 708 if (boost::algorithm::starts_with(uri, fwUriFile)) 709 { 710 std::string fname = uri.substr(sizeof(fwUriFile) - 1); 711 if (fname != firmwareBufferFile) 712 { 713 return 0 == transferImageFromFile(fname); 714 } 715 return true; 716 } 717 if (boost::algorithm::starts_with(uri, fwUriUsb)) 718 { 719 std::string fname = uri.substr(sizeof(fwUriUsb) - 1); 720 return 0 == transferImageFromUsb(fname); 721 } 722 return false; 723 } 724 725 /* Get USB-mass-storage device status: inserted => true, ejected => false */ 726 static bool getUsbStatus() 727 { 728 std::filesystem::path usbDevPath = 729 std::filesystem::path("/sys/kernel/config/usb_gadget") / 730 fwUpdateUSBDevName; 731 return (std::filesystem::exists(usbDevPath) ? true : false); 732 } 733 734 /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */ 735 static int attachUsbDevice() 736 { 737 if (getUsbStatus()) 738 { 739 return 1; 740 } 741 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage, 742 std::to_string(maxFirmwareImageSize / 1_MB).c_str()); 743 if (!ret) 744 { 745 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName, 746 fwUpdateUsbVolImage); 747 } 748 return ret; 749 } 750 751 /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */ 752 static int detachUsbDevice() 753 { 754 if (!getUsbStatus()) 755 { 756 return 1; 757 } 758 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName); 759 } 760 static uint8_t getActiveBootImage(ipmi::Context::ptr ctx) 761 { 762 constexpr uint8_t undefinedImage = 0x00; 763 constexpr uint8_t primaryImage = 0x01; 764 constexpr uint8_t secondaryImage = 0x02; 765 constexpr const char* secondaryFitImageStartAddr = "22480000"; 766 767 uint8_t bootImage = primaryImage; 768 boost::system::error_code ec; 769 std::string value = ctx->bus->yield_method_call<std::string>( 770 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager", 771 "/xyz/openbmc_project/u_boot/environment/mgr", 772 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd"); 773 if (ec) 774 { 775 phosphor::logging::log<phosphor::logging::level::ERR>( 776 "Failed to read the bootcmd value"); 777 /* don't fail, just give back undefined until it is ready */ 778 bootImage = undefinedImage; 779 } 780 781 /* cheking for secondary FitImage Address 22480000 */ 782 else if (value.find(secondaryFitImageStartAddr) != std::string::npos) 783 { 784 bootImage = secondaryImage; 785 } 786 else 787 { 788 bootImage = primaryImage; 789 } 790 791 return bootImage; 792 } 793 794 #ifdef INTEL_PFR_ENABLED 795 using fwVersionInfoType = std::tuple<uint8_t, // ID Tag 796 uint8_t, // Major Version Number 797 uint8_t, // Minor Version Number 798 uint32_t, // Build Number 799 uint32_t, // Build Timestamp 800 uint32_t>; // Update Timestamp 801 ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo() 802 { 803 // Byte 1 - Count (N) Number of devices data is being returned for. 804 // Bytes 2:16 - Device firmare information(fwVersionInfoType) 805 // Bytes - 17:(15xN) - Repeat of 2 through 16 806 807 std::vector<fwVersionInfoType> fwVerInfoList; 808 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 809 for (const auto& fwDev : fwVersionIdMap) 810 { 811 std::string verStr; 812 try 813 { 814 auto service = ipmi::getService(*busp, versionIntf, fwDev.second); 815 816 ipmi::Value result = ipmi::getDbusProperty( 817 *busp, service, fwDev.second, versionIntf, "Version"); 818 verStr = std::get<std::string>(result); 819 } 820 catch (const std::exception& e) 821 { 822 phosphor::logging::log<phosphor::logging::level::INFO>( 823 "Failed to fetch Version property", 824 phosphor::logging::entry("ERROR=%s", e.what()), 825 phosphor::logging::entry("PATH=%s", fwDev.second), 826 phosphor::logging::entry("INTERFACE=%s", versionIntf)); 827 continue; 828 } 829 830 if (verStr.empty()) 831 { 832 phosphor::logging::log<phosphor::logging::level::INFO>( 833 "Version is empty.", 834 phosphor::logging::entry("PATH=%s", fwDev.second), 835 phosphor::logging::entry("INTERFACE=%s", versionIntf)); 836 continue; 837 } 838 839 // BMC Version format: <major>.<minor>-<build bum>-<build hash> 840 std::vector<std::string> splitVer; 841 boost::split(splitVer, verStr, boost::is_any_of(".-")); 842 if (splitVer.size() < 3) 843 { 844 phosphor::logging::log<phosphor::logging::level::INFO>( 845 "Invalid Version format.", 846 phosphor::logging::entry("Version=%s", verStr.c_str()), 847 phosphor::logging::entry("PATH=%s", fwDev.second)); 848 continue; 849 } 850 851 uint8_t majorNum = 0; 852 uint8_t minorNum = 0; 853 uint32_t buildNum = 0; 854 try 855 { 856 majorNum = std::stoul(splitVer[0], nullptr, 16); 857 minorNum = std::stoul(splitVer[1], nullptr, 16); 858 buildNum = std::stoul(splitVer[2], nullptr, 16); 859 } 860 catch (const std::exception& e) 861 { 862 phosphor::logging::log<phosphor::logging::level::INFO>( 863 "Failed to convert stoul.", 864 phosphor::logging::entry("ERROR=%s", e.what())); 865 continue; 866 } 867 868 // Build Timestamp - Not supported. 869 // Update Timestamp - TODO: Need to check with CPLD team. 870 fwVerInfoList.emplace_back( 871 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum, 872 minorNum, buildNum, 0, 0)); 873 } 874 875 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList); 876 } 877 using fwSecurityVersionInfoType = std::tuple<uint8_t, // ID Tag 878 uint8_t, // BKC Version 879 uint8_t>; // SVN Version 880 ipmi::RspType<uint8_t, std::vector<fwSecurityVersionInfoType>> 881 ipmiGetFwSecurityVersionInfo() 882 { 883 // TODO: Need to add support. 884 return ipmi::responseInvalidCommand(); 885 } 886 887 ipmi::RspType<std::array<uint8_t, certKeyLen>, 888 std::optional<std::array<uint8_t, cskSignatureLen>>> 889 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId) 890 { 891 bool isIPMBChannel = false; 892 893 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 894 { 895 return ipmi::responseUnspecifiedError(); 896 } 897 if (isIPMBChannel) 898 { 899 phosphor::logging::log<phosphor::logging::level::INFO>( 900 "Command not supported. Failed to get root certificate data."); 901 return ipmi::responseCommandNotAvailable(); 902 } 903 904 size_t certKeyOffset = 0; 905 size_t cskSigOffset = 0; 906 std::string mtdDev; 907 908 switch (static_cast<FwGetRootCertDataTag>(certId)) 909 { 910 case FwGetRootCertDataTag::activeRootKey: 911 { 912 mtdDev = bmcActivePfmMTDDev; 913 certKeyOffset = rootkeyOffsetInPfm; 914 break; 915 } 916 case FwGetRootCertDataTag::recoveryRootKey: 917 { 918 mtdDev = bmcRecoveryImgMTDDev; 919 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm; 920 break; 921 } 922 case FwGetRootCertDataTag::activeCSK: 923 { 924 mtdDev = bmcActivePfmMTDDev; 925 certKeyOffset = cskKeyOffsetInPfm; 926 cskSigOffset = cskSignatureOffsetInPfm; 927 break; 928 } 929 case FwGetRootCertDataTag::recoveryCSK: 930 { 931 mtdDev = bmcRecoveryImgMTDDev; 932 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm; 933 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm; 934 break; 935 } 936 default: 937 { 938 return ipmi::responseInvalidFieldRequest(); 939 } 940 } 941 942 std::array<uint8_t, certKeyLen> certKey = {0}; 943 944 try 945 { 946 SPIDev spiDev(mtdDev); 947 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data()); 948 949 if (cskSigOffset) 950 { 951 std::array<uint8_t, cskSignatureLen> cskSignature = {0}; 952 spiDev.spiReadData(cskSigOffset, cskSignatureLen, 953 cskSignature.data()); 954 return ipmi::responseSuccess(certKey, cskSignature); 955 } 956 } 957 catch (const std::exception& e) 958 { 959 phosphor::logging::log<phosphor::logging::level::ERR>( 960 "Exception caught in ipmiGetFwRootCertData", 961 phosphor::logging::entry("MSG=%s", e.what())); 962 return ipmi::responseUnspecifiedError(); 963 } 964 965 return ipmi::responseSuccess(certKey, std::nullopt); 966 } 967 #endif // INTEL_PFR_ENABLED 968 969 static constexpr uint8_t channelListSize = 3; 970 /** @brief implements Maximum Firmware Transfer size command 971 * @parameter 972 * - none 973 * @returns IPMI completion code plus response data 974 * - count - channel count 975 * - channelList - channel list information 976 */ 977 ipmi::RspType<uint8_t, // channel count 978 std::array<std::tuple<uint8_t, uint32_t>, 979 channelListSize> // Channel List 980 > 981 ipmiFirmwareMaxTransferSize() 982 { 983 constexpr size_t kcsMaxBufSize = 128; 984 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024; 985 // Byte 1 - Count (N) Number of devices data is being returned for. 986 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb 987 // Byte 3-6 - transfer size (little endian) 988 // Bytes - 7:(5xN) - Repeat of 2 through 6 989 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize> 990 channelList = { 991 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize}, 992 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus), 993 rmcpPlusMaxBufSize}}}; 994 995 return ipmi::responseSuccess(channelListSize, channelList); 996 } 997 998 ipmi::RspType<uint8_t, uint8_t> 999 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx) 1000 { 1001 // Byte 1 - Current execution context 1002 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode 1003 // Byte 2 - Partition pointer 1004 // 0x01 - primary, 0x02 - secondary 1005 uint8_t partitionPtr = getActiveBootImage(ctx); 1006 1007 return ipmi::responseSuccess( 1008 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr); 1009 } 1010 /** @brief Get Firmware Update Random Number 1011 * 1012 * This function generate the random number used for 1013 * setting the firmware update mode as authentication key. 1014 * 1015 * @param[in] ctx - context of current session 1016 * @returns IPMI completion code along with 1017 * - random number 1018 **/ 1019 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>> 1020 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx) 1021 { 1022 phosphor::logging::log<phosphor::logging::level::INFO>( 1023 "Generate FW update random number"); 1024 bool isIPMBChannel = false; 1025 1026 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1027 { 1028 return ipmi::responseUnspecifiedError(); 1029 } 1030 if (isIPMBChannel) 1031 { 1032 phosphor::logging::log<phosphor::logging::level::INFO>( 1033 "Channel not supported. Failed to fetch FW update random number"); 1034 return ipmi::responseCommandNotAvailable(); 1035 } 1036 std::random_device rd; 1037 std::default_random_engine gen(rd()); 1038 std::uniform_int_distribution<> dist{0, 255}; 1039 1040 fwRandomNumGenTs = std::chrono::steady_clock::now(); 1041 1042 for (int i = 0; i < fwRandomNumLength; i++) 1043 { 1044 fwRandomNum[i] = dist(gen); 1045 } 1046 1047 return ipmi::responseSuccess(fwRandomNum); 1048 } 1049 1050 /** @brief Set Firmware Update Mode 1051 * 1052 * This function sets BMC into firmware update mode 1053 * after validating Random number obtained from the Get 1054 * Firmware Update Random Number command 1055 * 1056 * @param[in] ctx - context of current session 1057 * @parameter randNum - Random number(token) 1058 * @returns IPMI completion code 1059 **/ 1060 ipmi::RspType<> 1061 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx, 1062 std::array<uint8_t, fwRandomNumLength>& randNum) 1063 { 1064 phosphor::logging::log<phosphor::logging::level::INFO>( 1065 "Start FW update mode"); 1066 1067 bool isIPMBChannel = false; 1068 1069 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1070 { 1071 return ipmi::responseUnspecifiedError(); 1072 } 1073 if (isIPMBChannel) 1074 { 1075 phosphor::logging::log<phosphor::logging::level::INFO>( 1076 "Channel not supported. Failed to set FW update mode"); 1077 return ipmi::responseCommandNotAvailable(); 1078 } 1079 /* Firmware Update Random number is valid for 30 seconds only */ 1080 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs); 1081 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed) 1082 .count() > std::chrono::duration_cast<std::chrono::microseconds>( 1083 fwRandomNumExpirySeconds) 1084 .count()) 1085 { 1086 phosphor::logging::log<phosphor::logging::level::INFO>( 1087 "Firmware update random number expired."); 1088 return ipmi::responseInvalidFieldRequest(); 1089 } 1090 1091 /* Validate random number */ 1092 for (int i = 0; i < fwRandomNumLength; i++) 1093 { 1094 if (fwRandomNum[i] != randNum[i]) 1095 { 1096 phosphor::logging::log<phosphor::logging::level::INFO>( 1097 "Invalid random number specified."); 1098 return ipmi::responseInvalidFieldRequest(); 1099 } 1100 } 1101 1102 try 1103 { 1104 if (getFirmwareUpdateMode()) 1105 { 1106 phosphor::logging::log<phosphor::logging::level::INFO>( 1107 "Already firmware update is in progress."); 1108 return ipmi::responseBusy(); 1109 } 1110 } 1111 catch (const std::exception& e) 1112 { 1113 return ipmi::responseUnspecifiedError(); 1114 } 1115 1116 // FIXME? c++ doesn't off an option for exclusive file creation 1117 FILE* fp = fopen(firmwareBufferFile, "wx"); 1118 if (!fp) 1119 { 1120 phosphor::logging::log<phosphor::logging::level::INFO>( 1121 "Unable to open file."); 1122 return ipmi::responseUnspecifiedError(); 1123 } 1124 fclose(fp); 1125 1126 try 1127 { 1128 setFirmwareUpdateMode(true); 1129 } 1130 catch (const std::exception& e) 1131 { 1132 unlink(firmwareBufferFile); 1133 return ipmi::responseUnspecifiedError(); 1134 } 1135 1136 return ipmi::responseSuccess(); 1137 } 1138 1139 /** @brief implements exit firmware update mode command 1140 * @param None 1141 * 1142 * @returns IPMI completion code 1143 */ 1144 ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx) 1145 { 1146 phosphor::logging::log<phosphor::logging::level::INFO>( 1147 "Exit FW update mode"); 1148 bool isIPMBChannel = false; 1149 1150 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1151 { 1152 return ipmi::responseUnspecifiedError(); 1153 } 1154 if (isIPMBChannel) 1155 { 1156 phosphor::logging::log<phosphor::logging::level::INFO>( 1157 "Command not supported. Failed to exit firmware update mode"); 1158 return ipmi::responseCommandNotAvailable(); 1159 } 1160 1161 switch (fwUpdateStatus.getState()) 1162 { 1163 case FwUpdateStatusCache::fwStateInit: 1164 case FwUpdateStatusCache::fwStateIdle: 1165 return ipmi::responseInvalidFieldRequest(); 1166 break; 1167 case FwUpdateStatusCache::fwStateDownload: 1168 case FwUpdateStatusCache::fwStateVerify: 1169 break; 1170 case FwUpdateStatusCache::fwStateProgram: 1171 break; 1172 case FwUpdateStatusCache::fwStateUpdateSuccess: 1173 case FwUpdateStatusCache::fwStateError: 1174 break; 1175 case FwUpdateStatusCache::fwStateAcCycleRequired: 1176 return ipmi::responseInvalidFieldRequest(); 1177 break; 1178 } 1179 fwUpdateStatus.firmwareUpdateAbortState(); 1180 1181 try 1182 { 1183 setFirmwareUpdateMode(false); 1184 } 1185 catch (const std::exception& e) 1186 { 1187 return ipmi::responseUnspecifiedError(); 1188 } 1189 1190 return ipmi::responseSuccess(); 1191 } 1192 1193 /** @brief implements Get/Set Firmware Update Control 1194 * @param[in] ctx - context of current session 1195 * @parameter 1196 * - Byte 1: Control Byte 1197 * - Byte 2: Firmware filename length (Optional) 1198 * - Byte 3:N: Firmware filename data (Optional) 1199 * @returns IPMI completion code plus response data 1200 * - Byte 2: Current control status 1201 **/ 1202 ipmi::RspType<bool, bool, bool, bool, uint4_t> 1203 ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr& ctx, 1204 const uint8_t controlReq, 1205 const std::optional<std::string>& fileName) 1206 { 1207 bool isIPMBChannel = false; 1208 1209 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1210 { 1211 return ipmi::responseUnspecifiedError(); 1212 } 1213 if (isIPMBChannel) 1214 { 1215 phosphor::logging::log<phosphor::logging::level::INFO>( 1216 "Channel not supported. Failed to get or set FW update control"); 1217 return ipmi::responseCommandNotAvailable(); 1218 } 1219 1220 static std::string fwXferUriPath; 1221 static bool imageTransferStarted = false; 1222 static bool imageTransferCompleted = false; 1223 static bool imageTransferAborted = false; 1224 1225 if ((controlReq != 1226 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) && 1227 (fileName)) 1228 { 1229 phosphor::logging::log<phosphor::logging::level::ERR>( 1230 "Invalid request field (Filename)."); 1231 return ipmi::responseInvalidFieldRequest(); 1232 } 1233 1234 static bool usbAttached = getUsbStatus(); 1235 1236 switch (static_cast<FwUpdateCtrlReq>(controlReq)) 1237 { 1238 case FwUpdateCtrlReq::getCurrentControlStatus: 1239 phosphor::logging::log<phosphor::logging::level::INFO>( 1240 "ipmiGetSetFirmwareUpdateControl: Get status"); 1241 break; 1242 case FwUpdateCtrlReq::imageTransferStart: 1243 { 1244 phosphor::logging::log<phosphor::logging::level::INFO>( 1245 "ipmiGetSetFirmwareUpdateControl: Set transfer start"); 1246 imageTransferStarted = true; 1247 // reset buffer to empty (truncate file) 1248 std::ofstream out(firmwareBufferFile, 1249 std::ofstream::binary | std::ofstream::trunc); 1250 fwXferUriPath = std::string("file://") + firmwareBufferFile; 1251 if (xferHashCheck) 1252 { 1253 xferHashCheck->clear(); 1254 } 1255 // Setting state to download 1256 fwUpdateStatus.setState( 1257 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload)); 1258 #ifdef INTEL_PFR_ENABLED 1259 imgLength = 0; 1260 imgType = 0; 1261 block0Mapped = false; 1262 #endif 1263 } 1264 break; 1265 case FwUpdateCtrlReq::imageTransferComplete: 1266 { 1267 phosphor::logging::log<phosphor::logging::level::INFO>( 1268 "ipmiGetSetFirmwareUpdateControl: Set transfer complete."); 1269 if (usbAttached) 1270 { 1271 phosphor::logging::log<phosphor::logging::level::ERR>( 1272 "USB should be detached to perform this operation."); 1273 return ipmi::responseNotSupportedInPresentState(); 1274 } 1275 // finish transfer based on URI 1276 if (!transferFirmwareFromUri(fwXferUriPath)) 1277 { 1278 phosphor::logging::log<phosphor::logging::level::ERR>( 1279 "transferFirmwareFromUri failed."); 1280 return ipmi::responseUnspecifiedError(); 1281 } 1282 // transfer complete 1283 if (xferHashCheck) 1284 { 1285 if (TransferHashCheck::HashCheck::sha2Success != 1286 xferHashCheck->verify()) 1287 { 1288 phosphor::logging::log<phosphor::logging::level::ERR>( 1289 "xferHashCheck failed."); 1290 return ipmi::responseUnspecifiedError(); 1291 } 1292 } 1293 // Set state to verify and start the update 1294 fwUpdateStatus.setState( 1295 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify)); 1296 // start the request 1297 if (!startFirmwareUpdate(firmwareBufferFile)) 1298 { 1299 phosphor::logging::log<phosphor::logging::level::ERR>( 1300 "startFirmwareUpdate failed."); 1301 return ipmi::responseUnspecifiedError(); 1302 } 1303 imageTransferCompleted = true; 1304 } 1305 break; 1306 case FwUpdateCtrlReq::imageTransferAbort: 1307 phosphor::logging::log<phosphor::logging::level::INFO>( 1308 "ipmiGetSetFirmwareUpdateControl: Set transfer abort."); 1309 if (usbAttached) 1310 { 1311 if (detachUsbDevice()) 1312 { 1313 phosphor::logging::log<phosphor::logging::level::ERR>( 1314 "Detach USB device failed."); 1315 return ipmi::responseUsbAttachOrDetachFailed(); 1316 } 1317 usbAttached = false; 1318 } 1319 // During abort request reset the state to Init by cleaning update 1320 // file. 1321 fwUpdateStatus.firmwareUpdateAbortState(); 1322 imageTransferAborted = true; 1323 break; 1324 case FwUpdateCtrlReq::setFirmwareFilename: 1325 phosphor::logging::log<phosphor::logging::level::INFO>( 1326 "ipmiGetSetFirmwareUpdateControl: Set filename."); 1327 if (!fileName || ((*fileName).length() == 0)) 1328 { 1329 phosphor::logging::log<phosphor::logging::level::ERR>( 1330 "Invalid Filename specified."); 1331 return ipmi::responseInvalidFieldRequest(); 1332 } 1333 1334 fwXferUriPath = *fileName; 1335 break; 1336 case FwUpdateCtrlReq::attachUsbDevice: 1337 phosphor::logging::log<phosphor::logging::level::INFO>( 1338 "ipmiGetSetFirmwareUpdateControl: Attach USB device."); 1339 if (usbAttached) 1340 { 1341 phosphor::logging::log<phosphor::logging::level::ERR>( 1342 "USB device is already attached."); 1343 return ipmi::responseInvalidFieldRequest(); 1344 } 1345 if (attachUsbDevice()) 1346 { 1347 phosphor::logging::log<phosphor::logging::level::ERR>( 1348 "Attach USB device failed."); 1349 return ipmi::responseUsbAttachOrDetachFailed(); 1350 } 1351 usbAttached = true; 1352 break; 1353 case FwUpdateCtrlReq::detachUsbDevice: 1354 phosphor::logging::log<phosphor::logging::level::INFO>( 1355 "ipmiGetSetFirmwareUpdateControl: Detach USB device."); 1356 if (!usbAttached) 1357 { 1358 phosphor::logging::log<phosphor::logging::level::ERR>( 1359 "USB device is not attached."); 1360 return ipmi::responseInvalidFieldRequest(); 1361 } 1362 if (detachUsbDevice()) 1363 { 1364 phosphor::logging::log<phosphor::logging::level::ERR>( 1365 "Detach USB device failed."); 1366 return ipmi::responseUsbAttachOrDetachFailed(); 1367 } 1368 usbAttached = false; 1369 break; 1370 default: 1371 phosphor::logging::log<phosphor::logging::level::ERR>( 1372 "Invalid control option specified."); 1373 return ipmi::responseInvalidFieldRequest(); 1374 } 1375 1376 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted, 1377 imageTransferAborted, usbAttached, uint4_t(0)); 1378 } 1379 1380 /** @brief implements firmware get status command 1381 * @parameter 1382 * - none 1383 * @returns IPMI completion code plus response data 1384 * - status - processing status 1385 * - percentage - percentage completion 1386 * - check - channel integrity check status 1387 **/ 1388 ipmi::RspType<uint8_t, // status 1389 uint8_t, // percentage 1390 uint8_t // check 1391 > 1392 ipmiGetFirmwareUpdateStatus() 1393 1394 { 1395 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write, 1396 // 5=ready, f=error, 83=ac cycle required) 1397 // Byte 2 - percent 1398 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail) 1399 uint8_t status = fwUpdateStatus.getState(); 1400 uint8_t percent = fwUpdateStatus.percent(); 1401 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0; 1402 1403 // Status code. 1404 return ipmi::responseSuccess(status, percent, check); 1405 } 1406 1407 ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions( 1408 const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask, 1409 bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart, 1410 bool sha2Check, uint5_t reserved2, 1411 std::optional<std::vector<uint8_t>> integrityCheckVal) 1412 { 1413 phosphor::logging::log<phosphor::logging::level::INFO>( 1414 "Set firmware update options."); 1415 bool isIPMBChannel = false; 1416 1417 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1418 { 1419 return ipmi::responseUnspecifiedError(); 1420 } 1421 if (isIPMBChannel) 1422 { 1423 phosphor::logging::log<phosphor::logging::level::INFO>( 1424 "Channel not supported. Failed to set firmware update options"); 1425 return ipmi::responseCommandNotAvailable(); 1426 } 1427 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade(); 1428 bool deferRestartState = fwUpdateStatus.getDeferRestart(); 1429 bool sha2CheckState = xferHashCheck ? true : false; 1430 1431 if (noDowngradeMask && (noDowngradeState != noDowngrade)) 1432 { 1433 fwUpdateStatus.setInhibitDowngrade(noDowngrade); 1434 noDowngradeState = noDowngrade; 1435 } 1436 if (deferRestartMask && (deferRestartState != deferRestart)) 1437 { 1438 fwUpdateStatus.setDeferRestart(deferRestart); 1439 deferRestartState = deferRestart; 1440 } 1441 if (sha2CheckMask) 1442 { 1443 if (sha2Check) 1444 { 1445 auto hashSize = EVP_MD_size(EVP_sha256()); 1446 if ((*integrityCheckVal).size() != hashSize) 1447 { 1448 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1449 "Invalid size of Hash specified."); 1450 return ipmi::responseInvalidFieldRequest(); 1451 } 1452 xferHashCheck = std::make_shared<TransferHashCheck>(); 1453 xferHashCheck->init(*integrityCheckVal); 1454 } 1455 else 1456 { 1457 // delete the xferHashCheck object 1458 xferHashCheck.reset(); 1459 } 1460 sha2CheckState = sha2CheckMask; 1461 } 1462 return ipmi::responseSuccess(noDowngradeState, deferRestartState, 1463 sha2CheckState, reserved1); 1464 } 1465 1466 ipmi::RspType<uint32_t> 1467 ipmiFwImageWriteData(const std::vector<uint8_t>& writeData) 1468 { 1469 const uint8_t ccCmdNotSupportedInPresentState = 0xD5; 1470 size_t writeDataLen = writeData.size(); 1471 1472 if (!writeDataLen) 1473 { 1474 return ipmi::responseReqDataLenInvalid(); 1475 } 1476 1477 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload) 1478 { 1479 phosphor::logging::log<phosphor::logging::level::ERR>( 1480 "Invalid firmware update state."); 1481 return ipmi::response(ccCmdNotSupportedInPresentState); 1482 } 1483 1484 std::ofstream out(firmwareBufferFile, 1485 std::ofstream::binary | std::ofstream::app); 1486 if (!out) 1487 { 1488 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1489 "Error while opening file."); 1490 return ipmi::responseUnspecifiedError(); 1491 } 1492 1493 uint64_t fileDataLen = out.tellp(); 1494 1495 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize) 1496 { 1497 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1498 "Firmware image size exceeds the limit"); 1499 return ipmi::responseInvalidFieldRequest(); 1500 } 1501 1502 const char* data = reinterpret_cast<const char*>(writeData.data()); 1503 out.write(data, writeDataLen); 1504 out.close(); 1505 1506 if (xferHashCheck) 1507 { 1508 xferHashCheck->hash(writeData); 1509 } 1510 1511 #ifdef INTEL_PFR_ENABLED 1512 /* PFR image block 0 - As defined in HAS */ 1513 struct PFRImageBlock0 1514 { 1515 uint32_t tag; 1516 uint32_t pcLength; 1517 uint32_t pcType; 1518 uint32_t reserved1; 1519 uint8_t hash256[32]; 1520 uint8_t hash384[48]; 1521 uint8_t reserved2[32]; 1522 } __attribute__((packed)); 1523 1524 /* Get the PFR block 0 data and read the uploaded image 1525 * information( Image type, length etc) */ 1526 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) && 1527 (!block0Mapped)) 1528 { 1529 struct PFRImageBlock0 block0Data = {0}; 1530 1531 std::ifstream inFile(firmwareBufferFile, 1532 std::ios::binary | std::ios::in); 1533 inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data)); 1534 inFile.close(); 1535 1536 uint32_t magicNum = block0Data.tag; 1537 1538 /* Validate the magic number */ 1539 if (magicNum != perBlock0MagicNum) 1540 { 1541 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1542 "PFR image magic number not matched"); 1543 return ipmi::responseInvalidFieldRequest(); 1544 } 1545 // Note:imgLength, imgType and block0Mapped are in global scope, as 1546 // these are used in cascaded updates. 1547 imgLength = block0Data.pcLength; 1548 imgType = block0Data.pcType; 1549 block0Mapped = true; 1550 } 1551 #endif // end of INTEL_PFR_ENABLED 1552 return ipmi::responseSuccess(writeDataLen); 1553 } 1554 1555 static void registerFirmwareFunctions() 1556 { 1557 // guarantee that we start with an already timed out timestamp 1558 fwRandomNumGenTs = 1559 std::chrono::steady_clock::now() - fwRandomNumExpirySeconds; 1560 fwUpdateStatus.setState( 1561 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit)); 1562 1563 unlink(firmwareBufferFile); 1564 1565 #ifdef INTEL_PFR_ENABLED 1566 // Following commands are supported only for PFR enabled platforms 1567 // CMD:0x20 - Get Firmware Version Information 1568 // CMD:0x21 - Get Firmware Security Version Information 1569 // CMD:0x25 - Get Root Certificate Data 1570 1571 // get firmware version information 1572 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1573 ipmi::firmware::cmdGetFwVersionInfo, 1574 ipmi::Privilege::Admin, ipmiGetFwVersionInfo); 1575 1576 // get firmware security version information 1577 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1578 ipmi::firmware::cmdGetFwSecurityVersionInfo, 1579 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo); 1580 1581 // get root certificate data 1582 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1583 ipmi::firmware::cmdFwGetRootCertData, 1584 ipmi::Privilege::Admin, ipmiGetFwRootCertData); 1585 #endif 1586 1587 // get firmware update channel information (max transfer sizes) 1588 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1589 ipmi::firmware::cmdGetFwUpdateChannelInfo, 1590 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize); 1591 1592 // get bmc execution context 1593 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1594 ipmi::firmware::cmdGetBmcExecutionContext, 1595 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext); 1596 1597 // Get Firmware Update Random number 1598 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1599 ipmi::firmware::cmdGetFwUpdateRandomNumber, 1600 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber); 1601 1602 // Set Firmware Update Mode 1603 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1604 ipmi::firmware::cmdSetFirmwareUpdateMode, 1605 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode); 1606 1607 // Exit Firmware Update Mode 1608 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1609 ipmi::firmware::cmdExitFirmwareUpdateMode, 1610 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode); 1611 1612 // Get/Set Firmware Update Control 1613 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1614 ipmi::firmware::cmdGetSetFwUpdateControl, 1615 ipmi::Privilege::Admin, 1616 ipmiGetSetFirmwareUpdateControl); 1617 1618 // Get Firmware Update Status 1619 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1620 ipmi::firmware::cmdGetFirmwareUpdateStatus, 1621 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus); 1622 1623 // Set Firmware Update Options 1624 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1625 ipmi::firmware::cmdSetFirmwareUpdateOptions, 1626 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions); 1627 // write image data 1628 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1629 ipmi::firmware::cmdFwImageWriteData, 1630 ipmi::Privilege::Admin, ipmiFwImageWriteData); 1631 return; 1632 } 1633