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