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