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 = 33_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(
566 [&fwUpdateMatchSignal]() { fwUpdateMatchSignal = nullptr; });
567
568 static sdbusplus::Timer activationStatusTimer([]() {
569 if (fwUpdateStatus.activationTimerTimeout() > 95)
570 {
571 activationStatusTimer.stop();
572 fwUpdateStatus.setState(
573 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
574 }
575 });
576
577 timer.start(std::chrono::microseconds(5000000), false);
578
579 // callback function for capturing signal
580 auto callback = [&](sdbusplus::message_t& m) {
581 std::vector<
582 std::pair<std::string,
583 std::vector<std::pair<std::string, ipmi::DbusVariant>>>>
584 intfPropsPair;
585 sdbusplus::message::object_path objPath;
586
587 try
588 {
589 m.read(objPath, intfPropsPair); // Read in the object path
590 // that was just created
591 }
592 catch (const std::exception& e)
593 {
594 phosphor::logging::log<phosphor::logging::level::ERR>(
595 "Exception caught in reading created object path.");
596 return;
597 }
598 // constructing response message
599 phosphor::logging::log<phosphor::logging::level::INFO>(
600 "New Interface Added.",
601 phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
602 for (auto& interface : intfPropsPair)
603 {
604 if (interface.first == "xyz.openbmc_project.Software.Activation")
605 {
606 // There are chances of getting two signals for
607 // InterfacesAdded. So cross check and discrad second instance.
608 if (fwUpdateMatchSignal == nullptr)
609 {
610 return;
611 }
612 // Found our interface, disable callbacks
613 fwUpdateMatchSignal = nullptr;
614
615 phosphor::logging::log<phosphor::logging::level::INFO>(
616 "Start activationStatusTimer for status.");
617 try
618 {
619 timer.stop();
620 activationStatusTimer.start(
621 std::chrono::microseconds(3000000), true);
622 }
623 catch (const std::exception& e)
624 {
625 phosphor::logging::log<phosphor::logging::level::ERR>(
626 "Exception caught in start activationStatusTimer.",
627 phosphor::logging::entry("ERROR=%s", e.what()));
628 }
629
630 fwUpdateStatus.updateActivationPercent(objPath.str);
631 activateImage(objPath.str);
632 }
633 }
634 };
635
636 // Adding matcher
637 fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match_t>(
638 *getSdBus(),
639 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
640 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
641 callback);
642 }
startFirmwareUpdate(const std::string & uri)643 static bool startFirmwareUpdate(const std::string& uri)
644 {
645 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
646 // the code gets to this point, the file should be transferred start the
647 // request (creating a new file in /tmp/images causes the update manager to
648 // check if it is ready for activation)
649 static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatchSignal;
650 postTransferCompleteHandler(fwUpdateMatchSignal);
651 std::string randomFname =
652 "/tmp/images/" +
653 boost::uuids::to_string(boost::uuids::random_generator()());
654 std::error_code fsError{};
655 std::filesystem::rename(uri, randomFname, fsError);
656 if (fsError)
657 {
658 // The source and destination may not be in the same
659 // filesystem.
660 std::filesystem::copy(uri, randomFname, fsError);
661 if (fsError)
662 {
663 phosphor::logging::log<phosphor::logging::level::ERR>(
664 "Unable to move/copy image to destination directory.",
665 phosphor::logging::entry("ERROR=%s",
666 fsError.message().c_str()));
667 return false;
668 }
669 std::filesystem::remove(uri);
670 }
671 return true;
672 }
673
transferImageFromFile(const std::string & uri,bool move=true)674 static bool transferImageFromFile(const std::string& uri, bool move = true)
675 {
676 std::error_code ec;
677 phosphor::logging::log<phosphor::logging::level::INFO>(
678 "Transfer Image From File.",
679 phosphor::logging::entry("URI=%s", uri.c_str()));
680 if (move)
681 {
682 std::filesystem::rename(uri, firmwareBufferFile, ec);
683 }
684 else
685 {
686 std::filesystem::copy(uri, firmwareBufferFile,
687 std::filesystem::copy_options::overwrite_existing,
688 ec);
689 }
690 if (xferHashCheck)
691 {
692 MappedFile mappedfw(uri);
693 if (!xferHashCheck->hash(
694 {mappedfw.data(), mappedfw.data() + mappedfw.size()}))
695 {
696 phosphor::logging::log<phosphor::logging::level::ERR>(
697 "transferImageFromFile: xferHashCheck->hash failed.");
698 return false;
699 }
700 }
701 if (ec.value())
702 {
703 phosphor::logging::log<phosphor::logging::level::ERR>(
704 "Image copy failed.");
705 return false;
706 }
707
708 return true;
709 }
710
711 template <typename... ArgTypes>
executeCmd(const char * path,ArgTypes &&...tArgs)712 static int executeCmd(const char* path, ArgTypes&&... tArgs)
713 {
714 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
715 execProg.wait();
716 return execProg.exit_code();
717 }
718
transferImageFromUsb(const std::string & uri)719 static bool transferImageFromUsb(const std::string& uri)
720 {
721 bool ret = false;
722
723 phosphor::logging::log<phosphor::logging::level::INFO>(
724 "Transfer Image From USB.",
725 phosphor::logging::entry("URI=%s", uri.c_str()));
726
727 if (executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
728 fwUpdateMountPoint) == 0)
729 {
730 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
731 ret = transferImageFromFile(usb_path, false);
732
733 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage,
734 fwUpdateMountPoint);
735 }
736
737 return ret;
738 }
739
transferFirmwareFromUri(const std::string & uri)740 static bool transferFirmwareFromUri(const std::string& uri)
741 {
742 static constexpr char fwUriFile[] = "file://";
743 static constexpr char fwUriUsb[] = "usb://";
744 phosphor::logging::log<phosphor::logging::level::INFO>(
745 "Transfer Image From URI.",
746 phosphor::logging::entry("URI=%s", uri.c_str()));
747 if (boost::algorithm::starts_with(uri, fwUriFile))
748 {
749 std::string fname = uri.substr(sizeof(fwUriFile) - 1);
750 if (fname != firmwareBufferFile)
751 {
752 return transferImageFromFile(fname);
753 }
754 return true;
755 }
756 if (boost::algorithm::starts_with(uri, fwUriUsb))
757 {
758 std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
759 return transferImageFromUsb(fname);
760 }
761 return false;
762 }
763
764 /* Get USB-mass-storage device status: inserted => true, ejected => false */
getUsbStatus()765 static bool getUsbStatus()
766 {
767 std::filesystem::path usbDevPath =
768 std::filesystem::path("/sys/kernel/config/usb_gadget") /
769 fwUpdateUSBDevName;
770 return (std::filesystem::exists(usbDevPath) ? true : false);
771 }
772
773 /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
attachUsbDevice()774 static int attachUsbDevice()
775 {
776 if (getUsbStatus())
777 {
778 return 1;
779 }
780 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
781 std::to_string(maxFirmwareImageSize / 1_MB).c_str());
782 if (!ret)
783 {
784 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
785 fwUpdateUsbVolImage);
786 }
787 return ret;
788 }
789
790 /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
detachUsbDevice()791 static int detachUsbDevice()
792 {
793 if (!getUsbStatus())
794 {
795 return 1;
796 }
797 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
798 }
getActiveBootImage(ipmi::Context::ptr ctx)799 static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
800 {
801 constexpr uint8_t undefinedImage = 0x00;
802 constexpr uint8_t primaryImage = 0x01;
803 constexpr uint8_t secondaryImage = 0x02;
804 constexpr const char* secondaryFitImageStartAddr = "22480000";
805
806 uint8_t bootImage = primaryImage;
807 boost::system::error_code ec;
808 std::string value = ctx->bus->yield_method_call<std::string>(
809 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
810 "/xyz/openbmc_project/u_boot/environment/mgr",
811 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
812 if (ec)
813 {
814 phosphor::logging::log<phosphor::logging::level::ERR>(
815 "Failed to read the bootcmd value");
816 /* don't fail, just give back undefined until it is ready */
817 bootImage = undefinedImage;
818 }
819
820 /* cheking for secondary FitImage Address 22480000 */
821 else if (value.find(secondaryFitImageStartAddr) != std::string::npos)
822 {
823 bootImage = secondaryImage;
824 }
825 else
826 {
827 bootImage = primaryImage;
828 }
829
830 return bootImage;
831 }
832
833 #ifdef INTEL_PFR_ENABLED
834 using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
835 uint8_t, // Major Version Number
836 uint8_t, // Minor Version Number
837 uint32_t, // Build Number
838 uint32_t, // Build Timestamp
839 uint32_t>; // Update Timestamp
ipmiGetFwVersionInfo()840 ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
841 {
842 // Byte 1 - Count (N) Number of devices data is being returned for.
843 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
844 // Bytes - 17:(15xN) - Repeat of 2 through 16
845
846 std::vector<fwVersionInfoType> fwVerInfoList;
847 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
848 for (const auto& fwDev : fwVersionIdMap)
849 {
850 std::string verStr;
851 try
852 {
853 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
854
855 ipmi::Value result = ipmi::getDbusProperty(
856 *busp, service, fwDev.second, versionIntf, "Version");
857 verStr = std::get<std::string>(result);
858 }
859 catch (const std::exception& e)
860 {
861 phosphor::logging::log<phosphor::logging::level::INFO>(
862 "Failed to fetch Version property",
863 phosphor::logging::entry("ERROR=%s", e.what()),
864 phosphor::logging::entry("PATH=%s", fwDev.second),
865 phosphor::logging::entry("INTERFACE=%s", versionIntf));
866 continue;
867 }
868
869 if (verStr.empty())
870 {
871 phosphor::logging::log<phosphor::logging::level::INFO>(
872 "Version is empty.",
873 phosphor::logging::entry("PATH=%s", fwDev.second),
874 phosphor::logging::entry("INTERFACE=%s", versionIntf));
875 continue;
876 }
877
878 uint8_t majorNum = 0;
879 uint8_t minorNum = 0;
880 uint32_t buildNum = 0;
881 try
882 {
883 std::optional<ipmi::MetaRevision> rev =
884 ipmi::convertIntelVersion(verStr);
885 if (rev.has_value())
886 {
887 ipmi::MetaRevision revision = rev.value();
888 majorNum = revision.major % 10 + (revision.major / 10) * 16;
889 minorNum = (revision.minor > 99 ? 99 : revision.minor);
890 minorNum = minorNum % 10 + (minorNum / 10) * 16;
891 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
892 hash = bswap_32(hash);
893 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
894 }
895 else
896 {
897 std::vector<std::string> splitVer;
898 boost::split(splitVer, verStr, boost::is_any_of(".-"));
899 if (splitVer.size() < 3)
900 {
901 phosphor::logging::log<phosphor::logging::level::INFO>(
902 "Invalid Version format.",
903 phosphor::logging::entry("Version=%s", verStr.c_str()),
904 phosphor::logging::entry("PATH=%s", fwDev.second));
905 continue;
906 }
907 majorNum = std::stoul(splitVer[0], nullptr, 16);
908 minorNum = std::stoul(splitVer[1], nullptr, 16);
909 buildNum = std::stoul(splitVer[2], nullptr, 16);
910 }
911 // Build Timestamp - Not supported.
912 // Update Timestamp - TODO: Need to check with CPLD team.
913 fwVerInfoList.emplace_back(
914 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
915 minorNum, buildNum, 0, 0));
916 }
917 catch (const std::exception& e)
918 {
919 phosphor::logging::log<phosphor::logging::level::INFO>(
920 "Failed to convert stoul.",
921 phosphor::logging::entry("ERROR=%s", e.what()));
922 continue;
923 }
924 }
925
926 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
927 }
928
getSecurityVersionInfo(const char * mtdDevBuf,size_t svnVerOffsetInPfm,size_t bkcVerOffsetInPfm)929 std::array<uint8_t, imageCount> getSecurityVersionInfo(const char* mtdDevBuf,
930 size_t svnVerOffsetInPfm,
931 size_t bkcVerOffsetInPfm)
932 {
933 constexpr size_t bufLength = 1;
934 std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp;
935 constexpr uint8_t svnIndexValue = 0x00;
936 constexpr uint8_t bkcIndexValue = 0x01;
937 constexpr uint8_t tempIndexValue = 0x00;
938 try
939 {
940 SPIDev spiDev(mtdDevBuf);
941 spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data());
942 fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue);
943 spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data());
944 fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue);
945 }
946 catch (const std::exception& e)
947 {
948 phosphor::logging::log<phosphor::logging::level::ERR>(
949 "Exception caught in getSecurityVersionInfo",
950 phosphor::logging::entry("MSG=%s", e.what()));
951 fwSecurityVersionBuf = {0, 0};
952 }
953
954 return fwSecurityVersionBuf;
955 }
956
957 ipmi::RspType<
958 uint8_t, // device ID
959 uint8_t, // Active Image Value
960 std::array<uint8_t, imageCount>, // Security version for Active Image
961 uint8_t, // recovery Image Value
962 std::array<uint8_t, imageCount>> // Security version for Recovery Image
ipmiGetFwSecurityVersionInfo()963 ipmiGetFwSecurityVersionInfo()
964 {
965 static bool cacheFlag = false;
966 constexpr std::array<const char*, imageCount> mtdDevBuf = {
967 bmcActivePfmMTDDev, bmcRecoveryImgMTDDev};
968
969 // To avoid multiple reading from SPI device
970 if (!cacheFlag)
971 {
972 imgFwSecurityVersion[0] = getSecurityVersionInfo(
973 mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm);
974 imgFwSecurityVersion[1] = getSecurityVersionInfo(
975 mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm);
976 cacheFlag = true;
977 }
978
979 constexpr uint8_t ActivePfmMTDDev = 0x00;
980 constexpr uint8_t RecoveryImgMTDDev = 0x01;
981
982 return ipmi::responseSuccess(
983 imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage),
984 imgFwSecurityVersion[ActivePfmMTDDev],
985 static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage),
986 imgFwSecurityVersion[RecoveryImgMTDDev]);
987 }
988
989 ipmi::RspType<std::array<uint8_t, certKeyLen>,
990 std::optional<std::array<uint8_t, cskSignatureLen>>>
ipmiGetFwRootCertData(const ipmi::Context::ptr & ctx,uint8_t certId)991 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId)
992 {
993 bool isIPMBChannel = false;
994
995 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
996 {
997 return ipmi::responseUnspecifiedError();
998 }
999 if (isIPMBChannel)
1000 {
1001 phosphor::logging::log<phosphor::logging::level::INFO>(
1002 "Command not supported. Failed to get root certificate data.");
1003 return ipmi::responseCommandNotAvailable();
1004 }
1005
1006 size_t certKeyOffset = 0;
1007 size_t cskSigOffset = 0;
1008 std::string mtdDev;
1009
1010 switch (static_cast<FwGetRootCertDataTag>(certId))
1011 {
1012 case FwGetRootCertDataTag::activeRootKey:
1013 {
1014 mtdDev = bmcActivePfmMTDDev;
1015 certKeyOffset = rootkeyOffsetInPfm;
1016 break;
1017 }
1018 case FwGetRootCertDataTag::recoveryRootKey:
1019 {
1020 mtdDev = bmcRecoveryImgMTDDev;
1021 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1022 break;
1023 }
1024 case FwGetRootCertDataTag::activeCSK:
1025 {
1026 mtdDev = bmcActivePfmMTDDev;
1027 certKeyOffset = cskKeyOffsetInPfm;
1028 cskSigOffset = cskSignatureOffsetInPfm;
1029 break;
1030 }
1031 case FwGetRootCertDataTag::recoveryCSK:
1032 {
1033 mtdDev = bmcRecoveryImgMTDDev;
1034 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1035 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1036 break;
1037 }
1038 default:
1039 {
1040 return ipmi::responseInvalidFieldRequest();
1041 }
1042 }
1043
1044 std::array<uint8_t, certKeyLen> certKey = {0};
1045
1046 try
1047 {
1048 SPIDev spiDev(mtdDev);
1049 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1050
1051 if (cskSigOffset)
1052 {
1053 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1054 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1055 cskSignature.data());
1056 return ipmi::responseSuccess(certKey, cskSignature);
1057 }
1058 }
1059 catch (const std::exception& e)
1060 {
1061 phosphor::logging::log<phosphor::logging::level::ERR>(
1062 "Exception caught in ipmiGetFwRootCertData",
1063 phosphor::logging::entry("MSG=%s", e.what()));
1064 return ipmi::responseUnspecifiedError();
1065 }
1066
1067 return ipmi::responseSuccess(certKey, std::nullopt);
1068 }
1069 #endif // INTEL_PFR_ENABLED
1070
1071 static constexpr uint8_t channelListSize = 3;
1072 /** @brief implements Maximum Firmware Transfer size command
1073 * @parameter
1074 * - none
1075 * @returns IPMI completion code plus response data
1076 * - count - channel count
1077 * - channelList - channel list information
1078 */
1079 ipmi::RspType<uint8_t, // channel count
1080 std::array<std::tuple<uint8_t, uint32_t>,
1081 channelListSize> // Channel List
1082 >
ipmiFirmwareMaxTransferSize()1083 ipmiFirmwareMaxTransferSize()
1084 {
1085 constexpr size_t kcsMaxBufSize = 128;
1086 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
1087 // Byte 1 - Count (N) Number of devices data is being returned for.
1088 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
1089 // Byte 3-6 - transfer size (little endian)
1090 // Bytes - 7:(5xN) - Repeat of 2 through 6
1091 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1092 channelList = {
1093 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
1094 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
1095 rmcpPlusMaxBufSize}}};
1096
1097 return ipmi::responseSuccess(channelListSize, channelList);
1098 }
1099
1100 ipmi::RspType<uint8_t, uint8_t>
ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)1101 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
1102 {
1103 // Byte 1 - Current execution context
1104 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
1105 // Byte 2 - Partition pointer
1106 // 0x01 - primary, 0x02 - secondary
1107 uint8_t partitionPtr = getActiveBootImage(ctx);
1108
1109 return ipmi::responseSuccess(
1110 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
1111 }
1112 /** @brief Get Firmware Update Random Number
1113 *
1114 * This function generate the random number used for
1115 * setting the firmware update mode as authentication key.
1116 *
1117 * @param[in] ctx - context of current session
1118 * @returns IPMI completion code along with
1119 * - random number
1120 **/
1121 ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr & ctx)1122 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx)
1123 {
1124 phosphor::logging::log<phosphor::logging::level::INFO>(
1125 "Generate FW update random number");
1126 bool isIPMBChannel = false;
1127
1128 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1129 {
1130 return ipmi::responseUnspecifiedError();
1131 }
1132 if (isIPMBChannel)
1133 {
1134 phosphor::logging::log<phosphor::logging::level::INFO>(
1135 "Channel not supported. Failed to fetch FW update random number");
1136 return ipmi::responseCommandNotAvailable();
1137 }
1138 std::random_device rd;
1139 std::default_random_engine gen(rd());
1140 std::uniform_int_distribution<> dist{0, 255};
1141
1142 fwRandomNumGenTs = std::chrono::steady_clock::now();
1143
1144 for (size_t i = 0; i < fwRandomNumLength; i++)
1145 {
1146 fwRandomNum[i] = dist(gen);
1147 }
1148
1149 return ipmi::responseSuccess(fwRandomNum);
1150 }
1151
1152 /** @brief Set Firmware Update Mode
1153 *
1154 * This function sets BMC into firmware update mode
1155 * after validating Random number obtained from the Get
1156 * Firmware Update Random Number command
1157 *
1158 * @param[in] ctx - context of current session
1159 * @parameter randNum - Random number(token)
1160 * @returns IPMI completion code
1161 **/
1162 ipmi::RspType<>
ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr & ctx,std::array<uint8_t,fwRandomNumLength> & randNum)1163 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx,
1164 std::array<uint8_t, fwRandomNumLength>& randNum)
1165 {
1166 phosphor::logging::log<phosphor::logging::level::INFO>(
1167 "Start FW update mode");
1168
1169 bool isIPMBChannel = false;
1170
1171 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1172 {
1173 return ipmi::responseUnspecifiedError();
1174 }
1175 if (isIPMBChannel)
1176 {
1177 phosphor::logging::log<phosphor::logging::level::INFO>(
1178 "Channel not supported. Failed to set FW update mode");
1179 return ipmi::responseCommandNotAvailable();
1180 }
1181 /* Firmware Update Random number is valid for 30 seconds only */
1182 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1183 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1184 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1185 fwRandomNumExpirySeconds)
1186 .count())
1187 {
1188 phosphor::logging::log<phosphor::logging::level::INFO>(
1189 "Firmware update random number expired.");
1190 return ipmi::responseInvalidFieldRequest();
1191 }
1192
1193 /* Validate random number */
1194 for (size_t i = 0; i < fwRandomNumLength; i++)
1195 {
1196 if (fwRandomNum[i] != randNum[i])
1197 {
1198 phosphor::logging::log<phosphor::logging::level::INFO>(
1199 "Invalid random number specified.");
1200 return ipmi::responseInvalidFieldRequest();
1201 }
1202 }
1203
1204 try
1205 {
1206 if (getFirmwareUpdateMode())
1207 {
1208 phosphor::logging::log<phosphor::logging::level::INFO>(
1209 "Already firmware update is in progress.");
1210 return ipmi::responseBusy();
1211 }
1212 }
1213 catch (const std::exception& e)
1214 {
1215 return ipmi::responseUnspecifiedError();
1216 }
1217
1218 // FIXME? c++ doesn't off an option for exclusive file creation
1219 FILE* fp = fopen(firmwareBufferFile, "wx");
1220 if (!fp)
1221 {
1222 phosphor::logging::log<phosphor::logging::level::INFO>(
1223 "Unable to open file.");
1224 return ipmi::responseUnspecifiedError();
1225 }
1226 fclose(fp);
1227
1228 try
1229 {
1230 setFirmwareUpdateMode(true);
1231 }
1232 catch (const std::exception& e)
1233 {
1234 unlink(firmwareBufferFile);
1235 return ipmi::responseUnspecifiedError();
1236 }
1237
1238 return ipmi::responseSuccess();
1239 }
1240
1241 /** @brief implements exit firmware update mode command
1242 * @param None
1243 *
1244 * @returns IPMI completion code
1245 */
ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr & ctx)1246 ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx)
1247 {
1248 phosphor::logging::log<phosphor::logging::level::INFO>(
1249 "Exit FW update mode");
1250 bool isIPMBChannel = false;
1251
1252 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1253 {
1254 return ipmi::responseUnspecifiedError();
1255 }
1256 if (isIPMBChannel)
1257 {
1258 phosphor::logging::log<phosphor::logging::level::INFO>(
1259 "Command not supported. Failed to exit firmware update mode");
1260 return ipmi::responseCommandNotAvailable();
1261 }
1262
1263 switch (fwUpdateStatus.getState())
1264 {
1265 case FwUpdateStatusCache::fwStateInit:
1266 case FwUpdateStatusCache::fwStateIdle:
1267 return ipmi::responseInvalidFieldRequest();
1268 break;
1269 case FwUpdateStatusCache::fwStateDownload:
1270 case FwUpdateStatusCache::fwStateVerify:
1271 break;
1272 case FwUpdateStatusCache::fwStateProgram:
1273 break;
1274 case FwUpdateStatusCache::fwStateUpdateSuccess:
1275 case FwUpdateStatusCache::fwStateError:
1276 break;
1277 case FwUpdateStatusCache::fwStateAcCycleRequired:
1278 return ipmi::responseInvalidFieldRequest();
1279 break;
1280 }
1281 fwUpdateStatus.firmwareUpdateAbortState();
1282
1283 try
1284 {
1285 setFirmwareUpdateMode(false);
1286 }
1287 catch (const std::exception& e)
1288 {
1289 return ipmi::responseUnspecifiedError();
1290 }
1291
1292 return ipmi::responseSuccess();
1293 }
1294
1295 /** @brief implements Get/Set Firmware Update Control
1296 * @param[in] ctx - context of current session
1297 * @parameter
1298 * - Byte 1: Control Byte
1299 * - Byte 2: Firmware filename length (Optional)
1300 * - Byte 3:N: Firmware filename data (Optional)
1301 * @returns IPMI completion code plus response data
1302 * - Byte 2: Current control status
1303 **/
1304 ipmi::RspType<bool, bool, bool, bool, uint4_t>
ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr & ctx,const uint8_t controlReq,const std::optional<std::string> & fileName)1305 ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr& ctx,
1306 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 = std::chrono::steady_clock::now() -
1691 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