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