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