1 #include <byteswap.h>
2 #include <ipmid/api.h>
3 #include <openssl/evp.h>
4 #include <openssl/sha.h>
5 #include <sys/mman.h>
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9
10 #include <appcommands.hpp>
11 #include <boost/algorithm/string.hpp>
12 #include <boost/container/flat_map.hpp>
13 #include <boost/process/child.hpp>
14 #include <boost/uuid/random_generator.hpp>
15 #include <boost/uuid/uuid_io.hpp>
16 #include <commandutils.hpp>
17 #include <ipmid/api.hpp>
18 #include <ipmid/utils.hpp>
19 #include <phosphor-logging/log.hpp>
20 #include <sdbusplus/bus.hpp>
21 #include <sdbusplus/bus/match.hpp>
22 #include <sdbusplus/server/object.hpp>
23 #include <sdbusplus/timer.hpp>
24 #include <types.hpp>
25
26 #include <chrono>
27 #include <cstdint>
28 #include <filesystem>
29 #include <fstream>
30 #include <iostream>
31 #include <map>
32 #include <random>
33 #ifdef INTEL_PFR_ENABLED
34 #include <spiDev.hpp>
35 #endif
36
37 static constexpr int openSslSuccess = 1;
38 static constexpr bool DEBUG = true;
39 static void registerFirmwareFunctions() __attribute__((constructor));
40
41 namespace ipmi
42 {
43 namespace firmware
44 {
45 constexpr Cmd cmdGetFwVersionInfo = 0x20;
46 constexpr Cmd cmdGetFwSecurityVersionInfo = 0x21;
47 constexpr Cmd cmdGetFwUpdateChannelInfo = 0x22;
48 constexpr Cmd cmdGetBmcExecutionContext = 0x23;
49 constexpr Cmd cmdFwGetRootCertData = 0x25;
50 constexpr Cmd cmdGetFwUpdateRandomNumber = 0x26;
51 constexpr Cmd cmdSetFirmwareUpdateMode = 0x27;
52 constexpr Cmd cmdExitFirmwareUpdateMode = 0x28;
53 constexpr Cmd cmdGetSetFwUpdateControl = 0x29;
54 constexpr Cmd cmdGetFirmwareUpdateStatus = 0x2A;
55 constexpr Cmd cmdSetFirmwareUpdateOptions = 0x2B;
56 constexpr Cmd cmdFwImageWriteData = 0x2c;
57 } // namespace firmware
58 } // namespace ipmi
59
60 namespace ipmi
61 {
62 // Custom completion codes
63 constexpr Cc ccUsbAttachOrDetachFailed = 0x80;
64 constexpr Cc ccNotSupportedInPresentState = 0xD5;
65
responseUsbAttachOrDetachFailed()66 static inline auto responseUsbAttachOrDetachFailed()
67 {
68 return response(ccUsbAttachOrDetachFailed);
69 }
responseNotSupportedInPresentState()70 static inline auto responseNotSupportedInPresentState()
71 {
72 return response(ccNotSupportedInPresentState);
73 }
74 } // namespace ipmi
75
76 static constexpr size_t imageCount = 2;
77 std::array<std::array<uint8_t, imageCount>, imageCount> imgFwSecurityVersion =
78 {};
79 static constexpr size_t svnActiveVerOffsetInPfm = 0x404;
80 static constexpr size_t bkcActiveVerOffsetInPfm = 0x405;
81 static constexpr size_t svnRecoveryVerOffsetInPfm = 0x804;
82 static constexpr size_t bkcRecoveryVerOffsetInPfm = 0x805;
83 static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC";
84 static constexpr const char* bmcStatePath = "/xyz/openbmc_project/state/bmc0";
85 static constexpr const char* bmcStateReady =
86 "xyz.openbmc_project.State.BMC.BMCState.Ready";
87 static constexpr const char* bmcStateUpdateInProgress =
88 "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
89
90 static constexpr char firmwareBufferFile[] = "/tmp/fw-download.bin";
91 static std::chrono::steady_clock::time_point fwRandomNumGenTs;
92 static constexpr auto fwRandomNumExpirySeconds = std::chrono::seconds(30);
93 static constexpr size_t fwRandomNumLength = 8;
94 static std::array<uint8_t, fwRandomNumLength> fwRandomNum;
95 constexpr char usbCtrlPath[] = "/usr/bin/usb-ctrl";
96 constexpr char fwUpdateMountPoint[] = "/tmp/usb-fwupd.mnt";
97 constexpr char fwUpdateUsbVolImage[] = "/tmp/usb-fwupd.img";
98 constexpr char fwUpdateUSBDevName[] = "fw-usb-mass-storage-dev";
99 constexpr size_t fwPathMaxLength = 255;
100
101 #ifdef INTEL_PFR_ENABLED
102 uint32_t imgLength = 0;
103 uint32_t imgType = 0;
104 bool block0Mapped = false;
105 static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
106
107 static constexpr const char* bmcActivePfmMTDDev = "/dev/mtd/pfm";
108 static constexpr const char* bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
109 static constexpr size_t pfmBaseOffsetInImage = 0x400;
110 static constexpr size_t rootkeyOffsetInPfm = 0xA0;
111 static constexpr size_t cskKeyOffsetInPfm = 0x124;
112 static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
113 static constexpr size_t certKeyLen = 96;
114 static constexpr size_t cskSignatureLen = 96;
115
116 static constexpr const char* versionIntf =
117 "xyz.openbmc_project.Software.Version";
118
119 enum class FwGetRootCertDataTag : uint8_t
120 {
121 activeRootKey = 1,
122 recoveryRootKey,
123 activeCSK,
124 recoveryCSK,
125 };
126
127 enum class FWDeviceIDTag : uint8_t
128 {
129 bmcActiveImage = 1,
130 bmcRecoveryImage,
131 };
132
133 const static boost::container::flat_map<FWDeviceIDTag, const char*>
134 fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
135 "/xyz/openbmc_project/software/bmc_active"},
136 {FWDeviceIDTag::bmcRecoveryImage,
137 "/xyz/openbmc_project/software/bmc_recovery"}};
138 #endif // INTEL_PFR_ENABLED
139
140 enum class ChannelIdTag : uint8_t
141 {
142 reserved = 0,
143 kcs = 1,
144 ipmb = 2,
145 rmcpPlus = 3
146 };
147
148 enum class BmcExecutionContext : uint8_t
149 {
150 reserved = 0,
151 linuxOs = 0x10,
152 bootLoader = 0x11,
153 };
154
155 enum class FwUpdateCtrlReq : uint8_t
156 {
157 getCurrentControlStatus = 0x00,
158 imageTransferStart = 0x01,
159 imageTransferComplete = 0x02,
160 imageTransferAbort = 0x03,
161 setFirmwareFilename = 0x04,
162 attachUsbDevice = 0x05,
163 detachUsbDevice = 0x06
164 };
165
operator ""_MB(unsigned long long v)166 constexpr std::size_t operator""_MB(unsigned long long v)
167 {
168 return 1024u * 1024u * v;
169 }
170 static constexpr size_t maxFirmwareImageSize = 37_MB;
171
localDownloadInProgress(void)172 static bool localDownloadInProgress(void)
173 {
174 struct stat sb;
175 if (stat(firmwareBufferFile, &sb) < 0)
176 {
177 return false;
178 }
179 return true;
180 }
181
182 class TransferHashCheck
183 {
184 public:
185 enum class HashCheck : uint8_t
186 {
187 notRequested = 0,
188 requested,
189 sha2Success,
190 sha2Failed = 0xe2,
191 };
192
193 protected:
194 std::unique_ptr<EVP_MD_CTX, std::function<void(EVP_MD_CTX*)>> ctx;
195 std::vector<uint8_t> expectedHash;
196 HashCheck check;
197
198 public:
TransferHashCheck(const std::vector<uint8_t> & expected)199 TransferHashCheck(const std::vector<uint8_t>& expected) :
200 ctx(EVP_MD_CTX_new(), &EVP_MD_CTX_free), expectedHash(expected)
201 {
202 if (!ctx)
203 {
204 throw std::runtime_error("Unable to allocate for ctx.");
205 }
206
207 if (EVP_DigestInit(ctx.get(), EVP_sha256()) != openSslSuccess)
208 {
209 throw std::runtime_error("Unable to allocate for ctx.");
210 }
211
212 check = HashCheck::requested;
213 }
214
~TransferHashCheck()215 ~TransferHashCheck() {}
216
hash(const std::vector<uint8_t> & data)217 bool hash(const std::vector<uint8_t>& data)
218 {
219 if (EVP_DigestUpdate(ctx.get(), data.data(), data.size()) !=
220 openSslSuccess)
221 {
222 return false;
223 }
224
225 return true;
226 }
227
clear()228 bool clear()
229 {
230 /*
231 * EVP_DigestInit() always uses the default digest implementation and
232 * calls EVP_MD_CTX_reset().
233 */
234 if (EVP_DigestInit(ctx.get(), EVP_sha256()) != openSslSuccess)
235 {
236 return false;
237 }
238
239 return true;
240 }
241
verify()242 enum HashCheck verify()
243 {
244 unsigned int len = 0;
245 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
246
247 check = HashCheck::sha2Failed;
248
249 if (EVP_DigestFinal(ctx.get(), digest.data(), &len) == openSslSuccess)
250 {
251 if (digest == expectedHash)
252 {
253 phosphor::logging::log<phosphor::logging::level::INFO>(
254 "Transfer sha2 verify passed.");
255 check = HashCheck::sha2Success;
256 }
257 else
258 {
259 phosphor::logging::log<phosphor::logging::level::ERR>(
260 "Transfer sha2 verify failed.");
261 check = HashCheck::sha2Failed;
262 }
263 }
264
265 return check;
266 }
267
status() const268 uint8_t status() const
269 {
270 return static_cast<uint8_t>(check);
271 }
272 };
273
274 class MappedFile
275 {
276 public:
MappedFile(const std::string & fname)277 MappedFile(const std::string& fname) : addr(nullptr), fsize(0)
278 {
279 std::error_code ec;
280 size_t sz = std::filesystem::file_size(fname, ec);
281 if (!ec)
282 {
283 return;
284 }
285 int fd = open(fname.c_str(), O_RDONLY);
286 if (fd < 0)
287 {
288 return;
289 }
290 void* tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
291 close(fd);
292 if (tmp == MAP_FAILED)
293 {
294 return;
295 }
296 addr = tmp;
297 fsize = sz;
298 }
299
~MappedFile()300 ~MappedFile()
301 {
302 if (addr)
303 {
304 munmap(addr, fsize);
305 }
306 }
data() const307 const uint8_t* data() const
308 {
309 return static_cast<const uint8_t*>(addr);
310 }
size() const311 size_t size() const
312 {
313 return fsize;
314 }
315
316 private:
317 void* addr;
318 size_t fsize;
319 };
320
321 class FwUpdateStatusCache
322 {
323 public:
324 enum
325 {
326 fwStateInit = 0,
327 fwStateIdle,
328 fwStateDownload,
329 fwStateVerify,
330 fwStateProgram,
331 fwStateUpdateSuccess,
332 fwStateError = 0x0f,
333 fwStateAcCycleRequired = 0x83,
334 };
getState()335 uint8_t getState()
336 {
337 if ((fwUpdateState == fwStateIdle || fwUpdateState == fwStateInit) &&
338 localDownloadInProgress())
339 {
340 fwUpdateState = fwStateDownload;
341 progressPercent = 0;
342 }
343 return fwUpdateState;
344 }
resetStatusCache()345 void resetStatusCache()
346 {
347 unlink(firmwareBufferFile);
348 }
setState(const uint8_t state)349 void setState(const uint8_t state)
350 {
351 switch (state)
352 {
353 case fwStateInit:
354 case fwStateIdle:
355 case fwStateError:
356 resetStatusCache();
357 break;
358 case fwStateDownload:
359 case fwStateVerify:
360 case fwStateProgram:
361 case fwStateUpdateSuccess:
362 break;
363 default:
364 // Error
365 break;
366 }
367 fwUpdateState = state;
368 }
percent()369 uint8_t percent()
370 {
371 return progressPercent;
372 }
updateActivationPercent(const std::string & objPath)373 void updateActivationPercent(const std::string& objPath)
374 {
375 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
376 fwUpdateState = fwStateProgram;
377 progressPercent = 0;
378 match = std::make_shared<sdbusplus::bus::match_t>(
379 *busp,
380 sdbusplus::bus::match::rules::propertiesChanged(
381 objPath, "xyz.openbmc_project.Software.ActivationProgress"),
382 [&](sdbusplus::message_t& msg) {
383 std::map<std::string, ipmi::DbusVariant> props;
384 std::vector<std::string> inVal;
385 std::string iface;
386 try
387 {
388 msg.read(iface, props, inVal);
389 }
390 catch (const std::exception& e)
391 {
392 phosphor::logging::log<phosphor::logging::level::ERR>(
393 "Exception caught in get ActivationProgress");
394 return;
395 }
396
397 auto it = props.find("Progress");
398 if (it != props.end())
399 {
400 progressPercent = std::get<uint8_t>(it->second);
401 if (progressPercent == 100)
402 {
403 fwUpdateState = fwStateUpdateSuccess;
404 }
405 }
406 });
407 }
activationTimerTimeout()408 uint8_t activationTimerTimeout()
409 {
410 phosphor::logging::log<phosphor::logging::level::INFO>(
411 "activationTimerTimeout: Increase percentage...",
412 phosphor::logging::entry("PERCENT:%d", progressPercent));
413 progressPercent = progressPercent + 5;
414 if (progressPercent > 95)
415 {
416 /*changing the state to ready to update firmware utility */
417 fwUpdateState = fwStateUpdateSuccess;
418 }
419 return progressPercent;
420 }
421 /* API for changing state to ERROR */
firmwareUpdateAbortState()422 void firmwareUpdateAbortState()
423 {
424 unlink(firmwareBufferFile);
425 // changing the state to error
426 fwUpdateState = fwStateError;
427 }
setDeferRestart(bool deferRestart)428 void setDeferRestart(bool deferRestart)
429 {
430 deferRestartState = deferRestart;
431 }
setInhibitDowngrade(bool inhibitDowngrade)432 void setInhibitDowngrade(bool inhibitDowngrade)
433 {
434 inhibitDowngradeState = inhibitDowngrade;
435 }
getDeferRestart()436 bool getDeferRestart()
437 {
438 return deferRestartState;
439 }
getInhibitDowngrade()440 bool getInhibitDowngrade()
441 {
442 return inhibitDowngradeState;
443 }
444
445 protected:
446 std::shared_ptr<sdbusplus::asio::connection> busp;
447 std::shared_ptr<sdbusplus::bus::match_t> match;
448 uint8_t fwUpdateState = 0;
449 uint8_t progressPercent = 0;
450 bool deferRestartState = false;
451 bool inhibitDowngradeState = false;
452 };
453
454 static FwUpdateStatusCache fwUpdateStatus;
455 std::unique_ptr<TransferHashCheck> xferHashCheck;
456
activateImage(const std::string & objPath)457 static void activateImage(const std::string& objPath)
458 {
459 // If flag is false means to reboot
460 if (fwUpdateStatus.getDeferRestart() == false)
461 {
462 phosphor::logging::log<phosphor::logging::level::INFO>(
463 "activating Image: ",
464 phosphor::logging::entry("OBJPATH =%s", objPath.c_str()));
465 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
466 bus->async_method_call(
467 [](const boost::system::error_code ec) {
468 if (ec)
469 {
470 phosphor::logging::log<phosphor::logging::level::ERR>(
471 "async_method_call error: activateImage failed");
472 return;
473 }
474 },
475 "xyz.openbmc_project.Software.BMC.Updater", objPath,
476 "org.freedesktop.DBus.Properties", "Set",
477 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
478 ipmi::DbusVariant("xyz.openbmc_project.Software.Activation."
479 "RequestedActivations.Active"));
480 }
481 else
482 {
483 phosphor::logging::log<phosphor::logging::level::INFO>(
484 "Firmware image activation is deferred.");
485 }
486 fwUpdateStatus.setState(
487 static_cast<uint8_t>(FwUpdateStatusCache::fwStateUpdateSuccess));
488 }
489
getFirmwareUpdateMode()490 static bool getFirmwareUpdateMode()
491 {
492 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
493 try
494 {
495 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
496 ipmi::Value state = ipmi::getDbusProperty(
497 *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState");
498 std::string bmcState = std::get<std::string>(state);
499 return (bmcState == bmcStateUpdateInProgress);
500 }
501 catch (const std::exception& e)
502 {
503 phosphor::logging::log<phosphor::logging::level::ERR>(
504 "Exception caught while getting BMC state.",
505 phosphor::logging::entry("EXCEPTION=%s", e.what()));
506 throw;
507 }
508 }
509
setFirmwareUpdateMode(const bool mode)510 static void setFirmwareUpdateMode(const bool mode)
511 {
512 std::string bmcState(bmcStateReady);
513 if (mode)
514 {
515 bmcState = bmcStateUpdateInProgress;
516 }
517
518 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
519 try
520 {
521 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
522 ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf,
523 "CurrentBMCState", bmcState);
524 }
525 catch (const std::exception& e)
526 {
527 phosphor::logging::log<phosphor::logging::level::ERR>(
528 "Exception caught while setting BMC state.",
529 phosphor::logging::entry("EXCEPTION=%s", e.what()));
530 throw;
531 }
532 }
533
534 /** @brief check if channel IPMB
535 *
536 * This function checks if the command is from IPMB
537 *
538 * @param[in] ctx - context of current session.
539 * @returns true if the medium is IPMB else return true.
540 **/
checkIPMBChannel(const ipmi::Context::ptr & ctx,bool & isIPMBChannel)541 ipmi::Cc checkIPMBChannel(const ipmi::Context::ptr& ctx, bool& isIPMBChannel)
542 {
543 ipmi::ChannelInfo chInfo;
544
545 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
546 {
547 phosphor::logging::log<phosphor::logging::level::ERR>(
548 "Failed to get Channel Info",
549 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
550 return ipmi::ccUnspecifiedError;
551 }
552
553 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
554 ipmi::EChannelMediumType::ipmb)
555 {
556 isIPMBChannel = true;
557 }
558 return ipmi::ccSuccess;
559 }
560
postTransferCompleteHandler(std::unique_ptr<sdbusplus::bus::match_t> & fwUpdateMatchSignal)561 static void postTransferCompleteHandler(
562 std::unique_ptr<sdbusplus::bus::match_t>& fwUpdateMatchSignal)
563 {
564 // Setup timer for watching signal
565 static sdbusplus::Timer timer([&fwUpdateMatchSignal]() {
566 fwUpdateMatchSignal = nullptr;
567 });
568
569 static sdbusplus::Timer activationStatusTimer([]() {
570 if (fwUpdateStatus.activationTimerTimeout() > 95)
571 {
572 activationStatusTimer.stop();
573 fwUpdateStatus.setState(
574 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
575 }
576 });
577
578 timer.start(std::chrono::microseconds(5000000), false);
579
580 // callback function for capturing signal
581 auto callback = [&](sdbusplus::message_t& m) {
582 std::vector<
583 std::pair<std::string,
584 std::vector<std::pair<std::string, ipmi::DbusVariant>>>>
585 intfPropsPair;
586 sdbusplus::message::object_path objPath;
587
588 try
589 {
590 m.read(objPath, intfPropsPair); // Read in the object path
591 // that was just created
592 }
593 catch (const std::exception& e)
594 {
595 phosphor::logging::log<phosphor::logging::level::ERR>(
596 "Exception caught in reading created object path.");
597 return;
598 }
599 // constructing response message
600 phosphor::logging::log<phosphor::logging::level::INFO>(
601 "New Interface Added.",
602 phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
603 for (auto& interface : intfPropsPair)
604 {
605 if (interface.first == "xyz.openbmc_project.Software.Activation")
606 {
607 // There are chances of getting two signals for
608 // InterfacesAdded. So cross check and discrad second instance.
609 if (fwUpdateMatchSignal == nullptr)
610 {
611 return;
612 }
613 // Found our interface, disable callbacks
614 fwUpdateMatchSignal = nullptr;
615
616 phosphor::logging::log<phosphor::logging::level::INFO>(
617 "Start activationStatusTimer for status.");
618 try
619 {
620 timer.stop();
621 activationStatusTimer.start(
622 std::chrono::microseconds(3000000), true);
623 }
624 catch (const std::exception& e)
625 {
626 phosphor::logging::log<phosphor::logging::level::ERR>(
627 "Exception caught in start activationStatusTimer.",
628 phosphor::logging::entry("ERROR=%s", e.what()));
629 }
630
631 fwUpdateStatus.updateActivationPercent(objPath.str);
632 activateImage(objPath.str);
633 }
634 }
635 };
636
637 // Adding matcher
638 fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match_t>(
639 *getSdBus(),
640 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
641 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
642 callback);
643 }
startFirmwareUpdate(const std::string & uri)644 static bool startFirmwareUpdate(const std::string& uri)
645 {
646 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
647 // the code gets to this point, the file should be transferred start the
648 // request (creating a new file in /tmp/images causes the update manager to
649 // check if it is ready for activation)
650 static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatchSignal;
651 postTransferCompleteHandler(fwUpdateMatchSignal);
652 std::string randomFname =
653 "/tmp/images/" +
654 boost::uuids::to_string(boost::uuids::random_generator()());
655 std::error_code fsError{};
656 std::filesystem::rename(uri, randomFname, fsError);
657 if (fsError)
658 {
659 // The source and destination may not be in the same
660 // filesystem.
661 std::filesystem::copy(uri, randomFname, fsError);
662 if (fsError)
663 {
664 phosphor::logging::log<phosphor::logging::level::ERR>(
665 "Unable to move/copy image to destination directory.",
666 phosphor::logging::entry("ERROR=%s",
667 fsError.message().c_str()));
668 return false;
669 }
670 std::filesystem::remove(uri);
671 }
672 return true;
673 }
674
transferImageFromFile(const std::string & uri,bool move=true)675 static bool transferImageFromFile(const std::string& uri, bool move = true)
676 {
677 std::error_code ec;
678 phosphor::logging::log<phosphor::logging::level::INFO>(
679 "Transfer Image From File.",
680 phosphor::logging::entry("URI=%s", uri.c_str()));
681 if (move)
682 {
683 std::filesystem::rename(uri, firmwareBufferFile, ec);
684 }
685 else
686 {
687 std::filesystem::copy(uri, firmwareBufferFile,
688 std::filesystem::copy_options::overwrite_existing,
689 ec);
690 }
691 if (xferHashCheck)
692 {
693 MappedFile mappedfw(uri);
694 if (!xferHashCheck->hash(
695 {mappedfw.data(), mappedfw.data() + mappedfw.size()}))
696 {
697 phosphor::logging::log<phosphor::logging::level::ERR>(
698 "transferImageFromFile: xferHashCheck->hash failed.");
699 return false;
700 }
701 }
702 if (ec.value())
703 {
704 phosphor::logging::log<phosphor::logging::level::ERR>(
705 "Image copy failed.");
706 return false;
707 }
708
709 return true;
710 }
711
712 template <typename... ArgTypes>
executeCmd(const char * path,ArgTypes &&...tArgs)713 static int executeCmd(const char* path, ArgTypes&&... tArgs)
714 {
715 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
716 execProg.wait();
717 return execProg.exit_code();
718 }
719
transferImageFromUsb(const std::string & uri)720 static bool transferImageFromUsb(const std::string& uri)
721 {
722 bool ret = false;
723
724 phosphor::logging::log<phosphor::logging::level::INFO>(
725 "Transfer Image From USB.",
726 phosphor::logging::entry("URI=%s", uri.c_str()));
727
728 if (executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
729 fwUpdateMountPoint) == 0)
730 {
731 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
732 ret = transferImageFromFile(usb_path, false);
733
734 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage,
735 fwUpdateMountPoint);
736 }
737
738 return ret;
739 }
740
transferFirmwareFromUri(const std::string & uri)741 static bool transferFirmwareFromUri(const std::string& uri)
742 {
743 static constexpr char fwUriFile[] = "file://";
744 static constexpr char fwUriUsb[] = "usb://";
745 phosphor::logging::log<phosphor::logging::level::INFO>(
746 "Transfer Image From URI.",
747 phosphor::logging::entry("URI=%s", uri.c_str()));
748 if (boost::algorithm::starts_with(uri, fwUriFile))
749 {
750 std::string fname = uri.substr(sizeof(fwUriFile) - 1);
751 if (fname != firmwareBufferFile)
752 {
753 return transferImageFromFile(fname);
754 }
755 return true;
756 }
757 if (boost::algorithm::starts_with(uri, fwUriUsb))
758 {
759 std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
760 return transferImageFromUsb(fname);
761 }
762 return false;
763 }
764
765 /* Get USB-mass-storage device status: inserted => true, ejected => false */
getUsbStatus()766 static bool getUsbStatus()
767 {
768 std::filesystem::path usbDevPath =
769 std::filesystem::path("/sys/kernel/config/usb_gadget") /
770 fwUpdateUSBDevName;
771 return (std::filesystem::exists(usbDevPath) ? true : false);
772 }
773
774 /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
attachUsbDevice()775 static int attachUsbDevice()
776 {
777 if (getUsbStatus())
778 {
779 return 1;
780 }
781 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
782 std::to_string(maxFirmwareImageSize / 1_MB).c_str());
783 if (!ret)
784 {
785 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
786 fwUpdateUsbVolImage);
787 }
788 return ret;
789 }
790
791 /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
detachUsbDevice()792 static int detachUsbDevice()
793 {
794 if (!getUsbStatus())
795 {
796 return 1;
797 }
798 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
799 }
getActiveBootImage(ipmi::Context::ptr ctx)800 static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
801 {
802 constexpr uint8_t undefinedImage = 0x00;
803 constexpr uint8_t primaryImage = 0x01;
804 constexpr uint8_t secondaryImage = 0x02;
805 constexpr const char* secondaryFitImageStartAddr = "22480000";
806
807 uint8_t bootImage = primaryImage;
808 boost::system::error_code ec;
809 std::string value = ctx->bus->yield_method_call<std::string>(
810 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
811 "/xyz/openbmc_project/u_boot/environment/mgr",
812 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
813 if (ec)
814 {
815 phosphor::logging::log<phosphor::logging::level::ERR>(
816 "Failed to read the bootcmd value");
817 /* don't fail, just give back undefined until it is ready */
818 bootImage = undefinedImage;
819 }
820
821 /* cheking for secondary FitImage Address 22480000 */
822 else if (value.find(secondaryFitImageStartAddr) != std::string::npos)
823 {
824 bootImage = secondaryImage;
825 }
826 else
827 {
828 bootImage = primaryImage;
829 }
830
831 return bootImage;
832 }
833
834 #ifdef INTEL_PFR_ENABLED
835 using fwVersionInfoType =
836 std::tuple<uint8_t, // ID Tag
837 uint8_t, // Major Version Number
838 uint8_t, // Minor Version Number
839 uint32_t, // Build Number
840 uint32_t, // Build Timestamp
841 uint32_t>; // Update Timestamp
ipmiGetFwVersionInfo()842 ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
843 {
844 // Byte 1 - Count (N) Number of devices data is being returned for.
845 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
846 // Bytes - 17:(15xN) - Repeat of 2 through 16
847
848 std::vector<fwVersionInfoType> fwVerInfoList;
849 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
850 for (const auto& fwDev : fwVersionIdMap)
851 {
852 std::string verStr;
853 try
854 {
855 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
856
857 ipmi::Value result = ipmi::getDbusProperty(
858 *busp, service, fwDev.second, versionIntf, "Version");
859 verStr = std::get<std::string>(result);
860 }
861 catch (const std::exception& e)
862 {
863 phosphor::logging::log<phosphor::logging::level::INFO>(
864 "Failed to fetch Version property",
865 phosphor::logging::entry("ERROR=%s", e.what()),
866 phosphor::logging::entry("PATH=%s", fwDev.second),
867 phosphor::logging::entry("INTERFACE=%s", versionIntf));
868 continue;
869 }
870
871 if (verStr.empty())
872 {
873 phosphor::logging::log<phosphor::logging::level::INFO>(
874 "Version is empty.",
875 phosphor::logging::entry("PATH=%s", fwDev.second),
876 phosphor::logging::entry("INTERFACE=%s", versionIntf));
877 continue;
878 }
879
880 uint8_t majorNum = 0;
881 uint8_t minorNum = 0;
882 uint32_t buildNum = 0;
883 try
884 {
885 std::optional<ipmi::MetaRevision> rev =
886 ipmi::convertIntelVersion(verStr);
887 if (rev.has_value())
888 {
889 ipmi::MetaRevision revision = rev.value();
890 majorNum = revision.major % 10 + (revision.major / 10) * 16;
891 minorNum = (revision.minor > 99 ? 99 : revision.minor);
892 minorNum = minorNum % 10 + (minorNum / 10) * 16;
893 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
894 hash = bswap_32(hash);
895 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
896 }
897 else
898 {
899 std::vector<std::string> splitVer;
900 boost::split(splitVer, verStr, boost::is_any_of(".-"));
901 if (splitVer.size() < 3)
902 {
903 phosphor::logging::log<phosphor::logging::level::INFO>(
904 "Invalid Version format.",
905 phosphor::logging::entry("Version=%s", verStr.c_str()),
906 phosphor::logging::entry("PATH=%s", fwDev.second));
907 continue;
908 }
909 majorNum = std::stoul(splitVer[0], nullptr, 16);
910 minorNum = std::stoul(splitVer[1], nullptr, 16);
911 buildNum = std::stoul(splitVer[2], nullptr, 16);
912 }
913 // Build Timestamp - Not supported.
914 // Update Timestamp - TODO: Need to check with CPLD team.
915 fwVerInfoList.emplace_back(
916 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
917 minorNum, buildNum, 0, 0));
918 }
919 catch (const std::exception& e)
920 {
921 phosphor::logging::log<phosphor::logging::level::INFO>(
922 "Failed to convert stoul.",
923 phosphor::logging::entry("ERROR=%s", e.what()));
924 continue;
925 }
926 }
927
928 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
929 }
930
getSecurityVersionInfo(const char * mtdDevBuf,size_t svnVerOffsetInPfm,size_t bkcVerOffsetInPfm)931 std::array<uint8_t, imageCount> getSecurityVersionInfo(
932 const char* mtdDevBuf, size_t svnVerOffsetInPfm, size_t bkcVerOffsetInPfm)
933 {
934 constexpr size_t bufLength = 1;
935 std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp;
936 constexpr uint8_t svnIndexValue = 0x00;
937 constexpr uint8_t bkcIndexValue = 0x01;
938 constexpr uint8_t tempIndexValue = 0x00;
939 try
940 {
941 SPIDev spiDev(mtdDevBuf);
942 spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data());
943 fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue);
944 spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data());
945 fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue);
946 }
947 catch (const std::exception& e)
948 {
949 phosphor::logging::log<phosphor::logging::level::ERR>(
950 "Exception caught in getSecurityVersionInfo",
951 phosphor::logging::entry("MSG=%s", e.what()));
952 fwSecurityVersionBuf = {0, 0};
953 }
954
955 return fwSecurityVersionBuf;
956 }
957
958 ipmi::RspType<
959 uint8_t, // device ID
960 uint8_t, // Active Image Value
961 std::array<uint8_t, imageCount>, // Security version for Active Image
962 uint8_t, // recovery Image Value
963 std::array<uint8_t, imageCount>> // Security version for Recovery Image
ipmiGetFwSecurityVersionInfo()964 ipmiGetFwSecurityVersionInfo()
965 {
966 static bool cacheFlag = false;
967 constexpr std::array<const char*, imageCount> mtdDevBuf = {
968 bmcActivePfmMTDDev, bmcRecoveryImgMTDDev};
969
970 // To avoid multiple reading from SPI device
971 if (!cacheFlag)
972 {
973 imgFwSecurityVersion[0] = getSecurityVersionInfo(
974 mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm);
975 imgFwSecurityVersion[1] = getSecurityVersionInfo(
976 mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm);
977 cacheFlag = true;
978 }
979
980 constexpr uint8_t ActivePfmMTDDev = 0x00;
981 constexpr uint8_t RecoveryImgMTDDev = 0x01;
982
983 return ipmi::responseSuccess(
984 imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage),
985 imgFwSecurityVersion[ActivePfmMTDDev],
986 static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage),
987 imgFwSecurityVersion[RecoveryImgMTDDev]);
988 }
989
990 ipmi::RspType<std::array<uint8_t, certKeyLen>,
991 std::optional<std::array<uint8_t, cskSignatureLen>>>
ipmiGetFwRootCertData(const ipmi::Context::ptr & ctx,uint8_t certId)992 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId)
993 {
994 bool isIPMBChannel = false;
995
996 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
997 {
998 return ipmi::responseUnspecifiedError();
999 }
1000 if (isIPMBChannel)
1001 {
1002 phosphor::logging::log<phosphor::logging::level::INFO>(
1003 "Command not supported. Failed to get root certificate data.");
1004 return ipmi::responseCommandNotAvailable();
1005 }
1006
1007 size_t certKeyOffset = 0;
1008 size_t cskSigOffset = 0;
1009 std::string mtdDev;
1010
1011 switch (static_cast<FwGetRootCertDataTag>(certId))
1012 {
1013 case FwGetRootCertDataTag::activeRootKey:
1014 {
1015 mtdDev = bmcActivePfmMTDDev;
1016 certKeyOffset = rootkeyOffsetInPfm;
1017 break;
1018 }
1019 case FwGetRootCertDataTag::recoveryRootKey:
1020 {
1021 mtdDev = bmcRecoveryImgMTDDev;
1022 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1023 break;
1024 }
1025 case FwGetRootCertDataTag::activeCSK:
1026 {
1027 mtdDev = bmcActivePfmMTDDev;
1028 certKeyOffset = cskKeyOffsetInPfm;
1029 cskSigOffset = cskSignatureOffsetInPfm;
1030 break;
1031 }
1032 case FwGetRootCertDataTag::recoveryCSK:
1033 {
1034 mtdDev = bmcRecoveryImgMTDDev;
1035 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1036 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1037 break;
1038 }
1039 default:
1040 {
1041 return ipmi::responseInvalidFieldRequest();
1042 }
1043 }
1044
1045 std::array<uint8_t, certKeyLen> certKey = {0};
1046
1047 try
1048 {
1049 SPIDev spiDev(mtdDev);
1050 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1051
1052 if (cskSigOffset)
1053 {
1054 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1055 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1056 cskSignature.data());
1057 return ipmi::responseSuccess(certKey, cskSignature);
1058 }
1059 }
1060 catch (const std::exception& e)
1061 {
1062 phosphor::logging::log<phosphor::logging::level::ERR>(
1063 "Exception caught in ipmiGetFwRootCertData",
1064 phosphor::logging::entry("MSG=%s", e.what()));
1065 return ipmi::responseUnspecifiedError();
1066 }
1067
1068 return ipmi::responseSuccess(certKey, std::nullopt);
1069 }
1070 #endif // INTEL_PFR_ENABLED
1071
1072 static constexpr uint8_t channelListSize = 3;
1073 /** @brief implements Maximum Firmware Transfer size command
1074 * @parameter
1075 * - none
1076 * @returns IPMI completion code plus response data
1077 * - count - channel count
1078 * - channelList - channel list information
1079 */
1080 ipmi::RspType<uint8_t, // channel count
1081 std::array<std::tuple<uint8_t, uint32_t>,
1082 channelListSize> // Channel List
1083 >
ipmiFirmwareMaxTransferSize()1084 ipmiFirmwareMaxTransferSize()
1085 {
1086 constexpr size_t kcsMaxBufSize = 128;
1087 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
1088 // Byte 1 - Count (N) Number of devices data is being returned for.
1089 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
1090 // Byte 3-6 - transfer size (little endian)
1091 // Bytes - 7:(5xN) - Repeat of 2 through 6
1092 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1093 channelList = {
1094 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
1095 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
1096 rmcpPlusMaxBufSize}}};
1097
1098 return ipmi::responseSuccess(channelListSize, channelList);
1099 }
1100
1101 ipmi::RspType<uint8_t, uint8_t>
ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)1102 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
1103 {
1104 // Byte 1 - Current execution context
1105 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
1106 // Byte 2 - Partition pointer
1107 // 0x01 - primary, 0x02 - secondary
1108 uint8_t partitionPtr = getActiveBootImage(ctx);
1109
1110 return ipmi::responseSuccess(
1111 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
1112 }
1113 /** @brief Get Firmware Update Random Number
1114 *
1115 * This function generate the random number used for
1116 * setting the firmware update mode as authentication key.
1117 *
1118 * @param[in] ctx - context of current session
1119 * @returns IPMI completion code along with
1120 * - random number
1121 **/
1122 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr & ctx)1123 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx)
1124 {
1125 phosphor::logging::log<phosphor::logging::level::INFO>(
1126 "Generate FW update random number");
1127 bool isIPMBChannel = false;
1128
1129 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1130 {
1131 return ipmi::responseUnspecifiedError();
1132 }
1133 if (isIPMBChannel)
1134 {
1135 phosphor::logging::log<phosphor::logging::level::INFO>(
1136 "Channel not supported. Failed to fetch FW update random number");
1137 return ipmi::responseCommandNotAvailable();
1138 }
1139 std::random_device rd;
1140 std::default_random_engine gen(rd());
1141 std::uniform_int_distribution<> dist{0, 255};
1142
1143 fwRandomNumGenTs = std::chrono::steady_clock::now();
1144
1145 for (size_t i = 0; i < fwRandomNumLength; i++)
1146 {
1147 fwRandomNum[i] = dist(gen);
1148 }
1149
1150 return ipmi::responseSuccess(fwRandomNum);
1151 }
1152
1153 /** @brief Set Firmware Update Mode
1154 *
1155 * This function sets BMC into firmware update mode
1156 * after validating Random number obtained from the Get
1157 * Firmware Update Random Number command
1158 *
1159 * @param[in] ctx - context of current session
1160 * @parameter randNum - Random number(token)
1161 * @returns IPMI completion code
1162 **/
1163 ipmi::RspType<>
ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr & ctx,std::array<uint8_t,fwRandomNumLength> & randNum)1164 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx,
1165 std::array<uint8_t, fwRandomNumLength>& randNum)
1166 {
1167 phosphor::logging::log<phosphor::logging::level::INFO>(
1168 "Start FW update mode");
1169
1170 bool isIPMBChannel = false;
1171
1172 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1173 {
1174 return ipmi::responseUnspecifiedError();
1175 }
1176 if (isIPMBChannel)
1177 {
1178 phosphor::logging::log<phosphor::logging::level::INFO>(
1179 "Channel not supported. Failed to set FW update mode");
1180 return ipmi::responseCommandNotAvailable();
1181 }
1182 /* Firmware Update Random number is valid for 30 seconds only */
1183 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1184 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1185 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1186 fwRandomNumExpirySeconds)
1187 .count())
1188 {
1189 phosphor::logging::log<phosphor::logging::level::INFO>(
1190 "Firmware update random number expired.");
1191 return ipmi::responseInvalidFieldRequest();
1192 }
1193
1194 /* Validate random number */
1195 for (size_t i = 0; i < fwRandomNumLength; i++)
1196 {
1197 if (fwRandomNum[i] != randNum[i])
1198 {
1199 phosphor::logging::log<phosphor::logging::level::INFO>(
1200 "Invalid random number specified.");
1201 return ipmi::responseInvalidFieldRequest();
1202 }
1203 }
1204
1205 try
1206 {
1207 if (getFirmwareUpdateMode())
1208 {
1209 phosphor::logging::log<phosphor::logging::level::INFO>(
1210 "Already firmware update is in progress.");
1211 return ipmi::responseBusy();
1212 }
1213 }
1214 catch (const std::exception& e)
1215 {
1216 return ipmi::responseUnspecifiedError();
1217 }
1218
1219 // FIXME? c++ doesn't off an option for exclusive file creation
1220 FILE* fp = fopen(firmwareBufferFile, "wx");
1221 if (!fp)
1222 {
1223 phosphor::logging::log<phosphor::logging::level::INFO>(
1224 "Unable to open file.");
1225 return ipmi::responseUnspecifiedError();
1226 }
1227 fclose(fp);
1228
1229 try
1230 {
1231 setFirmwareUpdateMode(true);
1232 }
1233 catch (const std::exception& e)
1234 {
1235 unlink(firmwareBufferFile);
1236 return ipmi::responseUnspecifiedError();
1237 }
1238
1239 return ipmi::responseSuccess();
1240 }
1241
1242 /** @brief implements exit firmware update mode command
1243 * @param None
1244 *
1245 * @returns IPMI completion code
1246 */
ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr & ctx)1247 ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx)
1248 {
1249 phosphor::logging::log<phosphor::logging::level::INFO>(
1250 "Exit FW update mode");
1251 bool isIPMBChannel = false;
1252
1253 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1254 {
1255 return ipmi::responseUnspecifiedError();
1256 }
1257 if (isIPMBChannel)
1258 {
1259 phosphor::logging::log<phosphor::logging::level::INFO>(
1260 "Command not supported. Failed to exit firmware update mode");
1261 return ipmi::responseCommandNotAvailable();
1262 }
1263
1264 switch (fwUpdateStatus.getState())
1265 {
1266 case FwUpdateStatusCache::fwStateInit:
1267 case FwUpdateStatusCache::fwStateIdle:
1268 return ipmi::responseInvalidFieldRequest();
1269 break;
1270 case FwUpdateStatusCache::fwStateDownload:
1271 case FwUpdateStatusCache::fwStateVerify:
1272 break;
1273 case FwUpdateStatusCache::fwStateProgram:
1274 break;
1275 case FwUpdateStatusCache::fwStateUpdateSuccess:
1276 case FwUpdateStatusCache::fwStateError:
1277 break;
1278 case FwUpdateStatusCache::fwStateAcCycleRequired:
1279 return ipmi::responseInvalidFieldRequest();
1280 break;
1281 }
1282 fwUpdateStatus.firmwareUpdateAbortState();
1283
1284 try
1285 {
1286 setFirmwareUpdateMode(false);
1287 }
1288 catch (const std::exception& e)
1289 {
1290 return ipmi::responseUnspecifiedError();
1291 }
1292
1293 return ipmi::responseSuccess();
1294 }
1295
1296 /** @brief implements Get/Set Firmware Update Control
1297 * @param[in] ctx - context of current session
1298 * @parameter
1299 * - Byte 1: Control Byte
1300 * - Byte 2: Firmware filename length (Optional)
1301 * - Byte 3:N: Firmware filename data (Optional)
1302 * @returns IPMI completion code plus response data
1303 * - Byte 2: Current control status
1304 **/
ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr & ctx,const uint8_t controlReq,const std::optional<std::string> & fileName)1305 ipmi::RspType<bool, bool, bool, bool, uint4_t> ipmiGetSetFirmwareUpdateControl(
1306 const ipmi::Context::ptr& ctx, const uint8_t controlReq,
1307 const std::optional<std::string>& fileName)
1308 {
1309 bool isIPMBChannel = false;
1310
1311 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1312 {
1313 return ipmi::responseUnspecifiedError();
1314 }
1315 if (isIPMBChannel)
1316 {
1317 phosphor::logging::log<phosphor::logging::level::INFO>(
1318 "Channel not supported. Failed to get or set FW update control");
1319 return ipmi::responseCommandNotAvailable();
1320 }
1321
1322 static std::string fwXferUriPath;
1323 static bool imageTransferStarted = false;
1324 static bool imageTransferCompleted = false;
1325 static bool imageTransferAborted = false;
1326
1327 if ((controlReq !=
1328 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
1329 (fileName))
1330 {
1331 phosphor::logging::log<phosphor::logging::level::ERR>(
1332 "Invalid request field (Filename).");
1333 return ipmi::responseInvalidFieldRequest();
1334 }
1335
1336 static bool usbAttached = getUsbStatus();
1337
1338 switch (static_cast<FwUpdateCtrlReq>(controlReq))
1339 {
1340 case FwUpdateCtrlReq::getCurrentControlStatus:
1341 phosphor::logging::log<phosphor::logging::level::INFO>(
1342 "ipmiGetSetFirmwareUpdateControl: Get status");
1343 break;
1344 case FwUpdateCtrlReq::imageTransferStart:
1345 {
1346 phosphor::logging::log<phosphor::logging::level::INFO>(
1347 "ipmiGetSetFirmwareUpdateControl: Set transfer start");
1348 imageTransferStarted = true;
1349 // reset buffer to empty (truncate file)
1350 std::ofstream out(firmwareBufferFile,
1351 std::ofstream::binary | std::ofstream::trunc);
1352 fwXferUriPath = std::string("file://") + firmwareBufferFile;
1353 if (xferHashCheck)
1354 {
1355 if (!xferHashCheck->clear())
1356 {
1357 phosphor::logging::log<phosphor::logging::level::ERR>(
1358 "clear() for xferHashCheck failed");
1359 return ipmi::responseUnspecifiedError();
1360 }
1361 }
1362 // Setting state to download
1363 fwUpdateStatus.setState(
1364 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
1365 #ifdef INTEL_PFR_ENABLED
1366 imgLength = 0;
1367 imgType = 0;
1368 block0Mapped = false;
1369 #endif
1370 }
1371 break;
1372 case FwUpdateCtrlReq::imageTransferComplete:
1373 {
1374 if (!imageTransferStarted)
1375 {
1376 phosphor::logging::log<phosphor::logging::level::ERR>(
1377 "transferFirmwareUpdate not started.");
1378 return ipmi::responseNotSupportedInPresentState();
1379 }
1380 phosphor::logging::log<phosphor::logging::level::INFO>(
1381 "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
1382 if (usbAttached)
1383 {
1384 phosphor::logging::log<phosphor::logging::level::ERR>(
1385 "USB should be detached to perform this operation.");
1386 return ipmi::responseNotSupportedInPresentState();
1387 }
1388 // finish transfer based on URI
1389 if (!transferFirmwareFromUri(fwXferUriPath))
1390 {
1391 phosphor::logging::log<phosphor::logging::level::ERR>(
1392 "transferFirmwareFromUri failed.");
1393 return ipmi::responseUnspecifiedError();
1394 }
1395 // transfer complete
1396 if (xferHashCheck)
1397 {
1398 if (TransferHashCheck::HashCheck::sha2Success !=
1399 xferHashCheck->verify())
1400 {
1401 phosphor::logging::log<phosphor::logging::level::ERR>(
1402 "xferHashCheck failed.");
1403 return ipmi::responseUnspecifiedError();
1404 }
1405 }
1406 // Set state to verify and start the update
1407 fwUpdateStatus.setState(
1408 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
1409 // start the request
1410 if (!startFirmwareUpdate(firmwareBufferFile))
1411 {
1412 phosphor::logging::log<phosphor::logging::level::ERR>(
1413 "startFirmwareUpdate failed.");
1414 return ipmi::responseUnspecifiedError();
1415 }
1416 imageTransferCompleted = true;
1417 }
1418 break;
1419 case FwUpdateCtrlReq::imageTransferAbort:
1420 phosphor::logging::log<phosphor::logging::level::INFO>(
1421 "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
1422 if (usbAttached)
1423 {
1424 if (detachUsbDevice())
1425 {
1426 phosphor::logging::log<phosphor::logging::level::ERR>(
1427 "Detach USB device failed.");
1428 return ipmi::responseUsbAttachOrDetachFailed();
1429 }
1430 usbAttached = false;
1431 }
1432 // During abort request reset the state to Init by cleaning update
1433 // file.
1434 fwUpdateStatus.firmwareUpdateAbortState();
1435 imageTransferAborted = true;
1436 break;
1437 case FwUpdateCtrlReq::setFirmwareFilename:
1438 phosphor::logging::log<phosphor::logging::level::INFO>(
1439 "ipmiGetSetFirmwareUpdateControl: Set filename.");
1440 if (!fileName || ((*fileName).length() == 0))
1441 {
1442 phosphor::logging::log<phosphor::logging::level::ERR>(
1443 "Invalid Filename specified.");
1444 return ipmi::responseInvalidFieldRequest();
1445 }
1446
1447 fwXferUriPath = *fileName;
1448 break;
1449 case FwUpdateCtrlReq::attachUsbDevice:
1450 phosphor::logging::log<phosphor::logging::level::INFO>(
1451 "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
1452 if (usbAttached)
1453 {
1454 phosphor::logging::log<phosphor::logging::level::ERR>(
1455 "USB device is already attached.");
1456 return ipmi::responseInvalidFieldRequest();
1457 }
1458 if (attachUsbDevice())
1459 {
1460 phosphor::logging::log<phosphor::logging::level::ERR>(
1461 "Attach USB device failed.");
1462 return ipmi::responseUsbAttachOrDetachFailed();
1463 }
1464 usbAttached = true;
1465 break;
1466 case FwUpdateCtrlReq::detachUsbDevice:
1467 phosphor::logging::log<phosphor::logging::level::INFO>(
1468 "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
1469 if (!usbAttached)
1470 {
1471 phosphor::logging::log<phosphor::logging::level::ERR>(
1472 "USB device is not attached.");
1473 return ipmi::responseInvalidFieldRequest();
1474 }
1475 if (detachUsbDevice())
1476 {
1477 phosphor::logging::log<phosphor::logging::level::ERR>(
1478 "Detach USB device failed.");
1479 return ipmi::responseUsbAttachOrDetachFailed();
1480 }
1481 usbAttached = false;
1482 break;
1483 default:
1484 phosphor::logging::log<phosphor::logging::level::ERR>(
1485 "Invalid control option specified.");
1486 return ipmi::responseInvalidFieldRequest();
1487 }
1488
1489 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
1490 imageTransferAborted, usbAttached, uint4_t(0));
1491 }
1492
1493 /** @brief implements firmware get status command
1494 * @parameter
1495 * - none
1496 * @returns IPMI completion code plus response data
1497 * - status - processing status
1498 * - percentage - percentage completion
1499 * - check - channel integrity check status
1500 **/
1501 ipmi::RspType<uint8_t, // status
1502 uint8_t, // percentage
1503 uint8_t // check
1504 >
ipmiGetFirmwareUpdateStatus()1505 ipmiGetFirmwareUpdateStatus()
1506
1507 {
1508 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1509 // 5=ready, f=error, 83=ac cycle required)
1510 // Byte 2 - percent
1511 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1512 uint8_t status = fwUpdateStatus.getState();
1513 uint8_t percent = fwUpdateStatus.percent();
1514 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
1515
1516 // Status code.
1517 return ipmi::responseSuccess(status, percent, check);
1518 }
1519
ipmiSetFirmwareUpdateOptions(const ipmi::Context::ptr & ctx,bool noDowngradeMask,bool deferRestartMask,bool sha2CheckMask,uint5_t reserved1,bool noDowngrade,bool deferRestart,bool sha2Check,uint5_t reserved2,std::optional<std::vector<uint8_t>> integrityCheckVal)1520 ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
1521 const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask,
1522 bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart,
1523 bool sha2Check, uint5_t reserved2,
1524 std::optional<std::vector<uint8_t>> integrityCheckVal)
1525 {
1526 phosphor::logging::log<phosphor::logging::level::INFO>(
1527 "Set firmware update options.");
1528 bool isIPMBChannel = false;
1529
1530 if (reserved1 || reserved2)
1531 {
1532 return ipmi::responseInvalidFieldRequest();
1533 }
1534 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1535 {
1536 return ipmi::responseUnspecifiedError();
1537 }
1538 if (isIPMBChannel)
1539 {
1540 phosphor::logging::log<phosphor::logging::level::INFO>(
1541 "Channel not supported. Failed to set firmware update options");
1542 return ipmi::responseCommandNotAvailable();
1543 }
1544 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
1545 bool deferRestartState = fwUpdateStatus.getDeferRestart();
1546 bool sha2CheckState = xferHashCheck ? true : false;
1547
1548 if (noDowngradeMask && (noDowngradeState != noDowngrade))
1549 {
1550 fwUpdateStatus.setInhibitDowngrade(noDowngrade);
1551 noDowngradeState = noDowngrade;
1552 }
1553 if (deferRestartMask && (deferRestartState != deferRestart))
1554 {
1555 fwUpdateStatus.setDeferRestart(deferRestart);
1556 deferRestartState = deferRestart;
1557 }
1558 if (sha2CheckMask)
1559 {
1560 if (sha2Check)
1561 {
1562 size_t hashSize = EVP_MD_size(EVP_sha256());
1563 if ((*integrityCheckVal).size() != hashSize)
1564 {
1565 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1566 "Invalid size of Hash specified.");
1567 return ipmi::responseInvalidFieldRequest();
1568 }
1569
1570 try
1571 {
1572 xferHashCheck =
1573 std::make_unique<TransferHashCheck>(*integrityCheckVal);
1574 }
1575 catch (const std::exception& ex)
1576 {
1577 phosphor::logging::log<phosphor::logging::level::ERR>(
1578 ex.what());
1579 return ipmi::responseUnspecifiedError();
1580 }
1581 }
1582 else
1583 {
1584 // delete the xferHashCheck object
1585 xferHashCheck.reset();
1586 }
1587 sha2CheckState = sha2CheckMask;
1588 }
1589 return ipmi::responseSuccess(noDowngradeState, deferRestartState,
1590 sha2CheckState, uint5_t{});
1591 }
1592
1593 ipmi::RspType<uint32_t>
ipmiFwImageWriteData(const std::vector<uint8_t> & writeData)1594 ipmiFwImageWriteData(const std::vector<uint8_t>& writeData)
1595 {
1596 const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1597 size_t writeDataLen = writeData.size();
1598
1599 if (!writeDataLen)
1600 {
1601 return ipmi::responseReqDataLenInvalid();
1602 }
1603
1604 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
1605 {
1606 phosphor::logging::log<phosphor::logging::level::ERR>(
1607 "Invalid firmware update state.");
1608 return ipmi::response(ccCmdNotSupportedInPresentState);
1609 }
1610
1611 std::ofstream out(firmwareBufferFile,
1612 std::ofstream::binary | std::ofstream::app);
1613 if (!out)
1614 {
1615 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1616 "Error while opening file.");
1617 return ipmi::responseUnspecifiedError();
1618 }
1619
1620 uint64_t fileDataLen = out.tellp();
1621
1622 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
1623 {
1624 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1625 "Firmware image size exceeds the limit");
1626 return ipmi::responseInvalidFieldRequest();
1627 }
1628
1629 const char* data = reinterpret_cast<const char*>(writeData.data());
1630 out.write(data, writeDataLen);
1631 out.close();
1632
1633 if (xferHashCheck)
1634 {
1635 if (!xferHashCheck->hash(writeData))
1636 {
1637 phosphor::logging::log<phosphor::logging::level::ERR>(
1638 "ipmiFwImageWriteData: xferHashCheck->hash failed.");
1639 return ipmi::responseUnspecifiedError();
1640 }
1641 }
1642
1643 #ifdef INTEL_PFR_ENABLED
1644 /* PFR image block 0 - As defined in HAS */
1645 struct PFRImageBlock0
1646 {
1647 uint32_t tag;
1648 uint32_t pcLength;
1649 uint32_t pcType;
1650 uint32_t reserved1;
1651 uint8_t hash256[32];
1652 uint8_t hash384[48];
1653 uint8_t reserved2[32];
1654 } __attribute__((packed));
1655
1656 /* Get the PFR block 0 data and read the uploaded image
1657 * information( Image type, length etc) */
1658 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1659 (!block0Mapped))
1660 {
1661 PFRImageBlock0 block0Data{};
1662
1663 std::ifstream inFile(firmwareBufferFile,
1664 std::ios::binary | std::ios::in);
1665 inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data));
1666 inFile.close();
1667
1668 uint32_t magicNum = block0Data.tag;
1669
1670 /* Validate the magic number */
1671 if (magicNum != perBlock0MagicNum)
1672 {
1673 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1674 "PFR image magic number not matched");
1675 return ipmi::responseInvalidFieldRequest();
1676 }
1677 // Note:imgLength, imgType and block0Mapped are in global scope, as
1678 // these are used in cascaded updates.
1679 imgLength = block0Data.pcLength;
1680 imgType = block0Data.pcType;
1681 block0Mapped = true;
1682 }
1683 #endif // end of INTEL_PFR_ENABLED
1684 return ipmi::responseSuccess(writeDataLen);
1685 }
1686
registerFirmwareFunctions()1687 static void registerFirmwareFunctions()
1688 {
1689 // guarantee that we start with an already timed out timestamp
1690 fwRandomNumGenTs =
1691 std::chrono::steady_clock::now() - fwRandomNumExpirySeconds;
1692 fwUpdateStatus.setState(
1693 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
1694
1695 unlink(firmwareBufferFile);
1696
1697 #ifdef INTEL_PFR_ENABLED
1698 // Following commands are supported only for PFR enabled platforms
1699 // CMD:0x20 - Get Firmware Version Information
1700 // CMD:0x21 - Get Firmware Security Version Information
1701 // CMD:0x25 - Get Root Certificate Data
1702
1703 // get firmware version information
1704 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1705 ipmi::firmware::cmdGetFwVersionInfo,
1706 ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
1707
1708 // get firmware security version information
1709 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1710 ipmi::firmware::cmdGetFwSecurityVersionInfo,
1711 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
1712
1713 // get root certificate data
1714 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1715 ipmi::firmware::cmdFwGetRootCertData,
1716 ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1717 #endif
1718
1719 // get firmware update channel information (max transfer sizes)
1720 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1721 ipmi::firmware::cmdGetFwUpdateChannelInfo,
1722 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
1723
1724 // get bmc execution context
1725 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1726 ipmi::firmware::cmdGetBmcExecutionContext,
1727 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
1728
1729 // Get Firmware Update Random number
1730 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1731 ipmi::firmware::cmdGetFwUpdateRandomNumber,
1732 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
1733
1734 // Set Firmware Update Mode
1735 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1736 ipmi::firmware::cmdSetFirmwareUpdateMode,
1737 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
1738
1739 // Exit Firmware Update Mode
1740 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1741 ipmi::firmware::cmdExitFirmwareUpdateMode,
1742 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
1743
1744 // Get/Set Firmware Update Control
1745 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1746 ipmi::firmware::cmdGetSetFwUpdateControl,
1747 ipmi::Privilege::Admin,
1748 ipmiGetSetFirmwareUpdateControl);
1749
1750 // Get Firmware Update Status
1751 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1752 ipmi::firmware::cmdGetFirmwareUpdateStatus,
1753 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
1754
1755 // Set Firmware Update Options
1756 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1757 ipmi::firmware::cmdSetFirmwareUpdateOptions,
1758 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
1759 // write image data
1760 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1761 ipmi::firmware::cmdFwImageWriteData,
1762 ipmi::Privilege::Admin, ipmiFwImageWriteData);
1763 return;
1764 }
1765