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