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 MaxPayloadParameters 110 }; 111 112 namespace payload1 113 { 114 115 enum class AttributesType : uint8_t 116 { 117 unknown = 0, 118 string, 119 integer, 120 enumeration 121 }; 122 123 using PendingAttributesType = 124 std::map<std::string, std::tuple<std::string, ipmi::DbusVariant>>; 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 ipmi::DbusVariant& 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 = getService(*dbus, biosConfigIntf, 203 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 try 500 { 501 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 502 std::string service = getService(*dbus, biosConfigIntf, 503 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 boost::process::child execProg(path, const_cast<char*>(tArgs)..., 607 boost::process::std_out > biosXMLFilePath); 608 execProg.wait(); 609 return execProg.exit_code(); 610 } 611 612 /** @brief implements to clean up the temp/ existing payload file 613 **/ 614 static void cleanUpPayloadFile(uint8_t& payloadType) 615 { 616 // Clear the payload Information 617 std::string FilePath = "/var/oob/temp" + std::to_string(payloadType); 618 unlink(FilePath.c_str()); 619 FilePath = "/var/oob/Payload" + std::to_string(payloadType); 620 unlink(FilePath.c_str()); 621 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 622 { 623 unlink("/var/oob/Payload1"); 624 gNVOOBdata.payloadInfo[static_cast<uint8_t>(ipmi::PType::IntelXMLType1)] 625 .payloadStatus = static_cast<uint8_t>(ipmi::PStatus::Unknown); 626 } 627 } 628 629 /** @brief implements to create the oob folders and nv space 630 **/ 631 static Cc InitNVOOBdata() 632 { 633 if (!(std::filesystem::exists(biosConfigFolder))) 634 { 635 std::filesystem::create_directory(biosConfigFolder); 636 } 637 638 std::ifstream ifs(biosConfigNVPath, std::ios::in | std::ios::binary); 639 640 if (ifs.good()) 641 { 642 ifs.seekg(std::ios_base::beg); 643 ifs.read(reinterpret_cast<char*>(&gNVOOBdata), 644 sizeof(struct NVOOBdata)); 645 ifs.close(); 646 return ipmi::ccSuccess; 647 } 648 return ipmi::ccResponseError; 649 } 650 651 ipmi::RspType<> ipmiOEMSetBIOSCap(ipmi::Context::ptr&, uint8_t BIOSCapabilties, 652 uint8_t reserved1, uint8_t reserved2, 653 uint8_t reserved3) 654 { 655 if (getPostCompleted()) 656 { 657 return ipmi::response(ipmiCCNotSupportedInCurrentState); 658 } 659 660 if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0) 661 { 662 return ipmi::responseInvalidFieldRequest(); 663 } 664 665 gNVOOBdata.mBIOSCapabilities.OOBCapability = BIOSCapabilties; 666 gNVOOBdata.mIsBIOSCapInitDone = true; 667 668 flushNVOOBdata(); 669 return ipmi::responseSuccess(); 670 } 671 672 ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t> 673 ipmiOEMGetBIOSCap(ipmi::Context::ptr&) 674 { 675 if (gNVOOBdata.mIsBIOSCapInitDone) 676 { 677 return ipmi::responseSuccess(gNVOOBdata.mBIOSCapabilities.OOBCapability, 678 0, 0, 0); 679 } 680 else 681 { 682 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 683 } 684 } 685 686 ipmi::RspType<uint32_t> ipmiOEMSetPayload(ipmi::Context::ptr&, uint8_t paramSel, 687 uint8_t payloadType, 688 std::vector<uint8_t> payload) 689 { 690 uint8_t biosCapOffsetBit = 2; // BIT:1 0-OOB BIOS config not supported 691 // 1-OOB BIOS config is supported 692 693 if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit))) 694 { 695 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 696 } 697 698 // Validate the Payload Type 699 if (payloadType > maxPayloadSupported) 700 { 701 return ipmi::responseInvalidFieldRequest(); 702 } 703 704 // We should support this Payload type 0 command only in KCS Interface 705 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 706 { 707 if (getPostCompleted()) 708 { 709 return ipmi::response(ipmiCCNotSupportedInCurrentState); 710 } 711 } 712 713 switch (static_cast<PTState>(paramSel)) 714 { 715 case ipmi::PTState::StartTransfer: 716 { 717 PayloadStartTransfer* pPayloadStartTransfer = 718 reinterpret_cast<PayloadStartTransfer*>(payload.data()); 719 if (payload.size() < sizeof(PayloadStartTransfer)) 720 { 721 phosphor::logging::log<phosphor::logging::level::ERR>( 722 "ipmiOEMSetPayload: BIOS Config Payload size is not " 723 "correct"); 724 return ipmi::responseReqDataLenInvalid(); 725 } 726 cleanUpPayloadFile(payloadType); 727 728 gNVOOBdata.payloadInfo[payloadType].payloadReservationID = rand(); 729 gNVOOBdata.payloadInfo[payloadType].payloadTotalChecksum = 730 pPayloadStartTransfer->payloadTotalChecksum; 731 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 732 pPayloadStartTransfer->payloadTotalSize; 733 gNVOOBdata.payloadInfo[payloadType].payloadVersion = 734 pPayloadStartTransfer->payloadVersion; 735 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten = 0; 736 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 737 static_cast<uint8_t>(ipmi::PStatus::Unknown); 738 gNVOOBdata.payloadInfo[payloadType].payloadType = payloadType; 739 740 return ipmi::responseSuccess( 741 gNVOOBdata.payloadInfo[payloadType].payloadReservationID); 742 } 743 break; 744 745 case ipmi::PTState::InProgress: 746 { 747 PayloadInProgress* pPayloadInProgress = 748 reinterpret_cast<PayloadInProgress*>(payload.data()); 749 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 750 751 if (payload.size() < sizeof(PayloadInProgress)) 752 { 753 phosphor::logging::log<phosphor::logging::level::ERR>( 754 "BIOS Config Payload size is not correct"); 755 return ipmi::responseReqDataLenInvalid(); 756 } 757 758 if (pPayloadInProgress->payloadReservationID != 759 payloadInfo.payloadReservationID) 760 { 761 phosphor::logging::log<phosphor::logging::level::ERR>( 762 "BIOS Config Payload reservation ID is not correct"); 763 return ipmi::responseInvalidReservationId(); 764 } 765 payloadInfo.payloadCurrentSize = 766 pPayloadInProgress->payloadCurrentSize; 767 // Need to verify the current Payload Checksum 768 const uint8_t* data = 769 reinterpret_cast<const uint8_t*>(payload.data()); 770 // we have to remove the current size, current offset, current 771 // length,checksum bytes , reservation bytes 772 boost::crc_32_type calcChecksum; 773 calcChecksum.process_bytes(data + 16, payload.size() - 16); 774 if (calcChecksum.checksum() != 775 pPayloadInProgress->payloadCurrentChecksum) 776 { 777 phosphor::logging::log<phosphor::logging::level::ERR>( 778 "ipmiOEMSetPayload: Payload Checksum Failed"); 779 return ipmi::response(ipmiCCPayloadChecksumFailed); 780 } 781 // store the data in temp file 782 std::string FilePath = "/var/oob/temp" + 783 std::to_string(payloadType); 784 785 std::ofstream outFile(FilePath, std::ios::binary | std::ios::app); 786 outFile.seekp(pPayloadInProgress->payloadOffset); 787 // we have to remove the current size, current offset, current 788 // length,checksum bytes , reservation bytes 789 790 const char* writedata = 791 reinterpret_cast<const char*>(payload.data()); 792 outFile.write(writedata + 16, payload.size() - 16); 793 outFile.close(); 794 795 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 796 static_cast<uint8_t>(ipmi::PStatus::Unknown); 797 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten += 798 payloadInfo.payloadCurrentSize; 799 return ipmi::responseSuccess(payloadInfo.payloadCurrentSize); 800 } 801 break; 802 case ipmi::PTState::EndTransfer: 803 { 804 PayloadEndTransfer* pPayloadEndTransfer = 805 reinterpret_cast<PayloadEndTransfer*>(payload.data()); 806 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 807 if (pPayloadEndTransfer->payloadReservationID != 808 payloadInfo.payloadReservationID) 809 { 810 return ipmi::responseInvalidReservationId(); 811 } 812 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 813 static_cast<uint8_t>(ipmi::PStatus::Unknown); 814 815 if (gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten != 816 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize) 817 { 818 return ipmi::response(ipmiCCPayloadPayloadInComplete); 819 } 820 std::string tempFilePath = "/var/oob/temp" + 821 std::to_string(payloadType); 822 std::string payloadFilePath = "/var/oob/Payload" + 823 std::to_string(payloadType); 824 auto renamestatus = std::rename(tempFilePath.c_str(), 825 payloadFilePath.c_str()); 826 if (renamestatus) 827 { 828 phosphor::logging::log<phosphor::logging::level::ERR>( 829 "ipmiOEMSetPayload: Renaming Payload file - failed"); 830 } 831 832 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) 833 { 834 // Unzip the Intel format XML file type 0 835 auto response = generateBIOSXMLFile("/usr/bin/lzcat", "-d", 836 payloadFilePath.c_str()); 837 if (response) 838 { 839 phosphor::logging::log<phosphor::logging::level::ERR>( 840 "ipmiOEMSetPayload: generateBIOSXMLFile - failed"); 841 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 842 static_cast<uint8_t>(ipmi::PStatus::Corrupted); 843 return ipmi::response(ipmiCCPayloadPayloadPacketMissed); 844 } 845 phosphor::logging::log<phosphor::logging::level::INFO>( 846 " ipmiOEMSetPayload : Convert XML into native-dbus DONE"); 847 848 /* So that we don't block the call */ 849 auto io = getIoContext(); 850 auto dbus = getSdBus(); 851 if (io && dbus) 852 { 853 std::string service = getService(*dbus, biosConfigIntf, 854 biosConfigBaseMgrPath); 855 856 boost::asio::post(*io, [service, payloadType] { 857 generateAndSendAttributesData(service, payloadType); 858 }); 859 } 860 else 861 { 862 phosphor::logging::log<phosphor::logging::level::INFO>( 863 "ipmiOEMSetPayload: Unable to get io context or sdbus"); 864 return ipmi::responseResponseError(); 865 } 866 } 867 868 struct stat filestat; 869 870 /* Get entry's information. */ 871 if (!stat(payloadFilePath.c_str(), &filestat)) 872 { 873 gNVOOBdata.payloadInfo[payloadType].payloadTimeStamp = 874 filestat.st_mtime; 875 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 876 filestat.st_size; 877 gNVOOBdata.payloadInfo[payloadType].payloadStatus = 878 static_cast<uint8_t>(ipmi::PStatus::Valid); 879 } 880 else 881 { 882 return ipmi::responseResponseError(); 883 } 884 flushNVOOBdata(); 885 return ipmi::responseSuccess( 886 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten); 887 } 888 break; 889 case ipmi::PTState::UserAbort: 890 { 891 PayloadEndTransfer* pPayloadEndTransfer = 892 reinterpret_cast<PayloadEndTransfer*>(payload.data()); 893 PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; 894 if (pPayloadEndTransfer->payloadReservationID != 895 payloadInfo.payloadReservationID) 896 { 897 return ipmi::responseInvalidReservationId(); 898 } 899 gNVOOBdata.payloadInfo[payloadType].payloadReservationID = 0; 900 gNVOOBdata.payloadInfo[payloadType].payloadType = 0; 901 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 0; 902 // Delete the temp file 903 std::string tempFilePath = "/var/oob/temp" + 904 std::to_string(payloadType); 905 unlink(tempFilePath.c_str()); 906 flushNVOOBdata(); 907 return ipmi::responseSuccess(); 908 } 909 break; 910 default: 911 return ipmi::responseInvalidFieldRequest(); 912 } 913 return ipmi::responseResponseError(); 914 } 915 916 ipmi::RspType<message::Payload> 917 ipmiOEMGetPayload(ipmi::Context::ptr& ctx, uint8_t paramSel, 918 uint8_t payloadType, ipmi::message::Payload& payload) 919 { 920 // 1-OOB BIOS config is supported 921 message::Payload retValue; 922 923 if (static_cast<GetPayloadParameter>(paramSel) >= 924 ipmi::GetPayloadParameter::MaxPayloadParameters) 925 { 926 return ipmi::responseInvalidFieldRequest(); 927 } 928 929 if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit))) 930 { 931 return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); 932 } 933 // Validate the Payload Type 934 if (payloadType > maxPayloadSupported) 935 { 936 return ipmi::responseInvalidFieldRequest(); 937 } 938 939 if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType1)) 940 { 941 if (!payload1::update(ctx)) 942 { 943 phosphor::logging::log<phosphor::logging::level::ERR>( 944 "ipmiOEMGetPayload: unable to update NVOOBdata for payloadType " 945 "= IntelXMLType1"); 946 return ipmi::response(ipmi::ccUnspecifiedError); 947 } 948 } 949 950 struct PayloadInfo res = gNVOOBdata.payloadInfo[payloadType]; 951 952 switch (static_cast<GetPayloadParameter>(paramSel)) 953 { 954 case ipmi::GetPayloadParameter::GetPayloadInfo: 955 { 956 std::string payloadFilePath = "/var/oob/Payload" + 957 std::to_string(payloadType); 958 959 std::ifstream ifs(payloadFilePath, 960 std::ios::in | std::ios::binary | std::ios::ate); 961 962 if (!ifs.good()) 963 { 964 phosphor::logging::log<phosphor::logging::level::ERR>( 965 "ipmiOEMGetPayload: Payload File Error"); 966 // File does not exist code here 967 return ipmi::response(ipmi::ccUnspecifiedError); 968 } 969 970 ifs.close(); 971 retValue.pack(res.payloadVersion); 972 retValue.pack(payloadType); 973 retValue.pack(res.payloadTotalSize); 974 retValue.pack(res.payloadTotalChecksum); 975 retValue.pack(res.payloadflag); 976 retValue.pack(res.payloadStatus); 977 retValue.pack(res.payloadTimeStamp); 978 979 return ipmi::responseSuccess(std::move(retValue)); 980 } 981 982 break; 983 case ipmi::GetPayloadParameter::GetPayloadData: 984 { 985 if (res.payloadStatus == 986 (static_cast<uint8_t>(ipmi::PStatus::Valid))) 987 { 988 std::vector<uint32_t> reqData; 989 if (payload.unpack(reqData) || !payload.fullyUnpacked()) 990 { 991 return ipmi::responseReqDataLenInvalid(); 992 } 993 uint32_t offset = reqData.at(0); 994 uint32_t length = reqData.at(1); 995 std::string payloadFilePath = "/var/oob/Payload" + 996 std::to_string(payloadType); 997 998 if (length > static_cast<uint32_t>(maxGetPayloadDataSize)) 999 { 1000 phosphor::logging::log<phosphor::logging::level::ERR>( 1001 "ipmiOEMGetPayload: length > maxGetPayloadDataSize", 1002 phosphor::logging::entry("LENGTH=%d", length), 1003 phosphor::logging::entry("maxGetPayloadDataSize=%d", 1004 maxGetPayloadDataSize)); 1005 return ipmi::responseInvalidFieldRequest(); 1006 } 1007 1008 std::ifstream ifs(payloadFilePath, std::ios::in | 1009 std::ios::binary | 1010 std::ios::ate); 1011 1012 if (!ifs.good()) 1013 { 1014 phosphor::logging::log<phosphor::logging::level::ERR>( 1015 "ipmiOEMGetPayload: Payload File Error"); 1016 // File does not exist code here 1017 return ipmi::response(ipmi::ccUnspecifiedError); 1018 } 1019 std::ifstream::pos_type fileSize = ifs.tellg(); 1020 // Total file data within given offset 1021 if (fileSize < static_cast<int64_t>(offset)) 1022 { 1023 phosphor::logging::log<phosphor::logging::level::ERR>( 1024 "ipmiOEMGetPayload: filesize < offset"); 1025 return ipmi::responseInvalidFieldRequest(); 1026 } 1027 1028 if ((static_cast<uint64_t>(fileSize) - offset) < length) 1029 { 1030 phosphor::logging::log<phosphor::logging::level::ERR>( 1031 "ipmiOEMGetPayload: (filesize - offset) < length "); 1032 return ipmi::responseInvalidFieldRequest(); 1033 } 1034 1035 ifs.seekg(offset, std::ios::beg); 1036 std::array<uint8_t, maxGetPayloadDataSize> Buffer; 1037 ifs.read(reinterpret_cast<char*>(Buffer.data()), length); 1038 uint32_t readCount = ifs.gcount(); 1039 ifs.close(); 1040 1041 boost::crc_32_type calcChecksum; 1042 calcChecksum.process_bytes( 1043 reinterpret_cast<char*>(Buffer.data()), readCount); 1044 uint32_t chkSum = calcChecksum.checksum(); 1045 retValue.pack(payloadType); 1046 retValue.pack(readCount); 1047 retValue.pack(chkSum); 1048 1049 for (uint32_t i = 0; i < readCount; i++) 1050 { 1051 retValue.pack(Buffer.at(i)); 1052 } 1053 1054 return ipmi::responseSuccess(std::move(retValue)); 1055 } 1056 else 1057 { 1058 return ipmi::responseResponseError(); 1059 } 1060 } 1061 break; 1062 case ipmi::GetPayloadParameter::GetPayloadStatus: 1063 { 1064 retValue.pack(gNVOOBdata.payloadInfo[payloadType].payloadStatus); 1065 return ipmi::responseSuccess(std::move(retValue)); 1066 } 1067 break; 1068 default: 1069 return ipmi::responseInvalidFieldRequest(); 1070 } 1071 return ipmi::responseInvalidFieldRequest(); 1072 } 1073 1074 ipmi::RspType<> ipmiOEMSetBIOSHashInfo( 1075 ipmi::Context::ptr&, std::array<uint8_t, maxSeedSize>& pwdSeed, 1076 uint8_t algoInfo, std::array<uint8_t, maxHashSize>& adminPwdHash) 1077 { 1078 // We should not support this command after System Booted - After Exit Boot 1079 // service called 1080 if (getPostCompleted()) 1081 { 1082 return ipmi::response(ipmiCCNotSupportedInCurrentState); 1083 } 1084 1085 nlohmann::json json; 1086 1087 if ((algoInfo & 0xF) == algoSHA384) 1088 { 1089 json["HashAlgo"] = "SHA384"; 1090 } 1091 else if ((algoInfo & 0xF) == algoSHA256) 1092 { 1093 json["HashAlgo"] = "SHA256"; 1094 } 1095 else 1096 { 1097 return ipmi::responseInvalidFieldRequest(); 1098 } 1099 1100 json["Seed"] = pwdSeed; 1101 json["IsAdminPwdChanged"] = false; 1102 json["AdminPwdHash"] = adminPwdHash; 1103 json["IsUserPwdChanged"] = false; 1104 1105 std::array<uint8_t, maxHashSize> userPwdHash; 1106 userPwdHash.fill({}); // initializing with 0 as user password hash field 1107 // is not used presently 1108 json["UserPwdHash"] = userPwdHash; 1109 json["StatusFlag"] = algoInfo; 1110 1111 std::string hashFilePath = "/var/lib/bios-settings-manager/seedData"; 1112 std::ofstream ofs(hashFilePath, std::ios::out); 1113 const auto& writeData = json.dump(); 1114 ofs << writeData; 1115 ofs.close(); 1116 return ipmi::responseSuccess(); 1117 } 1118 1119 ipmi::RspType<std::array<uint8_t, maxSeedSize>, uint8_t, 1120 std::array<uint8_t, maxHashSize>> 1121 ipmiOEMGetBIOSHash(ipmi::Context::ptr&) 1122 { 1123 nlohmann::json data = nullptr; 1124 1125 // We should not support this command after System Booted - After Exit Boot 1126 // service called 1127 if (getPostCompleted()) 1128 { 1129 return ipmi::response(ipmiCCNotSupportedInCurrentState); 1130 } 1131 1132 std::string HashFilePath = "/var/lib/bios-settings-manager/seedData"; 1133 1134 std::ifstream devIdFile(HashFilePath); 1135 if (!devIdFile.is_open()) 1136 { 1137 return ipmi::responseResponseError(); 1138 } 1139 1140 try 1141 { 1142 data = nlohmann::json::parse(devIdFile, nullptr, false); 1143 } 1144 catch (const nlohmann::json::parse_error& e) 1145 { 1146 return ipmi::responseResponseError(); 1147 } 1148 1149 if (data.is_discarded()) 1150 { 1151 return ipmi::responseResponseError(); 1152 } 1153 1154 std::array<uint8_t, maxHashSize> newAdminHash; 1155 std::array<uint8_t, maxSeedSize> seed; 1156 1157 uint8_t flag = 0; 1158 uint8_t adminPwdChangedFlag = 0; 1159 if (!data.is_discarded()) 1160 { 1161 adminPwdChangedFlag = data["IsAdminPwdChanged"]; 1162 newAdminHash = data["AdminPwdHash"]; 1163 seed = data["Seed"]; 1164 } 1165 1166 auto status = getResetBIOSSettings(flag); 1167 if (status) 1168 { 1169 return ipmi::responseResponseError(); 1170 } 1171 if (flag) 1172 { 1173 flag |= restoreDefaultValues; 1174 } 1175 if (adminPwdChangedFlag) 1176 { 1177 flag |= adminPasswordChanged; 1178 } 1179 1180 std::copy(std::begin(newAdminHash), std::end(newAdminHash), 1181 std::begin(newAdminHash)); 1182 1183 return ipmi::responseSuccess(seed, flag, newAdminHash); 1184 } 1185 1186 static void registerBIOSConfigFunctions(void) 1187 { 1188 phosphor::logging::log<phosphor::logging::level::INFO>( 1189 "BIOSConfig module initialization"); 1190 InitNVOOBdata(); 1191 1192 registerHandler(prioOemBase, intel::netFnGeneral, 1193 intel::general::cmdSetBIOSCap, Privilege::Admin, 1194 ipmiOEMSetBIOSCap); 1195 1196 registerHandler(prioOemBase, intel::netFnGeneral, 1197 intel::general::cmdGetBIOSCap, Privilege::User, 1198 ipmiOEMGetBIOSCap); 1199 registerHandler(prioOemBase, intel::netFnGeneral, 1200 intel::general::cmdSetBIOSPwdHashInfo, Privilege::Admin, 1201 ipmiOEMSetBIOSHashInfo); 1202 1203 registerHandler(prioOemBase, intel::netFnGeneral, 1204 intel::general::cmdGetBIOSPwdHash, Privilege::User, 1205 ipmiOEMGetBIOSHash); 1206 1207 registerHandler(prioOemBase, intel::netFnGeneral, 1208 intel::general::cmdGetPayload, Privilege::User, 1209 ipmiOEMGetPayload); 1210 registerHandler(prioOemBase, intel::netFnGeneral, 1211 intel::general::cmdSetPayload, Privilege::Admin, 1212 ipmiOEMSetPayload); 1213 1214 return; 1215 } 1216 1217 } // namespace ipmi 1218