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