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