| Kuiying Wang | 6d6dc7a | 2020-04-02 10:15:19 +0800 | [diff] [blame] | 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 <openssl/sha.h> | 
 | 18 | #include <tinyxml2.h> | 
 | 19 |  | 
 | 20 | #include <biosconfigcommands.hpp> | 
 | 21 | #include <boost/crc.hpp> | 
 | 22 | #include <boost/process/child.hpp> | 
 | 23 | #include <boost/process/io.hpp> | 
 | 24 | #include <ipmid/api.hpp> | 
 | 25 | #include <ipmid/message.hpp> | 
 | 26 | #include <ipmid/message/types.hpp> | 
 | 27 | #include <ipmid/types.hpp> | 
 | 28 | #include <ipmid/utils.hpp> | 
 | 29 | #include <nlohmann/json.hpp> | 
 | 30 | #include <oemcommands.hpp> | 
 | 31 | #include <phosphor-logging/elog-errors.hpp> | 
 | 32 | #include <phosphor-logging/log.hpp> | 
 | 33 | #include <sdbusplus/bus.hpp> | 
 | 34 | #include <sdbusplus/message/types.hpp> | 
 | 35 |  | 
 | 36 | #include <filesystem> | 
 | 37 |  | 
 | 38 | namespace ipmi | 
 | 39 | { | 
 | 40 | static void registerBIOSConfigFunctions() __attribute__((constructor)); | 
 | 41 |  | 
 | 42 | // Define BIOS config related Completion Code | 
 | 43 | using Cc = uint8_t; | 
 | 44 | static constexpr Cc ipmiCCPayloadPayloadPacketMissed = 0x80; | 
 | 45 | static constexpr Cc ipmiCCBIOSPasswordInitNotDone = 0x80; | 
 | 46 | static constexpr Cc ipmiCCPayloadChecksumFailed = 0x81; | 
 | 47 | static constexpr Cc ipmiCCNotSupportedInCurrentState = 0x82; | 
 | 48 | static constexpr Cc ipmiCCPayloadPayloadInComplete = 0x83; | 
 | 49 | static constexpr Cc ipmiCCBIOSCapabilityInitNotDone = 0x85; | 
 | 50 | static constexpr Cc ipmiCCPayloadLengthIllegal = 0x85; | 
 | 51 |  | 
 | 52 | static constexpr uint8_t userPasswordChanged = (1 << 5); | 
 | 53 | static constexpr uint8_t adminPasswordChanged = (1 << 4); | 
 | 54 |  | 
 | 55 | static constexpr const char* biosConfigFolder = "/var/oob"; | 
 | 56 | static constexpr const char* biosConfigNVPath = "/var/oob/nvoobdata.dat"; | 
 | 57 | static constexpr const uint8_t algoSHA384 = 2; | 
 | 58 | static constexpr const uint8_t biosCapOffsetBit = 0x3; | 
 | 59 | static constexpr uint16_t maxGetPayloadDataSize = 4096; | 
 | 60 | static constexpr const char* biosXMLFilePath = "/var/oob/bios.xml"; | 
 | 61 | static constexpr const char* biosXMLFilePath1 = "/var/oob/tempbios.xml"; | 
 | 62 |  | 
 | 63 | static constexpr const char* biosConfigBaseMgrPath = | 
 | 64 |     "/xyz/openbmc_project/bios_config/manager"; | 
 | 65 | static constexpr const char* biosConfigIntf = | 
 | 66 |     "xyz.openbmc_project.BIOSConfig.Manager"; | 
 | 67 | static constexpr const char* resetBIOSSettingsProp = "ResetBIOSSettings"; | 
 | 68 | /*baseBIOSTable | 
 | 69 | map{attributeName,struct{attributeType,readonlyStatus,displayname, | 
 | 70 |               description,menuPath,current,default, | 
 | 71 |               array{struct{optionstring,optionvalue}}}} | 
 | 72 | */ | 
 | 73 | std::map<std::string, | 
 | 74 |          std::tuple<std::string, bool, std::string, std::string, std::string, | 
 | 75 |                     std::variant<int64_t, std::string>, | 
 | 76 |                     std::variant<int64_t, std::string>, | 
 | 77 |                     std::map<std::string, std::variant<int64_t, std::string>>>> | 
 | 78 |     AttributesData; | 
 | 79 |  | 
 | 80 | NVOOBdata gNVOOBdata; | 
 | 81 |  | 
 | 82 | enum class PTState : uint8_t | 
 | 83 | { | 
 | 84 |     StartTransfer = 0, | 
 | 85 |     InProgress = 1, | 
 | 86 |     EndTransfer = 2, | 
 | 87 |     UserAbort = 3 | 
 | 88 | }; | 
 | 89 | enum class PStatus : uint8_t | 
 | 90 | { | 
 | 91 |     Unknown = 0, | 
 | 92 |     Valid = 1, | 
 | 93 |     Corrupted = 2 | 
 | 94 | }; | 
 | 95 | enum class PType : uint8_t | 
 | 96 | { | 
 | 97 |     IntelXMLType0 = 0, | 
 | 98 |     IntelXMLType1 = 1, | 
 | 99 |     OTAPayload = 5, | 
 | 100 | }; | 
 | 101 |  | 
 | 102 | // | 
 | 103 | // GetPayload Payload status enumeration | 
 | 104 | // | 
 | 105 | enum class GetPayloadParameter : uint8_t | 
 | 106 | { | 
 | 107 |     GetPayloadInfo = 0, // 0 | 
 | 108 |     GetPayloadData = 1, // 1 | 
 | 109 |     GetPayloadStatus = 2 | 
 | 110 | }; | 
 | 111 |  | 
 | 112 | /** @brief implement to set the BaseBIOSTable property | 
 | 113 |  *  @returns status | 
 | 114 |  */ | 
 | 115 | static int sendAllAttributes(ipmi::Context::ptr ctx) | 
 | 116 | { | 
 | 117 |     boost::system::error_code ec; | 
 | 118 |     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); | 
 | 119 |     std::string service = | 
 | 120 |         getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath); | 
 | 121 |     ec.clear(); | 
 | 122 |     ctx->bus->yield_method_call<>( | 
 | 123 |         ctx->yield, ec, service, biosConfigBaseMgrPath, | 
 | 124 |         "org.freedesktop.DBus.Properties", "Set", biosConfigIntf, | 
 | 125 |         "BaseBIOSTable", AttributesData); | 
 | 126 |     if (ec) | 
 | 127 |     { | 
 | 128 |         phosphor::logging::log<phosphor::logging::level::ERR>( | 
 | 129 |             "Failed to sendAllAttributes"); | 
 | 130 |         return -1; | 
 | 131 |     } | 
 | 132 |     return 0; | 
 | 133 | } | 
 | 134 |  | 
 | 135 | /** @brief implement to flush the updated data in nv space | 
 | 136 |  *  @returns status | 
 | 137 |  */ | 
 | 138 | static uint8_t flushNVOOBdata() | 
 | 139 | { | 
 | 140 |     std::ofstream outFile(biosConfigNVPath, std::ios::binary | std::ios::app); | 
 | 141 |     if (outFile.good()) | 
 | 142 |     { | 
 | 143 |         outFile.seekp(std::ios_base::beg); | 
 | 144 |         const char* writedata = reinterpret_cast<const char*>(&gNVOOBdata); | 
 | 145 |         outFile.write(writedata, sizeof(struct NVOOBdata)); | 
 | 146 |         outFile.close(); | 
 | 147 |     } | 
 | 148 |     return 0; | 
 | 149 | } | 
 | 150 |  | 
 | 151 | /** @brief implement to get the System State | 
 | 152 |  *  @returns status | 
 | 153 |  */ | 
 | 154 |  | 
 | 155 | static int getSystemOSState(std::string& OsStatus) | 
 | 156 | { | 
 | 157 |  | 
 | 158 |     try | 
 | 159 |     { | 
 | 160 |         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); | 
 | 161 |         Value variant = | 
 | 162 |             getDbusProperty(*dbus, "xyz.openbmc_project.State.OperatingSystem", | 
 | 163 |                             "/xyz/openbmc_project/state/os", | 
 | 164 |                             "xyz.openbmc_project.State.OperatingSystem.Status", | 
 | 165 |                             "OperatingSystemState"); | 
 | 166 |         OsStatus = std::get<std::string>(variant); | 
 | 167 |         return ipmi::ccSuccess; | 
 | 168 |     } | 
 | 169 |     catch (const std::exception& e) | 
 | 170 |     { | 
 | 171 |         return ipmi::ccUnspecifiedError; | 
 | 172 |     } | 
 | 173 | } | 
 | 174 |  | 
 | 175 | /** @brief implement to get the Rest BIOS property | 
 | 176 |  *  @returns status | 
 | 177 |  */ | 
 | 178 | static int getResetBIOSSettings(uint8_t& ResetFlag) | 
 | 179 | { | 
 | 180 |  | 
 | 181 |     try | 
 | 182 |     { | 
 | 183 |         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); | 
 | 184 |         std::string service = | 
 | 185 |             getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath); | 
 | 186 |         Value variant = getDbusProperty(*dbus, service, biosConfigBaseMgrPath, | 
 | 187 |                                         biosConfigIntf, resetBIOSSettingsProp); | 
 | 188 |         ResetFlag = static_cast<uint8_t>(std::get<std::uint8_t>(variant)); | 
 | 189 |         return ipmi::ccSuccess; | 
 | 190 |     } | 
 | 191 |     catch (const std::exception& e) | 
 | 192 |     { | 
 | 193 |         return ipmi::ccUnspecifiedError; | 
 | 194 |     } | 
 | 195 | } | 
 | 196 |  | 
 | 197 | /** @brief implement generate naive- dbus from XML file | 
 | 198 |  *  @returns status | 
 | 199 |  */ | 
 | 200 | static int generateAttributesData() | 
 | 201 | { | 
 | 202 |     // Open the bios.xml and parse it | 
 | 203 |     // Extract the needed data and store it in AttributesData variable | 
 | 204 |     // Close the bios.xml | 
 | 205 |     phosphor::logging::log<phosphor::logging::level::ERR>( | 
 | 206 |         "generateAttributesData"); | 
 | 207 |     tinyxml2::XMLDocument xmlDoc; | 
 | 208 |  | 
 | 209 |     xmlDoc.LoadFile(biosXMLFilePath); | 
 | 210 |     tinyxml2::XMLNode* pRoot = xmlDoc.FirstChild(); | 
 | 211 |     if (pRoot == nullptr) | 
 | 212 |     { | 
 | 213 |         return 0; | 
 | 214 |     } | 
 | 215 |     tinyxml2::XMLElement* pElement = pRoot->FirstChildElement("biosknobs"); | 
 | 216 |     if (pElement == nullptr) | 
 | 217 |     { | 
 | 218 |         return 0; | 
 | 219 |     } | 
 | 220 |     tinyxml2::XMLElement* pKnobsElement = pElement->FirstChildElement("knob"); | 
 | 221 |  | 
 | 222 |     while (pKnobsElement != nullptr) | 
 | 223 |     { | 
 | 224 |         bool readOnlyStatus = false; | 
 | 225 |         std::string attrType = | 
 | 226 |             "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String"; | 
 | 227 |         std::string name, curvalue, dname, menupath, defaultvalue, description; | 
 | 228 |         name = pKnobsElement->Attribute("name") | 
 | 229 |                    ? pKnobsElement->Attribute("name") | 
 | 230 |                    : ""; | 
 | 231 |         curvalue = pKnobsElement->Attribute("CurrentVal") | 
 | 232 |                        ? pKnobsElement->Attribute("CurrentVal") | 
 | 233 |                        : ""; | 
 | 234 |         dname = pKnobsElement->Attribute("prompt") | 
 | 235 |                     ? pKnobsElement->Attribute("prompt") | 
 | 236 |                     : ""; | 
 | 237 |         menupath = pKnobsElement->Attribute("SetupPgPtr") | 
 | 238 |                        ? pKnobsElement->Attribute("SetupPgPtr") | 
 | 239 |                        : ""; | 
 | 240 |         defaultvalue = pKnobsElement->Attribute("default") | 
 | 241 |                            ? pKnobsElement->Attribute("default") | 
 | 242 |                            : ""; | 
 | 243 |         description = pKnobsElement->Attribute("description") | 
 | 244 |                           ? pKnobsElement->Attribute("description") | 
 | 245 |                           : ""; | 
 | 246 |         phosphor::logging::log<phosphor::logging::level::INFO>(name.c_str()); | 
 | 247 |         if (!name.empty() && !curvalue.empty() && !dname.empty() && | 
 | 248 |             !menupath.empty() && !defaultvalue.empty()) | 
 | 249 |         { | 
 | 250 |             std::string rootPath = "./" + std::string(menupath); | 
 | 251 |  | 
 | 252 |             std::map<std::string, std::variant<int64_t, std::string>> optionMap; | 
 | 253 |             tinyxml2::XMLElement* pOptionsElement = | 
 | 254 |                 pKnobsElement->FirstChildElement("options"); | 
 | 255 |             nlohmann::json optionsArray = nlohmann::json::array(); | 
 | 256 |             if (pOptionsElement != nullptr) | 
 | 257 |             { | 
 | 258 |                 tinyxml2::XMLElement* pOptionElement = | 
 | 259 |                     pOptionsElement->FirstChildElement("option"); | 
 | 260 |                 while (pOptionElement != nullptr) | 
 | 261 |                 { | 
 | 262 |                     const std::string text = pOptionElement->Attribute("text"); | 
 | 263 |                     const std::string attrValue = | 
 | 264 |                         pOptionElement->Attribute("value"); | 
 | 265 |                     if (!text.empty() && !attrValue.empty()) | 
 | 266 |                     { | 
 | 267 |  | 
 | 268 |                         optionMap.emplace(std::make_pair(std::move(text), | 
 | 269 |                                                          std::move(attrValue))); | 
 | 270 |                     } | 
 | 271 |                     pOptionElement = | 
 | 272 |                         pOptionElement->NextSiblingElement("option"); | 
 | 273 |                 } | 
 | 274 |             } | 
 | 275 |  | 
 | 276 |             AttributesData.emplace(std::make_pair( | 
 | 277 |                 name, | 
 | 278 |                 std::make_tuple(attrType, readOnlyStatus, dname, description, | 
 | 279 |                                 rootPath, curvalue, defaultvalue, optionMap))); | 
 | 280 |         } | 
 | 281 |         pKnobsElement = pKnobsElement->NextSiblingElement("knob"); | 
 | 282 |     } | 
 | 283 |  | 
 | 284 |     return 0; | 
 | 285 | } | 
 | 286 |  | 
 | 287 | /** @brief implement executing the linux command to uncompress and generate the | 
 | 288 |  * xmlfile | 
 | 289 |  *  @param[in] linux command | 
 | 290 |  *  @returns status | 
 | 291 |  */ | 
 | 292 | template <typename... ArgTypes> | 
 | 293 | static int generateBIOSXMLFile(const char* path, ArgTypes&&... tArgs) | 
 | 294 | { | 
 | 295 |  | 
 | 296 |     boost::process::child execProg(path, const_cast<char*>(tArgs)..., | 
 | 297 |                                    boost::process::std_out > biosXMLFilePath); | 
 | 298 |     execProg.wait(); | 
 | 299 |     return execProg.exit_code(); | 
 | 300 | } | 
 | 301 |  | 
 | 302 | /** @brief implements to clean up the temp/ existing payload file | 
 | 303 |  **/ | 
 | 304 | static void cleanUpPayloadFile(uint8_t& payloadType) | 
 | 305 | { | 
 | 306 |     // Clear the payload Information | 
 | 307 |     std::string FilePath = "/var/oob/temp" + std::to_string(payloadType); | 
 | 308 |     unlink(FilePath.c_str()); | 
 | 309 |     FilePath = "/var/oob/Payload" + std::to_string(payloadType); | 
 | 310 |     unlink(FilePath.c_str()); | 
 | 311 |     if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) | 
 | 312 |     { | 
 | 313 |         unlink("/var/oob/Payload1"); | 
 | 314 |         gNVOOBdata.payloadInfo[static_cast<uint8_t>(ipmi::PType::IntelXMLType1)] | 
 | 315 |             .payloadStatus = static_cast<uint8_t>(ipmi::PStatus::Unknown); | 
 | 316 |     } | 
 | 317 | } | 
 | 318 |  | 
 | 319 | /** @brief implements to create the oob folders and nv space | 
 | 320 |  **/ | 
 | 321 | static Cc InitNVOOBdata() | 
 | 322 | { | 
 | 323 |     FILE* fptr; | 
 | 324 |     uint16_t size; | 
 | 325 |  | 
 | 326 |     if (!(std::filesystem::exists(biosConfigFolder))) | 
 | 327 |     { | 
 | 328 |         std::filesystem::create_directory(biosConfigFolder); | 
 | 329 |     } | 
 | 330 |  | 
 | 331 |     std::ifstream ifs(biosConfigNVPath, | 
 | 332 |                       std::ios::in | std::ios::binary | std::ios::ate); | 
 | 333 |  | 
 | 334 |     if (ifs.good()) | 
 | 335 |     { | 
 | 336 |  | 
 | 337 |         ifs.read(reinterpret_cast<char*>(&gNVOOBdata), | 
 | 338 |                  sizeof(struct NVOOBdata)); | 
 | 339 |         ifs.close(); | 
 | 340 |         return ipmi::ccSuccess; | 
 | 341 |     } | 
 | 342 |     return ipmi::ccResponseError; | 
 | 343 | } | 
 | 344 |  | 
 | 345 | /** @brief implements check the command interface is | 
 | 346 |  ** system interface or not | 
 | 347 |  **  true mean System interface and false mean LAN or IPMB | 
 | 348 |  **/ | 
 | 349 | static bool IsSystemInterface(ipmi::Context::ptr ctx) | 
 | 350 | { | 
 | 351 |     ChannelInfo chInfo; | 
 | 352 |     Cc status = false; | 
 | 353 |  | 
 | 354 |     try | 
 | 355 |     { | 
 | 356 |         getChannelInfo(ctx->channel, chInfo); | 
 | 357 |     } | 
 | 358 |     catch (sdbusplus::exception_t& e) | 
 | 359 |     { | 
 | 360 |         return false; | 
 | 361 |     } | 
 | 362 |     if (chInfo.mediumType != | 
 | 363 |         static_cast<uint8_t>(EChannelMediumType::systemInterface)) | 
 | 364 |     { | 
 | 365 |         return false; | 
 | 366 |     } | 
 | 367 |     return true; | 
 | 368 | } | 
 | 369 |  | 
 | 370 | ipmi::RspType<> ipmiOEMSetBIOSCap(ipmi::Context::ptr ctx, | 
 | 371 |                                   uint8_t BIOSCapabilties, uint8_t reserved1, | 
 | 372 |                                   uint8_t reserved2, uint8_t reserved3) | 
 | 373 | { | 
 | 374 |     std::string OSState; | 
 | 375 |     getSystemOSState(OSState); | 
 | 376 |  | 
 | 377 |     if (OSState != "OperatingState" && IsSystemInterface(ctx)) | 
 | 378 |     { | 
 | 379 |         if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0) | 
 | 380 |         { | 
 | 381 |             return ipmi::responseInvalidFieldRequest(); | 
 | 382 |         } | 
 | 383 |  | 
 | 384 |         gNVOOBdata.mBIOSCapabilities.OOBCapability = BIOSCapabilties; | 
 | 385 |         gNVOOBdata.mIsBIOSCapInitDone = true; | 
 | 386 |  | 
 | 387 |         flushNVOOBdata(); | 
 | 388 |         return ipmi::responseSuccess(); | 
 | 389 |     } | 
 | 390 |     else | 
 | 391 |     { | 
 | 392 |  | 
 | 393 |         return ipmi::response(ipmiCCNotSupportedInCurrentState); | 
 | 394 |     } | 
 | 395 | } | 
 | 396 |  | 
 | 397 | ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t> | 
 | 398 |     ipmiOEMGetBIOSCap(ipmi::Context::ptr ctx) | 
 | 399 | { | 
 | 400 |     if (gNVOOBdata.mIsBIOSCapInitDone) | 
 | 401 |     { | 
 | 402 |         return ipmi::responseSuccess(gNVOOBdata.mBIOSCapabilities.OOBCapability, | 
 | 403 |                                      0, 0, 0); | 
 | 404 |     } | 
 | 405 |     else | 
 | 406 |     { | 
 | 407 |         return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); | 
 | 408 |     } | 
 | 409 | } | 
 | 410 |  | 
 | 411 | ipmi::RspType<uint32_t> ipmiOEMSetPayload(ipmi::Context::ptr ctx, | 
 | 412 |                                           uint8_t paramSel, uint8_t payloadType, | 
 | 413 |                                           std::vector<uint8_t> payload) | 
 | 414 | { | 
 | 415 |     uint8_t biosCapOffsetBit = 2; // BIT:1 0-OOB BIOS config not supported | 
 | 416 |                                   //      1-OOB BIOS config is supported | 
 | 417 |  | 
 | 418 |     if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit))) | 
 | 419 |     { | 
 | 420 |         return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); | 
 | 421 |     } | 
 | 422 |     // Validate the Payload Type | 
 | 423 |     if (payloadType > maxPayloadSupported) | 
 | 424 |     { | 
 | 425 |         return ipmi::responseInvalidFieldRequest(); | 
 | 426 |     } | 
 | 427 |  | 
 | 428 |     // We should support this Payload type 0 command only in KCS Interface | 
 | 429 |     if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) | 
 | 430 |     { | 
 | 431 |         std::string OSState; | 
 | 432 |  | 
 | 433 |         getSystemOSState(OSState); | 
 | 434 |         if (!IsSystemInterface(ctx) || OSState == "OperatingState") | 
 | 435 |         { | 
 | 436 |             return ipmi::responseCommandNotAvailable(); | 
 | 437 |         } | 
 | 438 |     } | 
 | 439 |  | 
 | 440 |     switch (static_cast<PTState>(paramSel)) | 
 | 441 |     { | 
 | 442 |         case ipmi::PTState::StartTransfer: | 
 | 443 |         { | 
 | 444 |             PayloadStartTransfer* pPayloadStartTransfer = | 
 | 445 |                 reinterpret_cast<PayloadStartTransfer*>(payload.data()); | 
 | 446 |             if (payload.size() < sizeof(PayloadStartTransfer)) | 
 | 447 |             { | 
 | 448 |                 phosphor::logging::log<phosphor::logging::level::ERR>( | 
 | 449 |                     "ipmiOEMSetPayload: BIOS Config Payload size is not " | 
 | 450 |                     "correct"); | 
 | 451 |                 return ipmi::responseReqDataLenInvalid(); | 
 | 452 |             } | 
 | 453 |             cleanUpPayloadFile(payloadType); | 
 | 454 |  | 
 | 455 |             gNVOOBdata.payloadInfo[payloadType].payloadReservationID = rand(); | 
 | 456 |             gNVOOBdata.payloadInfo[payloadType].payloadTotalChecksum = | 
 | 457 |                 pPayloadStartTransfer->payloadTotalChecksum; | 
 | 458 |             gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = | 
 | 459 |                 pPayloadStartTransfer->payloadTotalSize; | 
 | 460 |             gNVOOBdata.payloadInfo[payloadType].payloadVersion = | 
 | 461 |                 pPayloadStartTransfer->payloadVersion; | 
 | 462 |             gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten = 0; | 
 | 463 |             gNVOOBdata.payloadInfo[payloadType].payloadStatus = | 
 | 464 |                 static_cast<uint8_t>(ipmi::PStatus::Unknown); | 
 | 465 |  | 
 | 466 |             return ipmi::responseSuccess( | 
 | 467 |                 gNVOOBdata.payloadInfo[payloadType].payloadReservationID); | 
 | 468 |         } | 
 | 469 |         break; | 
 | 470 |  | 
 | 471 |         case ipmi::PTState::InProgress: | 
 | 472 |         { | 
 | 473 |             PayloadInProgress* pPayloadInProgress = | 
 | 474 |                 reinterpret_cast<PayloadInProgress*>(payload.data()); | 
 | 475 |             PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; | 
 | 476 |  | 
 | 477 |             if (payload.size() < sizeof(PayloadInProgress)) | 
 | 478 |             { | 
 | 479 |                 phosphor::logging::log<phosphor::logging::level::ERR>( | 
 | 480 |                     "BIOS Config Payload size is not correct"); | 
 | 481 |                 return ipmi::responseReqDataLenInvalid(); | 
 | 482 |             } | 
 | 483 |  | 
 | 484 |             if (pPayloadInProgress->payloadReservationID != | 
 | 485 |                 payloadInfo.payloadReservationID) | 
 | 486 |             { | 
 | 487 |                 return ipmi::responseInvalidReservationId(); | 
 | 488 |             } | 
 | 489 |             payloadInfo.payloadCurrentSize = | 
 | 490 |                 pPayloadInProgress->payloadCurrentSize; | 
 | 491 |             // Need to verify the current Payload Checksum | 
 | 492 |             const uint8_t* data = | 
 | 493 |                 reinterpret_cast<const uint8_t*>(payload.data()); | 
 | 494 |             // we have to remove the current size, current offset, current | 
 | 495 |             // length,checksum bytes , reservation bytes | 
 | 496 |             boost::crc_32_type calcChecksum; | 
 | 497 |             calcChecksum.process_bytes(data + 16, payload.size() - 16); | 
 | 498 |             if (calcChecksum.checksum() != | 
 | 499 |                 pPayloadInProgress->payloadCurrentChecksum) | 
 | 500 |             { | 
 | 501 |                 phosphor::logging::log<phosphor::logging::level::ERR>( | 
 | 502 |                     "ipmiOEMSetPayload: Payload Checksum Failed"); | 
 | 503 |                 return ipmi::response(ipmiCCPayloadChecksumFailed); | 
 | 504 |             } | 
 | 505 |             // store the data in temp file | 
 | 506 |             std::string FilePath = | 
 | 507 |                 "/var/oob/temp" + std::to_string(payloadType); | 
 | 508 |  | 
 | 509 |             std::ofstream outFile(FilePath, std::ios::binary | std::ios::app); | 
 | 510 |             outFile.seekp(pPayloadInProgress->payloadOffset); | 
 | 511 |             // we have to remove the current size, current offset, current | 
 | 512 |             // length,checksum bytes , reservation bytes | 
 | 513 |  | 
 | 514 |             const char* writedata = | 
 | 515 |                 reinterpret_cast<const char*>(payload.data()); | 
 | 516 |             outFile.write(writedata + 16, payload.size() - 16); | 
 | 517 |             outFile.close(); | 
 | 518 |  | 
 | 519 |             gNVOOBdata.payloadInfo[payloadType].payloadStatus = | 
 | 520 |                 static_cast<uint8_t>(ipmi::PStatus::Unknown); | 
 | 521 |             gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten += | 
 | 522 |                 payloadInfo.payloadCurrentSize; | 
 | 523 |             return ipmi::responseSuccess(payloadInfo.payloadCurrentSize); | 
 | 524 |         } | 
 | 525 |         break; | 
 | 526 |         case ipmi::PTState::EndTransfer: | 
 | 527 |         { | 
 | 528 |             PayloadEndTransfer* pPayloadEndTransfer = | 
 | 529 |                 reinterpret_cast<PayloadEndTransfer*>(payload.data()); | 
 | 530 |             PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; | 
 | 531 |             if (pPayloadEndTransfer->payloadReservationID != | 
 | 532 |                 payloadInfo.payloadReservationID) | 
 | 533 |             { | 
 | 534 |                 return ipmi::responseInvalidReservationId(); | 
 | 535 |             } | 
 | 536 |             gNVOOBdata.payloadInfo[payloadType].payloadStatus = | 
 | 537 |                 static_cast<uint8_t>(ipmi::PStatus::Unknown); | 
 | 538 |  | 
 | 539 |             if (gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten != | 
 | 540 |                 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize) | 
 | 541 |             { | 
 | 542 |  | 
 | 543 |                 return ipmi::response(ipmiCCPayloadPayloadInComplete); | 
 | 544 |             } | 
 | 545 |             std::string tempFilePath = | 
 | 546 |                 "/var/oob/temp" + std::to_string(payloadType); | 
 | 547 |             std::string PayloadFilePath = | 
 | 548 |                 "/var/oob/Payload" + std::to_string(payloadType); | 
 | 549 |             auto renamestatus = | 
 | 550 |                 std::rename(tempFilePath.c_str(), PayloadFilePath.c_str()); | 
 | 551 |             if (renamestatus) | 
 | 552 |             { | 
 | 553 |                 phosphor::logging::log<phosphor::logging::level::ERR>( | 
 | 554 |                     "ipmiOEMSetPayload: Renaming Payload file - failed"); | 
 | 555 |             } | 
 | 556 |  | 
 | 557 |             gNVOOBdata.payloadInfo[payloadType].payloadFilePath = | 
 | 558 |                 PayloadFilePath; | 
 | 559 |             if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0)) | 
 | 560 |             { | 
 | 561 |                 // Unzip the Intel format XML file type 0 | 
 | 562 |                 auto response = generateBIOSXMLFile("/usr/bin/lzcat", "-d", | 
 | 563 |                                                     PayloadFilePath.c_str()); | 
 | 564 |                 if (response) | 
 | 565 |                 { | 
 | 566 |  | 
 | 567 |                     phosphor::logging::log<phosphor::logging::level::ERR>( | 
 | 568 |                         "ipmiOEMSetPayload: generateBIOSXMLFile - failed"); | 
 | 569 |                     gNVOOBdata.payloadInfo[payloadType].payloadStatus = | 
 | 570 |                         static_cast<uint8_t>(ipmi::PStatus::Corrupted); | 
 | 571 |                     return ipmi::response(ipmiCCPayloadPayloadPacketMissed); | 
 | 572 |                 } | 
 | 573 |             } | 
 | 574 |             gNVOOBdata.payloadInfo[payloadType].payloadStatus = | 
 | 575 |                 static_cast<uint8_t>(ipmi::PStatus::Valid); | 
 | 576 |  | 
 | 577 |             struct stat filestat; | 
 | 578 |  | 
 | 579 |             /* Get entry's information. */ | 
 | 580 |             if (!stat(PayloadFilePath.c_str(), &filestat)) | 
 | 581 |             { | 
 | 582 |                 gNVOOBdata.payloadInfo[payloadType].payloadTimeStamp = | 
 | 583 |                     filestat.st_mtime; | 
 | 584 |                 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = | 
 | 585 |                     filestat.st_size; | 
 | 586 |                 gNVOOBdata.payloadInfo[payloadType].payloadFilePath = | 
 | 587 |                     PayloadFilePath; | 
 | 588 |             } | 
 | 589 |             else | 
 | 590 |             { | 
 | 591 |                 return ipmi::responseResponseError(); | 
 | 592 |             } | 
 | 593 |  | 
 | 594 |             phosphor::logging::log<phosphor::logging::level::INFO>( | 
 | 595 |                 " ipmiOEMSetPayload : Convert XML into native-dbus DONE"); | 
 | 596 |             generateAttributesData(); | 
 | 597 |  | 
 | 598 |             phosphor::logging::log<phosphor::logging::level::INFO>( | 
 | 599 |                 " ipmiOEMSetPayload : BaseBIOSTable Property  is set"); | 
 | 600 |             sendAllAttributes(ctx); | 
 | 601 |  | 
 | 602 |             flushNVOOBdata(); | 
 | 603 |             return ipmi::responseSuccess( | 
 | 604 |                 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten); | 
 | 605 |         } | 
 | 606 |         break; | 
 | 607 |         case ipmi::PTState::UserAbort: | 
 | 608 |         { | 
 | 609 |             PayloadEndTransfer* pPayloadEndTransfer = | 
 | 610 |                 reinterpret_cast<PayloadEndTransfer*>(payload.data()); | 
 | 611 |             PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; | 
 | 612 |             if (pPayloadEndTransfer->payloadReservationID != | 
 | 613 |                 payloadInfo.payloadReservationID) | 
 | 614 |             { | 
 | 615 |                 return ipmi::responseInvalidReservationId(); | 
 | 616 |             } | 
 | 617 |             gNVOOBdata.payloadInfo[payloadType].payloadReservationID = 0; | 
 | 618 |             gNVOOBdata.payloadInfo[payloadType].payloadType = 0; | 
 | 619 |             gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 0; | 
 | 620 |             // Delete the temp file | 
 | 621 |             std::string tempFilePath = | 
 | 622 |                 "/var/oob/temp" + std::to_string(payloadType); | 
 | 623 |             unlink(tempFilePath.c_str()); | 
 | 624 |             flushNVOOBdata(); | 
 | 625 |             return ipmi::responseSuccess(); | 
 | 626 |         } | 
 | 627 |         break; | 
 | 628 |         default: | 
 | 629 |             return ipmi::responseInvalidFieldRequest(); | 
 | 630 |     } | 
 | 631 |     return ipmi::responseResponseError(); | 
 | 632 | } | 
 | 633 |  | 
 | 634 | ipmi::RspType<message::Payload> | 
 | 635 |     ipmiOEMGetPayload(ipmi::Context::ptr ctx, uint8_t paramSel, | 
 | 636 |                       uint8_t payloadType, ipmi::message::Payload& payload) | 
 | 637 | { | 
 | 638 |     //      1-OOB BIOS config is supported | 
 | 639 |     message::Payload retValue; | 
 | 640 |  | 
 | 641 |     if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit))) | 
 | 642 |     { | 
 | 643 |         return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); | 
 | 644 |     } | 
 | 645 |     // Validate the Payload Type | 
 | 646 |     if (payloadType > maxPayloadSupported) | 
 | 647 |     { | 
 | 648 |         return ipmi::responseInvalidFieldRequest(); | 
 | 649 |     } | 
 | 650 |  | 
 | 651 |     struct PayloadInfo res = gNVOOBdata.payloadInfo[payloadType]; | 
 | 652 |  | 
 | 653 |     switch (static_cast<GetPayloadParameter>(paramSel)) | 
 | 654 |     { | 
 | 655 |         case ipmi::GetPayloadParameter::GetPayloadInfo: | 
 | 656 |         { | 
 | 657 |             retValue.pack(res.payloadVersion); | 
 | 658 |             retValue.pack(res.payloadType); | 
 | 659 |             retValue.pack(res.payloadTotalSize); | 
 | 660 |             retValue.pack(res.payloadTotalChecksum); | 
 | 661 |             retValue.pack(res.payloadflag); | 
 | 662 |             retValue.pack(res.payloadStatus); | 
 | 663 |             retValue.pack(res.payloadTimeStamp); | 
 | 664 |  | 
 | 665 |             return ipmi::responseSuccess(std::move(retValue)); | 
 | 666 |         } | 
 | 667 |  | 
 | 668 |         break; | 
 | 669 |         case ipmi::GetPayloadParameter::GetPayloadData: | 
 | 670 |         { | 
 | 671 |             if (res.payloadStatus == | 
 | 672 |                 (static_cast<uint8_t>(ipmi::PStatus::Valid))) | 
 | 673 |             { | 
 | 674 |                 std::vector<uint32_t> reqData; | 
 | 675 |                 if (payload.unpack(reqData) || !payload.fullyUnpacked()) | 
 | 676 |                 { | 
 | 677 |                     return ipmi::responseReqDataLenInvalid(); | 
 | 678 |                 } | 
 | 679 |                 uint32_t offset = reqData.at(0); | 
 | 680 |                 uint32_t length = reqData.at(1); | 
 | 681 |  | 
 | 682 |                 std::ifstream ifs(res.payloadFilePath, std::ios::in | | 
 | 683 |                                                            std::ios::binary | | 
 | 684 |                                                            std::ios::ate); | 
 | 685 |                 std::ifstream::pos_type fileSize = ifs.tellg(); | 
 | 686 |                 // Total file data within given offset | 
 | 687 |                 if (fileSize < static_cast<uint64_t>(offset)) | 
 | 688 |                 { | 
 | 689 |                     ifs.close(); | 
 | 690 |                     return ipmi::responseInvalidFieldRequest(); | 
 | 691 |                 } | 
 | 692 |  | 
 | 693 |                 ifs.seekg(offset, std::ios::beg); | 
 | 694 |                 std::array<uint8_t, maxGetPayloadDataSize> Buffer; | 
 | 695 |                 ifs.read(reinterpret_cast<char*>(Buffer.data()), length); | 
 | 696 |                 uint32_t readCount = ifs.gcount(); | 
 | 697 |                 ifs.close(); | 
 | 698 |  | 
 | 699 |                 boost::crc_32_type calcChecksum; | 
 | 700 |                 calcChecksum.process_bytes( | 
 | 701 |                     reinterpret_cast<char*>(Buffer.data()), readCount); | 
 | 702 |                 uint32_t chkSum = calcChecksum.checksum(); | 
 | 703 |                 retValue.pack(payloadType); | 
 | 704 |                 retValue.pack(readCount); | 
 | 705 |                 retValue.pack(chkSum); | 
 | 706 |  | 
 | 707 |                 for (int i = 0; i < readCount; i++) | 
 | 708 |                 { | 
 | 709 |                     retValue.pack(Buffer.at(i)); | 
 | 710 |                 } | 
 | 711 |                 return ipmi::responseSuccess(std::move(retValue)); | 
 | 712 |             } | 
 | 713 |             else | 
 | 714 |             { | 
 | 715 |                 return ipmi::responseResponseError(); | 
 | 716 |             } | 
 | 717 |         } | 
 | 718 |         break; | 
 | 719 |         case ipmi::GetPayloadParameter::GetPayloadStatus: | 
 | 720 |         { | 
 | 721 |             retValue.pack(gNVOOBdata.payloadInfo[payloadType].payloadStatus); | 
 | 722 |             return ipmi::responseSuccess(std::move(retValue)); | 
 | 723 |         } | 
 | 724 |         break; | 
 | 725 |         default: | 
 | 726 |             return ipmi::responseInvalidFieldRequest(); | 
 | 727 |     } | 
 | 728 |     return ipmi::responseInvalidFieldRequest(); | 
 | 729 | } | 
 | 730 |  | 
 | 731 | ipmi::RspType<> ipmiOEMSetBIOSHashInfo( | 
 | 732 |     ipmi::Context::ptr ctx, std::array<uint8_t, maxSeedSize>& pwdSeed, | 
 | 733 |     uint8_t algoInfo, std::array<uint8_t, maxHashSize>& adminPwdHash, | 
 | 734 |     std::array<uint8_t, maxHashSize>& userPwdHash) | 
 | 735 | { | 
 | 736 |  | 
 | 737 |     std::string OSState; | 
 | 738 |  | 
 | 739 |     // We should support this command only in KCS Interface | 
 | 740 |     if (!IsSystemInterface(ctx)) | 
 | 741 |     { | 
 | 742 |         return ipmi::responseCommandNotAvailable(); | 
 | 743 |     } | 
 | 744 |     getSystemOSState(OSState); | 
 | 745 |     // We should not support this command after System Booted - After Exit Boot | 
 | 746 |     // service called | 
 | 747 |  | 
 | 748 |     if (OSState != "OperatingState") | 
 | 749 |     { | 
 | 750 |  | 
| Suryakanth Sekar | f486695 | 2021-02-25 16:06:57 +0530 | [diff] [blame] | 751 |         if ((algoInfo & 0xF) != algoSHA384) | 
| Kuiying Wang | 6d6dc7a | 2020-04-02 10:15:19 +0800 | [diff] [blame] | 752 |         { | 
 | 753 |             // Atpresent, we are supporting only SHA384- HASH algo in BIOS side | 
 | 754 |             return ipmi::responseInvalidFieldRequest(); | 
 | 755 |         } | 
 | 756 |         std::string HashFilePath = "/var/lib/bios-settings-manager/seedData"; | 
 | 757 |  | 
 | 758 |         nlohmann::json json; | 
 | 759 |         json["Seed"] = pwdSeed; | 
 | 760 |         json["HashAlgo"] = "SHA384"; | 
 | 761 |         json["IsAdminPwdChanged"] = false; | 
 | 762 |         json["AdminPwdHash"] = adminPwdHash; | 
 | 763 |         json["IsUserPwdChanged"] = false; | 
 | 764 |         json["UserPwdHash"] = userPwdHash; | 
| Suryakanth Sekar | f486695 | 2021-02-25 16:06:57 +0530 | [diff] [blame] | 765 |         json["StatusFlag"] = algoInfo; | 
| Kuiying Wang | 6d6dc7a | 2020-04-02 10:15:19 +0800 | [diff] [blame] | 766 |         std::ofstream ofs(HashFilePath, std::ios::out); | 
 | 767 |         const auto& writeData = json.dump(); | 
 | 768 |         ofs << writeData; | 
 | 769 |         ofs.close(); | 
 | 770 |         return ipmi::responseSuccess(); | 
 | 771 |     } | 
 | 772 |     else | 
 | 773 |     { | 
 | 774 |  | 
 | 775 |         return ipmi::response(ipmiCCNotSupportedInCurrentState); | 
 | 776 |     } | 
 | 777 | } | 
 | 778 |  | 
 | 779 | ipmi::RspType<uint8_t, std::array<uint8_t, maxHashSize>, | 
 | 780 |               std::array<uint8_t, maxHashSize>> | 
 | 781 |     ipmiOEMGetBIOSHash(ipmi::Context::ptr ctx) | 
 | 782 | { | 
 | 783 |  | 
 | 784 |     std::string OSState; | 
 | 785 |     nlohmann::json data = nullptr; | 
 | 786 |  | 
 | 787 |     // We should support this command only in KCS Interface | 
 | 788 |     if (!IsSystemInterface(ctx)) | 
 | 789 |     { | 
 | 790 |         return ipmi::responseCommandNotAvailable(); | 
 | 791 |     } | 
 | 792 |  | 
 | 793 |     getSystemOSState(OSState); | 
 | 794 |     // We should not support this command after System Booted - After Exit Boot | 
 | 795 |     // service called | 
 | 796 |  | 
 | 797 |     if (OSState != "OperatingState") | 
 | 798 |     { | 
 | 799 |         std::string HashFilePath = | 
 | 800 |             "/var/lib/bios-settings-manager/passwordData"; | 
 | 801 |  | 
 | 802 |         std::ifstream devIdFile(HashFilePath); | 
 | 803 |         if (devIdFile.is_open()) | 
 | 804 |         { | 
 | 805 |  | 
 | 806 |             try | 
 | 807 |             { | 
 | 808 |                 data = nlohmann::json::parse(devIdFile, nullptr, false); | 
 | 809 |             } | 
 | 810 |             catch (const nlohmann::json::parse_error& e) | 
 | 811 |             { | 
 | 812 |                 return ipmi::responseResponseError(); | 
 | 813 |             } | 
 | 814 |  | 
 | 815 |             if (data.is_discarded()) | 
 | 816 |             { | 
 | 817 |                 return ipmi::responseResponseError(); | 
 | 818 |             } | 
 | 819 |  | 
 | 820 |             std::array<uint8_t, maxHashSize> newAdminHash; | 
 | 821 |             std::array<uint8_t, maxHashSize> newUserHash; | 
 | 822 |             uint8_t flag = 0; | 
 | 823 |             uint8_t adminPwdChangedFlag = 0; | 
 | 824 |             uint8_t userPwdChangedFlag = 0; | 
 | 825 |             if (!data.is_discarded()) | 
 | 826 |             { | 
 | 827 |  | 
 | 828 |                 adminPwdChangedFlag = data["IsAdminPwdChanged"]; | 
 | 829 |                 newAdminHash = data["AdminPwdHash"]; | 
 | 830 |                 newUserHash = data["UserPwdHash"]; | 
 | 831 |                 userPwdChangedFlag = data["IsUserPwdChanged"]; | 
 | 832 |             } | 
 | 833 |  | 
 | 834 |             // 0: BIT 4 - New Admin Password Not Present | 
 | 835 |             // 1: BIT 4 - New Admin Password Hash Present | 
 | 836 |             // 0: BIT 5 - New User Password Not Present | 
 | 837 |             // 1: BIT 5 - New User Password Hash Present | 
 | 838 |             // 0: BIT 0 - Default Setting flag is not set | 
 | 839 |             // 1: BIT 0 - Default Setting flag is set | 
 | 840 |             auto status = getResetBIOSSettings(flag); | 
 | 841 |             if (status) | 
 | 842 |             { | 
 | 843 |                 return ipmi::responseResponseError(); | 
 | 844 |             } | 
 | 845 |             if (adminPwdChangedFlag) | 
 | 846 |             { | 
 | 847 |                 flag |= adminPasswordChanged; | 
 | 848 |             } | 
 | 849 |             if (userPwdChangedFlag) | 
 | 850 |             { | 
 | 851 |                 flag |= userPasswordChanged; | 
 | 852 |             } | 
 | 853 |  | 
 | 854 |             std::copy(std::begin(newAdminHash), std::end(newAdminHash), | 
 | 855 |                       std::begin(newAdminHash)); | 
 | 856 |             std::copy(std::begin(newUserHash), std::end(newUserHash), | 
 | 857 |                       std::begin(newUserHash)); | 
 | 858 |             return ipmi::responseSuccess(flag, newAdminHash, newUserHash); | 
 | 859 |         } | 
 | 860 |         else | 
 | 861 |         { | 
 | 862 |             return ipmi::responseResponseError(); | 
 | 863 |         } | 
 | 864 |     } | 
 | 865 |     else | 
 | 866 |     { | 
 | 867 |  | 
 | 868 |         return ipmi::response(ipmiCCNotSupportedInCurrentState); | 
 | 869 |     } | 
 | 870 | } | 
 | 871 |  | 
 | 872 | static void registerBIOSConfigFunctions(void) | 
 | 873 | { | 
 | 874 |     phosphor::logging::log<phosphor::logging::level::INFO>( | 
 | 875 |         "BIOSConfig module initialization"); | 
 | 876 |     InitNVOOBdata(); | 
 | 877 |  | 
 | 878 |     registerHandler(prioOemBase, intel::netFnGeneral, | 
 | 879 |                     intel::general::cmdSetBIOSCap, Privilege::Admin, | 
 | 880 |                     ipmiOEMSetBIOSCap); | 
 | 881 |  | 
 | 882 |     registerHandler(prioOemBase, intel::netFnGeneral, | 
 | 883 |                     intel::general::cmdGetBIOSCap, Privilege::User, | 
 | 884 |                     ipmiOEMGetBIOSCap); | 
 | 885 |     registerHandler(prioOemBase, intel::netFnGeneral, | 
 | 886 |                     intel::general::cmdSetBIOSPwdHashInfo, Privilege::Admin, | 
 | 887 |                     ipmiOEMSetBIOSHashInfo); | 
 | 888 |  | 
 | 889 |     registerHandler(prioOemBase, intel::netFnGeneral, | 
 | 890 |                     intel::general::cmdGetBIOSPwdHash, Privilege::User, | 
 | 891 |                     ipmiOEMGetBIOSHash); | 
 | 892 |  | 
 | 893 |     registerHandler(prioOemBase, intel::netFnGeneral, | 
 | 894 |                     intel::general::cmdGetPayload, Privilege::User, | 
 | 895 |                     ipmiOEMGetPayload); | 
 | 896 |     registerHandler(prioOemBase, intel::netFnGeneral, | 
 | 897 |                     intel::general::cmdSetPayload, Privilege::Admin, | 
 | 898 |                     ipmiOEMSetPayload); | 
 | 899 |  | 
 | 900 |     return; | 
 | 901 | } | 
 | 902 |  | 
 | 903 | } // namespace ipmi |