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(uint8_t certId) 860 { 861 size_t certKeyOffset = 0; 862 size_t cskSigOffset = 0; 863 std::string mtdDev; 864 865 switch (static_cast<FwGetRootCertDataTag>(certId)) 866 { 867 case FwGetRootCertDataTag::activeRootKey: 868 { 869 mtdDev = bmcActivePfmMTDDev; 870 certKeyOffset = rootkeyOffsetInPfm; 871 break; 872 } 873 case FwGetRootCertDataTag::recoveryRootKey: 874 { 875 mtdDev = bmcRecoveryImgMTDDev; 876 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm; 877 break; 878 } 879 case FwGetRootCertDataTag::activeCSK: 880 { 881 mtdDev = bmcActivePfmMTDDev; 882 certKeyOffset = cskKeyOffsetInPfm; 883 cskSigOffset = cskSignatureOffsetInPfm; 884 break; 885 } 886 case FwGetRootCertDataTag::recoveryCSK: 887 { 888 mtdDev = bmcRecoveryImgMTDDev; 889 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm; 890 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm; 891 break; 892 } 893 default: 894 { 895 return ipmi::responseInvalidFieldRequest(); 896 } 897 } 898 899 std::array<uint8_t, certKeyLen> certKey = {0}; 900 901 try 902 { 903 SPIDev spiDev(mtdDev); 904 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data()); 905 906 if (cskSigOffset) 907 { 908 std::array<uint8_t, cskSignatureLen> cskSignature = {0}; 909 spiDev.spiReadData(cskSigOffset, cskSignatureLen, 910 cskSignature.data()); 911 return ipmi::responseSuccess(certKey, cskSignature); 912 } 913 } 914 catch (const std::exception &e) 915 { 916 phosphor::logging::log<phosphor::logging::level::ERR>( 917 "Exception caught in ipmiGetFwRootCertData", 918 phosphor::logging::entry("MSG=%s", e.what())); 919 return ipmi::responseUnspecifiedError(); 920 } 921 922 return ipmi::responseSuccess(certKey, std::nullopt); 923 } 924 #endif // INTEL_PFR_ENABLED 925 926 static constexpr uint8_t channelListSize = 3; 927 /** @brief implements Maximum Firmware Transfer size command 928 * @parameter 929 * - none 930 * @returns IPMI completion code plus response data 931 * - count - channel count 932 * - channelList - channel list information 933 */ 934 ipmi::RspType<uint8_t, // channel count 935 std::array<std::tuple<uint8_t, uint32_t>, 936 channelListSize> // Channel List 937 > 938 ipmiFirmwareMaxTransferSize() 939 { 940 constexpr size_t kcsMaxBufSize = 128; 941 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024; 942 constexpr size_t ipmbMaxBufSize = 4 * 1024; 943 // Byte 1 - Count (N) Number of devices data is being returned for. 944 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb 945 // Byte 3-6 - transfer size (little endian) 946 // Bytes - 7:(5xN) - Repeat of 2 through 6 947 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize> 948 channelList = { 949 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize}, 950 {static_cast<uint8_t>(ChannelIdTag::ipmb), ipmbMaxBufSize}, 951 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus), 952 rmcpPlusMaxBufSize}}}; 953 954 return ipmi::responseSuccess(channelListSize, channelList); 955 } 956 957 ipmi::RspType<uint8_t, uint8_t> 958 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx) 959 { 960 // Byte 1 - Current execution context 961 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode 962 // Byte 2 - Partition pointer 963 // 0x01 - primary, 0x02 - secondary 964 uint8_t partitionPtr = getActiveBootImage(ctx); 965 966 return ipmi::responseSuccess( 967 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr); 968 } 969 970 /** @brief Get Firmware Update Random Number 971 * 972 * This function generate the random number used for 973 * setting the firmware update mode as authentication key. 974 * 975 * @parameter : None 976 * @returns IPMI completion code along with 977 * - random number 978 **/ 979 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>> 980 ipmiGetFwUpdateRandomNumber() 981 { 982 phosphor::logging::log<phosphor::logging::level::INFO>( 983 "Generate FW update random number"); 984 std::random_device rd; 985 std::default_random_engine gen(rd()); 986 std::uniform_int_distribution<> dist{0, 255}; 987 988 fwRandomNumGenTs = std::chrono::steady_clock::now(); 989 990 for (int i = 0; i < fwRandomNumLength; i++) 991 { 992 fwRandomNum[i] = dist(gen); 993 } 994 995 return ipmi::responseSuccess(fwRandomNum); 996 } 997 998 /** @brief Set Firmware Update Mode 999 * 1000 * This function sets BMC into firmware update mode 1001 * after validating Random number obtained from the Get 1002 * Firmware Update Random Number command 1003 * 1004 * @parameter 1005 * - randNum - Random number(token) 1006 * @returns IPMI completion code 1007 **/ 1008 ipmi::RspType<> 1009 ipmiSetFirmwareUpdateMode(std::array<uint8_t, fwRandomNumLength> &randNum) 1010 { 1011 phosphor::logging::log<phosphor::logging::level::INFO>( 1012 "Start FW update mode"); 1013 /* Firmware Update Random number is valid for 30 seconds only */ 1014 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs); 1015 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed) 1016 .count() > std::chrono::duration_cast<std::chrono::microseconds>( 1017 fwRandomNumExpirySeconds) 1018 .count()) 1019 { 1020 phosphor::logging::log<phosphor::logging::level::INFO>( 1021 "Firmware update random number expired."); 1022 return ipmi::responseInvalidFieldRequest(); 1023 } 1024 1025 /* Validate random number */ 1026 for (int i = 0; i < fwRandomNumLength; i++) 1027 { 1028 if (fwRandomNum[i] != randNum[i]) 1029 { 1030 phosphor::logging::log<phosphor::logging::level::INFO>( 1031 "Invalid random number specified."); 1032 return ipmi::responseInvalidFieldRequest(); 1033 } 1034 } 1035 1036 try 1037 { 1038 if (getFirmwareUpdateMode()) 1039 { 1040 phosphor::logging::log<phosphor::logging::level::INFO>( 1041 "Already firmware update is in progress."); 1042 return ipmi::responseBusy(); 1043 } 1044 } 1045 catch (const std::exception &e) 1046 { 1047 return ipmi::responseUnspecifiedError(); 1048 } 1049 1050 // FIXME? c++ doesn't off an option for exclusive file creation 1051 FILE *fp = fopen(firmwareBufferFile, "wx"); 1052 if (!fp) 1053 { 1054 phosphor::logging::log<phosphor::logging::level::INFO>( 1055 "Unable to open file."); 1056 return ipmi::responseUnspecifiedError(); 1057 } 1058 fclose(fp); 1059 1060 try 1061 { 1062 setFirmwareUpdateMode(true); 1063 } 1064 catch (const std::exception &e) 1065 { 1066 unlink(firmwareBufferFile); 1067 return ipmi::responseUnspecifiedError(); 1068 } 1069 1070 return ipmi::responseSuccess(); 1071 } 1072 1073 /** @brief implements exit firmware update mode command 1074 * @param None 1075 * 1076 * @returns IPMI completion code 1077 */ 1078 ipmi::RspType<> ipmiExitFirmwareUpdateMode() 1079 { 1080 phosphor::logging::log<phosphor::logging::level::INFO>( 1081 "Exit FW update mode"); 1082 switch (fwUpdateStatus.getState()) 1083 { 1084 case FwUpdateStatusCache::fwStateInit: 1085 case FwUpdateStatusCache::fwStateIdle: 1086 return ipmi::responseInvalidFieldRequest(); 1087 break; 1088 case FwUpdateStatusCache::fwStateDownload: 1089 case FwUpdateStatusCache::fwStateVerify: 1090 break; 1091 case FwUpdateStatusCache::fwStateProgram: 1092 break; 1093 case FwUpdateStatusCache::fwStateUpdateSuccess: 1094 case FwUpdateStatusCache::fwStateError: 1095 break; 1096 case FwUpdateStatusCache::fwStateAcCycleRequired: 1097 return ipmi::responseInvalidFieldRequest(); 1098 break; 1099 } 1100 fwUpdateStatus.firmwareUpdateAbortState(); 1101 1102 try 1103 { 1104 setFirmwareUpdateMode(false); 1105 } 1106 catch (const std::exception &e) 1107 { 1108 return ipmi::responseUnspecifiedError(); 1109 } 1110 1111 return ipmi::responseSuccess(); 1112 } 1113 1114 /** @brief implements Get/Set Firmware Update Control 1115 * @parameter 1116 * - Byte 1: Control Byte 1117 * - Byte 2: Firmware filename length (Optional) 1118 * - Byte 3:N: Firmware filename data (Optional) 1119 * @returns IPMI completion code plus response data 1120 * - Byte 2: Current control status 1121 **/ 1122 ipmi::RspType<bool, bool, bool, bool, uint4_t> 1123 ipmiGetSetFirmwareUpdateControl(const uint8_t controlReq, 1124 const std::optional<std::string> &fileName) 1125 { 1126 static std::string fwXferUriPath; 1127 static bool imageTransferStarted = false; 1128 static bool imageTransferCompleted = false; 1129 static bool imageTransferAborted = false; 1130 1131 if ((controlReq != 1132 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) && 1133 (fileName)) 1134 { 1135 phosphor::logging::log<phosphor::logging::level::ERR>( 1136 "Invalid request field (Filename)."); 1137 return ipmi::responseInvalidFieldRequest(); 1138 } 1139 1140 static bool usbAttached = getUsbStatus(); 1141 1142 switch (static_cast<FwUpdateCtrlReq>(controlReq)) 1143 { 1144 case FwUpdateCtrlReq::getCurrentControlStatus: 1145 phosphor::logging::log<phosphor::logging::level::INFO>( 1146 "ipmiGetSetFirmwareUpdateControl: Get status"); 1147 break; 1148 case FwUpdateCtrlReq::imageTransferStart: 1149 { 1150 phosphor::logging::log<phosphor::logging::level::INFO>( 1151 "ipmiGetSetFirmwareUpdateControl: Set transfer start"); 1152 imageTransferStarted = true; 1153 // reset buffer to empty (truncate file) 1154 std::ofstream out(firmwareBufferFile, 1155 std::ofstream::binary | std::ofstream::trunc); 1156 fwXferUriPath = std::string("file://") + firmwareBufferFile; 1157 if (xferHashCheck) 1158 { 1159 xferHashCheck->clear(); 1160 } 1161 // Setting state to download 1162 fwUpdateStatus.setState( 1163 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload)); 1164 #ifdef INTEL_PFR_ENABLED 1165 imgLength = 0; 1166 imgType = 0; 1167 block0Mapped = false; 1168 #endif 1169 } 1170 break; 1171 case FwUpdateCtrlReq::imageTransferComplete: 1172 { 1173 phosphor::logging::log<phosphor::logging::level::INFO>( 1174 "ipmiGetSetFirmwareUpdateControl: Set transfer complete."); 1175 if (usbAttached) 1176 { 1177 phosphor::logging::log<phosphor::logging::level::ERR>( 1178 "USB should be detached to perform this operation."); 1179 return ipmi::responseNotSupportedInPresentState(); 1180 } 1181 // finish transfer based on URI 1182 if (!transferFirmwareFromUri(fwXferUriPath)) 1183 { 1184 phosphor::logging::log<phosphor::logging::level::ERR>( 1185 "transferFirmwareFromUri failed."); 1186 return ipmi::responseUnspecifiedError(); 1187 } 1188 // transfer complete 1189 if (xferHashCheck) 1190 { 1191 if (TransferHashCheck::HashCheck::sha2Success != 1192 xferHashCheck->verify()) 1193 { 1194 phosphor::logging::log<phosphor::logging::level::ERR>( 1195 "xferHashCheck failed."); 1196 return ipmi::responseUnspecifiedError(); 1197 } 1198 } 1199 // Set state to verify and start the update 1200 fwUpdateStatus.setState( 1201 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify)); 1202 // start the request 1203 if (!startFirmwareUpdate(firmwareBufferFile)) 1204 { 1205 phosphor::logging::log<phosphor::logging::level::ERR>( 1206 "startFirmwareUpdate failed."); 1207 return ipmi::responseUnspecifiedError(); 1208 } 1209 imageTransferCompleted = true; 1210 } 1211 break; 1212 case FwUpdateCtrlReq::imageTransferAbort: 1213 phosphor::logging::log<phosphor::logging::level::INFO>( 1214 "ipmiGetSetFirmwareUpdateControl: Set transfer abort."); 1215 if (usbAttached) 1216 { 1217 if (detachUsbDevice()) 1218 { 1219 phosphor::logging::log<phosphor::logging::level::ERR>( 1220 "Detach USB device failed."); 1221 return ipmi::responseUsbAttachOrDetachFailed(); 1222 } 1223 usbAttached = false; 1224 } 1225 // During abort request reset the state to Init by cleaning update 1226 // file. 1227 fwUpdateStatus.firmwareUpdateAbortState(); 1228 imageTransferAborted = true; 1229 break; 1230 case FwUpdateCtrlReq::setFirmwareFilename: 1231 phosphor::logging::log<phosphor::logging::level::INFO>( 1232 "ipmiGetSetFirmwareUpdateControl: Set filename."); 1233 if (!fileName || ((*fileName).length() == 0)) 1234 { 1235 phosphor::logging::log<phosphor::logging::level::ERR>( 1236 "Invalid Filename specified."); 1237 return ipmi::responseInvalidFieldRequest(); 1238 } 1239 1240 fwXferUriPath = *fileName; 1241 break; 1242 case FwUpdateCtrlReq::attachUsbDevice: 1243 phosphor::logging::log<phosphor::logging::level::INFO>( 1244 "ipmiGetSetFirmwareUpdateControl: Attach USB device."); 1245 if (usbAttached) 1246 { 1247 phosphor::logging::log<phosphor::logging::level::ERR>( 1248 "USB device is already attached."); 1249 return ipmi::responseInvalidFieldRequest(); 1250 } 1251 if (attachUsbDevice()) 1252 { 1253 phosphor::logging::log<phosphor::logging::level::ERR>( 1254 "Attach USB device failed."); 1255 return ipmi::responseUsbAttachOrDetachFailed(); 1256 } 1257 usbAttached = true; 1258 break; 1259 case FwUpdateCtrlReq::detachUsbDevice: 1260 phosphor::logging::log<phosphor::logging::level::INFO>( 1261 "ipmiGetSetFirmwareUpdateControl: Detach USB device."); 1262 if (!usbAttached) 1263 { 1264 phosphor::logging::log<phosphor::logging::level::ERR>( 1265 "USB device is not attached."); 1266 return ipmi::responseInvalidFieldRequest(); 1267 } 1268 if (detachUsbDevice()) 1269 { 1270 phosphor::logging::log<phosphor::logging::level::ERR>( 1271 "Detach USB device failed."); 1272 return ipmi::responseUsbAttachOrDetachFailed(); 1273 } 1274 usbAttached = false; 1275 break; 1276 default: 1277 phosphor::logging::log<phosphor::logging::level::ERR>( 1278 "Invalid control option specified."); 1279 return ipmi::responseInvalidFieldRequest(); 1280 } 1281 1282 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted, 1283 imageTransferAborted, usbAttached, uint4_t(0)); 1284 } 1285 1286 /** @brief implements firmware get status command 1287 * @parameter 1288 * - none 1289 * @returns IPMI completion code plus response data 1290 * - status - processing status 1291 * - percentage - percentage completion 1292 * - check - channel integrity check status 1293 **/ 1294 ipmi::RspType<uint8_t, // status 1295 uint8_t, // percentage 1296 uint8_t // check 1297 > 1298 ipmiGetFirmwareUpdateStatus() 1299 1300 { 1301 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write, 1302 // 5=ready, f=error, 83=ac cycle required) 1303 // Byte 2 - percent 1304 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail) 1305 uint8_t status = fwUpdateStatus.getState(); 1306 uint8_t percent = fwUpdateStatus.percent(); 1307 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0; 1308 1309 // Status code. 1310 return ipmi::responseSuccess(status, percent, check); 1311 } 1312 1313 ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions( 1314 bool noDowngradeMask, bool deferRestartMask, bool sha2CheckMask, 1315 uint5_t reserved1, bool noDowngrade, bool deferRestart, bool sha2Check, 1316 uint5_t reserved2, std::optional<std::vector<uint8_t>> integrityCheckVal) 1317 { 1318 phosphor::logging::log<phosphor::logging::level::INFO>( 1319 "Set firmware update options."); 1320 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade(); 1321 bool deferRestartState = fwUpdateStatus.getDeferRestart(); 1322 bool sha2CheckState = xferHashCheck ? true : false; 1323 1324 if (noDowngradeMask && (noDowngradeState != noDowngrade)) 1325 { 1326 fwUpdateStatus.setInhibitDowngrade(noDowngrade); 1327 noDowngradeState = noDowngrade; 1328 } 1329 if (deferRestartMask && (deferRestartState != deferRestart)) 1330 { 1331 fwUpdateStatus.setDeferRestart(deferRestart); 1332 deferRestartState = deferRestart; 1333 } 1334 if (sha2CheckMask) 1335 { 1336 if (sha2Check) 1337 { 1338 auto hashSize = EVP_MD_size(EVP_sha256()); 1339 if ((*integrityCheckVal).size() != hashSize) 1340 { 1341 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1342 "Invalid size of Hash specified."); 1343 return ipmi::responseInvalidFieldRequest(); 1344 } 1345 xferHashCheck = std::make_shared<TransferHashCheck>(); 1346 xferHashCheck->init(*integrityCheckVal); 1347 } 1348 else 1349 { 1350 // delete the xferHashCheck object 1351 xferHashCheck.reset(); 1352 } 1353 sha2CheckState = sha2CheckMask; 1354 } 1355 return ipmi::responseSuccess(noDowngradeState, deferRestartState, 1356 sha2CheckState, reserved1); 1357 } 1358 1359 ipmi::RspType<uint32_t> 1360 ipmiFwImageWriteData(const std::vector<uint8_t> &writeData) 1361 { 1362 const uint8_t ccCmdNotSupportedInPresentState = 0xD5; 1363 size_t writeDataLen = writeData.size(); 1364 1365 if (!writeDataLen) 1366 { 1367 return ipmi::responseReqDataLenInvalid(); 1368 } 1369 1370 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload) 1371 { 1372 phosphor::logging::log<phosphor::logging::level::ERR>( 1373 "Invalid firmware update state."); 1374 return ipmi::response(ccCmdNotSupportedInPresentState); 1375 } 1376 1377 std::ofstream out(firmwareBufferFile, 1378 std::ofstream::binary | std::ofstream::app); 1379 if (!out) 1380 { 1381 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1382 "Error while opening file."); 1383 return ipmi::responseUnspecifiedError(); 1384 } 1385 1386 uint64_t fileDataLen = out.tellp(); 1387 1388 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize) 1389 { 1390 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1391 "Firmware image size exceeds the limit"); 1392 return ipmi::responseInvalidFieldRequest(); 1393 } 1394 1395 const char *data = reinterpret_cast<const char *>(writeData.data()); 1396 out.write(data, writeDataLen); 1397 out.close(); 1398 1399 if (xferHashCheck) 1400 { 1401 xferHashCheck->hash(writeData); 1402 } 1403 1404 #ifdef INTEL_PFR_ENABLED 1405 /* PFR image block 0 - As defined in HAS */ 1406 struct PFRImageBlock0 1407 { 1408 uint32_t tag; 1409 uint32_t pcLength; 1410 uint32_t pcType; 1411 uint32_t reserved1; 1412 uint8_t hash256[32]; 1413 uint8_t hash384[48]; 1414 uint8_t reserved2[32]; 1415 } __attribute__((packed)); 1416 1417 /* Get the PFR block 0 data and read the uploaded image 1418 * information( Image type, length etc) */ 1419 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) && 1420 (!block0Mapped)) 1421 { 1422 struct PFRImageBlock0 block0Data = {0}; 1423 1424 std::ifstream inFile(firmwareBufferFile, 1425 std::ios::binary | std::ios::in); 1426 inFile.read(reinterpret_cast<char *>(&block0Data), sizeof(block0Data)); 1427 inFile.close(); 1428 1429 uint32_t magicNum = block0Data.tag; 1430 1431 /* Validate the magic number */ 1432 if (magicNum != perBlock0MagicNum) 1433 { 1434 phosphor::logging::log<phosphor::logging::level::DEBUG>( 1435 "PFR image magic number not matched"); 1436 return ipmi::responseInvalidFieldRequest(); 1437 } 1438 // Note:imgLength, imgType and block0Mapped are in global scope, as 1439 // these are used in cascaded updates. 1440 imgLength = block0Data.pcLength; 1441 imgType = block0Data.pcType; 1442 block0Mapped = true; 1443 } 1444 #endif // end of INTEL_PFR_ENABLED 1445 return ipmi::responseSuccess(writeDataLen); 1446 } 1447 1448 static void registerFirmwareFunctions() 1449 { 1450 // guarantee that we start with an already timed out timestamp 1451 fwRandomNumGenTs = 1452 std::chrono::steady_clock::now() - fwRandomNumExpirySeconds; 1453 fwUpdateStatus.setState( 1454 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit)); 1455 1456 unlink(firmwareBufferFile); 1457 1458 #ifdef INTEL_PFR_ENABLED 1459 // Following commands are supported only for PFR enabled platforms 1460 // CMD:0x20 - Get Firmware Version Information 1461 // CMD:0x21 - Get Firmware Security Version Information 1462 // CMD:0x25 - Get Root Certificate Data 1463 1464 // get firmware version information 1465 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1466 ipmi::firmware::cmdGetFwVersionInfo, 1467 ipmi::Privilege::Admin, ipmiGetFwVersionInfo); 1468 1469 // get firmware security version information 1470 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1471 ipmi::firmware::cmdGetFwSecurityVersionInfo, 1472 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo); 1473 1474 // get root certificate data 1475 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1476 ipmi::firmware::cmdFwGetRootCertData, 1477 ipmi::Privilege::Admin, ipmiGetFwRootCertData); 1478 #endif 1479 1480 // get firmware update channel information (max transfer sizes) 1481 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1482 ipmi::firmware::cmdGetFwUpdateChannelInfo, 1483 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize); 1484 1485 // get bmc execution context 1486 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1487 ipmi::firmware::cmdGetBmcExecutionContext, 1488 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext); 1489 1490 // Get Firmware Update Random number 1491 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1492 ipmi::firmware::cmdGetFwUpdateRandomNumber, 1493 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber); 1494 1495 // Set Firmware Update Mode 1496 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1497 ipmi::firmware::cmdSetFirmwareUpdateMode, 1498 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode); 1499 1500 // Exit Firmware Update Mode 1501 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1502 ipmi::firmware::cmdExitFirmwareUpdateMode, 1503 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode); 1504 1505 // Get/Set Firmware Update Control 1506 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1507 ipmi::firmware::cmdGetSetFwUpdateControl, 1508 ipmi::Privilege::Admin, 1509 ipmiGetSetFirmwareUpdateControl); 1510 1511 // Get Firmware Update Status 1512 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1513 ipmi::firmware::cmdGetFirmwareUpdateStatus, 1514 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus); 1515 1516 // Set Firmware Update Options 1517 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware, 1518 ipmi::firmware::cmdSetFirmwareUpdateOptions, 1519 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions); 1520 // write image data 1521 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware, 1522 ipmi::firmware::cmdFwImageWriteData, 1523 ipmi::Privilege::Admin, ipmiFwImageWriteData); 1524 return; 1525 } 1526