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