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 primaryImage = 0x01; 762 constexpr uint8_t secondaryImage = 0x02; 763 constexpr const char *secondaryFitImageStartAddr = "22480000"; 764 765 uint8_t bootImage = primaryImage; 766 boost::system::error_code ec; 767 std::string value = ctx->bus->yield_method_call<std::string>( 768 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager", 769 "/xyz/openbmc_project/u_boot/environment/mgr", 770 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd"); 771 if (ec) 772 { 773 phosphor::logging::log<phosphor::logging::level::ERR>( 774 "Failed to read the bootcmd value"); 775 return ipmi::ccUnspecifiedError; 776 } 777 778 /* cheking for secondary FitImage Address 22480000 */ 779 if (value.find(secondaryFitImageStartAddr) != std::string::npos) 780 { 781 bootImage = secondaryImage; 782 } 783 else 784 { 785 bootImage = primaryImage; 786 } 787 788 return bootImage; 789 } 790 791 #ifdef INTEL_PFR_ENABLED 792 using fwVersionInfoType = std::tuple<uint8_t, // ID Tag 793 uint8_t, // Major Version Number 794 uint8_t, // Minor Version Number 795 uint32_t, // Build Number 796 uint32_t, // Build Timestamp 797 uint32_t>; // Update Timestamp 798 ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo() 799 { 800 // Byte 1 - Count (N) Number of devices data is being returned for. 801 // Bytes 2:16 - Device firmare information(fwVersionInfoType) 802 // Bytes - 17:(15xN) - Repeat of 2 through 16 803 804 std::vector<fwVersionInfoType> fwVerInfoList; 805 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 806 for (const auto &fwDev : fwVersionIdMap) 807 { 808 std::string verStr; 809 try 810 { 811 auto service = ipmi::getService(*busp, versionIntf, fwDev.second); 812 813 ipmi::Value result = ipmi::getDbusProperty( 814 *busp, service, fwDev.second, versionIntf, "Version"); 815 verStr = std::get<std::string>(result); 816 } 817 catch (const std::exception &e) 818 { 819 phosphor::logging::log<phosphor::logging::level::INFO>( 820 "Failed to fetch Version property", 821 phosphor::logging::entry("ERROR=%s", e.what()), 822 phosphor::logging::entry("PATH=%s", fwDev.second), 823 phosphor::logging::entry("INTERFACE=%s", versionIntf)); 824 continue; 825 } 826 827 if (verStr.empty()) 828 { 829 phosphor::logging::log<phosphor::logging::level::INFO>( 830 "Version is empty.", 831 phosphor::logging::entry("PATH=%s", fwDev.second), 832 phosphor::logging::entry("INTERFACE=%s", versionIntf)); 833 continue; 834 } 835 836 // BMC Version format: <major>.<minor>-<build bum>-<build hash> 837 std::vector<std::string> splitVer; 838 boost::split(splitVer, verStr, boost::is_any_of(".-")); 839 if (splitVer.size() < 3) 840 { 841 phosphor::logging::log<phosphor::logging::level::INFO>( 842 "Invalid Version format.", 843 phosphor::logging::entry("Version=%s", verStr.c_str()), 844 phosphor::logging::entry("PATH=%s", fwDev.second)); 845 continue; 846 } 847 848 uint8_t majorNum = 0; 849 uint8_t minorNum = 0; 850 uint32_t buildNum = 0; 851 try 852 { 853 majorNum = std::stoul(splitVer[0], nullptr, 16); 854 minorNum = std::stoul(splitVer[1], nullptr, 16); 855 buildNum = std::stoul(splitVer[2], nullptr, 16); 856 } 857 catch (const std::exception &e) 858 { 859 phosphor::logging::log<phosphor::logging::level::INFO>( 860 "Failed to convert stoul.", 861 phosphor::logging::entry("ERROR=%s", e.what())); 862 continue; 863 } 864 865 // Build Timestamp - Not supported. 866 // Update Timestamp - TODO: Need to check with CPLD team. 867 fwVerInfoList.emplace_back( 868 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum, 869 minorNum, buildNum, 0, 0)); 870 } 871 872 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList); 873 } 874 using fwSecurityVersionInfoType = std::tuple<uint8_t, // ID Tag 875 uint8_t, // BKC Version 876 uint8_t>; // SVN Version 877 ipmi::RspType<uint8_t, std::vector<fwSecurityVersionInfoType>> 878 ipmiGetFwSecurityVersionInfo() 879 { 880 // TODO: Need to add support. 881 return ipmi::responseInvalidCommand(); 882 } 883 884 ipmi::RspType<std::array<uint8_t, certKeyLen>, 885 std::optional<std::array<uint8_t, cskSignatureLen>>> 886 ipmiGetFwRootCertData(const ipmi::Context::ptr &ctx, uint8_t certId) 887 { 888 bool isIPMBChannel = false; 889 890 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 891 { 892 return ipmi::responseUnspecifiedError(); 893 } 894 if (isIPMBChannel) 895 { 896 phosphor::logging::log<phosphor::logging::level::INFO>( 897 "Command not supported. Failed to get root certificate data."); 898 return ipmi::responseCommandNotAvailable(); 899 } 900 901 size_t certKeyOffset = 0; 902 size_t cskSigOffset = 0; 903 std::string mtdDev; 904 905 switch (static_cast<FwGetRootCertDataTag>(certId)) 906 { 907 case FwGetRootCertDataTag::activeRootKey: 908 { 909 mtdDev = bmcActivePfmMTDDev; 910 certKeyOffset = rootkeyOffsetInPfm; 911 break; 912 } 913 case FwGetRootCertDataTag::recoveryRootKey: 914 { 915 mtdDev = bmcRecoveryImgMTDDev; 916 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm; 917 break; 918 } 919 case FwGetRootCertDataTag::activeCSK: 920 { 921 mtdDev = bmcActivePfmMTDDev; 922 certKeyOffset = cskKeyOffsetInPfm; 923 cskSigOffset = cskSignatureOffsetInPfm; 924 break; 925 } 926 case FwGetRootCertDataTag::recoveryCSK: 927 { 928 mtdDev = bmcRecoveryImgMTDDev; 929 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm; 930 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm; 931 break; 932 } 933 default: 934 { 935 return ipmi::responseInvalidFieldRequest(); 936 } 937 } 938 939 std::array<uint8_t, certKeyLen> certKey = {0}; 940 941 try 942 { 943 SPIDev spiDev(mtdDev); 944 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data()); 945 946 if (cskSigOffset) 947 { 948 std::array<uint8_t, cskSignatureLen> cskSignature = {0}; 949 spiDev.spiReadData(cskSigOffset, cskSignatureLen, 950 cskSignature.data()); 951 return ipmi::responseSuccess(certKey, cskSignature); 952 } 953 } 954 catch (const std::exception &e) 955 { 956 phosphor::logging::log<phosphor::logging::level::ERR>( 957 "Exception caught in ipmiGetFwRootCertData", 958 phosphor::logging::entry("MSG=%s", e.what())); 959 return ipmi::responseUnspecifiedError(); 960 } 961 962 return ipmi::responseSuccess(certKey, std::nullopt); 963 } 964 #endif // INTEL_PFR_ENABLED 965 966 static constexpr uint8_t channelListSize = 3; 967 /** @brief implements Maximum Firmware Transfer size command 968 * @parameter 969 * - none 970 * @returns IPMI completion code plus response data 971 * - count - channel count 972 * - channelList - channel list information 973 */ 974 ipmi::RspType<uint8_t, // channel count 975 std::array<std::tuple<uint8_t, uint32_t>, 976 channelListSize> // Channel List 977 > 978 ipmiFirmwareMaxTransferSize() 979 { 980 constexpr size_t kcsMaxBufSize = 128; 981 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024; 982 // Byte 1 - Count (N) Number of devices data is being returned for. 983 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb 984 // Byte 3-6 - transfer size (little endian) 985 // Bytes - 7:(5xN) - Repeat of 2 through 6 986 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize> 987 channelList = { 988 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize}, 989 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus), 990 rmcpPlusMaxBufSize}}}; 991 992 return ipmi::responseSuccess(channelListSize, channelList); 993 } 994 995 ipmi::RspType<uint8_t, uint8_t> 996 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx) 997 { 998 // Byte 1 - Current execution context 999 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode 1000 // Byte 2 - Partition pointer 1001 // 0x01 - primary, 0x02 - secondary 1002 uint8_t partitionPtr = getActiveBootImage(ctx); 1003 1004 return ipmi::responseSuccess( 1005 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr); 1006 } 1007 /** @brief Get Firmware Update Random Number 1008 * 1009 * This function generate the random number used for 1010 * setting the firmware update mode as authentication key. 1011 * 1012 * @param[in] ctx - context of current session 1013 * @returns IPMI completion code along with 1014 * - random number 1015 **/ 1016 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>> 1017 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr &ctx) 1018 { 1019 phosphor::logging::log<phosphor::logging::level::INFO>( 1020 "Generate FW update random number"); 1021 bool isIPMBChannel = false; 1022 1023 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1024 { 1025 return ipmi::responseUnspecifiedError(); 1026 } 1027 if (isIPMBChannel) 1028 { 1029 phosphor::logging::log<phosphor::logging::level::INFO>( 1030 "Channel not supported. Failed to fetch FW update random number"); 1031 return ipmi::responseCommandNotAvailable(); 1032 } 1033 std::random_device rd; 1034 std::default_random_engine gen(rd()); 1035 std::uniform_int_distribution<> dist{0, 255}; 1036 1037 fwRandomNumGenTs = std::chrono::steady_clock::now(); 1038 1039 for (int i = 0; i < fwRandomNumLength; i++) 1040 { 1041 fwRandomNum[i] = dist(gen); 1042 } 1043 1044 return ipmi::responseSuccess(fwRandomNum); 1045 } 1046 1047 /** @brief Set Firmware Update Mode 1048 * 1049 * This function sets BMC into firmware update mode 1050 * after validating Random number obtained from the Get 1051 * Firmware Update Random Number command 1052 * 1053 * @param[in] ctx - context of current session 1054 * @parameter randNum - Random number(token) 1055 * @returns IPMI completion code 1056 **/ 1057 ipmi::RspType<> 1058 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr &ctx, 1059 std::array<uint8_t, fwRandomNumLength> &randNum) 1060 { 1061 phosphor::logging::log<phosphor::logging::level::INFO>( 1062 "Start FW update mode"); 1063 1064 bool isIPMBChannel = false; 1065 1066 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1067 { 1068 return ipmi::responseUnspecifiedError(); 1069 } 1070 if (isIPMBChannel) 1071 { 1072 phosphor::logging::log<phosphor::logging::level::INFO>( 1073 "Channel not supported. Failed to set FW update mode"); 1074 return ipmi::responseCommandNotAvailable(); 1075 } 1076 /* Firmware Update Random number is valid for 30 seconds only */ 1077 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs); 1078 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed) 1079 .count() > std::chrono::duration_cast<std::chrono::microseconds>( 1080 fwRandomNumExpirySeconds) 1081 .count()) 1082 { 1083 phosphor::logging::log<phosphor::logging::level::INFO>( 1084 "Firmware update random number expired."); 1085 return ipmi::responseInvalidFieldRequest(); 1086 } 1087 1088 /* Validate random number */ 1089 for (int i = 0; i < fwRandomNumLength; i++) 1090 { 1091 if (fwRandomNum[i] != randNum[i]) 1092 { 1093 phosphor::logging::log<phosphor::logging::level::INFO>( 1094 "Invalid random number specified."); 1095 return ipmi::responseInvalidFieldRequest(); 1096 } 1097 } 1098 1099 try 1100 { 1101 if (getFirmwareUpdateMode()) 1102 { 1103 phosphor::logging::log<phosphor::logging::level::INFO>( 1104 "Already firmware update is in progress."); 1105 return ipmi::responseBusy(); 1106 } 1107 } 1108 catch (const std::exception &e) 1109 { 1110 return ipmi::responseUnspecifiedError(); 1111 } 1112 1113 // FIXME? c++ doesn't off an option for exclusive file creation 1114 FILE *fp = fopen(firmwareBufferFile, "wx"); 1115 if (!fp) 1116 { 1117 phosphor::logging::log<phosphor::logging::level::INFO>( 1118 "Unable to open file."); 1119 return ipmi::responseUnspecifiedError(); 1120 } 1121 fclose(fp); 1122 1123 try 1124 { 1125 setFirmwareUpdateMode(true); 1126 } 1127 catch (const std::exception &e) 1128 { 1129 unlink(firmwareBufferFile); 1130 return ipmi::responseUnspecifiedError(); 1131 } 1132 1133 return ipmi::responseSuccess(); 1134 } 1135 1136 /** @brief implements exit firmware update mode command 1137 * @param None 1138 * 1139 * @returns IPMI completion code 1140 */ 1141 ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr &ctx) 1142 { 1143 phosphor::logging::log<phosphor::logging::level::INFO>( 1144 "Exit FW update mode"); 1145 bool isIPMBChannel = false; 1146 1147 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1148 { 1149 return ipmi::responseUnspecifiedError(); 1150 } 1151 if (isIPMBChannel) 1152 { 1153 phosphor::logging::log<phosphor::logging::level::INFO>( 1154 "Command not supported. Failed to exit firmware update mode"); 1155 return ipmi::responseCommandNotAvailable(); 1156 } 1157 1158 switch (fwUpdateStatus.getState()) 1159 { 1160 case FwUpdateStatusCache::fwStateInit: 1161 case FwUpdateStatusCache::fwStateIdle: 1162 return ipmi::responseInvalidFieldRequest(); 1163 break; 1164 case FwUpdateStatusCache::fwStateDownload: 1165 case FwUpdateStatusCache::fwStateVerify: 1166 break; 1167 case FwUpdateStatusCache::fwStateProgram: 1168 break; 1169 case FwUpdateStatusCache::fwStateUpdateSuccess: 1170 case FwUpdateStatusCache::fwStateError: 1171 break; 1172 case FwUpdateStatusCache::fwStateAcCycleRequired: 1173 return ipmi::responseInvalidFieldRequest(); 1174 break; 1175 } 1176 fwUpdateStatus.firmwareUpdateAbortState(); 1177 1178 try 1179 { 1180 setFirmwareUpdateMode(false); 1181 } 1182 catch (const std::exception &e) 1183 { 1184 return ipmi::responseUnspecifiedError(); 1185 } 1186 1187 return ipmi::responseSuccess(); 1188 } 1189 1190 /** @brief implements Get/Set Firmware Update Control 1191 * @param[in] ctx - context of current session 1192 * @parameter 1193 * - Byte 1: Control Byte 1194 * - Byte 2: Firmware filename length (Optional) 1195 * - Byte 3:N: Firmware filename data (Optional) 1196 * @returns IPMI completion code plus response data 1197 * - Byte 2: Current control status 1198 **/ 1199 ipmi::RspType<bool, bool, bool, bool, uint4_t> 1200 ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr &ctx, 1201 const uint8_t controlReq, 1202 const std::optional<std::string> &fileName) 1203 { 1204 bool isIPMBChannel = false; 1205 1206 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1207 { 1208 return ipmi::responseUnspecifiedError(); 1209 } 1210 if (isIPMBChannel) 1211 { 1212 phosphor::logging::log<phosphor::logging::level::INFO>( 1213 "Channel not supported. Failed to get or set FW update control"); 1214 return ipmi::responseCommandNotAvailable(); 1215 } 1216 1217 static std::string fwXferUriPath; 1218 static bool imageTransferStarted = false; 1219 static bool imageTransferCompleted = false; 1220 static bool imageTransferAborted = false; 1221 1222 if ((controlReq != 1223 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) && 1224 (fileName)) 1225 { 1226 phosphor::logging::log<phosphor::logging::level::ERR>( 1227 "Invalid request field (Filename)."); 1228 return ipmi::responseInvalidFieldRequest(); 1229 } 1230 1231 static bool usbAttached = getUsbStatus(); 1232 1233 switch (static_cast<FwUpdateCtrlReq>(controlReq)) 1234 { 1235 case FwUpdateCtrlReq::getCurrentControlStatus: 1236 phosphor::logging::log<phosphor::logging::level::INFO>( 1237 "ipmiGetSetFirmwareUpdateControl: Get status"); 1238 break; 1239 case FwUpdateCtrlReq::imageTransferStart: 1240 { 1241 phosphor::logging::log<phosphor::logging::level::INFO>( 1242 "ipmiGetSetFirmwareUpdateControl: Set transfer start"); 1243 imageTransferStarted = true; 1244 // reset buffer to empty (truncate file) 1245 std::ofstream out(firmwareBufferFile, 1246 std::ofstream::binary | std::ofstream::trunc); 1247 fwXferUriPath = std::string("file://") + firmwareBufferFile; 1248 if (xferHashCheck) 1249 { 1250 xferHashCheck->clear(); 1251 } 1252 // Setting state to download 1253 fwUpdateStatus.setState( 1254 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload)); 1255 #ifdef INTEL_PFR_ENABLED 1256 imgLength = 0; 1257 imgType = 0; 1258 block0Mapped = false; 1259 #endif 1260 } 1261 break; 1262 case FwUpdateCtrlReq::imageTransferComplete: 1263 { 1264 phosphor::logging::log<phosphor::logging::level::INFO>( 1265 "ipmiGetSetFirmwareUpdateControl: Set transfer complete."); 1266 if (usbAttached) 1267 { 1268 phosphor::logging::log<phosphor::logging::level::ERR>( 1269 "USB should be detached to perform this operation."); 1270 return ipmi::responseNotSupportedInPresentState(); 1271 } 1272 // finish transfer based on URI 1273 if (!transferFirmwareFromUri(fwXferUriPath)) 1274 { 1275 phosphor::logging::log<phosphor::logging::level::ERR>( 1276 "transferFirmwareFromUri failed."); 1277 return ipmi::responseUnspecifiedError(); 1278 } 1279 // transfer complete 1280 if (xferHashCheck) 1281 { 1282 if (TransferHashCheck::HashCheck::sha2Success != 1283 xferHashCheck->verify()) 1284 { 1285 phosphor::logging::log<phosphor::logging::level::ERR>( 1286 "xferHashCheck failed."); 1287 return ipmi::responseUnspecifiedError(); 1288 } 1289 } 1290 // Set state to verify and start the update 1291 fwUpdateStatus.setState( 1292 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify)); 1293 // start the request 1294 if (!startFirmwareUpdate(firmwareBufferFile)) 1295 { 1296 phosphor::logging::log<phosphor::logging::level::ERR>( 1297 "startFirmwareUpdate failed."); 1298 return ipmi::responseUnspecifiedError(); 1299 } 1300 imageTransferCompleted = true; 1301 } 1302 break; 1303 case FwUpdateCtrlReq::imageTransferAbort: 1304 phosphor::logging::log<phosphor::logging::level::INFO>( 1305 "ipmiGetSetFirmwareUpdateControl: Set transfer abort."); 1306 if (usbAttached) 1307 { 1308 if (detachUsbDevice()) 1309 { 1310 phosphor::logging::log<phosphor::logging::level::ERR>( 1311 "Detach USB device failed."); 1312 return ipmi::responseUsbAttachOrDetachFailed(); 1313 } 1314 usbAttached = false; 1315 } 1316 // During abort request reset the state to Init by cleaning update 1317 // file. 1318 fwUpdateStatus.firmwareUpdateAbortState(); 1319 imageTransferAborted = true; 1320 break; 1321 case FwUpdateCtrlReq::setFirmwareFilename: 1322 phosphor::logging::log<phosphor::logging::level::INFO>( 1323 "ipmiGetSetFirmwareUpdateControl: Set filename."); 1324 if (!fileName || ((*fileName).length() == 0)) 1325 { 1326 phosphor::logging::log<phosphor::logging::level::ERR>( 1327 "Invalid Filename specified."); 1328 return ipmi::responseInvalidFieldRequest(); 1329 } 1330 1331 fwXferUriPath = *fileName; 1332 break; 1333 case FwUpdateCtrlReq::attachUsbDevice: 1334 phosphor::logging::log<phosphor::logging::level::INFO>( 1335 "ipmiGetSetFirmwareUpdateControl: Attach USB device."); 1336 if (usbAttached) 1337 { 1338 phosphor::logging::log<phosphor::logging::level::ERR>( 1339 "USB device is already attached."); 1340 return ipmi::responseInvalidFieldRequest(); 1341 } 1342 if (attachUsbDevice()) 1343 { 1344 phosphor::logging::log<phosphor::logging::level::ERR>( 1345 "Attach USB device failed."); 1346 return ipmi::responseUsbAttachOrDetachFailed(); 1347 } 1348 usbAttached = true; 1349 break; 1350 case FwUpdateCtrlReq::detachUsbDevice: 1351 phosphor::logging::log<phosphor::logging::level::INFO>( 1352 "ipmiGetSetFirmwareUpdateControl: Detach USB device."); 1353 if (!usbAttached) 1354 { 1355 phosphor::logging::log<phosphor::logging::level::ERR>( 1356 "USB device is not attached."); 1357 return ipmi::responseInvalidFieldRequest(); 1358 } 1359 if (detachUsbDevice()) 1360 { 1361 phosphor::logging::log<phosphor::logging::level::ERR>( 1362 "Detach USB device failed."); 1363 return ipmi::responseUsbAttachOrDetachFailed(); 1364 } 1365 usbAttached = false; 1366 break; 1367 default: 1368 phosphor::logging::log<phosphor::logging::level::ERR>( 1369 "Invalid control option specified."); 1370 return ipmi::responseInvalidFieldRequest(); 1371 } 1372 1373 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted, 1374 imageTransferAborted, usbAttached, uint4_t(0)); 1375 } 1376 1377 /** @brief implements firmware get status command 1378 * @parameter 1379 * - none 1380 * @returns IPMI completion code plus response data 1381 * - status - processing status 1382 * - percentage - percentage completion 1383 * - check - channel integrity check status 1384 **/ 1385 ipmi::RspType<uint8_t, // status 1386 uint8_t, // percentage 1387 uint8_t // check 1388 > 1389 ipmiGetFirmwareUpdateStatus() 1390 1391 { 1392 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write, 1393 // 5=ready, f=error, 83=ac cycle required) 1394 // Byte 2 - percent 1395 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail) 1396 uint8_t status = fwUpdateStatus.getState(); 1397 uint8_t percent = fwUpdateStatus.percent(); 1398 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0; 1399 1400 // Status code. 1401 return ipmi::responseSuccess(status, percent, check); 1402 } 1403 1404 ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions( 1405 const ipmi::Context::ptr &ctx, bool noDowngradeMask, bool deferRestartMask, 1406 bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart, 1407 bool sha2Check, uint5_t reserved2, 1408 std::optional<std::vector<uint8_t>> integrityCheckVal) 1409 { 1410 phosphor::logging::log<phosphor::logging::level::INFO>( 1411 "Set firmware update options."); 1412 bool isIPMBChannel = false; 1413 1414 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess) 1415 { 1416 return ipmi::responseUnspecifiedError(); 1417 } 1418 if (isIPMBChannel) 1419 { 1420 phosphor::logging::log<phosphor::logging::level::INFO>( 1421 "Channel not supported. Failed to set firmware update options"); 1422 return ipmi::responseCommandNotAvailable(); 1423 } 1424 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade(); 1425 bool deferRestartState = fwUpdateStatus.getDeferRestart(); 1426 bool sha2CheckState = xferHashCheck ? true : false; 1427 1428 if (noDowngradeMask && (noDowngradeState != noDowngrade)) 1429 { 1430 fwUpdateStatus.setInhibitDowngrade(noDowngrade); 1431 noDowngradeState = noDowngrade; 1432 } 1433 if (deferRestartMask && (deferRestartState != deferRestart)) 1434 { 1435 fwUpdateStatus.setDeferRestart(deferRestart); 1436 deferRestartState = deferRestart; 1437 } 1438 if (sha2CheckMask) 1439 { 1440 if (sha2Check) 1441 { 1442 auto hashSize = EVP_MD_size(EVP_sha256()); 1443 if ((*integrityCheckVal).size() != hashSize) 1444 { 1445 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1446 "Invalid size of Hash specified."); 1447 return ipmi::responseInvalidFieldRequest(); 1448 } 1449 xferHashCheck = std::make_shared<TransferHashCheck>(); 1450 xferHashCheck->init(*integrityCheckVal); 1451 } 1452 else 1453 { 1454 // delete the xferHashCheck object 1455 xferHashCheck.reset(); 1456 } 1457 sha2CheckState = sha2CheckMask; 1458 } 1459 return ipmi::responseSuccess(noDowngradeState, deferRestartState, 1460 sha2CheckState, reserved1); 1461 } 1462 1463 ipmi::RspType<uint32_t> 1464 ipmiFwImageWriteData(const std::vector<uint8_t> &writeData) 1465 { 1466 const uint8_t ccCmdNotSupportedInPresentState = 0xD5; 1467 size_t writeDataLen = writeData.size(); 1468 1469 if (!writeDataLen) 1470 { 1471 return ipmi::responseReqDataLenInvalid(); 1472 } 1473 1474 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload) 1475 { 1476 phosphor::logging::log<phosphor::logging::level::ERR>( 1477 "Invalid firmware update state."); 1478 return ipmi::response(ccCmdNotSupportedInPresentState); 1479 } 1480 1481 std::ofstream out(firmwareBufferFile, 1482 std::ofstream::binary | std::ofstream::app); 1483 if (!out) 1484 { 1485 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1486 "Error while opening file."); 1487 return ipmi::responseUnspecifiedError(); 1488 } 1489 1490 uint64_t fileDataLen = out.tellp(); 1491 1492 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize) 1493 { 1494 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1495 "Firmware image size exceeds the limit"); 1496 return ipmi::responseInvalidFieldRequest(); 1497 } 1498 1499 const char *data = reinterpret_cast<const char *>(writeData.data()); 1500 out.write(data, writeDataLen); 1501 out.close(); 1502 1503 if (xferHashCheck) 1504 { 1505 xferHashCheck->hash(writeData); 1506 } 1507 1508 #ifdef INTEL_PFR_ENABLED 1509 /* PFR image block 0 - As defined in HAS */ 1510 struct PFRImageBlock0 1511 { 1512 uint32_t tag; 1513 uint32_t pcLength; 1514 uint32_t pcType; 1515 uint32_t reserved1; 1516 uint8_t hash256[32]; 1517 uint8_t hash384[48]; 1518 uint8_t reserved2[32]; 1519 } __attribute__((packed)); 1520 1521 /* Get the PFR block 0 data and read the uploaded image 1522 * information( Image type, length etc) */ 1523 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) && 1524 (!block0Mapped)) 1525 { 1526 struct PFRImageBlock0 block0Data = {0}; 1527 1528 std::ifstream inFile(firmwareBufferFile, 1529 std::ios::binary | std::ios::in); 1530 inFile.read(reinterpret_cast<char *>(&block0Data), sizeof(block0Data)); 1531 inFile.close(); 1532 1533 uint32_t magicNum = block0Data.tag; 1534 1535 /* Validate the magic number */ 1536 if (magicNum != perBlock0MagicNum) 1537 { 1538 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1539 "PFR image magic number not matched"); 1540 return ipmi::responseInvalidFieldRequest(); 1541 } 1542 // Note:imgLength, imgType and block0Mapped are in global scope, as 1543 // these are used in cascaded updates. 1544 imgLength = block0Data.pcLength; 1545 imgType = block0Data.pcType; 1546 block0Mapped = true; 1547 } 1548 #endif // end of INTEL_PFR_ENABLED 1549 return ipmi::responseSuccess(writeDataLen); 1550 } 1551 1552 static void registerFirmwareFunctions() 1553 { 1554 // guarantee that we start with an already timed out timestamp 1555 fwRandomNumGenTs = 1556 std::chrono::steady_clock::now() - fwRandomNumExpirySeconds; 1557 fwUpdateStatus.setState( 1558 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit)); 1559 1560 unlink(firmwareBufferFile); 1561 1562 #ifdef INTEL_PFR_ENABLED 1563 // Following commands are supported only for PFR enabled platforms 1564 // CMD:0x20 - Get Firmware Version Information 1565 // CMD:0x21 - Get Firmware Security Version Information 1566 // CMD:0x25 - Get Root Certificate Data 1567 1568 // get firmware version information 1569 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1570 ipmi::firmware::cmdGetFwVersionInfo, 1571 ipmi::Privilege::Admin, ipmiGetFwVersionInfo); 1572 1573 // get firmware security version information 1574 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1575 ipmi::firmware::cmdGetFwSecurityVersionInfo, 1576 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo); 1577 1578 // get root certificate data 1579 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1580 ipmi::firmware::cmdFwGetRootCertData, 1581 ipmi::Privilege::Admin, ipmiGetFwRootCertData); 1582 #endif 1583 1584 // get firmware update channel information (max transfer sizes) 1585 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1586 ipmi::firmware::cmdGetFwUpdateChannelInfo, 1587 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize); 1588 1589 // get bmc execution context 1590 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1591 ipmi::firmware::cmdGetBmcExecutionContext, 1592 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext); 1593 1594 // Get Firmware Update Random number 1595 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1596 ipmi::firmware::cmdGetFwUpdateRandomNumber, 1597 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber); 1598 1599 // Set Firmware Update Mode 1600 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1601 ipmi::firmware::cmdSetFirmwareUpdateMode, 1602 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode); 1603 1604 // Exit Firmware Update Mode 1605 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1606 ipmi::firmware::cmdExitFirmwareUpdateMode, 1607 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode); 1608 1609 // Get/Set Firmware Update Control 1610 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1611 ipmi::firmware::cmdGetSetFwUpdateControl, 1612 ipmi::Privilege::Admin, 1613 ipmiGetSetFirmwareUpdateControl); 1614 1615 // Get Firmware Update Status 1616 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1617 ipmi::firmware::cmdGetFirmwareUpdateStatus, 1618 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus); 1619 1620 // Set Firmware Update Options 1621 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1622 ipmi::firmware::cmdSetFirmwareUpdateOptions, 1623 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions); 1624 // write image data 1625 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1626 ipmi::firmware::cmdFwImageWriteData, 1627 ipmi::Privilege::Admin, ipmiFwImageWriteData); 1628 return; 1629 } 1630