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