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