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