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