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