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::filesystem::rename( 654 uri, "/tmp/images/" + 655 boost::uuids::to_string(boost::uuids::random_generator()())); 656 return true; 657 } 658 659 static bool transferImageFromFile(const std::string& uri, bool move = true) 660 { 661 std::error_code ec; 662 phosphor::logging::log<phosphor::logging::level::INFO>( 663 "Transfer Image From File.", 664 phosphor::logging::entry("URI=%s", uri.c_str())); 665 if (move) 666 { 667 std::filesystem::rename(uri, firmwareBufferFile, ec); 668 } 669 else 670 { 671 std::filesystem::copy(uri, firmwareBufferFile, 672 std::filesystem::copy_options::overwrite_existing, 673 ec); 674 } 675 if (xferHashCheck) 676 { 677 MappedFile mappedfw(uri); 678 if (!xferHashCheck->hash( 679 {mappedfw.data(), mappedfw.data() + mappedfw.size()})) 680 { 681 phosphor::logging::log<phosphor::logging::level::ERR>( 682 "transferImageFromFile: xferHashCheck->hash failed."); 683 return false; 684 } 685 } 686 if (ec.value()) 687 { 688 phosphor::logging::log<phosphor::logging::level::ERR>( 689 "Image copy failed."); 690 return false; 691 } 692 693 return true; 694 } 695 696 template <typename... ArgTypes> 697 static int executeCmd(const char* path, ArgTypes&&... tArgs) 698 { 699 boost::process::child execProg(path, const_cast<char*>(tArgs)...); 700 execProg.wait(); 701 return execProg.exit_code(); 702 } 703 704 static bool transferImageFromUsb(const std::string& uri) 705 { 706 bool ret = false; 707 char fwpath[fwPathMaxLength]; 708 709 phosphor::logging::log<phosphor::logging::level::INFO>( 710 "Transfer Image From USB.", 711 phosphor::logging::entry("URI=%s", uri.c_str())); 712 713 if (executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage, 714 fwUpdateMountPoint) == 0) 715 { 716 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri; 717 ret = transferImageFromFile(usb_path, false); 718 719 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage, 720 fwUpdateMountPoint); 721 } 722 723 return ret; 724 } 725 726 static bool transferFirmwareFromUri(const std::string& uri) 727 { 728 static constexpr char fwUriFile[] = "file://"; 729 static constexpr char fwUriUsb[] = "usb://"; 730 phosphor::logging::log<phosphor::logging::level::INFO>( 731 "Transfer Image From URI.", 732 phosphor::logging::entry("URI=%s", uri.c_str())); 733 if (boost::algorithm::starts_with(uri, fwUriFile)) 734 { 735 std::string fname = uri.substr(sizeof(fwUriFile) - 1); 736 if (fname != firmwareBufferFile) 737 { 738 return transferImageFromFile(fname); 739 } 740 return true; 741 } 742 if (boost::algorithm::starts_with(uri, fwUriUsb)) 743 { 744 std::string fname = uri.substr(sizeof(fwUriUsb) - 1); 745 return transferImageFromUsb(fname); 746 } 747 return false; 748 } 749 750 /* Get USB-mass-storage device status: inserted => true, ejected => false */ 751 static bool getUsbStatus() 752 { 753 std::filesystem::path usbDevPath = 754 std::filesystem::path("/sys/kernel/config/usb_gadget") / 755 fwUpdateUSBDevName; 756 return (std::filesystem::exists(usbDevPath) ? true : false); 757 } 758 759 /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */ 760 static int attachUsbDevice() 761 { 762 if (getUsbStatus()) 763 { 764 return 1; 765 } 766 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage, 767 std::to_string(maxFirmwareImageSize / 1_MB).c_str()); 768 if (!ret) 769 { 770 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName, 771 fwUpdateUsbVolImage); 772 } 773 return ret; 774 } 775 776 /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */ 777 static int detachUsbDevice() 778 { 779 if (!getUsbStatus()) 780 { 781 return 1; 782 } 783 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName); 784 } 785 static uint8_t getActiveBootImage(ipmi::Context::ptr ctx) 786 { 787 constexpr uint8_t undefinedImage = 0x00; 788 constexpr uint8_t primaryImage = 0x01; 789 constexpr uint8_t secondaryImage = 0x02; 790 constexpr const char* secondaryFitImageStartAddr = "22480000"; 791 792 uint8_t bootImage = primaryImage; 793 boost::system::error_code ec; 794 std::string value = ctx->bus->yield_method_call<std::string>( 795 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager", 796 "/xyz/openbmc_project/u_boot/environment/mgr", 797 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd"); 798 if (ec) 799 { 800 phosphor::logging::log<phosphor::logging::level::ERR>( 801 "Failed to read the bootcmd value"); 802 /* don't fail, just give back undefined until it is ready */ 803 bootImage = undefinedImage; 804 } 805 806 /* cheking for secondary FitImage Address 22480000 */ 807 else if (value.find(secondaryFitImageStartAddr) != std::string::npos) 808 { 809 bootImage = secondaryImage; 810 } 811 else 812 { 813 bootImage = primaryImage; 814 } 815 816 return bootImage; 817 } 818 819 #ifdef INTEL_PFR_ENABLED 820 using fwVersionInfoType = std::tuple<uint8_t, // ID Tag 821 uint8_t, // Major Version Number 822 uint8_t, // Minor Version Number 823 uint32_t, // Build Number 824 uint32_t, // Build Timestamp 825 uint32_t>; // Update Timestamp 826 ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo() 827 { 828 // Byte 1 - Count (N) Number of devices data is being returned for. 829 // Bytes 2:16 - Device firmare information(fwVersionInfoType) 830 // Bytes - 17:(15xN) - Repeat of 2 through 16 831 832 std::vector<fwVersionInfoType> fwVerInfoList; 833 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 834 for (const auto& fwDev : fwVersionIdMap) 835 { 836 std::string verStr; 837 try 838 { 839 auto service = ipmi::getService(*busp, versionIntf, fwDev.second); 840 841 ipmi::Value result = ipmi::getDbusProperty( 842 *busp, service, fwDev.second, versionIntf, "Version"); 843 verStr = std::get<std::string>(result); 844 } 845 catch (const std::exception& e) 846 { 847 phosphor::logging::log<phosphor::logging::level::INFO>( 848 "Failed to fetch Version property", 849 phosphor::logging::entry("ERROR=%s", e.what()), 850 phosphor::logging::entry("PATH=%s", fwDev.second), 851 phosphor::logging::entry("INTERFACE=%s", versionIntf)); 852 continue; 853 } 854 855 if (verStr.empty()) 856 { 857 phosphor::logging::log<phosphor::logging::level::INFO>( 858 "Version is empty.", 859 phosphor::logging::entry("PATH=%s", fwDev.second), 860 phosphor::logging::entry("INTERFACE=%s", versionIntf)); 861 continue; 862 } 863 864 uint8_t majorNum = 0; 865 uint8_t minorNum = 0; 866 uint32_t buildNum = 0; 867 try 868 { 869 std::optional<ipmi::MetaRevision> rev = 870 ipmi::convertIntelVersion(verStr); 871 if (rev.has_value()) 872 { 873 ipmi::MetaRevision revision = rev.value(); 874 majorNum = revision.major % 10 + (revision.major / 10) * 16; 875 minorNum = (revision.minor > 99 ? 99 : revision.minor); 876 minorNum = minorNum % 10 + (minorNum / 10) * 16; 877 uint32_t hash = std::stoul(revision.metaHash, 0, 16); 878 hash = bswap_32(hash); 879 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00); 880 } 881 else 882 { 883 std::vector<std::string> splitVer; 884 boost::split(splitVer, verStr, boost::is_any_of(".-")); 885 if (splitVer.size() < 3) 886 { 887 phosphor::logging::log<phosphor::logging::level::INFO>( 888 "Invalid Version format.", 889 phosphor::logging::entry("Version=%s", verStr.c_str()), 890 phosphor::logging::entry("PATH=%s", fwDev.second)); 891 continue; 892 } 893 majorNum = std::stoul(splitVer[0], nullptr, 16); 894 minorNum = std::stoul(splitVer[1], nullptr, 16); 895 buildNum = std::stoul(splitVer[2], nullptr, 16); 896 } 897 // Build Timestamp - Not supported. 898 // Update Timestamp - TODO: Need to check with CPLD team. 899 fwVerInfoList.emplace_back( 900 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum, 901 minorNum, buildNum, 0, 0)); 902 } 903 catch (const std::exception& e) 904 { 905 phosphor::logging::log<phosphor::logging::level::INFO>( 906 "Failed to convert stoul.", 907 phosphor::logging::entry("ERROR=%s", e.what())); 908 continue; 909 } 910 } 911 912 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList); 913 } 914 915 std::array<uint8_t, imageCount> getSecurityVersionInfo(const char* mtdDevBuf, 916 size_t svnVerOffsetInPfm, 917 size_t bkcVerOffsetInPfm) 918 { 919 constexpr size_t bufLength = 1; 920 std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp; 921 constexpr uint8_t svnIndexValue = 0x00; 922 constexpr uint8_t bkcIndexValue = 0x01; 923 constexpr uint8_t tempIndexValue = 0x00; 924 try 925 { 926 SPIDev spiDev(mtdDevBuf); 927 spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data()); 928 fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue); 929 spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data()); 930 fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue); 931 } 932 catch (const std::exception& e) 933 { 934 phosphor::logging::log<phosphor::logging::level::ERR>( 935 "Exception caught in getSecurityVersionInfo", 936 phosphor::logging::entry("MSG=%s", e.what())); 937 fwSecurityVersionBuf = {0, 0}; 938 } 939 940 return fwSecurityVersionBuf; 941 } 942 943 ipmi::RspType< 944 uint8_t, // device ID 945 uint8_t, // Active Image Value 946 std::array<uint8_t, imageCount>, // Security version for Active Image 947 uint8_t, // recovery Image Value 948 std::array<uint8_t, imageCount>> // Security version for Recovery Image 949 ipmiGetFwSecurityVersionInfo() 950 { 951 static bool cacheFlag = false; 952 constexpr std::array<const char*, imageCount> mtdDevBuf = { 953 bmcActivePfmMTDDev, bmcRecoveryImgMTDDev}; 954 955 // To avoid multiple reading from SPI device 956 if (!cacheFlag) 957 { 958 imgFwSecurityVersion[0] = getSecurityVersionInfo( 959 mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm); 960 imgFwSecurityVersion[1] = getSecurityVersionInfo( 961 mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm); 962 cacheFlag = true; 963 } 964 965 constexpr uint8_t ActivePfmMTDDev = 0x00; 966 constexpr uint8_t RecoveryImgMTDDev = 0x01; 967 968 return ipmi::responseSuccess( 969 imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage), 970 imgFwSecurityVersion[ActivePfmMTDDev], 971 static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage), 972 imgFwSecurityVersion[RecoveryImgMTDDev]); 973 } 974 975 ipmi::RspType<std::array<uint8_t, certKeyLen>, 976 std::optional<std::array<uint8_t, cskSignatureLen>>> 977 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId) 978 { 979 bool isIPMBChannel = false; 980 981 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 982 { 983 return ipmi::responseUnspecifiedError(); 984 } 985 if (isIPMBChannel) 986 { 987 phosphor::logging::log<phosphor::logging::level::INFO>( 988 "Command not supported. Failed to get root certificate data."); 989 return ipmi::responseCommandNotAvailable(); 990 } 991 992 size_t certKeyOffset = 0; 993 size_t cskSigOffset = 0; 994 std::string mtdDev; 995 996 switch (static_cast<FwGetRootCertDataTag>(certId)) 997 { 998 case FwGetRootCertDataTag::activeRootKey: 999 { 1000 mtdDev = bmcActivePfmMTDDev; 1001 certKeyOffset = rootkeyOffsetInPfm; 1002 break; 1003 } 1004 case FwGetRootCertDataTag::recoveryRootKey: 1005 { 1006 mtdDev = bmcRecoveryImgMTDDev; 1007 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm; 1008 break; 1009 } 1010 case FwGetRootCertDataTag::activeCSK: 1011 { 1012 mtdDev = bmcActivePfmMTDDev; 1013 certKeyOffset = cskKeyOffsetInPfm; 1014 cskSigOffset = cskSignatureOffsetInPfm; 1015 break; 1016 } 1017 case FwGetRootCertDataTag::recoveryCSK: 1018 { 1019 mtdDev = bmcRecoveryImgMTDDev; 1020 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm; 1021 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm; 1022 break; 1023 } 1024 default: 1025 { 1026 return ipmi::responseInvalidFieldRequest(); 1027 } 1028 } 1029 1030 std::array<uint8_t, certKeyLen> certKey = {0}; 1031 1032 try 1033 { 1034 SPIDev spiDev(mtdDev); 1035 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data()); 1036 1037 if (cskSigOffset) 1038 { 1039 std::array<uint8_t, cskSignatureLen> cskSignature = {0}; 1040 spiDev.spiReadData(cskSigOffset, cskSignatureLen, 1041 cskSignature.data()); 1042 return ipmi::responseSuccess(certKey, cskSignature); 1043 } 1044 } 1045 catch (const std::exception& e) 1046 { 1047 phosphor::logging::log<phosphor::logging::level::ERR>( 1048 "Exception caught in ipmiGetFwRootCertData", 1049 phosphor::logging::entry("MSG=%s", e.what())); 1050 return ipmi::responseUnspecifiedError(); 1051 } 1052 1053 return ipmi::responseSuccess(certKey, std::nullopt); 1054 } 1055 #endif // INTEL_PFR_ENABLED 1056 1057 static constexpr uint8_t channelListSize = 3; 1058 /** @brief implements Maximum Firmware Transfer size command 1059 * @parameter 1060 * - none 1061 * @returns IPMI completion code plus response data 1062 * - count - channel count 1063 * - channelList - channel list information 1064 */ 1065 ipmi::RspType<uint8_t, // channel count 1066 std::array<std::tuple<uint8_t, uint32_t>, 1067 channelListSize> // Channel List 1068 > 1069 ipmiFirmwareMaxTransferSize() 1070 { 1071 constexpr size_t kcsMaxBufSize = 128; 1072 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024; 1073 // Byte 1 - Count (N) Number of devices data is being returned for. 1074 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb 1075 // Byte 3-6 - transfer size (little endian) 1076 // Bytes - 7:(5xN) - Repeat of 2 through 6 1077 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize> 1078 channelList = { 1079 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize}, 1080 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus), 1081 rmcpPlusMaxBufSize}}}; 1082 1083 return ipmi::responseSuccess(channelListSize, channelList); 1084 } 1085 1086 ipmi::RspType<uint8_t, uint8_t> 1087 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx) 1088 { 1089 // Byte 1 - Current execution context 1090 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode 1091 // Byte 2 - Partition pointer 1092 // 0x01 - primary, 0x02 - secondary 1093 uint8_t partitionPtr = getActiveBootImage(ctx); 1094 1095 return ipmi::responseSuccess( 1096 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr); 1097 } 1098 /** @brief Get Firmware Update Random Number 1099 * 1100 * This function generate the random number used for 1101 * setting the firmware update mode as authentication key. 1102 * 1103 * @param[in] ctx - context of current session 1104 * @returns IPMI completion code along with 1105 * - random number 1106 **/ 1107 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>> 1108 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx) 1109 { 1110 phosphor::logging::log<phosphor::logging::level::INFO>( 1111 "Generate FW update random number"); 1112 bool isIPMBChannel = false; 1113 1114 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1115 { 1116 return ipmi::responseUnspecifiedError(); 1117 } 1118 if (isIPMBChannel) 1119 { 1120 phosphor::logging::log<phosphor::logging::level::INFO>( 1121 "Channel not supported. Failed to fetch FW update random number"); 1122 return ipmi::responseCommandNotAvailable(); 1123 } 1124 std::random_device rd; 1125 std::default_random_engine gen(rd()); 1126 std::uniform_int_distribution<> dist{0, 255}; 1127 1128 fwRandomNumGenTs = std::chrono::steady_clock::now(); 1129 1130 for (int i = 0; i < fwRandomNumLength; i++) 1131 { 1132 fwRandomNum[i] = dist(gen); 1133 } 1134 1135 return ipmi::responseSuccess(fwRandomNum); 1136 } 1137 1138 /** @brief Set Firmware Update Mode 1139 * 1140 * This function sets BMC into firmware update mode 1141 * after validating Random number obtained from the Get 1142 * Firmware Update Random Number command 1143 * 1144 * @param[in] ctx - context of current session 1145 * @parameter randNum - Random number(token) 1146 * @returns IPMI completion code 1147 **/ 1148 ipmi::RspType<> 1149 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx, 1150 std::array<uint8_t, fwRandomNumLength>& randNum) 1151 { 1152 phosphor::logging::log<phosphor::logging::level::INFO>( 1153 "Start FW update mode"); 1154 1155 bool isIPMBChannel = false; 1156 1157 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1158 { 1159 return ipmi::responseUnspecifiedError(); 1160 } 1161 if (isIPMBChannel) 1162 { 1163 phosphor::logging::log<phosphor::logging::level::INFO>( 1164 "Channel not supported. Failed to set FW update mode"); 1165 return ipmi::responseCommandNotAvailable(); 1166 } 1167 /* Firmware Update Random number is valid for 30 seconds only */ 1168 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs); 1169 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed) 1170 .count() > std::chrono::duration_cast<std::chrono::microseconds>( 1171 fwRandomNumExpirySeconds) 1172 .count()) 1173 { 1174 phosphor::logging::log<phosphor::logging::level::INFO>( 1175 "Firmware update random number expired."); 1176 return ipmi::responseInvalidFieldRequest(); 1177 } 1178 1179 /* Validate random number */ 1180 for (int i = 0; i < fwRandomNumLength; i++) 1181 { 1182 if (fwRandomNum[i] != randNum[i]) 1183 { 1184 phosphor::logging::log<phosphor::logging::level::INFO>( 1185 "Invalid random number specified."); 1186 return ipmi::responseInvalidFieldRequest(); 1187 } 1188 } 1189 1190 try 1191 { 1192 if (getFirmwareUpdateMode()) 1193 { 1194 phosphor::logging::log<phosphor::logging::level::INFO>( 1195 "Already firmware update is in progress."); 1196 return ipmi::responseBusy(); 1197 } 1198 } 1199 catch (const std::exception& e) 1200 { 1201 return ipmi::responseUnspecifiedError(); 1202 } 1203 1204 // FIXME? c++ doesn't off an option for exclusive file creation 1205 FILE* fp = fopen(firmwareBufferFile, "wx"); 1206 if (!fp) 1207 { 1208 phosphor::logging::log<phosphor::logging::level::INFO>( 1209 "Unable to open file."); 1210 return ipmi::responseUnspecifiedError(); 1211 } 1212 fclose(fp); 1213 1214 try 1215 { 1216 setFirmwareUpdateMode(true); 1217 } 1218 catch (const std::exception& e) 1219 { 1220 unlink(firmwareBufferFile); 1221 return ipmi::responseUnspecifiedError(); 1222 } 1223 1224 return ipmi::responseSuccess(); 1225 } 1226 1227 /** @brief implements exit firmware update mode command 1228 * @param None 1229 * 1230 * @returns IPMI completion code 1231 */ 1232 ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx) 1233 { 1234 phosphor::logging::log<phosphor::logging::level::INFO>( 1235 "Exit FW update mode"); 1236 bool isIPMBChannel = false; 1237 1238 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1239 { 1240 return ipmi::responseUnspecifiedError(); 1241 } 1242 if (isIPMBChannel) 1243 { 1244 phosphor::logging::log<phosphor::logging::level::INFO>( 1245 "Command not supported. Failed to exit firmware update mode"); 1246 return ipmi::responseCommandNotAvailable(); 1247 } 1248 1249 switch (fwUpdateStatus.getState()) 1250 { 1251 case FwUpdateStatusCache::fwStateInit: 1252 case FwUpdateStatusCache::fwStateIdle: 1253 return ipmi::responseInvalidFieldRequest(); 1254 break; 1255 case FwUpdateStatusCache::fwStateDownload: 1256 case FwUpdateStatusCache::fwStateVerify: 1257 break; 1258 case FwUpdateStatusCache::fwStateProgram: 1259 break; 1260 case FwUpdateStatusCache::fwStateUpdateSuccess: 1261 case FwUpdateStatusCache::fwStateError: 1262 break; 1263 case FwUpdateStatusCache::fwStateAcCycleRequired: 1264 return ipmi::responseInvalidFieldRequest(); 1265 break; 1266 } 1267 fwUpdateStatus.firmwareUpdateAbortState(); 1268 1269 try 1270 { 1271 setFirmwareUpdateMode(false); 1272 } 1273 catch (const std::exception& e) 1274 { 1275 return ipmi::responseUnspecifiedError(); 1276 } 1277 1278 return ipmi::responseSuccess(); 1279 } 1280 1281 /** @brief implements Get/Set Firmware Update Control 1282 * @param[in] ctx - context of current session 1283 * @parameter 1284 * - Byte 1: Control Byte 1285 * - Byte 2: Firmware filename length (Optional) 1286 * - Byte 3:N: Firmware filename data (Optional) 1287 * @returns IPMI completion code plus response data 1288 * - Byte 2: Current control status 1289 **/ 1290 ipmi::RspType<bool, bool, bool, bool, uint4_t> 1291 ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr& ctx, 1292 const uint8_t controlReq, 1293 const std::optional<std::string>& fileName) 1294 { 1295 bool isIPMBChannel = false; 1296 1297 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1298 { 1299 return ipmi::responseUnspecifiedError(); 1300 } 1301 if (isIPMBChannel) 1302 { 1303 phosphor::logging::log<phosphor::logging::level::INFO>( 1304 "Channel not supported. Failed to get or set FW update control"); 1305 return ipmi::responseCommandNotAvailable(); 1306 } 1307 1308 static std::string fwXferUriPath; 1309 static bool imageTransferStarted = false; 1310 static bool imageTransferCompleted = false; 1311 static bool imageTransferAborted = false; 1312 1313 if ((controlReq != 1314 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) && 1315 (fileName)) 1316 { 1317 phosphor::logging::log<phosphor::logging::level::ERR>( 1318 "Invalid request field (Filename)."); 1319 return ipmi::responseInvalidFieldRequest(); 1320 } 1321 1322 static bool usbAttached = getUsbStatus(); 1323 1324 switch (static_cast<FwUpdateCtrlReq>(controlReq)) 1325 { 1326 case FwUpdateCtrlReq::getCurrentControlStatus: 1327 phosphor::logging::log<phosphor::logging::level::INFO>( 1328 "ipmiGetSetFirmwareUpdateControl: Get status"); 1329 break; 1330 case FwUpdateCtrlReq::imageTransferStart: 1331 { 1332 phosphor::logging::log<phosphor::logging::level::INFO>( 1333 "ipmiGetSetFirmwareUpdateControl: Set transfer start"); 1334 imageTransferStarted = true; 1335 // reset buffer to empty (truncate file) 1336 std::ofstream out(firmwareBufferFile, 1337 std::ofstream::binary | std::ofstream::trunc); 1338 fwXferUriPath = std::string("file://") + firmwareBufferFile; 1339 if (xferHashCheck) 1340 { 1341 if (!xferHashCheck->clear()) 1342 { 1343 phosphor::logging::log<phosphor::logging::level::ERR>( 1344 "clear() for xferHashCheck failed"); 1345 return ipmi::responseUnspecifiedError(); 1346 } 1347 } 1348 // Setting state to download 1349 fwUpdateStatus.setState( 1350 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload)); 1351 #ifdef INTEL_PFR_ENABLED 1352 imgLength = 0; 1353 imgType = 0; 1354 block0Mapped = false; 1355 #endif 1356 } 1357 break; 1358 case FwUpdateCtrlReq::imageTransferComplete: 1359 { 1360 if (!imageTransferStarted) 1361 { 1362 phosphor::logging::log<phosphor::logging::level::ERR>( 1363 "transferFirmwareUpdate not started."); 1364 return ipmi::responseNotSupportedInPresentState(); 1365 } 1366 phosphor::logging::log<phosphor::logging::level::INFO>( 1367 "ipmiGetSetFirmwareUpdateControl: Set transfer complete."); 1368 if (usbAttached) 1369 { 1370 phosphor::logging::log<phosphor::logging::level::ERR>( 1371 "USB should be detached to perform this operation."); 1372 return ipmi::responseNotSupportedInPresentState(); 1373 } 1374 // finish transfer based on URI 1375 if (!transferFirmwareFromUri(fwXferUriPath)) 1376 { 1377 phosphor::logging::log<phosphor::logging::level::ERR>( 1378 "transferFirmwareFromUri failed."); 1379 return ipmi::responseUnspecifiedError(); 1380 } 1381 // transfer complete 1382 if (xferHashCheck) 1383 { 1384 if (TransferHashCheck::HashCheck::sha2Success != 1385 xferHashCheck->verify()) 1386 { 1387 phosphor::logging::log<phosphor::logging::level::ERR>( 1388 "xferHashCheck failed."); 1389 return ipmi::responseUnspecifiedError(); 1390 } 1391 } 1392 // Set state to verify and start the update 1393 fwUpdateStatus.setState( 1394 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify)); 1395 // start the request 1396 if (!startFirmwareUpdate(firmwareBufferFile)) 1397 { 1398 phosphor::logging::log<phosphor::logging::level::ERR>( 1399 "startFirmwareUpdate failed."); 1400 return ipmi::responseUnspecifiedError(); 1401 } 1402 imageTransferCompleted = true; 1403 } 1404 break; 1405 case FwUpdateCtrlReq::imageTransferAbort: 1406 phosphor::logging::log<phosphor::logging::level::INFO>( 1407 "ipmiGetSetFirmwareUpdateControl: Set transfer abort."); 1408 if (usbAttached) 1409 { 1410 if (detachUsbDevice()) 1411 { 1412 phosphor::logging::log<phosphor::logging::level::ERR>( 1413 "Detach USB device failed."); 1414 return ipmi::responseUsbAttachOrDetachFailed(); 1415 } 1416 usbAttached = false; 1417 } 1418 // During abort request reset the state to Init by cleaning update 1419 // file. 1420 fwUpdateStatus.firmwareUpdateAbortState(); 1421 imageTransferAborted = true; 1422 break; 1423 case FwUpdateCtrlReq::setFirmwareFilename: 1424 phosphor::logging::log<phosphor::logging::level::INFO>( 1425 "ipmiGetSetFirmwareUpdateControl: Set filename."); 1426 if (!fileName || ((*fileName).length() == 0)) 1427 { 1428 phosphor::logging::log<phosphor::logging::level::ERR>( 1429 "Invalid Filename specified."); 1430 return ipmi::responseInvalidFieldRequest(); 1431 } 1432 1433 fwXferUriPath = *fileName; 1434 break; 1435 case FwUpdateCtrlReq::attachUsbDevice: 1436 phosphor::logging::log<phosphor::logging::level::INFO>( 1437 "ipmiGetSetFirmwareUpdateControl: Attach USB device."); 1438 if (usbAttached) 1439 { 1440 phosphor::logging::log<phosphor::logging::level::ERR>( 1441 "USB device is already attached."); 1442 return ipmi::responseInvalidFieldRequest(); 1443 } 1444 if (attachUsbDevice()) 1445 { 1446 phosphor::logging::log<phosphor::logging::level::ERR>( 1447 "Attach USB device failed."); 1448 return ipmi::responseUsbAttachOrDetachFailed(); 1449 } 1450 usbAttached = true; 1451 break; 1452 case FwUpdateCtrlReq::detachUsbDevice: 1453 phosphor::logging::log<phosphor::logging::level::INFO>( 1454 "ipmiGetSetFirmwareUpdateControl: Detach USB device."); 1455 if (!usbAttached) 1456 { 1457 phosphor::logging::log<phosphor::logging::level::ERR>( 1458 "USB device is not attached."); 1459 return ipmi::responseInvalidFieldRequest(); 1460 } 1461 if (detachUsbDevice()) 1462 { 1463 phosphor::logging::log<phosphor::logging::level::ERR>( 1464 "Detach USB device failed."); 1465 return ipmi::responseUsbAttachOrDetachFailed(); 1466 } 1467 usbAttached = false; 1468 break; 1469 default: 1470 phosphor::logging::log<phosphor::logging::level::ERR>( 1471 "Invalid control option specified."); 1472 return ipmi::responseInvalidFieldRequest(); 1473 } 1474 1475 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted, 1476 imageTransferAborted, usbAttached, uint4_t(0)); 1477 } 1478 1479 /** @brief implements firmware get status command 1480 * @parameter 1481 * - none 1482 * @returns IPMI completion code plus response data 1483 * - status - processing status 1484 * - percentage - percentage completion 1485 * - check - channel integrity check status 1486 **/ 1487 ipmi::RspType<uint8_t, // status 1488 uint8_t, // percentage 1489 uint8_t // check 1490 > 1491 ipmiGetFirmwareUpdateStatus() 1492 1493 { 1494 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write, 1495 // 5=ready, f=error, 83=ac cycle required) 1496 // Byte 2 - percent 1497 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail) 1498 uint8_t status = fwUpdateStatus.getState(); 1499 uint8_t percent = fwUpdateStatus.percent(); 1500 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0; 1501 1502 // Status code. 1503 return ipmi::responseSuccess(status, percent, check); 1504 } 1505 1506 ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions( 1507 const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask, 1508 bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart, 1509 bool sha2Check, uint5_t reserved2, 1510 std::optional<std::vector<uint8_t>> integrityCheckVal) 1511 { 1512 phosphor::logging::log<phosphor::logging::level::INFO>( 1513 "Set firmware update options."); 1514 bool isIPMBChannel = false; 1515 1516 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1517 { 1518 return ipmi::responseUnspecifiedError(); 1519 } 1520 if (isIPMBChannel) 1521 { 1522 phosphor::logging::log<phosphor::logging::level::INFO>( 1523 "Channel not supported. Failed to set firmware update options"); 1524 return ipmi::responseCommandNotAvailable(); 1525 } 1526 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade(); 1527 bool deferRestartState = fwUpdateStatus.getDeferRestart(); 1528 bool sha2CheckState = xferHashCheck ? true : false; 1529 1530 if (noDowngradeMask && (noDowngradeState != noDowngrade)) 1531 { 1532 fwUpdateStatus.setInhibitDowngrade(noDowngrade); 1533 noDowngradeState = noDowngrade; 1534 } 1535 if (deferRestartMask && (deferRestartState != deferRestart)) 1536 { 1537 fwUpdateStatus.setDeferRestart(deferRestart); 1538 deferRestartState = deferRestart; 1539 } 1540 if (sha2CheckMask) 1541 { 1542 if (sha2Check) 1543 { 1544 auto hashSize = EVP_MD_size(EVP_sha256()); 1545 if ((*integrityCheckVal).size() != hashSize) 1546 { 1547 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1548 "Invalid size of Hash specified."); 1549 return ipmi::responseInvalidFieldRequest(); 1550 } 1551 1552 try 1553 { 1554 xferHashCheck = 1555 std::make_unique<TransferHashCheck>(*integrityCheckVal); 1556 } 1557 catch (const std::exception& ex) 1558 { 1559 phosphor::logging::log<phosphor::logging::level::ERR>( 1560 ex.what()); 1561 return ipmi::responseUnspecifiedError(); 1562 } 1563 } 1564 else 1565 { 1566 // delete the xferHashCheck object 1567 xferHashCheck.reset(); 1568 } 1569 sha2CheckState = sha2CheckMask; 1570 } 1571 return ipmi::responseSuccess(noDowngradeState, deferRestartState, 1572 sha2CheckState, reserved1); 1573 } 1574 1575 ipmi::RspType<uint32_t> 1576 ipmiFwImageWriteData(const std::vector<uint8_t>& writeData) 1577 { 1578 const uint8_t ccCmdNotSupportedInPresentState = 0xD5; 1579 size_t writeDataLen = writeData.size(); 1580 1581 if (!writeDataLen) 1582 { 1583 return ipmi::responseReqDataLenInvalid(); 1584 } 1585 1586 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload) 1587 { 1588 phosphor::logging::log<phosphor::logging::level::ERR>( 1589 "Invalid firmware update state."); 1590 return ipmi::response(ccCmdNotSupportedInPresentState); 1591 } 1592 1593 std::ofstream out(firmwareBufferFile, 1594 std::ofstream::binary | std::ofstream::app); 1595 if (!out) 1596 { 1597 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1598 "Error while opening file."); 1599 return ipmi::responseUnspecifiedError(); 1600 } 1601 1602 uint64_t fileDataLen = out.tellp(); 1603 1604 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize) 1605 { 1606 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1607 "Firmware image size exceeds the limit"); 1608 return ipmi::responseInvalidFieldRequest(); 1609 } 1610 1611 const char* data = reinterpret_cast<const char*>(writeData.data()); 1612 out.write(data, writeDataLen); 1613 out.close(); 1614 1615 if (xferHashCheck) 1616 { 1617 if (!xferHashCheck->hash(writeData)) 1618 { 1619 phosphor::logging::log<phosphor::logging::level::ERR>( 1620 "ipmiFwImageWriteData: xferHashCheck->hash failed."); 1621 return ipmi::responseUnspecifiedError(); 1622 } 1623 } 1624 1625 #ifdef INTEL_PFR_ENABLED 1626 /* PFR image block 0 - As defined in HAS */ 1627 struct PFRImageBlock0 1628 { 1629 uint32_t tag; 1630 uint32_t pcLength; 1631 uint32_t pcType; 1632 uint32_t reserved1; 1633 uint8_t hash256[32]; 1634 uint8_t hash384[48]; 1635 uint8_t reserved2[32]; 1636 } __attribute__((packed)); 1637 1638 /* Get the PFR block 0 data and read the uploaded image 1639 * information( Image type, length etc) */ 1640 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) && 1641 (!block0Mapped)) 1642 { 1643 struct PFRImageBlock0 block0Data = {0}; 1644 1645 std::ifstream inFile(firmwareBufferFile, 1646 std::ios::binary | std::ios::in); 1647 inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data)); 1648 inFile.close(); 1649 1650 uint32_t magicNum = block0Data.tag; 1651 1652 /* Validate the magic number */ 1653 if (magicNum != perBlock0MagicNum) 1654 { 1655 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1656 "PFR image magic number not matched"); 1657 return ipmi::responseInvalidFieldRequest(); 1658 } 1659 // Note:imgLength, imgType and block0Mapped are in global scope, as 1660 // these are used in cascaded updates. 1661 imgLength = block0Data.pcLength; 1662 imgType = block0Data.pcType; 1663 block0Mapped = true; 1664 } 1665 #endif // end of INTEL_PFR_ENABLED 1666 return ipmi::responseSuccess(writeDataLen); 1667 } 1668 1669 static void registerFirmwareFunctions() 1670 { 1671 // guarantee that we start with an already timed out timestamp 1672 fwRandomNumGenTs = std::chrono::steady_clock::now() - 1673 fwRandomNumExpirySeconds; 1674 fwUpdateStatus.setState( 1675 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit)); 1676 1677 unlink(firmwareBufferFile); 1678 1679 #ifdef INTEL_PFR_ENABLED 1680 // Following commands are supported only for PFR enabled platforms 1681 // CMD:0x20 - Get Firmware Version Information 1682 // CMD:0x21 - Get Firmware Security Version Information 1683 // CMD:0x25 - Get Root Certificate Data 1684 1685 // get firmware version information 1686 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1687 ipmi::firmware::cmdGetFwVersionInfo, 1688 ipmi::Privilege::Admin, ipmiGetFwVersionInfo); 1689 1690 // get firmware security version information 1691 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1692 ipmi::firmware::cmdGetFwSecurityVersionInfo, 1693 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo); 1694 1695 // get root certificate data 1696 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1697 ipmi::firmware::cmdFwGetRootCertData, 1698 ipmi::Privilege::Admin, ipmiGetFwRootCertData); 1699 #endif 1700 1701 // get firmware update channel information (max transfer sizes) 1702 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1703 ipmi::firmware::cmdGetFwUpdateChannelInfo, 1704 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize); 1705 1706 // get bmc execution context 1707 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1708 ipmi::firmware::cmdGetBmcExecutionContext, 1709 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext); 1710 1711 // Get Firmware Update Random number 1712 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1713 ipmi::firmware::cmdGetFwUpdateRandomNumber, 1714 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber); 1715 1716 // Set Firmware Update Mode 1717 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1718 ipmi::firmware::cmdSetFirmwareUpdateMode, 1719 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode); 1720 1721 // Exit Firmware Update Mode 1722 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1723 ipmi::firmware::cmdExitFirmwareUpdateMode, 1724 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode); 1725 1726 // Get/Set Firmware Update Control 1727 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1728 ipmi::firmware::cmdGetSetFwUpdateControl, 1729 ipmi::Privilege::Admin, 1730 ipmiGetSetFirmwareUpdateControl); 1731 1732 // Get Firmware Update Status 1733 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1734 ipmi::firmware::cmdGetFirmwareUpdateStatus, 1735 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus); 1736 1737 // Set Firmware Update Options 1738 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1739 ipmi::firmware::cmdSetFirmwareUpdateOptions, 1740 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions); 1741 // write image data 1742 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1743 ipmi::firmware::cmdFwImageWriteData, 1744 ipmi::Privilege::Admin, ipmiFwImageWriteData); 1745 return; 1746 } 1747