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