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 = 37_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([&fwUpdateMatchSignal]() { 566 fwUpdateMatchSignal = nullptr; 567 }); 568 569 static sdbusplus::Timer activationStatusTimer([]() { 570 if (fwUpdateStatus.activationTimerTimeout() > 95) 571 { 572 activationStatusTimer.stop(); 573 fwUpdateStatus.setState( 574 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify)); 575 } 576 }); 577 578 timer.start(std::chrono::microseconds(5000000), false); 579 580 // callback function for capturing signal 581 auto callback = [&](sdbusplus::message_t& m) { 582 std::vector< 583 std::pair<std::string, 584 std::vector<std::pair<std::string, ipmi::DbusVariant>>>> 585 intfPropsPair; 586 sdbusplus::message::object_path objPath; 587 588 try 589 { 590 m.read(objPath, intfPropsPair); // Read in the object path 591 // that was just created 592 } 593 catch (const std::exception& e) 594 { 595 phosphor::logging::log<phosphor::logging::level::ERR>( 596 "Exception caught in reading created object path."); 597 return; 598 } 599 // constructing response message 600 phosphor::logging::log<phosphor::logging::level::INFO>( 601 "New Interface Added.", 602 phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str())); 603 for (auto& interface : intfPropsPair) 604 { 605 if (interface.first == "xyz.openbmc_project.Software.Activation") 606 { 607 // There are chances of getting two signals for 608 // InterfacesAdded. So cross check and discrad second instance. 609 if (fwUpdateMatchSignal == nullptr) 610 { 611 return; 612 } 613 // Found our interface, disable callbacks 614 fwUpdateMatchSignal = nullptr; 615 616 phosphor::logging::log<phosphor::logging::level::INFO>( 617 "Start activationStatusTimer for status."); 618 try 619 { 620 timer.stop(); 621 activationStatusTimer.start( 622 std::chrono::microseconds(3000000), true); 623 } 624 catch (const std::exception& e) 625 { 626 phosphor::logging::log<phosphor::logging::level::ERR>( 627 "Exception caught in start activationStatusTimer.", 628 phosphor::logging::entry("ERROR=%s", e.what())); 629 } 630 631 fwUpdateStatus.updateActivationPercent(objPath.str); 632 activateImage(objPath.str); 633 } 634 } 635 }; 636 637 // Adding matcher 638 fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match_t>( 639 *getSdBus(), 640 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 641 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 642 callback); 643 } 644 static bool startFirmwareUpdate(const std::string& uri) 645 { 646 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time 647 // the code gets to this point, the file should be transferred start the 648 // request (creating a new file in /tmp/images causes the update manager to 649 // check if it is ready for activation) 650 static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatchSignal; 651 postTransferCompleteHandler(fwUpdateMatchSignal); 652 std::string randomFname = 653 "/tmp/images/" + 654 boost::uuids::to_string(boost::uuids::random_generator()()); 655 std::error_code fsError{}; 656 std::filesystem::rename(uri, randomFname, fsError); 657 if (fsError) 658 { 659 // The source and destination may not be in the same 660 // filesystem. 661 std::filesystem::copy(uri, randomFname, fsError); 662 if (fsError) 663 { 664 phosphor::logging::log<phosphor::logging::level::ERR>( 665 "Unable to move/copy image to destination directory.", 666 phosphor::logging::entry("ERROR=%s", 667 fsError.message().c_str())); 668 return false; 669 } 670 std::filesystem::remove(uri); 671 } 672 return true; 673 } 674 675 static bool transferImageFromFile(const std::string& uri, bool move = true) 676 { 677 std::error_code ec; 678 phosphor::logging::log<phosphor::logging::level::INFO>( 679 "Transfer Image From File.", 680 phosphor::logging::entry("URI=%s", uri.c_str())); 681 if (move) 682 { 683 std::filesystem::rename(uri, firmwareBufferFile, ec); 684 } 685 else 686 { 687 std::filesystem::copy(uri, firmwareBufferFile, 688 std::filesystem::copy_options::overwrite_existing, 689 ec); 690 } 691 if (xferHashCheck) 692 { 693 MappedFile mappedfw(uri); 694 if (!xferHashCheck->hash( 695 {mappedfw.data(), mappedfw.data() + mappedfw.size()})) 696 { 697 phosphor::logging::log<phosphor::logging::level::ERR>( 698 "transferImageFromFile: xferHashCheck->hash failed."); 699 return false; 700 } 701 } 702 if (ec.value()) 703 { 704 phosphor::logging::log<phosphor::logging::level::ERR>( 705 "Image copy failed."); 706 return false; 707 } 708 709 return true; 710 } 711 712 template <typename... ArgTypes> 713 static int executeCmd(const char* path, ArgTypes&&... tArgs) 714 { 715 boost::process::child execProg(path, const_cast<char*>(tArgs)...); 716 execProg.wait(); 717 return execProg.exit_code(); 718 } 719 720 static bool transferImageFromUsb(const std::string& uri) 721 { 722 bool ret = false; 723 724 phosphor::logging::log<phosphor::logging::level::INFO>( 725 "Transfer Image From USB.", 726 phosphor::logging::entry("URI=%s", uri.c_str())); 727 728 if (executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage, 729 fwUpdateMountPoint) == 0) 730 { 731 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri; 732 ret = transferImageFromFile(usb_path, false); 733 734 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage, 735 fwUpdateMountPoint); 736 } 737 738 return ret; 739 } 740 741 static bool transferFirmwareFromUri(const std::string& uri) 742 { 743 static constexpr char fwUriFile[] = "file://"; 744 static constexpr char fwUriUsb[] = "usb://"; 745 phosphor::logging::log<phosphor::logging::level::INFO>( 746 "Transfer Image From URI.", 747 phosphor::logging::entry("URI=%s", uri.c_str())); 748 if (boost::algorithm::starts_with(uri, fwUriFile)) 749 { 750 std::string fname = uri.substr(sizeof(fwUriFile) - 1); 751 if (fname != firmwareBufferFile) 752 { 753 return transferImageFromFile(fname); 754 } 755 return true; 756 } 757 if (boost::algorithm::starts_with(uri, fwUriUsb)) 758 { 759 std::string fname = uri.substr(sizeof(fwUriUsb) - 1); 760 return transferImageFromUsb(fname); 761 } 762 return false; 763 } 764 765 /* Get USB-mass-storage device status: inserted => true, ejected => false */ 766 static bool getUsbStatus() 767 { 768 std::filesystem::path usbDevPath = 769 std::filesystem::path("/sys/kernel/config/usb_gadget") / 770 fwUpdateUSBDevName; 771 return (std::filesystem::exists(usbDevPath) ? true : false); 772 } 773 774 /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */ 775 static int attachUsbDevice() 776 { 777 if (getUsbStatus()) 778 { 779 return 1; 780 } 781 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage, 782 std::to_string(maxFirmwareImageSize / 1_MB).c_str()); 783 if (!ret) 784 { 785 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName, 786 fwUpdateUsbVolImage); 787 } 788 return ret; 789 } 790 791 /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */ 792 static int detachUsbDevice() 793 { 794 if (!getUsbStatus()) 795 { 796 return 1; 797 } 798 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName); 799 } 800 static uint8_t getActiveBootImage(ipmi::Context::ptr ctx) 801 { 802 constexpr uint8_t undefinedImage = 0x00; 803 constexpr uint8_t primaryImage = 0x01; 804 constexpr uint8_t secondaryImage = 0x02; 805 constexpr const char* secondaryFitImageStartAddr = "22480000"; 806 807 uint8_t bootImage = primaryImage; 808 boost::system::error_code ec; 809 std::string value = ctx->bus->yield_method_call<std::string>( 810 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager", 811 "/xyz/openbmc_project/u_boot/environment/mgr", 812 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd"); 813 if (ec) 814 { 815 phosphor::logging::log<phosphor::logging::level::ERR>( 816 "Failed to read the bootcmd value"); 817 /* don't fail, just give back undefined until it is ready */ 818 bootImage = undefinedImage; 819 } 820 821 /* cheking for secondary FitImage Address 22480000 */ 822 else if (value.find(secondaryFitImageStartAddr) != std::string::npos) 823 { 824 bootImage = secondaryImage; 825 } 826 else 827 { 828 bootImage = primaryImage; 829 } 830 831 return bootImage; 832 } 833 834 #ifdef INTEL_PFR_ENABLED 835 using fwVersionInfoType = 836 std::tuple<uint8_t, // ID Tag 837 uint8_t, // Major Version Number 838 uint8_t, // Minor Version Number 839 uint32_t, // Build Number 840 uint32_t, // Build Timestamp 841 uint32_t>; // Update Timestamp 842 ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo() 843 { 844 // Byte 1 - Count (N) Number of devices data is being returned for. 845 // Bytes 2:16 - Device firmare information(fwVersionInfoType) 846 // Bytes - 17:(15xN) - Repeat of 2 through 16 847 848 std::vector<fwVersionInfoType> fwVerInfoList; 849 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 850 for (const auto& fwDev : fwVersionIdMap) 851 { 852 std::string verStr; 853 try 854 { 855 auto service = ipmi::getService(*busp, versionIntf, fwDev.second); 856 857 ipmi::Value result = ipmi::getDbusProperty( 858 *busp, service, fwDev.second, versionIntf, "Version"); 859 verStr = std::get<std::string>(result); 860 } 861 catch (const std::exception& e) 862 { 863 phosphor::logging::log<phosphor::logging::level::INFO>( 864 "Failed to fetch Version property", 865 phosphor::logging::entry("ERROR=%s", e.what()), 866 phosphor::logging::entry("PATH=%s", fwDev.second), 867 phosphor::logging::entry("INTERFACE=%s", versionIntf)); 868 continue; 869 } 870 871 if (verStr.empty()) 872 { 873 phosphor::logging::log<phosphor::logging::level::INFO>( 874 "Version is empty.", 875 phosphor::logging::entry("PATH=%s", fwDev.second), 876 phosphor::logging::entry("INTERFACE=%s", versionIntf)); 877 continue; 878 } 879 880 uint8_t majorNum = 0; 881 uint8_t minorNum = 0; 882 uint32_t buildNum = 0; 883 try 884 { 885 std::optional<ipmi::MetaRevision> rev = 886 ipmi::convertIntelVersion(verStr); 887 if (rev.has_value()) 888 { 889 ipmi::MetaRevision revision = rev.value(); 890 majorNum = revision.major % 10 + (revision.major / 10) * 16; 891 minorNum = (revision.minor > 99 ? 99 : revision.minor); 892 minorNum = minorNum % 10 + (minorNum / 10) * 16; 893 uint32_t hash = std::stoul(revision.metaHash, 0, 16); 894 hash = bswap_32(hash); 895 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00); 896 } 897 else 898 { 899 std::vector<std::string> splitVer; 900 boost::split(splitVer, verStr, boost::is_any_of(".-")); 901 if (splitVer.size() < 3) 902 { 903 phosphor::logging::log<phosphor::logging::level::INFO>( 904 "Invalid Version format.", 905 phosphor::logging::entry("Version=%s", verStr.c_str()), 906 phosphor::logging::entry("PATH=%s", fwDev.second)); 907 continue; 908 } 909 majorNum = std::stoul(splitVer[0], nullptr, 16); 910 minorNum = std::stoul(splitVer[1], nullptr, 16); 911 buildNum = std::stoul(splitVer[2], nullptr, 16); 912 } 913 // Build Timestamp - Not supported. 914 // Update Timestamp - TODO: Need to check with CPLD team. 915 fwVerInfoList.emplace_back( 916 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum, 917 minorNum, buildNum, 0, 0)); 918 } 919 catch (const std::exception& e) 920 { 921 phosphor::logging::log<phosphor::logging::level::INFO>( 922 "Failed to convert stoul.", 923 phosphor::logging::entry("ERROR=%s", e.what())); 924 continue; 925 } 926 } 927 928 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList); 929 } 930 931 std::array<uint8_t, imageCount> getSecurityVersionInfo( 932 const char* mtdDevBuf, size_t svnVerOffsetInPfm, size_t bkcVerOffsetInPfm) 933 { 934 constexpr size_t bufLength = 1; 935 std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp; 936 constexpr uint8_t svnIndexValue = 0x00; 937 constexpr uint8_t bkcIndexValue = 0x01; 938 constexpr uint8_t tempIndexValue = 0x00; 939 try 940 { 941 SPIDev spiDev(mtdDevBuf); 942 spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data()); 943 fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue); 944 spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data()); 945 fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue); 946 } 947 catch (const std::exception& e) 948 { 949 phosphor::logging::log<phosphor::logging::level::ERR>( 950 "Exception caught in getSecurityVersionInfo", 951 phosphor::logging::entry("MSG=%s", e.what())); 952 fwSecurityVersionBuf = {0, 0}; 953 } 954 955 return fwSecurityVersionBuf; 956 } 957 958 ipmi::RspType< 959 uint8_t, // device ID 960 uint8_t, // Active Image Value 961 std::array<uint8_t, imageCount>, // Security version for Active Image 962 uint8_t, // recovery Image Value 963 std::array<uint8_t, imageCount>> // Security version for Recovery Image 964 ipmiGetFwSecurityVersionInfo() 965 { 966 static bool cacheFlag = false; 967 constexpr std::array<const char*, imageCount> mtdDevBuf = { 968 bmcActivePfmMTDDev, bmcRecoveryImgMTDDev}; 969 970 // To avoid multiple reading from SPI device 971 if (!cacheFlag) 972 { 973 imgFwSecurityVersion[0] = getSecurityVersionInfo( 974 mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm); 975 imgFwSecurityVersion[1] = getSecurityVersionInfo( 976 mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm); 977 cacheFlag = true; 978 } 979 980 constexpr uint8_t ActivePfmMTDDev = 0x00; 981 constexpr uint8_t RecoveryImgMTDDev = 0x01; 982 983 return ipmi::responseSuccess( 984 imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage), 985 imgFwSecurityVersion[ActivePfmMTDDev], 986 static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage), 987 imgFwSecurityVersion[RecoveryImgMTDDev]); 988 } 989 990 ipmi::RspType<std::array<uint8_t, certKeyLen>, 991 std::optional<std::array<uint8_t, cskSignatureLen>>> 992 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId) 993 { 994 bool isIPMBChannel = false; 995 996 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 997 { 998 return ipmi::responseUnspecifiedError(); 999 } 1000 if (isIPMBChannel) 1001 { 1002 phosphor::logging::log<phosphor::logging::level::INFO>( 1003 "Command not supported. Failed to get root certificate data."); 1004 return ipmi::responseCommandNotAvailable(); 1005 } 1006 1007 size_t certKeyOffset = 0; 1008 size_t cskSigOffset = 0; 1009 std::string mtdDev; 1010 1011 switch (static_cast<FwGetRootCertDataTag>(certId)) 1012 { 1013 case FwGetRootCertDataTag::activeRootKey: 1014 { 1015 mtdDev = bmcActivePfmMTDDev; 1016 certKeyOffset = rootkeyOffsetInPfm; 1017 break; 1018 } 1019 case FwGetRootCertDataTag::recoveryRootKey: 1020 { 1021 mtdDev = bmcRecoveryImgMTDDev; 1022 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm; 1023 break; 1024 } 1025 case FwGetRootCertDataTag::activeCSK: 1026 { 1027 mtdDev = bmcActivePfmMTDDev; 1028 certKeyOffset = cskKeyOffsetInPfm; 1029 cskSigOffset = cskSignatureOffsetInPfm; 1030 break; 1031 } 1032 case FwGetRootCertDataTag::recoveryCSK: 1033 { 1034 mtdDev = bmcRecoveryImgMTDDev; 1035 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm; 1036 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm; 1037 break; 1038 } 1039 default: 1040 { 1041 return ipmi::responseInvalidFieldRequest(); 1042 } 1043 } 1044 1045 std::array<uint8_t, certKeyLen> certKey = {0}; 1046 1047 try 1048 { 1049 SPIDev spiDev(mtdDev); 1050 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data()); 1051 1052 if (cskSigOffset) 1053 { 1054 std::array<uint8_t, cskSignatureLen> cskSignature = {0}; 1055 spiDev.spiReadData(cskSigOffset, cskSignatureLen, 1056 cskSignature.data()); 1057 return ipmi::responseSuccess(certKey, cskSignature); 1058 } 1059 } 1060 catch (const std::exception& e) 1061 { 1062 phosphor::logging::log<phosphor::logging::level::ERR>( 1063 "Exception caught in ipmiGetFwRootCertData", 1064 phosphor::logging::entry("MSG=%s", e.what())); 1065 return ipmi::responseUnspecifiedError(); 1066 } 1067 1068 return ipmi::responseSuccess(certKey, std::nullopt); 1069 } 1070 #endif // INTEL_PFR_ENABLED 1071 1072 static constexpr uint8_t channelListSize = 3; 1073 /** @brief implements Maximum Firmware Transfer size command 1074 * @parameter 1075 * - none 1076 * @returns IPMI completion code plus response data 1077 * - count - channel count 1078 * - channelList - channel list information 1079 */ 1080 ipmi::RspType<uint8_t, // channel count 1081 std::array<std::tuple<uint8_t, uint32_t>, 1082 channelListSize> // Channel List 1083 > 1084 ipmiFirmwareMaxTransferSize() 1085 { 1086 constexpr size_t kcsMaxBufSize = 128; 1087 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024; 1088 // Byte 1 - Count (N) Number of devices data is being returned for. 1089 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb 1090 // Byte 3-6 - transfer size (little endian) 1091 // Bytes - 7:(5xN) - Repeat of 2 through 6 1092 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize> 1093 channelList = { 1094 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize}, 1095 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus), 1096 rmcpPlusMaxBufSize}}}; 1097 1098 return ipmi::responseSuccess(channelListSize, channelList); 1099 } 1100 1101 ipmi::RspType<uint8_t, uint8_t> 1102 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx) 1103 { 1104 // Byte 1 - Current execution context 1105 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode 1106 // Byte 2 - Partition pointer 1107 // 0x01 - primary, 0x02 - secondary 1108 uint8_t partitionPtr = getActiveBootImage(ctx); 1109 1110 return ipmi::responseSuccess( 1111 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr); 1112 } 1113 /** @brief Get Firmware Update Random Number 1114 * 1115 * This function generate the random number used for 1116 * setting the firmware update mode as authentication key. 1117 * 1118 * @param[in] ctx - context of current session 1119 * @returns IPMI completion code along with 1120 * - random number 1121 **/ 1122 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>> 1123 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx) 1124 { 1125 phosphor::logging::log<phosphor::logging::level::INFO>( 1126 "Generate FW update random number"); 1127 bool isIPMBChannel = false; 1128 1129 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1130 { 1131 return ipmi::responseUnspecifiedError(); 1132 } 1133 if (isIPMBChannel) 1134 { 1135 phosphor::logging::log<phosphor::logging::level::INFO>( 1136 "Channel not supported. Failed to fetch FW update random number"); 1137 return ipmi::responseCommandNotAvailable(); 1138 } 1139 std::random_device rd; 1140 std::default_random_engine gen(rd()); 1141 std::uniform_int_distribution<> dist{0, 255}; 1142 1143 fwRandomNumGenTs = std::chrono::steady_clock::now(); 1144 1145 for (size_t i = 0; i < fwRandomNumLength; i++) 1146 { 1147 fwRandomNum[i] = dist(gen); 1148 } 1149 1150 return ipmi::responseSuccess(fwRandomNum); 1151 } 1152 1153 /** @brief Set Firmware Update Mode 1154 * 1155 * This function sets BMC into firmware update mode 1156 * after validating Random number obtained from the Get 1157 * Firmware Update Random Number command 1158 * 1159 * @param[in] ctx - context of current session 1160 * @parameter randNum - Random number(token) 1161 * @returns IPMI completion code 1162 **/ 1163 ipmi::RspType<> 1164 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx, 1165 std::array<uint8_t, fwRandomNumLength>& randNum) 1166 { 1167 phosphor::logging::log<phosphor::logging::level::INFO>( 1168 "Start FW update mode"); 1169 1170 bool isIPMBChannel = false; 1171 1172 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1173 { 1174 return ipmi::responseUnspecifiedError(); 1175 } 1176 if (isIPMBChannel) 1177 { 1178 phosphor::logging::log<phosphor::logging::level::INFO>( 1179 "Channel not supported. Failed to set FW update mode"); 1180 return ipmi::responseCommandNotAvailable(); 1181 } 1182 /* Firmware Update Random number is valid for 30 seconds only */ 1183 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs); 1184 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed) 1185 .count() > std::chrono::duration_cast<std::chrono::microseconds>( 1186 fwRandomNumExpirySeconds) 1187 .count()) 1188 { 1189 phosphor::logging::log<phosphor::logging::level::INFO>( 1190 "Firmware update random number expired."); 1191 return ipmi::responseInvalidFieldRequest(); 1192 } 1193 1194 /* Validate random number */ 1195 for (size_t i = 0; i < fwRandomNumLength; i++) 1196 { 1197 if (fwRandomNum[i] != randNum[i]) 1198 { 1199 phosphor::logging::log<phosphor::logging::level::INFO>( 1200 "Invalid random number specified."); 1201 return ipmi::responseInvalidFieldRequest(); 1202 } 1203 } 1204 1205 try 1206 { 1207 if (getFirmwareUpdateMode()) 1208 { 1209 phosphor::logging::log<phosphor::logging::level::INFO>( 1210 "Already firmware update is in progress."); 1211 return ipmi::responseBusy(); 1212 } 1213 } 1214 catch (const std::exception& e) 1215 { 1216 return ipmi::responseUnspecifiedError(); 1217 } 1218 1219 // FIXME? c++ doesn't off an option for exclusive file creation 1220 FILE* fp = fopen(firmwareBufferFile, "wx"); 1221 if (!fp) 1222 { 1223 phosphor::logging::log<phosphor::logging::level::INFO>( 1224 "Unable to open file."); 1225 return ipmi::responseUnspecifiedError(); 1226 } 1227 fclose(fp); 1228 1229 try 1230 { 1231 setFirmwareUpdateMode(true); 1232 } 1233 catch (const std::exception& e) 1234 { 1235 unlink(firmwareBufferFile); 1236 return ipmi::responseUnspecifiedError(); 1237 } 1238 1239 return ipmi::responseSuccess(); 1240 } 1241 1242 /** @brief implements exit firmware update mode command 1243 * @param None 1244 * 1245 * @returns IPMI completion code 1246 */ 1247 ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx) 1248 { 1249 phosphor::logging::log<phosphor::logging::level::INFO>( 1250 "Exit FW update mode"); 1251 bool isIPMBChannel = false; 1252 1253 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1254 { 1255 return ipmi::responseUnspecifiedError(); 1256 } 1257 if (isIPMBChannel) 1258 { 1259 phosphor::logging::log<phosphor::logging::level::INFO>( 1260 "Command not supported. Failed to exit firmware update mode"); 1261 return ipmi::responseCommandNotAvailable(); 1262 } 1263 1264 switch (fwUpdateStatus.getState()) 1265 { 1266 case FwUpdateStatusCache::fwStateInit: 1267 case FwUpdateStatusCache::fwStateIdle: 1268 return ipmi::responseInvalidFieldRequest(); 1269 break; 1270 case FwUpdateStatusCache::fwStateDownload: 1271 case FwUpdateStatusCache::fwStateVerify: 1272 break; 1273 case FwUpdateStatusCache::fwStateProgram: 1274 break; 1275 case FwUpdateStatusCache::fwStateUpdateSuccess: 1276 case FwUpdateStatusCache::fwStateError: 1277 break; 1278 case FwUpdateStatusCache::fwStateAcCycleRequired: 1279 return ipmi::responseInvalidFieldRequest(); 1280 break; 1281 } 1282 fwUpdateStatus.firmwareUpdateAbortState(); 1283 1284 try 1285 { 1286 setFirmwareUpdateMode(false); 1287 } 1288 catch (const std::exception& e) 1289 { 1290 return ipmi::responseUnspecifiedError(); 1291 } 1292 1293 return ipmi::responseSuccess(); 1294 } 1295 1296 /** @brief implements Get/Set Firmware Update Control 1297 * @param[in] ctx - context of current session 1298 * @parameter 1299 * - Byte 1: Control Byte 1300 * - Byte 2: Firmware filename length (Optional) 1301 * - Byte 3:N: Firmware filename data (Optional) 1302 * @returns IPMI completion code plus response data 1303 * - Byte 2: Current control status 1304 **/ 1305 ipmi::RspType<bool, bool, bool, bool, uint4_t> ipmiGetSetFirmwareUpdateControl( 1306 const ipmi::Context::ptr& ctx, 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 = 1691 std::chrono::steady_clock::now() - 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