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