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 void registerBIOSConfigFunctions() __attribute__((constructor));
41 
42 // Define BIOS config related Completion Code
43 using Cc = uint8_t;
44 static constexpr Cc ipmiCCPayloadPayloadPacketMissed = 0x80;
45 static constexpr Cc ipmiCCBIOSPasswordInitNotDone = 0x80;
46 static constexpr Cc ipmiCCPayloadChecksumFailed = 0x81;
47 static constexpr Cc ipmiCCNotSupportedInCurrentState = 0x82;
48 static constexpr Cc ipmiCCPayloadPayloadInComplete = 0x83;
49 static constexpr Cc ipmiCCBIOSCapabilityInitNotDone = 0x85;
50 static constexpr Cc ipmiCCPayloadLengthIllegal = 0x85;
51 
52 static constexpr uint8_t userPasswordChanged = (1 << 5);
53 static constexpr uint8_t adminPasswordChanged = (1 << 4);
54 
55 static constexpr const char* biosConfigFolder = "/var/oob";
56 static constexpr const char* biosConfigNVPath = "/var/oob/nvoobdata.dat";
57 static constexpr const uint8_t algoSHA384 = 2;
58 static constexpr const uint8_t algoSHA256 = 1;
59 static constexpr const uint8_t biosCapOffsetBit = 0x3;
60 static constexpr uint16_t maxGetPayloadDataSize = 4096;
61 static constexpr const char* biosXMLFilePath = "/var/oob/bios.xml";
62 static constexpr const char* biosXMLFilePath1 = "/var/oob/tempbios.xml";
63 
64 static constexpr const char* biosConfigBaseMgrPath =
65     "/xyz/openbmc_project/bios_config/manager";
66 static constexpr const char* biosConfigIntf =
67     "xyz.openbmc_project.BIOSConfig.Manager";
68 static constexpr const char* resetBIOSSettingsProp = "ResetBIOSSettings";
69 /*baseBIOSTable
70 map{attributeName,struct{attributeType,readonlyStatus,displayname,
71               description,menuPath,current,default,
72               array{struct{optionstring,optionvalue}}}}
73 */
74 
75 bios::BiosBaseTableType attributesData;
76 
77 NVOOBdata gNVOOBdata;
78 
79 enum class PTState : uint8_t
80 {
81     StartTransfer = 0,
82     InProgress = 1,
83     EndTransfer = 2,
84     UserAbort = 3
85 };
86 enum class PStatus : uint8_t
87 {
88     Unknown = 0,
89     Valid = 1,
90     Corrupted = 2
91 };
92 enum class PType : uint8_t
93 {
94     IntelXMLType0 = 0,
95     IntelXMLType1 = 1,
96     OTAPayload = 5,
97 };
98 
99 //
100 // GetPayload Payload status enumeration
101 //
102 enum class GetPayloadParameter : uint8_t
103 {
104     GetPayloadInfo = 0, // 0
105     GetPayloadData = 1, // 1
106     GetPayloadStatus = 2
107 };
108 
109 /** @brief implement to set the BaseBIOSTable property
110  *  @returns status
111  */
112 static bool sendAllAttributes(std::string service)
113 {
114     std::shared_ptr<sdbusplus::asio::connection> pSdBusPlus = getSdBus();
115 
116     if (pSdBusPlus)
117     {
118         try
119         {
120             pSdBusPlus->async_method_call(
121                 [](const boost::system::error_code ec) {
122                     /* No more need to keep attributes data in memory */
123                     attributesData.clear();
124 
125                     if (ec)
126                     {
127                         phosphor::logging::log<phosphor::logging::level::ERR>(
128                             "sendAllAttributes error: send all attributes - "
129                             "failed");
130                         return;
131                     }
132 
133                     phosphor::logging::log<phosphor::logging::level::INFO>(
134                         "sendAllAttributes: send all attributes - done");
135                 },
136                 service, biosConfigBaseMgrPath,
137                 "org.freedesktop.DBus.Properties", "Set", biosConfigIntf,
138                 "BaseBIOSTable",
139                 std::variant<bios::BiosBaseTableType>(attributesData));
140 
141             return true;
142         }
143         catch (std::exception& ex)
144         {
145             phosphor::logging::log<phosphor::logging::level::ERR>(ex.what());
146         }
147     }
148 
149     return false;
150 }
151 
152 /** @brief implement to flush the updated data in nv space
153  *  @returns status
154  */
155 static uint8_t flushNVOOBdata()
156 {
157     std::ofstream outFile(biosConfigNVPath, std::ios::binary);
158     if (outFile.good())
159     {
160         outFile.seekp(std::ios_base::beg);
161         const char* writedata = reinterpret_cast<const char*>(&gNVOOBdata);
162         outFile.write(writedata, sizeof(struct NVOOBdata));
163         outFile.close();
164     }
165     return 0;
166 }
167 
168 /** @brief implement to get the System State
169  *  @returns status
170  */
171 
172 static int getSystemOSState(std::string& OsStatus)
173 {
174 
175     try
176     {
177         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
178         Value variant =
179             getDbusProperty(*dbus, "xyz.openbmc_project.State.OperatingSystem",
180                             "/xyz/openbmc_project/state/os",
181                             "xyz.openbmc_project.State.OperatingSystem.Status",
182                             "OperatingSystemState");
183         OsStatus = std::get<std::string>(variant);
184         return ipmi::ccSuccess;
185     }
186     catch (const std::exception& e)
187     {
188         return ipmi::ccUnspecifiedError;
189     }
190 }
191 
192 /** @brief implement to get the Rest BIOS property
193  *  @returns status
194  */
195 static int getResetBIOSSettings(uint8_t& ResetFlag)
196 {
197 
198     try
199     {
200         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
201         std::string service =
202             getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath);
203         Value variant = getDbusProperty(*dbus, service, biosConfigBaseMgrPath,
204                                         biosConfigIntf, resetBIOSSettingsProp);
205 
206         std::string_view ResetStr = std::get<std::string>(variant);
207         if (ResetStr ==
208             "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag.NoAction")
209         {
210             ResetFlag = 0;
211         }
212         else if (ResetStr == "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag."
213                              "FactoryDefaults")
214         {
215             ResetFlag = 1;
216         }
217         else if (ResetStr == "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag."
218                              "FailSafeDefaults")
219         {
220             ResetFlag = 2;
221         }
222         else
223         {
224             return ipmi::ccUnspecifiedError;
225         }
226 
227         return ipmi::ccSuccess;
228     }
229     catch (const std::exception& e)
230     {
231         return ipmi::ccUnspecifiedError;
232     }
233 }
234 
235 /** @brief Get attributes data (bios base table) from bios.xml
236  */
237 static bool generateAttributesData()
238 {
239     try
240     {
241         bios::Xml biosxml(biosXMLFilePath);
242 
243         if (!biosxml.doDepexCompute())
244         {
245             phosphor::logging::log<phosphor::logging::level::ERR>(
246                 "'depex' compute failed");
247         }
248 
249         if (!biosxml.getBaseTable(attributesData))
250         {
251             phosphor::logging::log<phosphor::logging::level::ERR>(
252                 "Failed to get bios base table");
253         }
254     }
255     catch (std::exception& ex)
256     {
257         phosphor::logging::log<phosphor::logging::level::ERR>(ex.what());
258         return false;
259     }
260 
261     return true;
262 }
263 
264 /** @brief Generate attributes data from bios.xml
265  * and send attributes data (bios base table) to dbus using set method.
266  */
267 static void generateAndSendAttributesData(std::string service,
268                                           uint8_t payloadType)
269 {
270     if (!generateAttributesData())
271     {
272         phosphor::logging::log<phosphor::logging::level::ERR>(
273             "generateAndSendAttributesData: generateAttributesData - failed");
274         gNVOOBdata.payloadInfo[payloadType].payloadStatus =
275             static_cast<uint8_t>(ipmi::PStatus::Corrupted);
276         return;
277     }
278 
279     phosphor::logging::log<phosphor::logging::level::INFO>(
280         "generateAndSendAttributesData : generateAttributesData is done");
281 
282     if (!sendAllAttributes(service))
283     {
284         phosphor::logging::log<phosphor::logging::level::ERR>(
285             "generateAndSendAttributesData: sendAllAttributes - failed");
286         gNVOOBdata.payloadInfo[payloadType].payloadStatus =
287             static_cast<uint8_t>(ipmi::PStatus::Corrupted);
288         return;
289     }
290 
291     phosphor::logging::log<phosphor::logging::level::INFO>(
292         "generateAndSendAttributesData : sendAllAttributes is done");
293     gNVOOBdata.payloadInfo[payloadType].payloadStatus =
294         static_cast<uint8_t>(ipmi::PStatus::Valid);
295 }
296 
297 /** @brief implement executing the linux command to uncompress and generate the
298  * xmlfile
299  *  @param[in] linux command
300  *  @returns status
301  */
302 template <typename... ArgTypes>
303 static int generateBIOSXMLFile(const char* path, ArgTypes&&... tArgs)
304 {
305 
306     boost::process::child execProg(path, const_cast<char*>(tArgs)...,
307                                    boost::process::std_out > biosXMLFilePath);
308     execProg.wait();
309     return execProg.exit_code();
310 }
311 
312 /** @brief implements to clean up the temp/ existing payload file
313  **/
314 static void cleanUpPayloadFile(uint8_t& payloadType)
315 {
316     // Clear the payload Information
317     std::string FilePath = "/var/oob/temp" + std::to_string(payloadType);
318     unlink(FilePath.c_str());
319     FilePath = "/var/oob/Payload" + std::to_string(payloadType);
320     unlink(FilePath.c_str());
321     if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0))
322     {
323         unlink("/var/oob/Payload1");
324         gNVOOBdata.payloadInfo[static_cast<uint8_t>(ipmi::PType::IntelXMLType1)]
325             .payloadStatus = static_cast<uint8_t>(ipmi::PStatus::Unknown);
326     }
327 }
328 
329 /** @brief implements to create the oob folders and nv space
330  **/
331 static Cc InitNVOOBdata()
332 {
333     FILE* fptr;
334     uint16_t size;
335 
336     if (!(std::filesystem::exists(biosConfigFolder)))
337     {
338         std::filesystem::create_directory(biosConfigFolder);
339     }
340 
341     std::ifstream ifs(biosConfigNVPath, std::ios::in | std::ios::binary);
342 
343     if (ifs.good())
344     {
345 
346         ifs.seekg(std::ios_base::beg);
347         ifs.read(reinterpret_cast<char*>(&gNVOOBdata),
348                  sizeof(struct NVOOBdata));
349         ifs.close();
350         return ipmi::ccSuccess;
351     }
352     return ipmi::ccResponseError;
353 }
354 
355 /** @brief implements check the command interface is
356  ** system interface or not
357  **  true mean System interface and false mean LAN or IPMB
358  **/
359 static bool IsSystemInterface(ipmi::Context::ptr ctx)
360 {
361     ChannelInfo chInfo;
362     Cc status = false;
363 
364     try
365     {
366         getChannelInfo(ctx->channel, chInfo);
367     }
368     catch (sdbusplus::exception_t& e)
369     {
370         return false;
371     }
372     if (chInfo.mediumType !=
373         static_cast<uint8_t>(EChannelMediumType::systemInterface))
374     {
375         return false;
376     }
377     return true;
378 }
379 
380 ipmi::RspType<> ipmiOEMSetBIOSCap(ipmi::Context::ptr ctx,
381                                   uint8_t BIOSCapabilties, uint8_t reserved1,
382                                   uint8_t reserved2, uint8_t reserved3)
383 {
384     std::string OSState;
385     getSystemOSState(OSState);
386 
387     if (OSState != "OperatingState" && IsSystemInterface(ctx))
388     {
389         if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0)
390         {
391             return ipmi::responseInvalidFieldRequest();
392         }
393 
394         gNVOOBdata.mBIOSCapabilities.OOBCapability = BIOSCapabilties;
395         gNVOOBdata.mIsBIOSCapInitDone = true;
396 
397         flushNVOOBdata();
398         return ipmi::responseSuccess();
399     }
400     else
401     {
402 
403         return ipmi::response(ipmiCCNotSupportedInCurrentState);
404     }
405 }
406 
407 ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t>
408     ipmiOEMGetBIOSCap(ipmi::Context::ptr ctx)
409 {
410     if (gNVOOBdata.mIsBIOSCapInitDone)
411     {
412         return ipmi::responseSuccess(gNVOOBdata.mBIOSCapabilities.OOBCapability,
413                                      0, 0, 0);
414     }
415     else
416     {
417         return ipmi::response(ipmiCCBIOSCapabilityInitNotDone);
418     }
419 }
420 
421 ipmi::RspType<uint32_t> ipmiOEMSetPayload(ipmi::Context::ptr ctx,
422                                           uint8_t paramSel, uint8_t payloadType,
423                                           std::vector<uint8_t> payload)
424 {
425     uint8_t biosCapOffsetBit = 2; // BIT:1 0-OOB BIOS config not supported
426                                   //      1-OOB BIOS config is supported
427 
428     if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit)))
429     {
430         return ipmi::response(ipmiCCBIOSCapabilityInitNotDone);
431     }
432     // Validate the Payload Type
433     if (payloadType > maxPayloadSupported)
434     {
435         return ipmi::responseInvalidFieldRequest();
436     }
437 
438     // We should support this Payload type 0 command only in KCS Interface
439     if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0))
440     {
441         std::string OSState;
442 
443         getSystemOSState(OSState);
444         if (!IsSystemInterface(ctx) || OSState == "OperatingState")
445         {
446             return ipmi::responseCommandNotAvailable();
447         }
448     }
449 
450     switch (static_cast<PTState>(paramSel))
451     {
452         case ipmi::PTState::StartTransfer:
453         {
454             PayloadStartTransfer* pPayloadStartTransfer =
455                 reinterpret_cast<PayloadStartTransfer*>(payload.data());
456             if (payload.size() < sizeof(PayloadStartTransfer))
457             {
458                 phosphor::logging::log<phosphor::logging::level::ERR>(
459                     "ipmiOEMSetPayload: BIOS Config Payload size is not "
460                     "correct");
461                 return ipmi::responseReqDataLenInvalid();
462             }
463             cleanUpPayloadFile(payloadType);
464 
465             gNVOOBdata.payloadInfo[payloadType].payloadReservationID = rand();
466             gNVOOBdata.payloadInfo[payloadType].payloadTotalChecksum =
467                 pPayloadStartTransfer->payloadTotalChecksum;
468             gNVOOBdata.payloadInfo[payloadType].payloadTotalSize =
469                 pPayloadStartTransfer->payloadTotalSize;
470             gNVOOBdata.payloadInfo[payloadType].payloadVersion =
471                 pPayloadStartTransfer->payloadVersion;
472             gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten = 0;
473             gNVOOBdata.payloadInfo[payloadType].payloadStatus =
474                 static_cast<uint8_t>(ipmi::PStatus::Unknown);
475             gNVOOBdata.payloadInfo[payloadType].payloadType = payloadType;
476 
477             return ipmi::responseSuccess(
478                 gNVOOBdata.payloadInfo[payloadType].payloadReservationID);
479         }
480         break;
481 
482         case ipmi::PTState::InProgress:
483         {
484             PayloadInProgress* pPayloadInProgress =
485                 reinterpret_cast<PayloadInProgress*>(payload.data());
486             PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType];
487 
488             if (payload.size() < sizeof(PayloadInProgress))
489             {
490                 phosphor::logging::log<phosphor::logging::level::ERR>(
491                     "BIOS Config Payload size is not correct");
492                 return ipmi::responseReqDataLenInvalid();
493             }
494 
495             if (pPayloadInProgress->payloadReservationID !=
496                 payloadInfo.payloadReservationID)
497             {
498                 return ipmi::responseInvalidReservationId();
499             }
500             payloadInfo.payloadCurrentSize =
501                 pPayloadInProgress->payloadCurrentSize;
502             // Need to verify the current Payload Checksum
503             const uint8_t* data =
504                 reinterpret_cast<const uint8_t*>(payload.data());
505             // we have to remove the current size, current offset, current
506             // length,checksum bytes , reservation bytes
507             boost::crc_32_type calcChecksum;
508             calcChecksum.process_bytes(data + 16, payload.size() - 16);
509             if (calcChecksum.checksum() !=
510                 pPayloadInProgress->payloadCurrentChecksum)
511             {
512                 phosphor::logging::log<phosphor::logging::level::ERR>(
513                     "ipmiOEMSetPayload: Payload Checksum Failed");
514                 return ipmi::response(ipmiCCPayloadChecksumFailed);
515             }
516             // store the data in temp file
517             std::string FilePath =
518                 "/var/oob/temp" + std::to_string(payloadType);
519 
520             std::ofstream outFile(FilePath, std::ios::binary | std::ios::app);
521             outFile.seekp(pPayloadInProgress->payloadOffset);
522             // we have to remove the current size, current offset, current
523             // length,checksum bytes , reservation bytes
524 
525             const char* writedata =
526                 reinterpret_cast<const char*>(payload.data());
527             outFile.write(writedata + 16, payload.size() - 16);
528             outFile.close();
529 
530             gNVOOBdata.payloadInfo[payloadType].payloadStatus =
531                 static_cast<uint8_t>(ipmi::PStatus::Unknown);
532             gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten +=
533                 payloadInfo.payloadCurrentSize;
534             return ipmi::responseSuccess(payloadInfo.payloadCurrentSize);
535         }
536         break;
537         case ipmi::PTState::EndTransfer:
538         {
539             PayloadEndTransfer* pPayloadEndTransfer =
540                 reinterpret_cast<PayloadEndTransfer*>(payload.data());
541             PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType];
542             if (pPayloadEndTransfer->payloadReservationID !=
543                 payloadInfo.payloadReservationID)
544             {
545                 return ipmi::responseInvalidReservationId();
546             }
547             gNVOOBdata.payloadInfo[payloadType].payloadStatus =
548                 static_cast<uint8_t>(ipmi::PStatus::Unknown);
549 
550             if (gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten !=
551                 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize)
552             {
553 
554                 return ipmi::response(ipmiCCPayloadPayloadInComplete);
555             }
556             std::string tempFilePath =
557                 "/var/oob/temp" + std::to_string(payloadType);
558             std::string payloadFilePath =
559                 "/var/oob/Payload" + std::to_string(payloadType);
560             auto renamestatus =
561                 std::rename(tempFilePath.c_str(), payloadFilePath.c_str());
562             if (renamestatus)
563             {
564                 phosphor::logging::log<phosphor::logging::level::ERR>(
565                     "ipmiOEMSetPayload: Renaming Payload file - failed");
566             }
567 
568             if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0))
569             {
570                 // Unzip the Intel format XML file type 0
571                 auto response = generateBIOSXMLFile("/usr/bin/lzcat", "-d",
572                                                     payloadFilePath.c_str());
573                 if (response)
574                 {
575 
576                     phosphor::logging::log<phosphor::logging::level::ERR>(
577                         "ipmiOEMSetPayload: generateBIOSXMLFile - failed");
578                     gNVOOBdata.payloadInfo[payloadType].payloadStatus =
579                         static_cast<uint8_t>(ipmi::PStatus::Corrupted);
580                     return ipmi::response(ipmiCCPayloadPayloadPacketMissed);
581                 }
582                 phosphor::logging::log<phosphor::logging::level::INFO>(
583                     " ipmiOEMSetPayload : Convert XML into native-dbus DONE");
584 
585                 /* So that we don't block the call */
586                 auto io = getIoContext();
587                 auto dbus = getSdBus();
588                 if (io && dbus)
589                 {
590                     std::string service = getService(*dbus, biosConfigIntf,
591                                                      biosConfigBaseMgrPath);
592 
593                     boost::asio::post(*io, [service, payloadType] {
594                         generateAndSendAttributesData(service, payloadType);
595                     });
596                 }
597                 else
598                 {
599                     phosphor::logging::log<phosphor::logging::level::INFO>(
600                         "ipmiOEMSetPayload: Unable to get io context or sdbus");
601                     return ipmi::responseResponseError();
602                 }
603             }
604 
605             struct stat filestat;
606 
607             /* Get entry's information. */
608             if (!stat(payloadFilePath.c_str(), &filestat))
609             {
610                 gNVOOBdata.payloadInfo[payloadType].payloadTimeStamp =
611                     filestat.st_mtime;
612                 gNVOOBdata.payloadInfo[payloadType].payloadTotalSize =
613                     filestat.st_size;
614             }
615             else
616             {
617                 return ipmi::responseResponseError();
618             }
619             flushNVOOBdata();
620             return ipmi::responseSuccess(
621                 gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten);
622         }
623         break;
624         case ipmi::PTState::UserAbort:
625         {
626             PayloadEndTransfer* pPayloadEndTransfer =
627                 reinterpret_cast<PayloadEndTransfer*>(payload.data());
628             PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType];
629             if (pPayloadEndTransfer->payloadReservationID !=
630                 payloadInfo.payloadReservationID)
631             {
632                 return ipmi::responseInvalidReservationId();
633             }
634             gNVOOBdata.payloadInfo[payloadType].payloadReservationID = 0;
635             gNVOOBdata.payloadInfo[payloadType].payloadType = 0;
636             gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 0;
637             // Delete the temp file
638             std::string tempFilePath =
639                 "/var/oob/temp" + std::to_string(payloadType);
640             unlink(tempFilePath.c_str());
641             flushNVOOBdata();
642             return ipmi::responseSuccess();
643         }
644         break;
645         default:
646             return ipmi::responseInvalidFieldRequest();
647     }
648     return ipmi::responseResponseError();
649 }
650 
651 ipmi::RspType<message::Payload>
652     ipmiOEMGetPayload(ipmi::Context::ptr ctx, uint8_t paramSel,
653                       uint8_t payloadType, ipmi::message::Payload& payload)
654 {
655     //      1-OOB BIOS config is supported
656     message::Payload retValue;
657 
658     if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit)))
659     {
660         return ipmi::response(ipmiCCBIOSCapabilityInitNotDone);
661     }
662     // Validate the Payload Type
663     if (payloadType > maxPayloadSupported)
664     {
665         return ipmi::responseInvalidFieldRequest();
666     }
667 
668     struct PayloadInfo res = gNVOOBdata.payloadInfo[payloadType];
669 
670     switch (static_cast<GetPayloadParameter>(paramSel))
671     {
672         case ipmi::GetPayloadParameter::GetPayloadInfo:
673         {
674 
675             std::string payloadFilePath =
676                 "/var/oob/Payload" + std::to_string(payloadType);
677 
678             std::ifstream ifs(payloadFilePath,
679                               std::ios::in | std::ios::binary | std::ios::ate);
680 
681             if (!ifs.good())
682             {
683 
684                 phosphor::logging::log<phosphor::logging::level::ERR>(
685                     "ipmiOEMGetPayload: Payload File Error");
686                 // File does not exist code here
687                 return ipmi::response(ipmi::ccUnspecifiedError);
688             }
689             ifs.close();
690             retValue.pack(res.payloadVersion);
691             retValue.pack(payloadType);
692             retValue.pack(res.payloadTotalSize);
693             retValue.pack(res.payloadTotalChecksum);
694             retValue.pack(res.payloadflag);
695             retValue.pack(res.payloadStatus);
696             retValue.pack(res.payloadTimeStamp);
697 
698             return ipmi::responseSuccess(std::move(retValue));
699         }
700 
701         break;
702         case ipmi::GetPayloadParameter::GetPayloadData:
703         {
704             if (res.payloadStatus ==
705                 (static_cast<uint8_t>(ipmi::PStatus::Valid)))
706             {
707                 std::vector<uint32_t> reqData;
708                 if (payload.unpack(reqData) || !payload.fullyUnpacked())
709                 {
710                     return ipmi::responseReqDataLenInvalid();
711                 }
712                 uint32_t offset = reqData.at(0);
713                 uint32_t length = reqData.at(1);
714                 std::string payloadFilePath =
715                     "/var/oob/Payload" + std::to_string(payloadType);
716 
717                 std::ifstream ifs(payloadFilePath, std::ios::in |
718                                                        std::ios::binary |
719                                                        std::ios::ate);
720 
721                 if (!ifs.good())
722                 {
723 
724                     phosphor::logging::log<phosphor::logging::level::ERR>(
725                         "ipmiOEMGetPayload: Payload File Error");
726                     // File does not exist code here
727                     return ipmi::response(ipmi::ccUnspecifiedError);
728                 }
729                 std::ifstream::pos_type fileSize = ifs.tellg();
730                 // Total file data within given offset
731                 if (fileSize < static_cast<uint64_t>(offset))
732                 {
733                     ifs.close();
734                     return ipmi::responseInvalidFieldRequest();
735                 }
736 
737                 ifs.seekg(offset, std::ios::beg);
738                 std::array<uint8_t, maxGetPayloadDataSize> Buffer;
739                 ifs.read(reinterpret_cast<char*>(Buffer.data()), length);
740                 uint32_t readCount = ifs.gcount();
741                 ifs.close();
742 
743                 boost::crc_32_type calcChecksum;
744                 calcChecksum.process_bytes(
745                     reinterpret_cast<char*>(Buffer.data()), readCount);
746                 uint32_t chkSum = calcChecksum.checksum();
747                 retValue.pack(payloadType);
748                 retValue.pack(readCount);
749                 retValue.pack(chkSum);
750 
751                 for (int i = 0; i < readCount; i++)
752                 {
753                     retValue.pack(Buffer.at(i));
754                 }
755                 return ipmi::responseSuccess(std::move(retValue));
756             }
757             else
758             {
759                 return ipmi::responseResponseError();
760             }
761         }
762         break;
763         case ipmi::GetPayloadParameter::GetPayloadStatus:
764         {
765             retValue.pack(gNVOOBdata.payloadInfo[payloadType].payloadStatus);
766             return ipmi::responseSuccess(std::move(retValue));
767         }
768         break;
769         default:
770             return ipmi::responseInvalidFieldRequest();
771     }
772     return ipmi::responseInvalidFieldRequest();
773 }
774 
775 ipmi::RspType<> ipmiOEMSetBIOSHashInfo(
776     ipmi::Context::ptr ctx, std::array<uint8_t, maxSeedSize>& pwdSeed,
777     uint8_t algoInfo, std::array<uint8_t, maxHashSize>& adminPwdHash)
778 {
779 
780     std::string OSState;
781 
782     // We should support this command only in KCS Interface
783     if (!IsSystemInterface(ctx))
784     {
785         return ipmi::responseCommandNotAvailable();
786     }
787     getSystemOSState(OSState);
788     // We should not support this command after System Booted - After Exit Boot
789     // service called
790 
791     if (OSState == "OperatingState")
792     {
793         return ipmi::response(ipmiCCNotSupportedInCurrentState);
794     }
795 
796     nlohmann::json json;
797 
798     if ((algoInfo & 0xF) == algoSHA384)
799     {
800         json["HashAlgo"] = "SHA384";
801     }
802     else if ((algoInfo & 0xF) == algoSHA256)
803     {
804         json["HashAlgo"] = "SHA256";
805     }
806     else
807     {
808         return ipmi::responseInvalidFieldRequest();
809     }
810 
811     json["Seed"] = pwdSeed;
812     json["IsAdminPwdChanged"] = false;
813     json["AdminPwdHash"] = adminPwdHash;
814     json["IsUserPwdChanged"] = false;
815 
816     std::array<uint8_t, maxHashSize> userPwdHash;
817     userPwdHash.fill({}); // initializing with 0 as user password hash field
818                           // is not used presently
819     json["UserPwdHash"] = userPwdHash;
820     json["StatusFlag"] = algoInfo;
821 
822     std::string hashFilePath = "/var/lib/bios-settings-manager/seedData";
823     std::ofstream ofs(hashFilePath, std::ios::out);
824     const auto& writeData = json.dump();
825     ofs << writeData;
826     ofs.close();
827     return ipmi::responseSuccess();
828 }
829 
830 ipmi::RspType<std::array<uint8_t, maxSeedSize>, uint8_t,
831               std::array<uint8_t, maxHashSize>>
832     ipmiOEMGetBIOSHash(ipmi::Context::ptr ctx)
833 {
834 
835     std::string OSState;
836     nlohmann::json data = nullptr;
837 
838     // We should support this command only in KCS Interface
839     if (!IsSystemInterface(ctx))
840     {
841         return ipmi::responseCommandNotAvailable();
842     }
843 
844     getSystemOSState(OSState);
845     // We should not support this command after System Booted - After Exit Boot
846     // service called
847 
848     if (OSState != "OperatingState")
849     {
850         std::string HashFilePath = "/var/lib/bios-settings-manager/seedData";
851 
852         std::ifstream devIdFile(HashFilePath);
853         if (devIdFile.is_open())
854         {
855 
856             try
857             {
858                 data = nlohmann::json::parse(devIdFile, nullptr, false);
859             }
860             catch (const nlohmann::json::parse_error& e)
861             {
862                 return ipmi::responseResponseError();
863             }
864 
865             if (data.is_discarded())
866             {
867                 return ipmi::responseResponseError();
868             }
869 
870             std::array<uint8_t, maxHashSize> newAdminHash;
871             std::array<uint8_t, maxSeedSize> seed;
872 
873             uint8_t flag = 0;
874             uint8_t adminPwdChangedFlag = 0;
875             if (!data.is_discarded())
876             {
877 
878                 adminPwdChangedFlag = data["IsAdminPwdChanged"];
879                 newAdminHash = data["AdminPwdHash"];
880                 seed = data["Seed"];
881             }
882 
883             auto status = getResetBIOSSettings(flag);
884             if (status)
885             {
886                 return ipmi::responseResponseError();
887             }
888             if (adminPwdChangedFlag)
889             {
890                 flag |= adminPasswordChanged;
891             }
892 
893             std::copy(std::begin(newAdminHash), std::end(newAdminHash),
894                       std::begin(newAdminHash));
895 
896             return ipmi::responseSuccess(seed, flag, newAdminHash);
897         }
898         else
899         {
900             return ipmi::responseResponseError();
901         }
902     }
903     else
904     {
905 
906         return ipmi::response(ipmiCCNotSupportedInCurrentState);
907     }
908 }
909 
910 static void registerBIOSConfigFunctions(void)
911 {
912     phosphor::logging::log<phosphor::logging::level::INFO>(
913         "BIOSConfig module initialization");
914     InitNVOOBdata();
915 
916     registerHandler(prioOemBase, intel::netFnGeneral,
917                     intel::general::cmdSetBIOSCap, Privilege::Admin,
918                     ipmiOEMSetBIOSCap);
919 
920     registerHandler(prioOemBase, intel::netFnGeneral,
921                     intel::general::cmdGetBIOSCap, Privilege::User,
922                     ipmiOEMGetBIOSCap);
923     registerHandler(prioOemBase, intel::netFnGeneral,
924                     intel::general::cmdSetBIOSPwdHashInfo, Privilege::Admin,
925                     ipmiOEMSetBIOSHashInfo);
926 
927     registerHandler(prioOemBase, intel::netFnGeneral,
928                     intel::general::cmdGetBIOSPwdHash, Privilege::User,
929                     ipmiOEMGetBIOSHash);
930 
931     registerHandler(prioOemBase, intel::netFnGeneral,
932                     intel::general::cmdGetPayload, Privilege::User,
933                     ipmiOEMGetPayload);
934     registerHandler(prioOemBase, intel::netFnGeneral,
935                     intel::general::cmdSetPayload, Privilege::Admin,
936                     ipmiOEMSetPayload);
937 
938     return;
939 }
940 
941 } // namespace ipmi
942