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