1 /* 2 // Copyright (c) 2020 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include "biosxml.hpp" 18 19 #include <openssl/sha.h> 20 21 #include <biosconfigcommands.hpp> 22 #include <boost/crc.hpp> 23 #include <boost/process/child.hpp> 24 #include <boost/process/io.hpp> 25 #include <ipmid/api.hpp> 26 #include <ipmid/message.hpp> 27 #include <ipmid/message/types.hpp> 28 #include <ipmid/types.hpp> 29 #include <ipmid/utils.hpp> 30 #include <nlohmann/json.hpp> 31 #include <oemcommands.hpp> 32 #include <sdbusplus/bus.hpp> 33 #include <sdbusplus/message/types.hpp> 34 35 #include <filesystem> 36 #include <string_view> 37 38 namespace ipmi 39 { 40 static bool flushNVOOBdata(); 41 static void registerBIOSConfigFunctions() __attribute__((constructor)); 42 43 // Define BIOS config related Completion Code 44 using Cc = uint8_t; 45 static constexpr Cc ipmiCCPayloadPayloadPacketMissed = 0x80; 46 static constexpr Cc ipmiCCBIOSPasswordInitNotDone = 0x80; 47 static constexpr Cc ipmiCCPayloadChecksumFailed = 0x81; 48 static constexpr Cc ipmiCCNotSupportedInCurrentState = 0x82; 49 static constexpr Cc ipmiCCPayloadPayloadInComplete = 0x83; 50 static constexpr Cc ipmiCCBIOSCapabilityInitNotDone = 0x85; 51 static constexpr Cc ipmiCCPayloadLengthIllegal = 0x85; 52 53 static constexpr uint8_t userPasswordChanged = (1 << 5); 54 static constexpr uint8_t adminPasswordChanged = (1 << 4); 55 56 static constexpr const char* biosConfigFolder = "/var/oob"; 57 static constexpr const char* biosConfigNVPath = "/var/oob/nvoobdata.dat"; 58 static constexpr const uint8_t algoSHA384 = 2; 59 static constexpr const uint8_t algoSHA256 = 1; 60 static constexpr const uint8_t biosCapOffsetBit = 0x3; 61 static constexpr uint16_t maxGetPayloadDataSize = 4096; 62 static constexpr const char* biosXMLFilePath = "/var/oob/bios.xml"; 63 static constexpr const char* biosXMLFilePath1 = "/var/oob/tempbios.xml"; 64 65 static constexpr const char* biosConfigBaseMgrPath = 66 "/xyz/openbmc_project/bios_config/manager"; 67 static constexpr const char* biosConfigIntf = 68 "xyz.openbmc_project.BIOSConfig.Manager"; 69 static constexpr const char* resetBIOSSettingsProp = "ResetBIOSSettings"; 70 /*baseBIOSTable 71 map{attributeName,struct{attributeType,readonlyStatus,displayname, 72 description,menuPath,current,default, 73 array{struct{optionstring,optionvalue}}}} 74 */ 75 76 bios::BiosBaseTableType attributesData; 77 78 NVOOBdata gNVOOBdata; 79 80 enum class PTState : uint8_t 81 { 82 StartTransfer = 0, 83 InProgress = 1, 84 EndTransfer = 2, 85 UserAbort = 3 86 }; 87 enum class PStatus : uint8_t 88 { 89 Unknown = 0, 90 Valid = 1, 91 Corrupted = 2 92 }; 93 enum class PType : uint8_t 94 { 95 IntelXMLType0 = 0, 96 IntelXMLType1 = 1, 97 OTAPayload = 5, 98 }; 99 100 // 101 // GetPayload Payload status enumeration 102 // 103 enum class GetPayloadParameter : uint8_t 104 { 105 GetPayloadInfo = 0, // 0 106 GetPayloadData = 1, // 1 107 GetPayloadStatus = 2 108 }; 109 110 namespace payload1 111 { 112 113 enum class AttributesType : uint8_t 114 { 115 unknown = 0, 116 string, 117 integer, 118 enumeration 119 }; 120 121 using PendingAttributesType = 122 std::map<std::string, 123 std::tuple<std::string, std::variant<int64_t, std::string>>>; 124 125 AttributesType getAttrType(const std::string_view typeDbus) 126 { 127 if (typeDbus == "xyz.openbmc_project.BIOSConfig.Manager." 128 "AttributeType.String") 129 { 130 return AttributesType::string; 131 } 132 else if (typeDbus == "xyz.openbmc_project.BIOSConfig." 133 "Manager.AttributeType.Integer") 134 { 135 return AttributesType::integer; 136 } 137 else if (typeDbus == "xyz.openbmc_project.BIOSConfig." 138 "Manager.AttributeType.Enumeration") 139 { 140 return AttributesType::enumeration; 141 } 142 143 return AttributesType::unknown; 144 } 145 146 bool fillPayloadData(std::string& payloadData, 147 const std::variant<int64_t, std::string>& attributes, 148 const std::string_view key, AttributesType& attrType) 149 { 150 payloadData += key; 151 payloadData += '='; 152 153 if (attrType == AttributesType::string || 154 attrType == AttributesType::enumeration) 155 { 156 if (!std::holds_alternative<std::string>(attributes)) 157 { 158 phosphor::logging::log<phosphor::logging::level::ERR>( 159 "fillPayloadData: No string data in attributes"); 160 return false; 161 } 162 payloadData += std::get<std::string>(attributes); 163 } 164 else if (attrType == AttributesType::integer) 165 { 166 if (!std::holds_alternative<int64_t>(attributes)) 167 { 168 phosphor::logging::log<phosphor::logging::level::ERR>( 169 "fillPayloadData: No int64_t data in attributes"); 170 return false; 171 } 172 payloadData += std::to_string(std::get<int64_t>(attributes)); 173 } 174 else 175 { 176 phosphor::logging::log<phosphor::logging::level::ERR>( 177 "fillPayloadData: Unsupported attribute type"); 178 return false; 179 } 180 181 payloadData += '\n'; 182 183 return true; 184 } 185 186 bool getPendingList(ipmi::Context::ptr ctx, std::string& payloadData) 187 { 188 std::variant<PendingAttributesType> pendingAttributesData; 189 boost::system::error_code ec; 190 191 payloadData.clear(); 192 193 auto dbus = getSdBus(); 194 if (!dbus) 195 { 196 phosphor::logging::log<phosphor::logging::level::ERR>( 197 "getPendingList: getSdBus() failed"); 198 return false; 199 } 200 201 std::string service = 202 getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath); 203 204 try 205 { 206 pendingAttributesData = 207 dbus->yield_method_call<std::variant<PendingAttributesType>>( 208 ctx->yield, ec, service, 209 "/xyz/openbmc_project/bios_config/manager", 210 "org.freedesktop.DBus.Properties", "Get", 211 "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes"); 212 } 213 catch (const std::exception& ex) 214 { 215 phosphor::logging::log<phosphor::logging::level::ERR>(ex.what()); 216 return false; 217 } 218 219 if (ec) 220 { 221 std::string err = "getPendingList: error while trying to get " 222 "PendingAttributes, error = "; 223 err += ec.message(); 224 225 phosphor::logging::log<phosphor::logging::level::ERR>(err.c_str()); 226 227 return false; 228 } 229 230 const PendingAttributesType* pendingAttributes = 231 std::get_if<PendingAttributesType>(&pendingAttributesData); 232 if (!pendingAttributes) 233 { 234 phosphor::logging::log<phosphor::logging::level::ERR>( 235 "getPendingList: pendingAttributes is null"); 236 return false; 237 } 238 239 for (const auto& [key, attributes] : *pendingAttributes) 240 { 241 const std::string& itemType = std::get<0>(attributes); 242 AttributesType attrType = getAttrType(itemType); 243 244 if (!fillPayloadData(payloadData, std::get<1>(attributes), key, 245 attrType)) 246 { 247 return false; 248 } 249 } 250 251 if (payloadData.empty()) 252 { 253 phosphor::logging::log<phosphor::logging::level::ERR>( 254 "getPendingList: payloadData is empty"); 255 return false; 256 } 257 258 return true; 259 } 260 bool updatePayloadFile(std::string& payloadFilePath, std::string payloadData) 261 { 262 std::ofstream payloadFile(payloadFilePath, 263 std::ios::out | std::ios::binary); 264 265 payloadFile << payloadData; 266 267 if (!payloadFile.good()) 268 { 269 return false; 270 } 271 272 return true; 273 } 274 275 bool computeCheckSum(std::string& payloadFilePath, 276 boost::crc_32_type& calcChecksum) 277 { 278 std::ifstream payloadFile(payloadFilePath.c_str(), 279 std::ios::in | std::ios::binary | std::ios::ate); 280 281 if (!payloadFile.good()) 282 { 283 phosphor::logging::log<phosphor::logging::level::ERR>( 284 "computeCheckSum: Cannot open Payload1 file"); 285 return false; 286 } 287 288 payloadFile.seekg(0, payloadFile.end); 289 int length = payloadFile.tellg(); 290 payloadFile.seekg(0, payloadFile.beg); 291 292 if (maxGetPayloadDataSize < length) 293 { 294 phosphor::logging::log<phosphor::logging::level::ERR>( 295 "computeCheckSum: length > maxGetPayloadDataSize"); 296 return false; 297 } 298 299 std::unique_ptr<char[]> payloadBuffer(new char[length]); 300 301 payloadFile.read(payloadBuffer.get(), length); 302 uint32_t readCount = payloadFile.gcount(); 303 304 calcChecksum.process_bytes(payloadBuffer.get(), readCount); 305 306 return true; 307 } 308 309 bool updatePayloadInfo(std::string& payloadFilePath) 310 { 311 boost::crc_32_type calcChecksum; 312 313 uint8_t payloadType = static_cast<uint8_t>(ipmi::PType::IntelXMLType1); 314 auto& payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 315 316 if (!computeCheckSum(payloadFilePath, calcChecksum)) 317 { 318 phosphor::logging::log<phosphor::logging::level::ERR>( 319 "updatePayloadInfo: Cannot compute checksum for Payload1 file"); 320 return false; 321 } 322 323 payloadInfo.payloadVersion = 0; 324 payloadInfo.payloadflag = 0; 325 payloadInfo.payloadReservationID = rand(); 326 327 payloadInfo.payloadType = payloadType; 328 329 payloadInfo.payloadTotalChecksum = calcChecksum.checksum(); 330 payloadInfo.payloadCurrentChecksum = payloadInfo.payloadTotalChecksum; 331 332 payloadInfo.payloadStatus = (static_cast<uint8_t>(ipmi::PStatus::Valid)); 333 334 struct stat filestat; 335 /* Get entry's information. */ 336 if (!stat(payloadFilePath.c_str(), &filestat)) 337 { 338 payloadInfo.payloadTimeStamp = filestat.st_mtime; 339 payloadInfo.payloadTotalSize = filestat.st_size; 340 payloadInfo.payloadCurrentSize = filestat.st_size; 341 payloadInfo.actualTotalPayloadWritten = filestat.st_size; 342 } 343 else 344 { 345 phosphor::logging::log<phosphor::logging::level::ERR>( 346 "updatePayloadInfo: Cannot get file status for Payload1 file"); 347 return false; 348 } 349 350 if (!flushNVOOBdata()) 351 { 352 phosphor::logging::log<phosphor::logging::level::ERR>( 353 "updatePayloadInfo: flushNVOOBdata failed"); 354 return false; 355 } 356 357 return true; 358 } 359 360 bool update(ipmi::Context::ptr ctx) 361 { 362 std::string payloadFilePath = 363 "/var/oob/Payload" + 364 std::to_string(static_cast<uint8_t>(ipmi::PType::IntelXMLType1)); 365 366 std::string payloadData; 367 368 if (!getPendingList(ctx, payloadData)) 369 { 370 phosphor::logging::log<phosphor::logging::level::ERR>( 371 "payload1::update : getPendingList() failed"); 372 return false; 373 } 374 375 if (!updatePayloadFile(payloadFilePath, payloadData)) 376 { 377 phosphor::logging::log<phosphor::logging::level::ERR>( 378 "payload1::update : updatePayloadFile() failed"); 379 return false; 380 } 381 382 if (!updatePayloadInfo(payloadFilePath)) 383 { 384 phosphor::logging::log<phosphor::logging::level::ERR>( 385 "payload1::update : updatePayloadInfo() failed"); 386 return false; 387 } 388 389 return true; 390 } 391 } // namespace payload1 392 393 /** @brief implement to set the BaseBIOSTable property 394 * @returns status 395 */ 396 static bool sendAllAttributes(std::string service) 397 { 398 std::shared_ptr<sdbusplus::asio::connection> pSdBusPlus = getSdBus(); 399 400 if (pSdBusPlus) 401 { 402 try 403 { 404 pSdBusPlus->async_method_call( 405 [](const boost::system::error_code ec) { 406 /* No more need to keep attributes data in memory */ 407 attributesData.clear(); 408 409 if (ec) 410 { 411 phosphor::logging::log<phosphor::logging::level::ERR>( 412 "sendAllAttributes error: send all attributes - " 413 "failed"); 414 return; 415 } 416 417 phosphor::logging::log<phosphor::logging::level::INFO>( 418 "sendAllAttributes: send all attributes - done"); 419 }, 420 service, biosConfigBaseMgrPath, 421 "org.freedesktop.DBus.Properties", "Set", biosConfigIntf, 422 "BaseBIOSTable", 423 std::variant<bios::BiosBaseTableType>(attributesData)); 424 425 return true; 426 } 427 catch (const std::exception& ex) 428 { 429 phosphor::logging::log<phosphor::logging::level::ERR>(ex.what()); 430 } 431 } 432 433 return false; 434 } 435 436 /** @brief implement to flush the updated data in nv space 437 * @returns status 438 */ 439 static bool flushNVOOBdata() 440 { 441 std::ofstream outFile(biosConfigNVPath, std::ios::binary); 442 443 outFile.seekp(std::ios_base::beg); 444 const char* writedata = reinterpret_cast<const char*>(&gNVOOBdata); 445 outFile.write(writedata, sizeof(struct NVOOBdata)); 446 447 if (!outFile.good()) 448 { 449 return false; 450 } 451 452 return true; 453 } 454 455 /** @brief implement to get the System State 456 * @returns status 457 */ 458 459 static int getSystemOSState(std::string& OsStatus) 460 { 461 462 try 463 { 464 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 465 Value variant = 466 getDbusProperty(*dbus, "xyz.openbmc_project.State.OperatingSystem", 467 "/xyz/openbmc_project/state/os", 468 "xyz.openbmc_project.State.OperatingSystem.Status", 469 "OperatingSystemState"); 470 OsStatus = std::get<std::string>(variant); 471 return ipmi::ccSuccess; 472 } 473 catch (const std::exception& e) 474 { 475 return ipmi::ccUnspecifiedError; 476 } 477 } 478 479 /** @brief implement to get the Rest BIOS property 480 * @returns status 481 */ 482 static int getResetBIOSSettings(uint8_t& ResetFlag) 483 { 484 485 try 486 { 487 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 488 std::string service = 489 getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath); 490 Value variant = getDbusProperty(*dbus, service, biosConfigBaseMgrPath, 491 biosConfigIntf, resetBIOSSettingsProp); 492 493 std::string_view ResetStr = std::get<std::string>(variant); 494 if (ResetStr == 495 "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag.NoAction") 496 { 497 ResetFlag = 0; 498 } 499 else if (ResetStr == "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag." 500 "FactoryDefaults") 501 { 502 ResetFlag = 1; 503 } 504 else if (ResetStr == "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag." 505 "FailSafeDefaults") 506 { 507 ResetFlag = 2; 508 } 509 else 510 { 511 return ipmi::ccUnspecifiedError; 512 } 513 514 return ipmi::ccSuccess; 515 } 516 catch (const std::exception& e) 517 { 518 return ipmi::ccUnspecifiedError; 519 } 520 } 521 522 /** @brief Get attributes data (bios base table) from bios.xml 523 */ 524 static bool generateAttributesData() 525 { 526 try 527 { 528 bios::Xml biosxml(biosXMLFilePath); 529 530 if (!biosxml.doDepexCompute()) 531 { 532 phosphor::logging::log<phosphor::logging::level::ERR>( 533 "'depex' compute failed"); 534 } 535 536 if (!biosxml.getBaseTable(attributesData)) 537 { 538 phosphor::logging::log<phosphor::logging::level::ERR>( 539 "Failed to get bios base table"); 540 } 541 } 542 catch (const std::exception& ex) 543 { 544 phosphor::logging::log<phosphor::logging::level::ERR>(ex.what()); 545 return false; 546 } 547 548 return true; 549 } 550 551 /** @brief Generate attributes data from bios.xml 552 * and send attributes data (bios base table) to dbus using set method. 553 */ 554 static void generateAndSendAttributesData(std::string service, 555 uint8_t payloadType) 556 { 557 if (!generateAttributesData()) 558 { 559 phosphor::logging::log<phosphor::logging::level::ERR>( 560 "generateAndSendAttributesData: generateAttributesData - failed"); 561 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 562 static_cast<uint8_t>(ipmi::PStatus::Corrupted); 563 return; 564 } 565 566 phosphor::logging::log<phosphor::logging::level::INFO>( 567 "generateAndSendAttributesData : generateAttributesData is done"); 568 569 if (!sendAllAttributes(service)) 570 { 571 phosphor::logging::log<phosphor::logging::level::ERR>( 572 "generateAndSendAttributesData: sendAllAttributes - failed"); 573 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 574 static_cast<uint8_t>(ipmi::PStatus::Corrupted); 575 return; 576 } 577 578 phosphor::logging::log<phosphor::logging::level::INFO>( 579 "generateAndSendAttributesData : sendAllAttributes is done"); 580 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 581 static_cast<uint8_t>(ipmi::PStatus::Valid); 582 } 583 584 /** @brief implement executing the linux command to uncompress and generate the 585 * xmlfile 586 * @param[in] linux command 587 * @returns status 588 */ 589 template <typename... ArgTypes> 590 static int generateBIOSXMLFile(const char* path, ArgTypes&&... tArgs) 591 { 592 593 boost::process::child execProg(path, const_cast<char*>(tArgs)..., 594 boost::process::std_out > biosXMLFilePath); 595 execProg.wait(); 596 return execProg.exit_code(); 597 } 598 599 /** @brief implements to clean up the temp/ existing payload file 600 **/ 601 static void cleanUpPayloadFile(uint8_t& payloadType) 602 { 603 // Clear the payload Information 604 std::string FilePath = "/var/oob/temp" + std::to_string(payloadType); 605 unlink(FilePath.c_str()); 606 FilePath = "/var/oob/Payload" + std::to_string(payloadType); 607 unlink(FilePath.c_str()); 608 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 609 { 610 unlink("/var/oob/Payload1"); 611 gNVOOBdata.payloadInfo[static_cast<uint8_t>(ipmi::PType::IntelXMLType1)] 612 .payloadStatus = static_cast<uint8_t>(ipmi::PStatus::Unknown); 613 } 614 } 615 616 /** @brief implements to create the oob folders and nv space 617 **/ 618 static Cc InitNVOOBdata() 619 { 620 FILE* fptr; 621 uint16_t size; 622 623 if (!(std::filesystem::exists(biosConfigFolder))) 624 { 625 std::filesystem::create_directory(biosConfigFolder); 626 } 627 628 std::ifstream ifs(biosConfigNVPath, std::ios::in | std::ios::binary); 629 630 if (ifs.good()) 631 { 632 633 ifs.seekg(std::ios_base::beg); 634 ifs.read(reinterpret_cast<char*>(&gNVOOBdata), 635 sizeof(struct NVOOBdata)); 636 ifs.close(); 637 return ipmi::ccSuccess; 638 } 639 return ipmi::ccResponseError; 640 } 641 642 /** @brief implements check the command interface is 643 ** system interface or not 644 ** true mean System interface and false mean LAN or IPMB 645 **/ 646 static bool IsSystemInterface(ipmi::Context::ptr ctx) 647 { 648 ChannelInfo chInfo; 649 Cc status = false; 650 651 try 652 { 653 getChannelInfo(ctx->channel, chInfo); 654 } 655 catch (const sdbusplus::exception_t& e) 656 { 657 return false; 658 } 659 if (chInfo.mediumType != 660 static_cast<uint8_t>(EChannelMediumType::systemInterface)) 661 { 662 return false; 663 } 664 return true; 665 } 666 667 ipmi::RspType<> ipmiOEMSetBIOSCap(ipmi::Context::ptr ctx, 668 uint8_t BIOSCapabilties, uint8_t reserved1, 669 uint8_t reserved2, uint8_t reserved3) 670 { 671 std::string OSState; 672 getSystemOSState(OSState); 673 674 if (OSState != "OperatingState" && IsSystemInterface(ctx)) 675 { 676 if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0) 677 { 678 return ipmi::responseInvalidFieldRequest(); 679 } 680 681 gNVOOBdata.mBIOSCapabilities.OOBCapability = BIOSCapabilties; 682 gNVOOBdata.mIsBIOSCapInitDone = true; 683 684 flushNVOOBdata(); 685 return ipmi::responseSuccess(); 686 } 687 else 688 { 689 return ipmi::response(ipmiCCNotSupportedInCurrentState); 690 } 691 } 692 693 ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t> 694 ipmiOEMGetBIOSCap(ipmi::Context::ptr ctx) 695 { 696 if (gNVOOBdata.mIsBIOSCapInitDone) 697 { 698 return ipmi::responseSuccess(gNVOOBdata.mBIOSCapabilities.OOBCapability, 699 0, 0, 0); 700 } 701 else 702 { 703 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 704 } 705 } 706 707 ipmi::RspType<uint32_t> ipmiOEMSetPayload(ipmi::Context::ptr ctx, 708 uint8_t paramSel, uint8_t payloadType, 709 std::vector<uint8_t> payload) 710 { 711 uint8_t biosCapOffsetBit = 2; // BIT:1 0-OOB BIOS config not supported 712 // 1-OOB BIOS config is supported 713 714 if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit))) 715 { 716 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 717 } 718 // Validate the Payload Type 719 if (payloadType > maxPayloadSupported) 720 { 721 return ipmi::responseInvalidFieldRequest(); 722 } 723 724 // We should support this Payload type 0 command only in KCS Interface 725 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 726 { 727 std::string OSState; 728 729 getSystemOSState(OSState); 730 if (!IsSystemInterface(ctx) || OSState == "OperatingState") 731 { 732 return ipmi::responseCommandNotAvailable(); 733 } 734 } 735 736 switch (static_cast<PTState>(paramSel)) 737 { 738 case ipmi::PTState::StartTransfer: 739 { 740 PayloadStartTransfer* pPayloadStartTransfer = 741 reinterpret_cast<PayloadStartTransfer*>(payload.data()); 742 if (payload.size() < sizeof(PayloadStartTransfer)) 743 { 744 phosphor::logging::log<phosphor::logging::level::ERR>( 745 "ipmiOEMSetPayload: BIOS Config Payload size is not " 746 "correct"); 747 return ipmi::responseReqDataLenInvalid(); 748 } 749 cleanUpPayloadFile(payloadType); 750 751 gNVOOBdata.payloadInfo[payloadType].payloadReservationID = rand(); 752 gNVOOBdata.payloadInfo[payloadType].payloadTotalChecksum = 753 pPayloadStartTransfer->payloadTotalChecksum; 754 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 755 pPayloadStartTransfer->payloadTotalSize; 756 gNVOOBdata.payloadInfo[payloadType].payloadVersion = 757 pPayloadStartTransfer->payloadVersion; 758 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten = 0; 759 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 760 static_cast<uint8_t>(ipmi::PStatus::Unknown); 761 gNVOOBdata.payloadInfo[payloadType].payloadType = payloadType; 762 763 return ipmi::responseSuccess( 764 gNVOOBdata.payloadInfo[payloadType].payloadReservationID); 765 } 766 break; 767 768 case ipmi::PTState::InProgress: 769 { 770 PayloadInProgress* pPayloadInProgress = 771 reinterpret_cast<PayloadInProgress*>(payload.data()); 772 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 773 774 if (payload.size() < sizeof(PayloadInProgress)) 775 { 776 phosphor::logging::log<phosphor::logging::level::ERR>( 777 "BIOS Config Payload size is not correct"); 778 return ipmi::responseReqDataLenInvalid(); 779 } 780 781 if (pPayloadInProgress->payloadReservationID != 782 payloadInfo.payloadReservationID) 783 { 784 phosphor::logging::log<phosphor::logging::level::ERR>( 785 "BIOS Config Payload reservation ID is not correct"); 786 return ipmi::responseInvalidReservationId(); 787 } 788 payloadInfo.payloadCurrentSize = 789 pPayloadInProgress->payloadCurrentSize; 790 // Need to verify the current Payload Checksum 791 const uint8_t* data = 792 reinterpret_cast<const uint8_t*>(payload.data()); 793 // we have to remove the current size, current offset, current 794 // length,checksum bytes , reservation bytes 795 boost::crc_32_type calcChecksum; 796 calcChecksum.process_bytes(data + 16, payload.size() - 16); 797 if (calcChecksum.checksum() != 798 pPayloadInProgress->payloadCurrentChecksum) 799 { 800 phosphor::logging::log<phosphor::logging::level::ERR>( 801 "ipmiOEMSetPayload: Payload Checksum Failed"); 802 return ipmi::response(ipmiCCPayloadChecksumFailed); 803 } 804 // store the data in temp file 805 std::string FilePath = 806 "/var/oob/temp" + std::to_string(payloadType); 807 808 std::ofstream outFile(FilePath, std::ios::binary | std::ios::app); 809 outFile.seekp(pPayloadInProgress->payloadOffset); 810 // we have to remove the current size, current offset, current 811 // length,checksum bytes , reservation bytes 812 813 const char* writedata = 814 reinterpret_cast<const char*>(payload.data()); 815 outFile.write(writedata + 16, payload.size() - 16); 816 outFile.close(); 817 818 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 819 static_cast<uint8_t>(ipmi::PStatus::Unknown); 820 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten += 821 payloadInfo.payloadCurrentSize; 822 return ipmi::responseSuccess(payloadInfo.payloadCurrentSize); 823 } 824 break; 825 case ipmi::PTState::EndTransfer: 826 { 827 PayloadEndTransfer* pPayloadEndTransfer = 828 reinterpret_cast<PayloadEndTransfer*>(payload.data()); 829 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 830 if (pPayloadEndTransfer->payloadReservationID != 831 payloadInfo.payloadReservationID) 832 { 833 return ipmi::responseInvalidReservationId(); 834 } 835 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 836 static_cast<uint8_t>(ipmi::PStatus::Unknown); 837 838 if (gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten != 839 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize) 840 { 841 return ipmi::response(ipmiCCPayloadPayloadInComplete); 842 } 843 std::string tempFilePath = 844 "/var/oob/temp" + std::to_string(payloadType); 845 std::string payloadFilePath = 846 "/var/oob/Payload" + std::to_string(payloadType); 847 auto renamestatus = 848 std::rename(tempFilePath.c_str(), payloadFilePath.c_str()); 849 if (renamestatus) 850 { 851 phosphor::logging::log<phosphor::logging::level::ERR>( 852 "ipmiOEMSetPayload: Renaming Payload file - failed"); 853 } 854 855 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 856 { 857 // Unzip the Intel format XML file type 0 858 auto response = generateBIOSXMLFile("/usr/bin/lzcat", "-d", 859 payloadFilePath.c_str()); 860 if (response) 861 { 862 863 phosphor::logging::log<phosphor::logging::level::ERR>( 864 "ipmiOEMSetPayload: generateBIOSXMLFile - failed"); 865 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 866 static_cast<uint8_t>(ipmi::PStatus::Corrupted); 867 return ipmi::response(ipmiCCPayloadPayloadPacketMissed); 868 } 869 phosphor::logging::log<phosphor::logging::level::INFO>( 870 " ipmiOEMSetPayload : Convert XML into native-dbus DONE"); 871 872 /* So that we don't block the call */ 873 auto io = getIoContext(); 874 auto dbus = getSdBus(); 875 if (io && dbus) 876 { 877 std::string service = getService(*dbus, biosConfigIntf, 878 biosConfigBaseMgrPath); 879 880 boost::asio::post(*io, [service, payloadType] { 881 generateAndSendAttributesData(service, payloadType); 882 }); 883 } 884 else 885 { 886 phosphor::logging::log<phosphor::logging::level::INFO>( 887 "ipmiOEMSetPayload: Unable to get io context or sdbus"); 888 return ipmi::responseResponseError(); 889 } 890 } 891 892 struct stat filestat; 893 894 /* Get entry's information. */ 895 if (!stat(payloadFilePath.c_str(), &filestat)) 896 { 897 gNVOOBdata.payloadInfo[payloadType].payloadTimeStamp = 898 filestat.st_mtime; 899 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 900 filestat.st_size; 901 } 902 else 903 { 904 return ipmi::responseResponseError(); 905 } 906 flushNVOOBdata(); 907 return ipmi::responseSuccess( 908 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten); 909 } 910 break; 911 case ipmi::PTState::UserAbort: 912 { 913 PayloadEndTransfer* pPayloadEndTransfer = 914 reinterpret_cast<PayloadEndTransfer*>(payload.data()); 915 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 916 if (pPayloadEndTransfer->payloadReservationID != 917 payloadInfo.payloadReservationID) 918 { 919 return ipmi::responseInvalidReservationId(); 920 } 921 gNVOOBdata.payloadInfo[payloadType].payloadReservationID = 0; 922 gNVOOBdata.payloadInfo[payloadType].payloadType = 0; 923 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 0; 924 // Delete the temp file 925 std::string tempFilePath = 926 "/var/oob/temp" + std::to_string(payloadType); 927 unlink(tempFilePath.c_str()); 928 flushNVOOBdata(); 929 return ipmi::responseSuccess(); 930 } 931 break; 932 default: 933 return ipmi::responseInvalidFieldRequest(); 934 } 935 return ipmi::responseResponseError(); 936 } 937 938 ipmi::RspType<message::Payload> 939 ipmiOEMGetPayload(ipmi::Context::ptr ctx, uint8_t paramSel, 940 uint8_t payloadType, ipmi::message::Payload& payload) 941 { 942 // 1-OOB BIOS config is supported 943 message::Payload retValue; 944 945 if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit))) 946 { 947 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 948 } 949 // Validate the Payload Type 950 if (payloadType > maxPayloadSupported) 951 { 952 return ipmi::responseInvalidFieldRequest(); 953 } 954 955 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType1)) 956 { 957 if (!payload1::update(ctx)) 958 { 959 phosphor::logging::log<phosphor::logging::level::ERR>( 960 "ipmiOEMGetPayload: unable to update NVOOBdata for payloadType " 961 "= IntelXMLType1"); 962 return ipmi::response(ipmi::ccUnspecifiedError); 963 } 964 } 965 966 struct PayloadInfo res = gNVOOBdata.payloadInfo[payloadType]; 967 968 switch (static_cast<GetPayloadParameter>(paramSel)) 969 { 970 case ipmi::GetPayloadParameter::GetPayloadInfo: 971 { 972 std::string payloadFilePath = 973 "/var/oob/Payload" + std::to_string(payloadType); 974 975 std::ifstream ifs(payloadFilePath, 976 std::ios::in | std::ios::binary | std::ios::ate); 977 978 if (!ifs.good()) 979 { 980 phosphor::logging::log<phosphor::logging::level::ERR>( 981 "ipmiOEMGetPayload: Payload File Error"); 982 // File does not exist code here 983 return ipmi::response(ipmi::ccUnspecifiedError); 984 } 985 986 ifs.close(); 987 retValue.pack(res.payloadVersion); 988 retValue.pack(payloadType); 989 retValue.pack(res.payloadTotalSize); 990 retValue.pack(res.payloadTotalChecksum); 991 retValue.pack(res.payloadflag); 992 retValue.pack(res.payloadStatus); 993 retValue.pack(res.payloadTimeStamp); 994 995 return ipmi::responseSuccess(std::move(retValue)); 996 } 997 998 break; 999 case ipmi::GetPayloadParameter::GetPayloadData: 1000 { 1001 if (res.payloadStatus == 1002 (static_cast<uint8_t>(ipmi::PStatus::Valid))) 1003 { 1004 std::vector<uint32_t> reqData; 1005 if (payload.unpack(reqData) || !payload.fullyUnpacked()) 1006 { 1007 return ipmi::responseReqDataLenInvalid(); 1008 } 1009 uint32_t offset = reqData.at(0); 1010 uint32_t length = reqData.at(1); 1011 std::string payloadFilePath = 1012 "/var/oob/Payload" + std::to_string(payloadType); 1013 1014 std::ifstream ifs(payloadFilePath, std::ios::in | 1015 std::ios::binary | 1016 std::ios::ate); 1017 1018 if (!ifs.good()) 1019 { 1020 phosphor::logging::log<phosphor::logging::level::ERR>( 1021 "ipmiOEMGetPayload: Payload File Error"); 1022 // File does not exist code here 1023 return ipmi::response(ipmi::ccUnspecifiedError); 1024 } 1025 std::ifstream::pos_type fileSize = ifs.tellg(); 1026 // Total file data within given offset 1027 if (fileSize < static_cast<uint64_t>(offset)) 1028 { 1029 ifs.close(); 1030 return ipmi::responseInvalidFieldRequest(); 1031 } 1032 1033 ifs.seekg(offset, std::ios::beg); 1034 std::array<uint8_t, maxGetPayloadDataSize> Buffer; 1035 ifs.read(reinterpret_cast<char*>(Buffer.data()), length); 1036 uint32_t readCount = ifs.gcount(); 1037 ifs.close(); 1038 1039 boost::crc_32_type calcChecksum; 1040 calcChecksum.process_bytes( 1041 reinterpret_cast<char*>(Buffer.data()), readCount); 1042 uint32_t chkSum = calcChecksum.checksum(); 1043 retValue.pack(payloadType); 1044 retValue.pack(readCount); 1045 retValue.pack(chkSum); 1046 1047 for (int i = 0; i < readCount; i++) 1048 { 1049 retValue.pack(Buffer.at(i)); 1050 } 1051 1052 return ipmi::responseSuccess(std::move(retValue)); 1053 } 1054 else 1055 { 1056 return ipmi::responseResponseError(); 1057 } 1058 } 1059 break; 1060 case ipmi::GetPayloadParameter::GetPayloadStatus: 1061 { 1062 retValue.pack(gNVOOBdata.payloadInfo[payloadType].payloadStatus); 1063 return ipmi::responseSuccess(std::move(retValue)); 1064 } 1065 break; 1066 default: 1067 return ipmi::responseInvalidFieldRequest(); 1068 } 1069 return ipmi::responseInvalidFieldRequest(); 1070 } 1071 1072 ipmi::RspType<> ipmiOEMSetBIOSHashInfo( 1073 ipmi::Context::ptr ctx, std::array<uint8_t, maxSeedSize>& pwdSeed, 1074 uint8_t algoInfo, std::array<uint8_t, maxHashSize>& adminPwdHash) 1075 { 1076 1077 std::string OSState; 1078 1079 // We should support this command only in KCS Interface 1080 if (!IsSystemInterface(ctx)) 1081 { 1082 return ipmi::responseCommandNotAvailable(); 1083 } 1084 getSystemOSState(OSState); 1085 // We should not support this command after System Booted - After Exit Boot 1086 // service called 1087 1088 if (OSState == "OperatingState") 1089 { 1090 return ipmi::response(ipmiCCNotSupportedInCurrentState); 1091 } 1092 1093 nlohmann::json json; 1094 1095 if ((algoInfo & 0xF) == algoSHA384) 1096 { 1097 json["HashAlgo"] = "SHA384"; 1098 } 1099 else if ((algoInfo & 0xF) == algoSHA256) 1100 { 1101 json["HashAlgo"] = "SHA256"; 1102 } 1103 else 1104 { 1105 return ipmi::responseInvalidFieldRequest(); 1106 } 1107 1108 json["Seed"] = pwdSeed; 1109 json["IsAdminPwdChanged"] = false; 1110 json["AdminPwdHash"] = adminPwdHash; 1111 json["IsUserPwdChanged"] = false; 1112 1113 std::array<uint8_t, maxHashSize> userPwdHash; 1114 userPwdHash.fill({}); // initializing with 0 as user password hash field 1115 // is not used presently 1116 json["UserPwdHash"] = userPwdHash; 1117 json["StatusFlag"] = algoInfo; 1118 1119 std::string hashFilePath = "/var/lib/bios-settings-manager/seedData"; 1120 std::ofstream ofs(hashFilePath, std::ios::out); 1121 const auto& writeData = json.dump(); 1122 ofs << writeData; 1123 ofs.close(); 1124 return ipmi::responseSuccess(); 1125 } 1126 1127 ipmi::RspType<std::array<uint8_t, maxSeedSize>, uint8_t, 1128 std::array<uint8_t, maxHashSize>> 1129 ipmiOEMGetBIOSHash(ipmi::Context::ptr ctx) 1130 { 1131 1132 std::string OSState; 1133 nlohmann::json data = nullptr; 1134 1135 // We should support this command only in KCS Interface 1136 if (!IsSystemInterface(ctx)) 1137 { 1138 return ipmi::responseCommandNotAvailable(); 1139 } 1140 1141 getSystemOSState(OSState); 1142 // We should not support this command after System Booted - After Exit Boot 1143 // service called 1144 1145 if (OSState != "OperatingState") 1146 { 1147 std::string HashFilePath = "/var/lib/bios-settings-manager/seedData"; 1148 1149 std::ifstream devIdFile(HashFilePath); 1150 if (devIdFile.is_open()) 1151 { 1152 1153 try 1154 { 1155 data = nlohmann::json::parse(devIdFile, nullptr, false); 1156 } 1157 catch (const nlohmann::json::parse_error& e) 1158 { 1159 return ipmi::responseResponseError(); 1160 } 1161 1162 if (data.is_discarded()) 1163 { 1164 return ipmi::responseResponseError(); 1165 } 1166 1167 std::array<uint8_t, maxHashSize> newAdminHash; 1168 std::array<uint8_t, maxSeedSize> seed; 1169 1170 uint8_t flag = 0; 1171 uint8_t adminPwdChangedFlag = 0; 1172 if (!data.is_discarded()) 1173 { 1174 1175 adminPwdChangedFlag = data["IsAdminPwdChanged"]; 1176 newAdminHash = data["AdminPwdHash"]; 1177 seed = data["Seed"]; 1178 } 1179 1180 auto status = getResetBIOSSettings(flag); 1181 if (status) 1182 { 1183 return ipmi::responseResponseError(); 1184 } 1185 if (adminPwdChangedFlag) 1186 { 1187 flag |= adminPasswordChanged; 1188 } 1189 1190 std::copy(std::begin(newAdminHash), std::end(newAdminHash), 1191 std::begin(newAdminHash)); 1192 1193 return ipmi::responseSuccess(seed, flag, newAdminHash); 1194 } 1195 else 1196 { 1197 return ipmi::responseResponseError(); 1198 } 1199 } 1200 else 1201 { 1202 1203 return ipmi::response(ipmiCCNotSupportedInCurrentState); 1204 } 1205 } 1206 1207 static void registerBIOSConfigFunctions(void) 1208 { 1209 phosphor::logging::log<phosphor::logging::level::INFO>( 1210 "BIOSConfig module initialization"); 1211 InitNVOOBdata(); 1212 1213 registerHandler(prioOemBase, intel::netFnGeneral, 1214 intel::general::cmdSetBIOSCap, Privilege::Admin, 1215 ipmiOEMSetBIOSCap); 1216 1217 registerHandler(prioOemBase, intel::netFnGeneral, 1218 intel::general::cmdGetBIOSCap, Privilege::User, 1219 ipmiOEMGetBIOSCap); 1220 registerHandler(prioOemBase, intel::netFnGeneral, 1221 intel::general::cmdSetBIOSPwdHashInfo, Privilege::Admin, 1222 ipmiOEMSetBIOSHashInfo); 1223 1224 registerHandler(prioOemBase, intel::netFnGeneral, 1225 intel::general::cmdGetBIOSPwdHash, Privilege::User, 1226 ipmiOEMGetBIOSHash); 1227 1228 registerHandler(prioOemBase, intel::netFnGeneral, 1229 intel::general::cmdGetPayload, Privilege::User, 1230 ipmiOEMGetPayload); 1231 registerHandler(prioOemBase, intel::netFnGeneral, 1232 intel::general::cmdSetPayload, Privilege::Admin, 1233 ipmiOEMSetPayload); 1234 1235 return; 1236 } 1237 1238 } // namespace ipmi 1239