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 static bool getPostCompleted() 459 { 460 /* 461 * In case of failure we treat postCompleted as true. 462 * So that BIOS config commands is not accepted by BMC by mistake. 463 */ 464 bool postCompleted = true; 465 466 try 467 { 468 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 469 Value variant = 470 getDbusProperty(*dbus, "xyz.openbmc_project.State.OperatingSystem", 471 "/xyz/openbmc_project/state/os", 472 "xyz.openbmc_project.State.OperatingSystem.Status", 473 "OperatingSystemState"); 474 auto& value = std::get<std::string>(variant); 475 476 // The short strings "Standby" is deprecated in favor of the 477 // full enum strings. Support for the short strings will be 478 // removed in the future. 479 postCompleted = (value == "Standby") || 480 (value == "xyz.openbmc_project.State.OperatingSystem." 481 "Status.OSStatus.Standby"); 482 } 483 catch (const std::exception& e) 484 { 485 phosphor::logging::log<phosphor::logging::level::ERR>( 486 "'getDbusProperty' failed to read " 487 "xyz.openbmc_project.State.OperatingSystem"); 488 } 489 490 return postCompleted; 491 } 492 493 /** @brief implement to get the Rest BIOS property 494 * @returns status 495 */ 496 static int getResetBIOSSettings(uint8_t& ResetFlag) 497 { 498 499 try 500 { 501 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 502 std::string service = 503 getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath); 504 Value variant = getDbusProperty(*dbus, service, biosConfigBaseMgrPath, 505 biosConfigIntf, resetBIOSSettingsProp); 506 507 std::string_view ResetStr = std::get<std::string>(variant); 508 if (ResetStr == 509 "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag.NoAction") 510 { 511 ResetFlag = 0; 512 } 513 else if (ResetStr == "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag." 514 "FactoryDefaults") 515 { 516 ResetFlag = 1; 517 } 518 else if (ResetStr == "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag." 519 "FailSafeDefaults") 520 { 521 ResetFlag = 2; 522 } 523 else 524 { 525 return ipmi::ccUnspecifiedError; 526 } 527 528 return ipmi::ccSuccess; 529 } 530 catch (const std::exception& e) 531 { 532 return ipmi::ccUnspecifiedError; 533 } 534 } 535 536 /** @brief Get attributes data (bios base table) from bios.xml 537 */ 538 static bool generateAttributesData() 539 { 540 try 541 { 542 bios::Xml biosxml(biosXMLFilePath); 543 544 if (!biosxml.doDepexCompute()) 545 { 546 phosphor::logging::log<phosphor::logging::level::ERR>( 547 "'depex' compute failed"); 548 } 549 550 if (!biosxml.getBaseTable(attributesData)) 551 { 552 phosphor::logging::log<phosphor::logging::level::ERR>( 553 "Failed to get bios base table"); 554 } 555 } 556 catch (const std::exception& ex) 557 { 558 phosphor::logging::log<phosphor::logging::level::ERR>(ex.what()); 559 return false; 560 } 561 562 return true; 563 } 564 565 /** @brief Generate attributes data from bios.xml 566 * and send attributes data (bios base table) to dbus using set method. 567 */ 568 static void generateAndSendAttributesData(std::string service, 569 uint8_t payloadType) 570 { 571 if (!generateAttributesData()) 572 { 573 phosphor::logging::log<phosphor::logging::level::ERR>( 574 "generateAndSendAttributesData: generateAttributesData - failed"); 575 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 576 static_cast<uint8_t>(ipmi::PStatus::Corrupted); 577 return; 578 } 579 580 phosphor::logging::log<phosphor::logging::level::INFO>( 581 "generateAndSendAttributesData : generateAttributesData is done"); 582 583 if (!sendAllAttributes(service)) 584 { 585 phosphor::logging::log<phosphor::logging::level::ERR>( 586 "generateAndSendAttributesData: sendAllAttributes - failed"); 587 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 588 static_cast<uint8_t>(ipmi::PStatus::Corrupted); 589 return; 590 } 591 592 phosphor::logging::log<phosphor::logging::level::INFO>( 593 "generateAndSendAttributesData : sendAllAttributes is done"); 594 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 595 static_cast<uint8_t>(ipmi::PStatus::Valid); 596 } 597 598 /** @brief implement executing the linux command to uncompress and generate the 599 * xmlfile 600 * @param[in] linux command 601 * @returns status 602 */ 603 template <typename... ArgTypes> 604 static int generateBIOSXMLFile(const char* path, ArgTypes&&... tArgs) 605 { 606 607 boost::process::child execProg(path, const_cast<char*>(tArgs)..., 608 boost::process::std_out > biosXMLFilePath); 609 execProg.wait(); 610 return execProg.exit_code(); 611 } 612 613 /** @brief implements to clean up the temp/ existing payload file 614 **/ 615 static void cleanUpPayloadFile(uint8_t& payloadType) 616 { 617 // Clear the payload Information 618 std::string FilePath = "/var/oob/temp" + std::to_string(payloadType); 619 unlink(FilePath.c_str()); 620 FilePath = "/var/oob/Payload" + std::to_string(payloadType); 621 unlink(FilePath.c_str()); 622 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 623 { 624 unlink("/var/oob/Payload1"); 625 gNVOOBdata.payloadInfo[static_cast<uint8_t>(ipmi::PType::IntelXMLType1)] 626 .payloadStatus = static_cast<uint8_t>(ipmi::PStatus::Unknown); 627 } 628 } 629 630 /** @brief implements to create the oob folders and nv space 631 **/ 632 static Cc InitNVOOBdata() 633 { 634 FILE* fptr; 635 uint16_t size; 636 637 if (!(std::filesystem::exists(biosConfigFolder))) 638 { 639 std::filesystem::create_directory(biosConfigFolder); 640 } 641 642 std::ifstream ifs(biosConfigNVPath, std::ios::in | std::ios::binary); 643 644 if (ifs.good()) 645 { 646 647 ifs.seekg(std::ios_base::beg); 648 ifs.read(reinterpret_cast<char*>(&gNVOOBdata), 649 sizeof(struct NVOOBdata)); 650 ifs.close(); 651 return ipmi::ccSuccess; 652 } 653 return ipmi::ccResponseError; 654 } 655 656 /** @brief implements check the command interface is 657 ** system interface or not 658 ** true mean System interface and false mean LAN or IPMB 659 **/ 660 static bool IsSystemInterface(ipmi::Context::ptr ctx) 661 { 662 ChannelInfo chInfo; 663 Cc status = false; 664 665 try 666 { 667 getChannelInfo(ctx->channel, chInfo); 668 } 669 catch (const sdbusplus::exception_t& e) 670 { 671 return false; 672 } 673 if (chInfo.mediumType != 674 static_cast<uint8_t>(EChannelMediumType::systemInterface)) 675 { 676 return false; 677 } 678 return true; 679 } 680 681 ipmi::RspType<> ipmiOEMSetBIOSCap(ipmi::Context::ptr ctx, 682 uint8_t BIOSCapabilties, uint8_t reserved1, 683 uint8_t reserved2, uint8_t reserved3) 684 { 685 if (!getPostCompleted() && IsSystemInterface(ctx)) 686 { 687 if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0) 688 { 689 return ipmi::responseInvalidFieldRequest(); 690 } 691 692 gNVOOBdata.mBIOSCapabilities.OOBCapability = BIOSCapabilties; 693 gNVOOBdata.mIsBIOSCapInitDone = true; 694 695 flushNVOOBdata(); 696 return ipmi::responseSuccess(); 697 } 698 else 699 { 700 return ipmi::response(ipmiCCNotSupportedInCurrentState); 701 } 702 } 703 704 ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t> 705 ipmiOEMGetBIOSCap(ipmi::Context::ptr ctx) 706 { 707 if (gNVOOBdata.mIsBIOSCapInitDone) 708 { 709 return ipmi::responseSuccess(gNVOOBdata.mBIOSCapabilities.OOBCapability, 710 0, 0, 0); 711 } 712 else 713 { 714 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 715 } 716 } 717 718 ipmi::RspType<uint32_t> ipmiOEMSetPayload(ipmi::Context::ptr ctx, 719 uint8_t paramSel, uint8_t payloadType, 720 std::vector<uint8_t> payload) 721 { 722 uint8_t biosCapOffsetBit = 2; // BIT:1 0-OOB BIOS config not supported 723 // 1-OOB BIOS config is supported 724 725 if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit))) 726 { 727 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 728 } 729 730 // Validate the Payload Type 731 if (payloadType > maxPayloadSupported) 732 { 733 return ipmi::responseInvalidFieldRequest(); 734 } 735 736 // We should support this Payload type 0 command only in KCS Interface 737 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 738 { 739 if (!IsSystemInterface(ctx) || getPostCompleted()) 740 { 741 return ipmi::responseCommandNotAvailable(); 742 } 743 } 744 745 switch (static_cast<PTState>(paramSel)) 746 { 747 case ipmi::PTState::StartTransfer: 748 { 749 PayloadStartTransfer* pPayloadStartTransfer = 750 reinterpret_cast<PayloadStartTransfer*>(payload.data()); 751 if (payload.size() < sizeof(PayloadStartTransfer)) 752 { 753 phosphor::logging::log<phosphor::logging::level::ERR>( 754 "ipmiOEMSetPayload: BIOS Config Payload size is not " 755 "correct"); 756 return ipmi::responseReqDataLenInvalid(); 757 } 758 cleanUpPayloadFile(payloadType); 759 760 gNVOOBdata.payloadInfo[payloadType].payloadReservationID = rand(); 761 gNVOOBdata.payloadInfo[payloadType].payloadTotalChecksum = 762 pPayloadStartTransfer->payloadTotalChecksum; 763 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 764 pPayloadStartTransfer->payloadTotalSize; 765 gNVOOBdata.payloadInfo[payloadType].payloadVersion = 766 pPayloadStartTransfer->payloadVersion; 767 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten = 0; 768 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 769 static_cast<uint8_t>(ipmi::PStatus::Unknown); 770 gNVOOBdata.payloadInfo[payloadType].payloadType = payloadType; 771 772 return ipmi::responseSuccess( 773 gNVOOBdata.payloadInfo[payloadType].payloadReservationID); 774 } 775 break; 776 777 case ipmi::PTState::InProgress: 778 { 779 PayloadInProgress* pPayloadInProgress = 780 reinterpret_cast<PayloadInProgress*>(payload.data()); 781 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 782 783 if (payload.size() < sizeof(PayloadInProgress)) 784 { 785 phosphor::logging::log<phosphor::logging::level::ERR>( 786 "BIOS Config Payload size is not correct"); 787 return ipmi::responseReqDataLenInvalid(); 788 } 789 790 if (pPayloadInProgress->payloadReservationID != 791 payloadInfo.payloadReservationID) 792 { 793 phosphor::logging::log<phosphor::logging::level::ERR>( 794 "BIOS Config Payload reservation ID is not correct"); 795 return ipmi::responseInvalidReservationId(); 796 } 797 payloadInfo.payloadCurrentSize = 798 pPayloadInProgress->payloadCurrentSize; 799 // Need to verify the current Payload Checksum 800 const uint8_t* data = 801 reinterpret_cast<const uint8_t*>(payload.data()); 802 // we have to remove the current size, current offset, current 803 // length,checksum bytes , reservation bytes 804 boost::crc_32_type calcChecksum; 805 calcChecksum.process_bytes(data + 16, payload.size() - 16); 806 if (calcChecksum.checksum() != 807 pPayloadInProgress->payloadCurrentChecksum) 808 { 809 phosphor::logging::log<phosphor::logging::level::ERR>( 810 "ipmiOEMSetPayload: Payload Checksum Failed"); 811 return ipmi::response(ipmiCCPayloadChecksumFailed); 812 } 813 // store the data in temp file 814 std::string FilePath = 815 "/var/oob/temp" + std::to_string(payloadType); 816 817 std::ofstream outFile(FilePath, std::ios::binary | std::ios::app); 818 outFile.seekp(pPayloadInProgress->payloadOffset); 819 // we have to remove the current size, current offset, current 820 // length,checksum bytes , reservation bytes 821 822 const char* writedata = 823 reinterpret_cast<const char*>(payload.data()); 824 outFile.write(writedata + 16, payload.size() - 16); 825 outFile.close(); 826 827 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 828 static_cast<uint8_t>(ipmi::PStatus::Unknown); 829 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten += 830 payloadInfo.payloadCurrentSize; 831 return ipmi::responseSuccess(payloadInfo.payloadCurrentSize); 832 } 833 break; 834 case ipmi::PTState::EndTransfer: 835 { 836 PayloadEndTransfer* pPayloadEndTransfer = 837 reinterpret_cast<PayloadEndTransfer*>(payload.data()); 838 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 839 if (pPayloadEndTransfer->payloadReservationID != 840 payloadInfo.payloadReservationID) 841 { 842 return ipmi::responseInvalidReservationId(); 843 } 844 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 845 static_cast<uint8_t>(ipmi::PStatus::Unknown); 846 847 if (gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten != 848 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize) 849 { 850 return ipmi::response(ipmiCCPayloadPayloadInComplete); 851 } 852 std::string tempFilePath = 853 "/var/oob/temp" + std::to_string(payloadType); 854 std::string payloadFilePath = 855 "/var/oob/Payload" + std::to_string(payloadType); 856 auto renamestatus = 857 std::rename(tempFilePath.c_str(), payloadFilePath.c_str()); 858 if (renamestatus) 859 { 860 phosphor::logging::log<phosphor::logging::level::ERR>( 861 "ipmiOEMSetPayload: Renaming Payload file - failed"); 862 } 863 864 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 865 { 866 // Unzip the Intel format XML file type 0 867 auto response = generateBIOSXMLFile("/usr/bin/lzcat", "-d", 868 payloadFilePath.c_str()); 869 if (response) 870 { 871 872 phosphor::logging::log<phosphor::logging::level::ERR>( 873 "ipmiOEMSetPayload: generateBIOSXMLFile - failed"); 874 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 875 static_cast<uint8_t>(ipmi::PStatus::Corrupted); 876 return ipmi::response(ipmiCCPayloadPayloadPacketMissed); 877 } 878 phosphor::logging::log<phosphor::logging::level::INFO>( 879 " ipmiOEMSetPayload : Convert XML into native-dbus DONE"); 880 881 /* So that we don't block the call */ 882 auto io = getIoContext(); 883 auto dbus = getSdBus(); 884 if (io && dbus) 885 { 886 std::string service = getService(*dbus, biosConfigIntf, 887 biosConfigBaseMgrPath); 888 889 boost::asio::post(*io, [service, payloadType] { 890 generateAndSendAttributesData(service, payloadType); 891 }); 892 } 893 else 894 { 895 phosphor::logging::log<phosphor::logging::level::INFO>( 896 "ipmiOEMSetPayload: Unable to get io context or sdbus"); 897 return ipmi::responseResponseError(); 898 } 899 } 900 901 struct stat filestat; 902 903 /* Get entry's information. */ 904 if (!stat(payloadFilePath.c_str(), &filestat)) 905 { 906 gNVOOBdata.payloadInfo[payloadType].payloadTimeStamp = 907 filestat.st_mtime; 908 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 909 filestat.st_size; 910 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 911 static_cast<uint8_t>(ipmi::PStatus::Valid); 912 } 913 else 914 { 915 return ipmi::responseResponseError(); 916 } 917 flushNVOOBdata(); 918 return ipmi::responseSuccess( 919 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten); 920 } 921 break; 922 case ipmi::PTState::UserAbort: 923 { 924 PayloadEndTransfer* pPayloadEndTransfer = 925 reinterpret_cast<PayloadEndTransfer*>(payload.data()); 926 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 927 if (pPayloadEndTransfer->payloadReservationID != 928 payloadInfo.payloadReservationID) 929 { 930 return ipmi::responseInvalidReservationId(); 931 } 932 gNVOOBdata.payloadInfo[payloadType].payloadReservationID = 0; 933 gNVOOBdata.payloadInfo[payloadType].payloadType = 0; 934 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 0; 935 // Delete the temp file 936 std::string tempFilePath = 937 "/var/oob/temp" + std::to_string(payloadType); 938 unlink(tempFilePath.c_str()); 939 flushNVOOBdata(); 940 return ipmi::responseSuccess(); 941 } 942 break; 943 default: 944 return ipmi::responseInvalidFieldRequest(); 945 } 946 return ipmi::responseResponseError(); 947 } 948 949 ipmi::RspType<message::Payload> 950 ipmiOEMGetPayload(ipmi::Context::ptr ctx, uint8_t paramSel, 951 uint8_t payloadType, ipmi::message::Payload& payload) 952 { 953 // 1-OOB BIOS config is supported 954 message::Payload retValue; 955 956 if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit))) 957 { 958 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 959 } 960 // Validate the Payload Type 961 if (payloadType > maxPayloadSupported) 962 { 963 return ipmi::responseInvalidFieldRequest(); 964 } 965 966 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType1)) 967 { 968 if (!payload1::update(ctx)) 969 { 970 phosphor::logging::log<phosphor::logging::level::ERR>( 971 "ipmiOEMGetPayload: unable to update NVOOBdata for payloadType " 972 "= IntelXMLType1"); 973 return ipmi::response(ipmi::ccUnspecifiedError); 974 } 975 } 976 977 struct PayloadInfo res = gNVOOBdata.payloadInfo[payloadType]; 978 979 switch (static_cast<GetPayloadParameter>(paramSel)) 980 { 981 case ipmi::GetPayloadParameter::GetPayloadInfo: 982 { 983 std::string payloadFilePath = 984 "/var/oob/Payload" + std::to_string(payloadType); 985 986 std::ifstream ifs(payloadFilePath, 987 std::ios::in | std::ios::binary | std::ios::ate); 988 989 if (!ifs.good()) 990 { 991 phosphor::logging::log<phosphor::logging::level::ERR>( 992 "ipmiOEMGetPayload: Payload File Error"); 993 // File does not exist code here 994 return ipmi::response(ipmi::ccUnspecifiedError); 995 } 996 997 ifs.close(); 998 retValue.pack(res.payloadVersion); 999 retValue.pack(payloadType); 1000 retValue.pack(res.payloadTotalSize); 1001 retValue.pack(res.payloadTotalChecksum); 1002 retValue.pack(res.payloadflag); 1003 retValue.pack(res.payloadStatus); 1004 retValue.pack(res.payloadTimeStamp); 1005 1006 return ipmi::responseSuccess(std::move(retValue)); 1007 } 1008 1009 break; 1010 case ipmi::GetPayloadParameter::GetPayloadData: 1011 { 1012 if (res.payloadStatus == 1013 (static_cast<uint8_t>(ipmi::PStatus::Valid))) 1014 { 1015 std::vector<uint32_t> reqData; 1016 if (payload.unpack(reqData) || !payload.fullyUnpacked()) 1017 { 1018 return ipmi::responseReqDataLenInvalid(); 1019 } 1020 uint32_t offset = reqData.at(0); 1021 uint32_t length = reqData.at(1); 1022 std::string payloadFilePath = 1023 "/var/oob/Payload" + std::to_string(payloadType); 1024 1025 if (length > static_cast<uint32_t>(maxGetPayloadDataSize)) 1026 { 1027 phosphor::logging::log<phosphor::logging::level::ERR>( 1028 "ipmiOEMGetPayload: length > maxGetPayloadDataSize", 1029 phosphor::logging::entry("LENGTH=%d", length), 1030 phosphor::logging::entry("maxGetPayloadDataSize=%d", 1031 maxGetPayloadDataSize)); 1032 return ipmi::responseInvalidFieldRequest(); 1033 } 1034 1035 std::ifstream ifs(payloadFilePath, std::ios::in | 1036 std::ios::binary | 1037 std::ios::ate); 1038 1039 if (!ifs.good()) 1040 { 1041 phosphor::logging::log<phosphor::logging::level::ERR>( 1042 "ipmiOEMGetPayload: Payload File Error"); 1043 // File does not exist code here 1044 return ipmi::response(ipmi::ccUnspecifiedError); 1045 } 1046 std::ifstream::pos_type fileSize = ifs.tellg(); 1047 // Total file data within given offset 1048 if (fileSize < static_cast<uint64_t>(offset)) 1049 { 1050 phosphor::logging::log<phosphor::logging::level::ERR>( 1051 "ipmiOEMGetPayload: filesize < offset"); 1052 return ipmi::responseInvalidFieldRequest(); 1053 } 1054 1055 if ((static_cast<uint64_t>(fileSize) - offset) < length) 1056 { 1057 phosphor::logging::log<phosphor::logging::level::ERR>( 1058 "ipmiOEMGetPayload: (filesize - offset) < length "); 1059 return ipmi::responseInvalidFieldRequest(); 1060 } 1061 1062 ifs.seekg(offset, std::ios::beg); 1063 std::array<uint8_t, maxGetPayloadDataSize> Buffer; 1064 ifs.read(reinterpret_cast<char*>(Buffer.data()), length); 1065 uint32_t readCount = ifs.gcount(); 1066 ifs.close(); 1067 1068 boost::crc_32_type calcChecksum; 1069 calcChecksum.process_bytes( 1070 reinterpret_cast<char*>(Buffer.data()), readCount); 1071 uint32_t chkSum = calcChecksum.checksum(); 1072 retValue.pack(payloadType); 1073 retValue.pack(readCount); 1074 retValue.pack(chkSum); 1075 1076 for (int i = 0; i < readCount; i++) 1077 { 1078 retValue.pack(Buffer.at(i)); 1079 } 1080 1081 return ipmi::responseSuccess(std::move(retValue)); 1082 } 1083 else 1084 { 1085 return ipmi::responseResponseError(); 1086 } 1087 } 1088 break; 1089 case ipmi::GetPayloadParameter::GetPayloadStatus: 1090 { 1091 retValue.pack(gNVOOBdata.payloadInfo[payloadType].payloadStatus); 1092 return ipmi::responseSuccess(std::move(retValue)); 1093 } 1094 break; 1095 default: 1096 return ipmi::responseInvalidFieldRequest(); 1097 } 1098 return ipmi::responseInvalidFieldRequest(); 1099 } 1100 1101 ipmi::RspType<> ipmiOEMSetBIOSHashInfo( 1102 ipmi::Context::ptr ctx, std::array<uint8_t, maxSeedSize>& pwdSeed, 1103 uint8_t algoInfo, std::array<uint8_t, maxHashSize>& adminPwdHash) 1104 { 1105 // We should support this command only in KCS Interface 1106 if (!IsSystemInterface(ctx)) 1107 { 1108 return ipmi::responseCommandNotAvailable(); 1109 } 1110 1111 // We should not support this command after System Booted - After Exit Boot 1112 // service called 1113 if (getPostCompleted()) 1114 { 1115 return ipmi::response(ipmiCCNotSupportedInCurrentState); 1116 } 1117 1118 nlohmann::json json; 1119 1120 if ((algoInfo & 0xF) == algoSHA384) 1121 { 1122 json["HashAlgo"] = "SHA384"; 1123 } 1124 else if ((algoInfo & 0xF) == algoSHA256) 1125 { 1126 json["HashAlgo"] = "SHA256"; 1127 } 1128 else 1129 { 1130 return ipmi::responseInvalidFieldRequest(); 1131 } 1132 1133 json["Seed"] = pwdSeed; 1134 json["IsAdminPwdChanged"] = false; 1135 json["AdminPwdHash"] = adminPwdHash; 1136 json["IsUserPwdChanged"] = false; 1137 1138 std::array<uint8_t, maxHashSize> userPwdHash; 1139 userPwdHash.fill({}); // initializing with 0 as user password hash field 1140 // is not used presently 1141 json["UserPwdHash"] = userPwdHash; 1142 json["StatusFlag"] = algoInfo; 1143 1144 std::string hashFilePath = "/var/lib/bios-settings-manager/seedData"; 1145 std::ofstream ofs(hashFilePath, std::ios::out); 1146 const auto& writeData = json.dump(); 1147 ofs << writeData; 1148 ofs.close(); 1149 return ipmi::responseSuccess(); 1150 } 1151 1152 ipmi::RspType<std::array<uint8_t, maxSeedSize>, uint8_t, 1153 std::array<uint8_t, maxHashSize>> 1154 ipmiOEMGetBIOSHash(ipmi::Context::ptr ctx) 1155 { 1156 nlohmann::json data = nullptr; 1157 1158 // We should support this command only in KCS Interface 1159 if (!IsSystemInterface(ctx)) 1160 { 1161 return ipmi::responseCommandNotAvailable(); 1162 } 1163 1164 // We should not support this command after System Booted - After Exit Boot 1165 // service called 1166 if (!getPostCompleted()) 1167 { 1168 std::string HashFilePath = "/var/lib/bios-settings-manager/seedData"; 1169 1170 std::ifstream devIdFile(HashFilePath); 1171 if (devIdFile.is_open()) 1172 { 1173 1174 try 1175 { 1176 data = nlohmann::json::parse(devIdFile, nullptr, false); 1177 } 1178 catch (const nlohmann::json::parse_error& e) 1179 { 1180 return ipmi::responseResponseError(); 1181 } 1182 1183 if (data.is_discarded()) 1184 { 1185 return ipmi::responseResponseError(); 1186 } 1187 1188 std::array<uint8_t, maxHashSize> newAdminHash; 1189 std::array<uint8_t, maxSeedSize> seed; 1190 1191 uint8_t flag = 0; 1192 uint8_t adminPwdChangedFlag = 0; 1193 if (!data.is_discarded()) 1194 { 1195 1196 adminPwdChangedFlag = data["IsAdminPwdChanged"]; 1197 newAdminHash = data["AdminPwdHash"]; 1198 seed = data["Seed"]; 1199 } 1200 1201 auto status = getResetBIOSSettings(flag); 1202 if (status) 1203 { 1204 return ipmi::responseResponseError(); 1205 } 1206 if (adminPwdChangedFlag) 1207 { 1208 flag |= adminPasswordChanged; 1209 } 1210 1211 std::copy(std::begin(newAdminHash), std::end(newAdminHash), 1212 std::begin(newAdminHash)); 1213 1214 return ipmi::responseSuccess(seed, flag, newAdminHash); 1215 } 1216 else 1217 { 1218 return ipmi::responseResponseError(); 1219 } 1220 } 1221 else 1222 { 1223 return ipmi::response(ipmiCCNotSupportedInCurrentState); 1224 } 1225 } 1226 1227 static void registerBIOSConfigFunctions(void) 1228 { 1229 phosphor::logging::log<phosphor::logging::level::INFO>( 1230 "BIOSConfig module initialization"); 1231 InitNVOOBdata(); 1232 1233 registerHandler(prioOemBase, intel::netFnGeneral, 1234 intel::general::cmdSetBIOSCap, Privilege::Admin, 1235 ipmiOEMSetBIOSCap); 1236 1237 registerHandler(prioOemBase, intel::netFnGeneral, 1238 intel::general::cmdGetBIOSCap, Privilege::User, 1239 ipmiOEMGetBIOSCap); 1240 registerHandler(prioOemBase, intel::netFnGeneral, 1241 intel::general::cmdSetBIOSPwdHashInfo, Privilege::Admin, 1242 ipmiOEMSetBIOSHashInfo); 1243 1244 registerHandler(prioOemBase, intel::netFnGeneral, 1245 intel::general::cmdGetBIOSPwdHash, Privilege::User, 1246 ipmiOEMGetBIOSHash); 1247 1248 registerHandler(prioOemBase, intel::netFnGeneral, 1249 intel::general::cmdGetPayload, Privilege::User, 1250 ipmiOEMGetPayload); 1251 registerHandler(prioOemBase, intel::netFnGeneral, 1252 intel::general::cmdSetPayload, Privilege::Admin, 1253 ipmiOEMSetPayload); 1254 1255 return; 1256 } 1257 1258 } // namespace ipmi 1259