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