blob: ec1d14a7e2149a50c27577c0eaa4ec1e9df8da5d [file] [log] [blame]
srikanta mondal52a292b2020-07-27 23:49:14 +00001#include <byteswap.h>
Vernon Mauery52ce6622019-05-22 09:19:46 -07002#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
srikanta mondal52a292b2020-07-27 23:49:14 +000010#include <appcommands.hpp>
Vernon Mauery52ce6622019-05-22 09:19:46 -070011#include <boost/algorithm/string.hpp>
AppaRao Puli28972062019-11-11 02:04:45 +053012#include <boost/container/flat_map.hpp>
Vernon Mauery52ce6622019-05-22 09:19:46 -070013#include <boost/process/child.hpp>
14#include <boost/uuid/random_generator.hpp>
15#include <boost/uuid/uuid_io.hpp>
Vernon Mauery52ce6622019-05-22 09:19:46 -070016#include <commandutils.hpp>
anil kumar appana6c7d9382019-05-31 14:33:13 +000017#include <ipmid/api.hpp>
AppaRao Puli37fde6b2019-10-25 16:37:50 +053018#include <ipmid/utils.hpp>
AppaRao Puli28972062019-11-11 02:04:45 +053019#include <phosphor-logging/log.hpp>
Vernon Mauery52ce6622019-05-22 09:19:46 -070020#include <sdbusplus/bus.hpp>
21#include <sdbusplus/bus/match.hpp>
22#include <sdbusplus/server/object.hpp>
23#include <sdbusplus/timer.hpp>
Patrick Venturec2a07d42020-05-30 16:35:03 -070024#include <types.hpp>
James Feistfcd2d3a2020-05-28 10:38:15 -070025
26#include <chrono>
27#include <cstdint>
28#include <filesystem>
29#include <fstream>
30#include <iostream>
31#include <map>
32#include <random>
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +053033#ifdef INTEL_PFR_ENABLED
34#include <spiDev.hpp>
35#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -070036
AppaRao Puli28972062019-11-11 02:04:45 +053037static constexpr bool DEBUG = true;
38static void registerFirmwareFunctions() __attribute__((constructor));
39
AppaRao Puli37fde6b2019-10-25 16:37:50 +053040namespace ipmi
41{
42namespace firmware
43{
44constexpr Cmd cmdGetFwVersionInfo = 0x20;
AppaRao Puli28972062019-11-11 02:04:45 +053045constexpr Cmd cmdGetFwSecurityVersionInfo = 0x21;
46constexpr Cmd cmdGetFwUpdateChannelInfo = 0x22;
47constexpr Cmd cmdGetBmcExecutionContext = 0x23;
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +053048constexpr Cmd cmdFwGetRootCertData = 0x25;
AppaRao Puli28972062019-11-11 02:04:45 +053049constexpr Cmd cmdGetFwUpdateRandomNumber = 0x26;
50constexpr Cmd cmdSetFirmwareUpdateMode = 0x27;
51constexpr Cmd cmdExitFirmwareUpdateMode = 0x28;
52constexpr Cmd cmdGetSetFwUpdateControl = 0x29;
53constexpr Cmd cmdGetFirmwareUpdateStatus = 0x2A;
54constexpr Cmd cmdSetFirmwareUpdateOptions = 0x2B;
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +053055constexpr Cmd cmdFwImageWriteData = 0x2c;
AppaRao Puli37fde6b2019-10-25 16:37:50 +053056} // namespace firmware
57} // namespace ipmi
58
AppaRao Puli28972062019-11-11 02:04:45 +053059namespace ipmi
60{
61// Custom completion codes
62constexpr Cc ccUsbAttachOrDetachFailed = 0x80;
63constexpr Cc ccNotSupportedInPresentState = 0xD5;
64
65static inline auto responseUsbAttachOrDetachFailed()
66{
67 return response(ccUsbAttachOrDetachFailed);
68}
69static inline auto responseNotSupportedInPresentState()
70{
71 return response(ccNotSupportedInPresentState);
72}
73} // namespace ipmi
74
James Feistfcd2d3a2020-05-28 10:38:15 -070075static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC";
76static constexpr const char* bmcStatePath = "/xyz/openbmc_project/state/bmc0";
77static constexpr const char* bmcStateReady =
AppaRao Puli28972062019-11-11 02:04:45 +053078 "xyz.openbmc_project.State.BMC.BMCState.Ready";
James Feistfcd2d3a2020-05-28 10:38:15 -070079static constexpr const char* bmcStateUpdateInProgress =
AppaRao Puli28972062019-11-11 02:04:45 +053080 "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
81
82static constexpr char firmwareBufferFile[] = "/tmp/fw-download.bin";
83static std::chrono::steady_clock::time_point fwRandomNumGenTs;
84static constexpr auto fwRandomNumExpirySeconds = std::chrono::seconds(30);
85static constexpr size_t fwRandomNumLength = 8;
86static std::array<uint8_t, fwRandomNumLength> fwRandomNum;
87constexpr char usbCtrlPath[] = "/usr/bin/usb-ctrl";
88constexpr char fwUpdateMountPoint[] = "/tmp/usb-fwupd.mnt";
89constexpr char fwUpdateUsbVolImage[] = "/tmp/usb-fwupd.img";
90constexpr char fwUpdateUSBDevName[] = "fw-usb-mass-storage-dev";
91constexpr size_t fwPathMaxLength = 255;
92
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +053093#ifdef INTEL_PFR_ENABLED
94uint32_t imgLength = 0;
95uint32_t imgType = 0;
96bool block0Mapped = false;
97static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
AppaRao Puli37fde6b2019-10-25 16:37:50 +053098
James Feistfcd2d3a2020-05-28 10:38:15 -070099static constexpr const char* bmcActivePfmMTDDev = "/dev/mtd/pfm";
100static constexpr const char* bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
AppaRao Puli28972062019-11-11 02:04:45 +0530101static constexpr size_t pfmBaseOffsetInImage = 0x400;
102static constexpr size_t rootkeyOffsetInPfm = 0xA0;
103static constexpr size_t cskKeyOffsetInPfm = 0x124;
104static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
105static constexpr size_t certKeyLen = 96;
106static constexpr size_t cskSignatureLen = 96;
107
James Feistfcd2d3a2020-05-28 10:38:15 -0700108static constexpr const char* versionIntf =
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530109 "xyz.openbmc_project.Software.Version";
110
AppaRao Puli28972062019-11-11 02:04:45 +0530111enum class FwGetRootCertDataTag : uint8_t
112{
113 activeRootKey = 1,
114 recoveryRootKey,
115 activeCSK,
116 recoveryCSK,
117};
118
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530119enum class FWDeviceIDTag : uint8_t
120{
121 bmcActiveImage = 1,
122 bmcRecoveryImage,
123};
124
James Feistfcd2d3a2020-05-28 10:38:15 -0700125const static boost::container::flat_map<FWDeviceIDTag, const char*>
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530126 fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
127 "/xyz/openbmc_project/software/bmc_active"},
128 {FWDeviceIDTag::bmcRecoveryImage,
129 "/xyz/openbmc_project/software/bmc_recovery"}};
AppaRao Puli28972062019-11-11 02:04:45 +0530130#endif // INTEL_PFR_ENABLED
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530131
AppaRao Puli28972062019-11-11 02:04:45 +0530132enum class ChannelIdTag : uint8_t
133{
134 reserved = 0,
135 kcs = 1,
136 ipmb = 2,
137 rmcpPlus = 3
138};
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +0530139
AppaRao Puli28972062019-11-11 02:04:45 +0530140enum class BmcExecutionContext : uint8_t
141{
142 reserved = 0,
143 linuxOs = 0x10,
144 bootLoader = 0x11,
145};
AppaRao Puli09a83142019-11-23 02:46:06 +0530146
AppaRao Puli28972062019-11-11 02:04:45 +0530147enum class FwUpdateCtrlReq : uint8_t
148{
149 getCurrentControlStatus = 0x00,
150 imageTransferStart = 0x01,
151 imageTransferComplete = 0x02,
152 imageTransferAbort = 0x03,
153 setFirmwareFilename = 0x04,
154 attachUsbDevice = 0x05,
155 detachUsbDevice = 0x06
156};
Vernon Mauery52ce6622019-05-22 09:19:46 -0700157
158constexpr std::size_t operator""_MB(unsigned long long v)
159{
160 return 1024u * 1024u * v;
161}
AppaRao Puli28972062019-11-11 02:04:45 +0530162static constexpr size_t maxFirmwareImageSize = 32_MB;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700163
AppaRao Puli28972062019-11-11 02:04:45 +0530164static bool localDownloadInProgress(void)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700165{
166 struct stat sb;
AppaRao Puli28972062019-11-11 02:04:45 +0530167 if (stat(firmwareBufferFile, &sb) < 0)
168 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700169 return false;
AppaRao Puli28972062019-11-11 02:04:45 +0530170 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700171 return true;
172}
173
AppaRao Puli28972062019-11-11 02:04:45 +0530174class TransferHashCheck
175{
176 public:
177 enum class HashCheck : uint8_t
178 {
179 notRequested = 0,
180 requested,
181 sha2Success,
182 sha2Failed = 0xe2,
183 };
184
185 protected:
James Feistfcd2d3a2020-05-28 10:38:15 -0700186 EVP_MD_CTX* ctx;
AppaRao Puli28972062019-11-11 02:04:45 +0530187 std::vector<uint8_t> expectedHash;
188 enum HashCheck check;
189 bool started;
190
191 public:
192 TransferHashCheck() : check(HashCheck::notRequested), started(false)
James Feistfcd2d3a2020-05-28 10:38:15 -0700193 {}
AppaRao Puli28972062019-11-11 02:04:45 +0530194 ~TransferHashCheck()
195 {
196 if (ctx)
197 {
198 EVP_MD_CTX_destroy(ctx);
199 ctx = NULL;
200 }
201 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700202 void init(const std::vector<uint8_t>& expected)
AppaRao Puli28972062019-11-11 02:04:45 +0530203 {
204 expectedHash = expected;
205 check = HashCheck::requested;
206 ctx = EVP_MD_CTX_create();
207 EVP_DigestInit(ctx, EVP_sha256());
208 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700209 void hash(const std::vector<uint8_t>& data)
AppaRao Puli28972062019-11-11 02:04:45 +0530210 {
211 if (!started)
212 {
213 started = true;
214 }
215 EVP_DigestUpdate(ctx, data.data(), data.size());
216 }
217 void clear()
218 {
219 // if not started, nothing to clear
220 if (started)
221 {
222 if (ctx)
223 {
224 EVP_MD_CTX_destroy(ctx);
225 }
226 if (check != HashCheck::notRequested)
227 {
228 check = HashCheck::requested;
229 }
230 ctx = EVP_MD_CTX_create();
231 EVP_DigestInit(ctx, EVP_sha256());
232 }
233 }
234 enum HashCheck verify()
235 {
236 if (check == HashCheck::requested)
237 {
238 unsigned int len = 0;
239 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
240 EVP_DigestFinal(ctx, digest.data(), &len);
241 if (digest == expectedHash)
242 {
243 phosphor::logging::log<phosphor::logging::level::INFO>(
244 "Transfer sha2 verify passed.");
245 check = HashCheck::sha2Success;
246 }
247 else
248 {
249 phosphor::logging::log<phosphor::logging::level::ERR>(
250 "Transfer sha2 verify failed.");
251 check = HashCheck::sha2Failed;
252 }
253 }
254 return check;
255 }
256 uint8_t status() const
257 {
258 return static_cast<uint8_t>(check);
259 }
260};
261
262class MappedFile
263{
264 public:
James Feistfcd2d3a2020-05-28 10:38:15 -0700265 MappedFile(const std::string& fname) : addr(nullptr), fsize(0)
AppaRao Puli28972062019-11-11 02:04:45 +0530266 {
267 std::error_code ec;
268 size_t sz = std::filesystem::file_size(fname, ec);
269 int fd = open(fname.c_str(), O_RDONLY);
270 if (!ec || fd < 0)
271 {
272 return;
273 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700274 void* tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
AppaRao Puli28972062019-11-11 02:04:45 +0530275 close(fd);
276 if (tmp == MAP_FAILED)
277 {
278 return;
279 }
280 addr = tmp;
281 fsize = sz;
282 }
283
284 ~MappedFile()
285 {
286 if (addr)
287 {
288 munmap(addr, fsize);
289 }
290 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700291 const uint8_t* data() const
AppaRao Puli28972062019-11-11 02:04:45 +0530292 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700293 return static_cast<const uint8_t*>(addr);
AppaRao Puli28972062019-11-11 02:04:45 +0530294 }
295 size_t size() const
296 {
297 return fsize;
298 }
299
300 private:
301 size_t fsize;
James Feistfcd2d3a2020-05-28 10:38:15 -0700302 void* addr;
AppaRao Puli28972062019-11-11 02:04:45 +0530303};
304
305class FwUpdateStatusCache
Vernon Mauery52ce6622019-05-22 09:19:46 -0700306{
307 public:
308 enum
309 {
AppaRao Puli28972062019-11-11 02:04:45 +0530310 fwStateInit = 0,
311 fwStateIdle,
312 fwStateDownload,
313 fwStateVerify,
314 fwStateProgram,
315 fwStateUpdateSuccess,
316 fwStateError = 0x0f,
317 fwStateAcCycleRequired = 0x83,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700318 };
AppaRao Puli28972062019-11-11 02:04:45 +0530319 uint8_t getState()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700320 {
AppaRao Puli28972062019-11-11 02:04:45 +0530321 if ((fwUpdateState == fwStateIdle || fwUpdateState == fwStateInit) &&
322 localDownloadInProgress())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700323 {
AppaRao Puli28972062019-11-11 02:04:45 +0530324 fwUpdateState = fwStateDownload;
325 progressPercent = 0;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700326 }
AppaRao Puli28972062019-11-11 02:04:45 +0530327 return fwUpdateState;
328 }
329 void resetStatusCache()
330 {
331 unlink(firmwareBufferFile);
332 }
333 void setState(const uint8_t state)
334 {
335 switch (state)
336 {
337 case fwStateInit:
338 case fwStateIdle:
339 case fwStateError:
340 resetStatusCache();
341 break;
342 case fwStateDownload:
343 case fwStateVerify:
344 case fwStateProgram:
345 case fwStateUpdateSuccess:
346 break;
347 default:
348 // Error
349 break;
350 }
351 fwUpdateState = state;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700352 }
353 uint8_t percent()
354 {
AppaRao Puli28972062019-11-11 02:04:45 +0530355 return progressPercent;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700356 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700357 void updateActivationPercent(const std::string& objPath)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700358 {
AppaRao Puli28972062019-11-11 02:04:45 +0530359 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
360 fwUpdateState = fwStateProgram;
361 progressPercent = 0;
362 match = std::make_shared<sdbusplus::bus::match::match>(
363 *busp,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700364 sdbusplus::bus::match::rules::propertiesChanged(
AppaRao Puli28972062019-11-11 02:04:45 +0530365 objPath, "xyz.openbmc_project.Software.ActivationProgress"),
James Feistfcd2d3a2020-05-28 10:38:15 -0700366 [&](sdbusplus::message::message& msg) {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700367 std::map<std::string, ipmi::DbusVariant> props;
AppaRao Puli28972062019-11-11 02:04:45 +0530368 std::vector<std::string> inVal;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700369 std::string iface;
AppaRao Puli28972062019-11-11 02:04:45 +0530370 try
371 {
372 msg.read(iface, props, inVal);
373 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700374 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +0530375 {
376 phosphor::logging::log<phosphor::logging::level::ERR>(
377 "Exception caught in get ActivationProgress");
378 return;
379 }
380
381 auto it = props.find("Progress");
382 if (it != props.end())
383 {
384 progressPercent = std::get<uint8_t>(it->second);
385 if (progressPercent == 100)
386 {
387 fwUpdateState = fwStateUpdateSuccess;
388 }
389 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700390 });
391 }
AppaRao Puli28972062019-11-11 02:04:45 +0530392 uint8_t activationTimerTimeout()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700393 {
AppaRao Puli28972062019-11-11 02:04:45 +0530394 phosphor::logging::log<phosphor::logging::level::INFO>(
395 "activationTimerTimeout: Increase percentage...",
396 phosphor::logging::entry("PERCENT:%d", progressPercent));
397 progressPercent = progressPercent + 5;
jayaprakash Mutyalab8f2bf92020-01-27 23:17:39 +0000398 if (progressPercent > 95)
anil kumar appana31f88872019-08-02 15:16:27 +0000399 {
400 /*changing the state to ready to update firmware utility */
AppaRao Puli28972062019-11-11 02:04:45 +0530401 fwUpdateState = fwStateUpdateSuccess;
anil kumar appana31f88872019-08-02 15:16:27 +0000402 }
AppaRao Puli28972062019-11-11 02:04:45 +0530403 return progressPercent;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700404 }
anil kumar appana31f88872019-08-02 15:16:27 +0000405 /* API for changing state to ERROR */
406 void firmwareUpdateAbortState()
407 {
AppaRao Puli28972062019-11-11 02:04:45 +0530408 unlink(firmwareBufferFile);
anil kumar appana31f88872019-08-02 15:16:27 +0000409 // changing the state to error
AppaRao Puli28972062019-11-11 02:04:45 +0530410 fwUpdateState = fwStateError;
anil kumar appana31f88872019-08-02 15:16:27 +0000411 }
412 void setDeferRestart(bool deferRestart)
413 {
AppaRao Puli28972062019-11-11 02:04:45 +0530414 deferRestartState = deferRestart;
anil kumar appana31f88872019-08-02 15:16:27 +0000415 }
416 void setInhibitDowngrade(bool inhibitDowngrade)
417 {
AppaRao Puli28972062019-11-11 02:04:45 +0530418 inhibitDowngradeState = inhibitDowngrade;
anil kumar appana31f88872019-08-02 15:16:27 +0000419 }
420 bool getDeferRestart()
421 {
AppaRao Puli28972062019-11-11 02:04:45 +0530422 return deferRestartState;
anil kumar appana31f88872019-08-02 15:16:27 +0000423 }
424 bool getInhibitDowngrade()
425 {
AppaRao Puli28972062019-11-11 02:04:45 +0530426 return inhibitDowngradeState;
anil kumar appana31f88872019-08-02 15:16:27 +0000427 }
428
Vernon Mauery52ce6622019-05-22 09:19:46 -0700429 protected:
AppaRao Puli28972062019-11-11 02:04:45 +0530430 std::shared_ptr<sdbusplus::asio::connection> busp;
431 std::shared_ptr<sdbusplus::bus::match::match> match;
432 uint8_t fwUpdateState = 0;
433 uint8_t progressPercent = 0;
434 bool deferRestartState = false;
435 bool inhibitDowngradeState = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700436};
437
AppaRao Puli28972062019-11-11 02:04:45 +0530438static FwUpdateStatusCache fwUpdateStatus;
439std::shared_ptr<TransferHashCheck> xferHashCheck;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700440
James Feistfcd2d3a2020-05-28 10:38:15 -0700441static void activateImage(const std::string& objPath)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700442{
AppaRao Puli28972062019-11-11 02:04:45 +0530443 // If flag is false means to reboot
444 if (fwUpdateStatus.getDeferRestart() == false)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700445 {
AppaRao Puli28972062019-11-11 02:04:45 +0530446 phosphor::logging::log<phosphor::logging::level::INFO>(
447 "activating Image: ",
448 phosphor::logging::entry("OBJPATH =%s", objPath.c_str()));
449 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
450 bus->async_method_call(
451 [](const boost::system::error_code ec) {
452 if (ec)
453 {
454 phosphor::logging::log<phosphor::logging::level::ERR>(
455 "async_method_call error: activateImage failed");
456 return;
457 }
458 },
459 "xyz.openbmc_project.Software.BMC.Updater", objPath,
460 "org.freedesktop.DBus.Properties", "Set",
461 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
462 std::variant<std::string>("xyz.openbmc_project.Software.Activation."
463 "RequestedActivations.Active"));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700464 }
AppaRao Puli28972062019-11-11 02:04:45 +0530465 else
466 {
467 phosphor::logging::log<phosphor::logging::level::INFO>(
468 "Firmware image activation is deferred.");
469 }
470 fwUpdateStatus.setState(
471 static_cast<uint8_t>(FwUpdateStatusCache::fwStateUpdateSuccess));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700472}
473
AppaRao Puli09a83142019-11-23 02:46:06 +0530474static bool getFirmwareUpdateMode()
475{
476 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
477 try
478 {
479 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
480 ipmi::Value state = ipmi::getDbusProperty(
481 *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState");
482 std::string bmcState = std::get<std::string>(state);
483 return (bmcState == bmcStateUpdateInProgress);
484 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700485 catch (const std::exception& e)
AppaRao Puli09a83142019-11-23 02:46:06 +0530486 {
487 phosphor::logging::log<phosphor::logging::level::ERR>(
488 "Exception caught while getting BMC state.",
489 phosphor::logging::entry("EXCEPTION=%s", e.what()));
490 throw;
491 }
492}
493
494static void setFirmwareUpdateMode(const bool mode)
495{
496
497 std::string bmcState(bmcStateReady);
498 if (mode)
499 {
500 bmcState = bmcStateUpdateInProgress;
501 }
502
503 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
504 try
505 {
506 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
507 ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf,
508 "CurrentBMCState", bmcState);
509 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700510 catch (const std::exception& e)
AppaRao Puli09a83142019-11-23 02:46:06 +0530511 {
512 phosphor::logging::log<phosphor::logging::level::ERR>(
513 "Exception caught while setting BMC state.",
514 phosphor::logging::entry("EXCEPTION=%s", e.what()));
515 throw;
516 }
517}
518
AppaRao Pulie2cddf62020-01-31 00:30:08 +0530519/** @brief check if channel IPMB
520 *
521 * This function checks if the command is from IPMB
522 *
523 * @param[in] ctx - context of current session.
524 * @returns true if the medium is IPMB else return true.
525 **/
James Feistfcd2d3a2020-05-28 10:38:15 -0700526ipmi::Cc checkIPMBChannel(const ipmi::Context::ptr& ctx, bool& isIPMBChannel)
AppaRao Pulie2cddf62020-01-31 00:30:08 +0530527{
528 ipmi::ChannelInfo chInfo;
529
530 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
531 {
532 phosphor::logging::log<phosphor::logging::level::ERR>(
533 "Failed to get Channel Info",
534 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
535 return ipmi::ccUnspecifiedError;
536 }
537
538 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
539 ipmi::EChannelMediumType::ipmb)
540 {
541 isIPMBChannel = true;
542 }
543 return ipmi::ccSuccess;
544}
545
AppaRao Puli28972062019-11-11 02:04:45 +0530546static void postTransferCompleteHandler(
James Feistfcd2d3a2020-05-28 10:38:15 -0700547 std::unique_ptr<sdbusplus::bus::match::match>& fwUpdateMatchSignal)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700548{
549 // Setup timer for watching signal
550 static phosphor::Timer timer(
AppaRao Puli28972062019-11-11 02:04:45 +0530551 [&fwUpdateMatchSignal]() { fwUpdateMatchSignal = nullptr; });
Vernon Mauery52ce6622019-05-22 09:19:46 -0700552
AppaRao Puli28972062019-11-11 02:04:45 +0530553 static phosphor::Timer activationStatusTimer([]() {
jayaprakash Mutyalab8f2bf92020-01-27 23:17:39 +0000554 if (fwUpdateStatus.activationTimerTimeout() > 95)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700555 {
AppaRao Puli28972062019-11-11 02:04:45 +0530556 activationStatusTimer.stop();
557 fwUpdateStatus.setState(
558 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700559 }
560 });
561
562 timer.start(std::chrono::microseconds(5000000), false);
563
564 // callback function for capturing signal
James Feistfcd2d3a2020-05-28 10:38:15 -0700565 auto callback = [&](sdbusplus::message::message& m) {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700566 bool flag = false;
567
568 std::vector<std::pair<
569 std::string,
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700570 std::vector<std::pair<std::string, std::variant<std::string>>>>>
AppaRao Puli28972062019-11-11 02:04:45 +0530571 intfPropsPair;
572 sdbusplus::message::object_path objPath;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700573
574 try
575 {
AppaRao Puli28972062019-11-11 02:04:45 +0530576 m.read(objPath, intfPropsPair); // Read in the object path
577 // that was just created
Vernon Mauery52ce6622019-05-22 09:19:46 -0700578 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700579 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700580 {
AppaRao Puli28972062019-11-11 02:04:45 +0530581 phosphor::logging::log<phosphor::logging::level::ERR>(
582 "Exception caught in reading created object path.");
583 return;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700584 }
585 // constructing response message
AppaRao Puli28972062019-11-11 02:04:45 +0530586 phosphor::logging::log<phosphor::logging::level::INFO>(
587 "New Interface Added.",
588 phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
James Feistfcd2d3a2020-05-28 10:38:15 -0700589 for (auto& interface : intfPropsPair)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700590 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700591 if (interface.first == "xyz.openbmc_project.Software.Activation")
592 {
AppaRao Puli28972062019-11-11 02:04:45 +0530593 // There are chances of getting two signals for
594 // InterfacesAdded. So cross check and discrad second instance.
595 if (fwUpdateMatchSignal == nullptr)
596 {
597 return;
598 }
599 // Found our interface, disable callbacks
600 fwUpdateMatchSignal = nullptr;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700601
AppaRao Puli28972062019-11-11 02:04:45 +0530602 phosphor::logging::log<phosphor::logging::level::INFO>(
603 "Start activationStatusTimer for status.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700604 try
605 {
606 timer.stop();
AppaRao Puli28972062019-11-11 02:04:45 +0530607 activationStatusTimer.start(
Vernon Mauery52ce6622019-05-22 09:19:46 -0700608 std::chrono::microseconds(3000000), true);
609 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700610 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700611 {
AppaRao Puli28972062019-11-11 02:04:45 +0530612 phosphor::logging::log<phosphor::logging::level::ERR>(
613 "Exception caught in start activationStatusTimer.",
614 phosphor::logging::entry("ERROR=%s", e.what()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700615 }
616
AppaRao Puli28972062019-11-11 02:04:45 +0530617 fwUpdateStatus.updateActivationPercent(objPath.str);
618 activateImage(objPath.str);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700619 }
620 }
621 };
622
623 // Adding matcher
AppaRao Puli28972062019-11-11 02:04:45 +0530624 fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700625 *getSdBus(),
Vernon Mauery52ce6622019-05-22 09:19:46 -0700626 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
627 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
628 callback);
629}
James Feistfcd2d3a2020-05-28 10:38:15 -0700630static bool startFirmwareUpdate(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700631{
AppaRao Puli28972062019-11-11 02:04:45 +0530632 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
633 // the code gets to this point, the file should be transferred start the
634 // request (creating a new file in /tmp/images causes the update manager to
635 // check if it is ready for activation)
636 static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatchSignal;
637 postTransferCompleteHandler(fwUpdateMatchSignal);
638 std::filesystem::rename(
639 uri, "/tmp/images/" +
640 boost::uuids::to_string(boost::uuids::random_generator()()));
641 return true;
642}
Vernon Mauery52ce6622019-05-22 09:19:46 -0700643
James Feistfcd2d3a2020-05-28 10:38:15 -0700644static int transferImageFromFile(const std::string& uri, bool move = true)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700645{
646 std::error_code ec;
AppaRao Puli28972062019-11-11 02:04:45 +0530647 phosphor::logging::log<phosphor::logging::level::INFO>(
648 "Transfer Image From File.",
649 phosphor::logging::entry("URI=%s", uri.c_str()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700650 if (move)
651 {
AppaRao Puli28972062019-11-11 02:04:45 +0530652 std::filesystem::rename(uri, firmwareBufferFile, ec);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700653 }
654 else
655 {
AppaRao Puli28972062019-11-11 02:04:45 +0530656 std::filesystem::copy(uri, firmwareBufferFile,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700657 std::filesystem::copy_options::overwrite_existing,
658 ec);
659 }
AppaRao Puli28972062019-11-11 02:04:45 +0530660 if (xferHashCheck)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700661 {
662 MappedFile mappedfw(uri);
AppaRao Puli28972062019-11-11 02:04:45 +0530663 xferHashCheck->hash(
Vernon Mauery52ce6622019-05-22 09:19:46 -0700664 {mappedfw.data(), mappedfw.data() + mappedfw.size()});
665 }
666 if (ec.value())
667 {
AppaRao Puli28972062019-11-11 02:04:45 +0530668 phosphor::logging::log<phosphor::logging::level::ERR>(
669 "Image copy failed.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700670 }
671 return ec.value();
672}
673
674template <typename... ArgTypes>
James Feistfcd2d3a2020-05-28 10:38:15 -0700675static int executeCmd(const char* path, ArgTypes&&... tArgs)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700676{
James Feistfcd2d3a2020-05-28 10:38:15 -0700677 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700678 execProg.wait();
679 return execProg.exit_code();
680}
681
James Feistfcd2d3a2020-05-28 10:38:15 -0700682static int transferImageFromUsb(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700683{
684 int ret, sysret;
685 char fwpath[fwPathMaxLength];
AppaRao Puli28972062019-11-11 02:04:45 +0530686 phosphor::logging::log<phosphor::logging::level::INFO>(
687 "Transfer Image From USB.",
688 phosphor::logging::entry("URI=%s", uri.c_str()));
689 ret = executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
690 fwUpdateMountPoint);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700691 if (ret)
692 {
693 return ret;
694 }
695
AppaRao Puli28972062019-11-11 02:04:45 +0530696 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
697 ret = transferImageFromFile(usb_path, false);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700698
AppaRao Puli28972062019-11-11 02:04:45 +0530699 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage, fwUpdateMountPoint);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700700 return ret;
701}
702
James Feistfcd2d3a2020-05-28 10:38:15 -0700703static bool transferFirmwareFromUri(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700704{
AppaRao Puli28972062019-11-11 02:04:45 +0530705 static constexpr char fwUriFile[] = "file://";
706 static constexpr char fwUriUsb[] = "usb://";
707 phosphor::logging::log<phosphor::logging::level::INFO>(
708 "Transfer Image From URI.",
709 phosphor::logging::entry("URI=%s", uri.c_str()));
710 if (boost::algorithm::starts_with(uri, fwUriFile))
Vernon Mauery52ce6622019-05-22 09:19:46 -0700711 {
AppaRao Puli28972062019-11-11 02:04:45 +0530712 std::string fname = uri.substr(sizeof(fwUriFile) - 1);
713 if (fname != firmwareBufferFile)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700714 {
AppaRao Puli28972062019-11-11 02:04:45 +0530715 return 0 == transferImageFromFile(fname);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700716 }
717 return true;
718 }
AppaRao Puli28972062019-11-11 02:04:45 +0530719 if (boost::algorithm::starts_with(uri, fwUriUsb))
Vernon Mauery52ce6622019-05-22 09:19:46 -0700720 {
AppaRao Puli28972062019-11-11 02:04:45 +0530721 std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
722 return 0 == transferImageFromUsb(fname);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700723 }
724 return false;
725}
726
727/* Get USB-mass-storage device status: inserted => true, ejected => false */
AppaRao Puli28972062019-11-11 02:04:45 +0530728static bool getUsbStatus()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700729{
AppaRao Puli28972062019-11-11 02:04:45 +0530730 std::filesystem::path usbDevPath =
731 std::filesystem::path("/sys/kernel/config/usb_gadget") /
732 fwUpdateUSBDevName;
733 return (std::filesystem::exists(usbDevPath) ? true : false);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700734}
735
736/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
AppaRao Puli28972062019-11-11 02:04:45 +0530737static int attachUsbDevice()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700738{
AppaRao Puli28972062019-11-11 02:04:45 +0530739 if (getUsbStatus())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700740 {
741 return 1;
742 }
AppaRao Puli28972062019-11-11 02:04:45 +0530743 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
744 std::to_string(maxFirmwareImageSize / 1_MB).c_str());
Vernon Mauery52ce6622019-05-22 09:19:46 -0700745 if (!ret)
746 {
AppaRao Puli28972062019-11-11 02:04:45 +0530747 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
748 fwUpdateUsbVolImage);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700749 }
750 return ret;
751}
752
753/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
AppaRao Puli28972062019-11-11 02:04:45 +0530754static int detachUsbDevice()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700755{
AppaRao Puli28972062019-11-11 02:04:45 +0530756 if (!getUsbStatus())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700757 {
758 return 1;
759 }
AppaRao Puli28972062019-11-11 02:04:45 +0530760 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700761}
AppaRao Puli28972062019-11-11 02:04:45 +0530762static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700763{
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800764 constexpr uint8_t undefinedImage = 0x00;
AppaRao Puli28972062019-11-11 02:04:45 +0530765 constexpr uint8_t primaryImage = 0x01;
766 constexpr uint8_t secondaryImage = 0x02;
James Feistfcd2d3a2020-05-28 10:38:15 -0700767 constexpr const char* secondaryFitImageStartAddr = "22480000";
AppaRao Puli28972062019-11-11 02:04:45 +0530768
769 uint8_t bootImage = primaryImage;
770 boost::system::error_code ec;
771 std::string value = ctx->bus->yield_method_call<std::string>(
772 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
773 "/xyz/openbmc_project/u_boot/environment/mgr",
774 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
775 if (ec)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700776 {
AppaRao Puli28972062019-11-11 02:04:45 +0530777 phosphor::logging::log<phosphor::logging::level::ERR>(
778 "Failed to read the bootcmd value");
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800779 /* don't fail, just give back undefined until it is ready */
780 bootImage = undefinedImage;
AppaRao Puli28972062019-11-11 02:04:45 +0530781 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700782
AppaRao Puli28972062019-11-11 02:04:45 +0530783 /* cheking for secondary FitImage Address 22480000 */
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800784 else if (value.find(secondaryFitImageStartAddr) != std::string::npos)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700785 {
AppaRao Puli28972062019-11-11 02:04:45 +0530786 bootImage = secondaryImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700787 }
788 else
789 {
AppaRao Puli28972062019-11-11 02:04:45 +0530790 bootImage = primaryImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700791 }
792
AppaRao Puli28972062019-11-11 02:04:45 +0530793 return bootImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700794}
795
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530796#ifdef INTEL_PFR_ENABLED
797using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
798 uint8_t, // Major Version Number
799 uint8_t, // Minor Version Number
800 uint32_t, // Build Number
801 uint32_t, // Build Timestamp
802 uint32_t>; // Update Timestamp
803ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700804{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700805 // Byte 1 - Count (N) Number of devices data is being returned for.
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530806 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700807 // Bytes - 17:(15xN) - Repeat of 2 through 16
808
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530809 std::vector<fwVersionInfoType> fwVerInfoList;
810 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
James Feistfcd2d3a2020-05-28 10:38:15 -0700811 for (const auto& fwDev : fwVersionIdMap)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700812 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530813 std::string verStr;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700814 try
815 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530816 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700817
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530818 ipmi::Value result = ipmi::getDbusProperty(
819 *busp, service, fwDev.second, versionIntf, "Version");
820 verStr = std::get<std::string>(result);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700821 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700822 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700823 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530824 phosphor::logging::log<phosphor::logging::level::INFO>(
825 "Failed to fetch Version property",
826 phosphor::logging::entry("ERROR=%s", e.what()),
827 phosphor::logging::entry("PATH=%s", fwDev.second),
828 phosphor::logging::entry("INTERFACE=%s", versionIntf));
829 continue;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700830 }
831
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530832 if (verStr.empty())
833 {
834 phosphor::logging::log<phosphor::logging::level::INFO>(
835 "Version is empty.",
836 phosphor::logging::entry("PATH=%s", fwDev.second),
837 phosphor::logging::entry("INTERFACE=%s", versionIntf));
838 continue;
839 }
840
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530841 uint8_t majorNum = 0;
842 uint8_t minorNum = 0;
843 uint32_t buildNum = 0;
844 try
845 {
srikanta mondal52a292b2020-07-27 23:49:14 +0000846 std::optional<ipmi::MetaRevision> rev =
847 ipmi::convertIntelVersion(verStr);
848 if (rev.has_value())
849 {
850 ipmi::MetaRevision revision = rev.value();
851 majorNum = revision.major % 10 + (revision.major / 10) * 16;
852 minorNum = (revision.minor > 99 ? 99 : revision.minor);
853 minorNum = minorNum % 10 + (minorNum / 10) * 16;
854 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
855 hash = bswap_32(hash);
856 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
857 }
858 else
859 {
860 std::vector<std::string> splitVer;
861 boost::split(splitVer, verStr, boost::is_any_of(".-"));
862 if (splitVer.size() < 3)
863 {
864 phosphor::logging::log<phosphor::logging::level::INFO>(
865 "Invalid Version format.",
866 phosphor::logging::entry("Version=%s", verStr.c_str()),
867 phosphor::logging::entry("PATH=%s", fwDev.second));
868 continue;
869 }
870 majorNum = std::stoul(splitVer[0], nullptr, 16);
871 minorNum = std::stoul(splitVer[1], nullptr, 16);
872 buildNum = std::stoul(splitVer[2], nullptr, 16);
873 }
874 // Build Timestamp - Not supported.
875 // Update Timestamp - TODO: Need to check with CPLD team.
876 fwVerInfoList.emplace_back(
877 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
878 minorNum, buildNum, 0, 0));
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530879 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700880 catch (const std::exception& e)
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530881 {
882 phosphor::logging::log<phosphor::logging::level::INFO>(
883 "Failed to convert stoul.",
884 phosphor::logging::entry("ERROR=%s", e.what()));
885 continue;
886 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700887 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700888
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530889 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700890}
AppaRao Puli28972062019-11-11 02:04:45 +0530891using fwSecurityVersionInfoType = std::tuple<uint8_t, // ID Tag
892 uint8_t, // BKC Version
893 uint8_t>; // SVN Version
894ipmi::RspType<uint8_t, std::vector<fwSecurityVersionInfoType>>
895 ipmiGetFwSecurityVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700896{
AppaRao Puli28972062019-11-11 02:04:45 +0530897 // TODO: Need to add support.
898 return ipmi::responseInvalidCommand();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700899}
900
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530901ipmi::RspType<std::array<uint8_t, certKeyLen>,
902 std::optional<std::array<uint8_t, cskSignatureLen>>>
James Feistfcd2d3a2020-05-28 10:38:15 -0700903 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700904{
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +0530905 bool isIPMBChannel = false;
906
907 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
908 {
909 return ipmi::responseUnspecifiedError();
910 }
911 if (isIPMBChannel)
912 {
913 phosphor::logging::log<phosphor::logging::level::INFO>(
914 "Command not supported. Failed to get root certificate data.");
915 return ipmi::responseCommandNotAvailable();
916 }
917
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530918 size_t certKeyOffset = 0;
919 size_t cskSigOffset = 0;
920 std::string mtdDev;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700921
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530922 switch (static_cast<FwGetRootCertDataTag>(certId))
923 {
924 case FwGetRootCertDataTag::activeRootKey:
925 {
926 mtdDev = bmcActivePfmMTDDev;
927 certKeyOffset = rootkeyOffsetInPfm;
928 break;
929 }
930 case FwGetRootCertDataTag::recoveryRootKey:
931 {
932 mtdDev = bmcRecoveryImgMTDDev;
933 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
934 break;
935 }
936 case FwGetRootCertDataTag::activeCSK:
937 {
938 mtdDev = bmcActivePfmMTDDev;
939 certKeyOffset = cskKeyOffsetInPfm;
940 cskSigOffset = cskSignatureOffsetInPfm;
941 break;
942 }
943 case FwGetRootCertDataTag::recoveryCSK:
944 {
945 mtdDev = bmcRecoveryImgMTDDev;
946 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
947 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
948 break;
949 }
950 default:
951 {
952 return ipmi::responseInvalidFieldRequest();
953 }
954 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700955
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530956 std::array<uint8_t, certKeyLen> certKey = {0};
Vernon Mauery52ce6622019-05-22 09:19:46 -0700957
Vernon Mauery52ce6622019-05-22 09:19:46 -0700958 try
959 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530960 SPIDev spiDev(mtdDev);
961 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
962
963 if (cskSigOffset)
964 {
965 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
966 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
967 cskSignature.data());
968 return ipmi::responseSuccess(certKey, cskSignature);
969 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700970 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700971 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700972 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530973 phosphor::logging::log<phosphor::logging::level::ERR>(
974 "Exception caught in ipmiGetFwRootCertData",
975 phosphor::logging::entry("MSG=%s", e.what()));
976 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700977 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700978
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530979 return ipmi::responseSuccess(certKey, std::nullopt);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700980}
AppaRao Puli28972062019-11-11 02:04:45 +0530981#endif // INTEL_PFR_ENABLED
982
983static constexpr uint8_t channelListSize = 3;
984/** @brief implements Maximum Firmware Transfer size command
985 * @parameter
986 * - none
987 * @returns IPMI completion code plus response data
988 * - count - channel count
989 * - channelList - channel list information
990 */
991ipmi::RspType<uint8_t, // channel count
992 std::array<std::tuple<uint8_t, uint32_t>,
993 channelListSize> // Channel List
994 >
995 ipmiFirmwareMaxTransferSize()
996{
997 constexpr size_t kcsMaxBufSize = 128;
998 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
AppaRao Puli28972062019-11-11 02:04:45 +0530999 // Byte 1 - Count (N) Number of devices data is being returned for.
1000 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
1001 // Byte 3-6 - transfer size (little endian)
1002 // Bytes - 7:(5xN) - Repeat of 2 through 6
1003 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1004 channelList = {
1005 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
AppaRao Puli28972062019-11-11 02:04:45 +05301006 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
1007 rmcpPlusMaxBufSize}}};
1008
1009 return ipmi::responseSuccess(channelListSize, channelList);
1010}
1011
1012ipmi::RspType<uint8_t, uint8_t>
1013 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
1014{
1015 // Byte 1 - Current execution context
1016 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
1017 // Byte 2 - Partition pointer
1018 // 0x01 - primary, 0x02 - secondary
1019 uint8_t partitionPtr = getActiveBootImage(ctx);
1020
1021 return ipmi::responseSuccess(
1022 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
1023}
AppaRao Puli28972062019-11-11 02:04:45 +05301024/** @brief Get Firmware Update Random Number
1025 *
1026 * This function generate the random number used for
1027 * setting the firmware update mode as authentication key.
1028 *
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301029 * @param[in] ctx - context of current session
AppaRao Puli28972062019-11-11 02:04:45 +05301030 * @returns IPMI completion code along with
1031 * - random number
1032 **/
1033ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
James Feistfcd2d3a2020-05-28 10:38:15 -07001034 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx)
AppaRao Puli28972062019-11-11 02:04:45 +05301035{
1036 phosphor::logging::log<phosphor::logging::level::INFO>(
1037 "Generate FW update random number");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301038 bool isIPMBChannel = false;
1039
1040 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1041 {
1042 return ipmi::responseUnspecifiedError();
1043 }
1044 if (isIPMBChannel)
1045 {
1046 phosphor::logging::log<phosphor::logging::level::INFO>(
1047 "Channel not supported. Failed to fetch FW update random number");
1048 return ipmi::responseCommandNotAvailable();
1049 }
AppaRao Puli28972062019-11-11 02:04:45 +05301050 std::random_device rd;
1051 std::default_random_engine gen(rd());
1052 std::uniform_int_distribution<> dist{0, 255};
1053
1054 fwRandomNumGenTs = std::chrono::steady_clock::now();
1055
1056 for (int i = 0; i < fwRandomNumLength; i++)
1057 {
1058 fwRandomNum[i] = dist(gen);
1059 }
1060
1061 return ipmi::responseSuccess(fwRandomNum);
1062}
1063
1064/** @brief Set Firmware Update Mode
1065 *
1066 * This function sets BMC into firmware update mode
1067 * after validating Random number obtained from the Get
1068 * Firmware Update Random Number command
1069 *
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301070 * @param[in] ctx - context of current session
1071 * @parameter randNum - Random number(token)
1072 * @returns IPMI completion code
AppaRao Puli28972062019-11-11 02:04:45 +05301073 **/
1074ipmi::RspType<>
James Feistfcd2d3a2020-05-28 10:38:15 -07001075 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx,
1076 std::array<uint8_t, fwRandomNumLength>& randNum)
AppaRao Puli28972062019-11-11 02:04:45 +05301077{
1078 phosphor::logging::log<phosphor::logging::level::INFO>(
1079 "Start FW update mode");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301080
1081 bool isIPMBChannel = false;
1082
1083 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1084 {
1085 return ipmi::responseUnspecifiedError();
1086 }
1087 if (isIPMBChannel)
1088 {
1089 phosphor::logging::log<phosphor::logging::level::INFO>(
1090 "Channel not supported. Failed to set FW update mode");
1091 return ipmi::responseCommandNotAvailable();
1092 }
AppaRao Puli28972062019-11-11 02:04:45 +05301093 /* Firmware Update Random number is valid for 30 seconds only */
1094 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1095 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1096 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1097 fwRandomNumExpirySeconds)
1098 .count())
1099 {
1100 phosphor::logging::log<phosphor::logging::level::INFO>(
1101 "Firmware update random number expired.");
1102 return ipmi::responseInvalidFieldRequest();
1103 }
1104
1105 /* Validate random number */
1106 for (int i = 0; i < fwRandomNumLength; i++)
1107 {
1108 if (fwRandomNum[i] != randNum[i])
1109 {
1110 phosphor::logging::log<phosphor::logging::level::INFO>(
1111 "Invalid random number specified.");
1112 return ipmi::responseInvalidFieldRequest();
1113 }
1114 }
1115
1116 try
1117 {
1118 if (getFirmwareUpdateMode())
1119 {
1120 phosphor::logging::log<phosphor::logging::level::INFO>(
1121 "Already firmware update is in progress.");
1122 return ipmi::responseBusy();
1123 }
1124 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001125 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301126 {
1127 return ipmi::responseUnspecifiedError();
1128 }
1129
1130 // FIXME? c++ doesn't off an option for exclusive file creation
James Feistfcd2d3a2020-05-28 10:38:15 -07001131 FILE* fp = fopen(firmwareBufferFile, "wx");
AppaRao Puli28972062019-11-11 02:04:45 +05301132 if (!fp)
1133 {
1134 phosphor::logging::log<phosphor::logging::level::INFO>(
1135 "Unable to open file.");
1136 return ipmi::responseUnspecifiedError();
1137 }
1138 fclose(fp);
1139
1140 try
1141 {
1142 setFirmwareUpdateMode(true);
1143 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001144 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301145 {
1146 unlink(firmwareBufferFile);
1147 return ipmi::responseUnspecifiedError();
1148 }
1149
1150 return ipmi::responseSuccess();
1151}
1152
1153/** @brief implements exit firmware update mode command
1154 * @param None
1155 *
1156 * @returns IPMI completion code
1157 */
James Feistfcd2d3a2020-05-28 10:38:15 -07001158ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx)
AppaRao Puli28972062019-11-11 02:04:45 +05301159{
1160 phosphor::logging::log<phosphor::logging::level::INFO>(
1161 "Exit FW update mode");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301162 bool isIPMBChannel = false;
1163
1164 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1165 {
1166 return ipmi::responseUnspecifiedError();
1167 }
1168 if (isIPMBChannel)
1169 {
1170 phosphor::logging::log<phosphor::logging::level::INFO>(
1171 "Command not supported. Failed to exit firmware update mode");
1172 return ipmi::responseCommandNotAvailable();
1173 }
1174
AppaRao Puli28972062019-11-11 02:04:45 +05301175 switch (fwUpdateStatus.getState())
1176 {
1177 case FwUpdateStatusCache::fwStateInit:
1178 case FwUpdateStatusCache::fwStateIdle:
1179 return ipmi::responseInvalidFieldRequest();
1180 break;
1181 case FwUpdateStatusCache::fwStateDownload:
1182 case FwUpdateStatusCache::fwStateVerify:
1183 break;
1184 case FwUpdateStatusCache::fwStateProgram:
1185 break;
1186 case FwUpdateStatusCache::fwStateUpdateSuccess:
1187 case FwUpdateStatusCache::fwStateError:
1188 break;
1189 case FwUpdateStatusCache::fwStateAcCycleRequired:
1190 return ipmi::responseInvalidFieldRequest();
1191 break;
1192 }
1193 fwUpdateStatus.firmwareUpdateAbortState();
1194
1195 try
1196 {
1197 setFirmwareUpdateMode(false);
1198 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001199 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301200 {
1201 return ipmi::responseUnspecifiedError();
1202 }
1203
1204 return ipmi::responseSuccess();
1205}
1206
1207/** @brief implements Get/Set Firmware Update Control
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301208 * @param[in] ctx - context of current session
AppaRao Puli28972062019-11-11 02:04:45 +05301209 * @parameter
1210 * - Byte 1: Control Byte
1211 * - Byte 2: Firmware filename length (Optional)
1212 * - Byte 3:N: Firmware filename data (Optional)
1213 * @returns IPMI completion code plus response data
1214 * - Byte 2: Current control status
1215 **/
1216ipmi::RspType<bool, bool, bool, bool, uint4_t>
James Feistfcd2d3a2020-05-28 10:38:15 -07001217 ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr& ctx,
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301218 const uint8_t controlReq,
James Feistfcd2d3a2020-05-28 10:38:15 -07001219 const std::optional<std::string>& fileName)
AppaRao Puli28972062019-11-11 02:04:45 +05301220{
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301221 bool isIPMBChannel = false;
1222
1223 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1224 {
1225 return ipmi::responseUnspecifiedError();
1226 }
1227 if (isIPMBChannel)
1228 {
1229 phosphor::logging::log<phosphor::logging::level::INFO>(
1230 "Channel not supported. Failed to get or set FW update control");
1231 return ipmi::responseCommandNotAvailable();
1232 }
1233
AppaRao Puli28972062019-11-11 02:04:45 +05301234 static std::string fwXferUriPath;
1235 static bool imageTransferStarted = false;
1236 static bool imageTransferCompleted = false;
1237 static bool imageTransferAborted = false;
1238
1239 if ((controlReq !=
1240 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
1241 (fileName))
1242 {
1243 phosphor::logging::log<phosphor::logging::level::ERR>(
1244 "Invalid request field (Filename).");
1245 return ipmi::responseInvalidFieldRequest();
1246 }
1247
1248 static bool usbAttached = getUsbStatus();
1249
1250 switch (static_cast<FwUpdateCtrlReq>(controlReq))
1251 {
1252 case FwUpdateCtrlReq::getCurrentControlStatus:
1253 phosphor::logging::log<phosphor::logging::level::INFO>(
1254 "ipmiGetSetFirmwareUpdateControl: Get status");
1255 break;
1256 case FwUpdateCtrlReq::imageTransferStart:
1257 {
1258 phosphor::logging::log<phosphor::logging::level::INFO>(
1259 "ipmiGetSetFirmwareUpdateControl: Set transfer start");
1260 imageTransferStarted = true;
1261 // reset buffer to empty (truncate file)
1262 std::ofstream out(firmwareBufferFile,
1263 std::ofstream::binary | std::ofstream::trunc);
1264 fwXferUriPath = std::string("file://") + firmwareBufferFile;
1265 if (xferHashCheck)
1266 {
1267 xferHashCheck->clear();
1268 }
1269 // Setting state to download
1270 fwUpdateStatus.setState(
1271 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
1272#ifdef INTEL_PFR_ENABLED
1273 imgLength = 0;
1274 imgType = 0;
1275 block0Mapped = false;
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301276#endif
AppaRao Puli28972062019-11-11 02:04:45 +05301277 }
1278 break;
1279 case FwUpdateCtrlReq::imageTransferComplete:
1280 {
1281 phosphor::logging::log<phosphor::logging::level::INFO>(
1282 "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
1283 if (usbAttached)
1284 {
1285 phosphor::logging::log<phosphor::logging::level::ERR>(
1286 "USB should be detached to perform this operation.");
1287 return ipmi::responseNotSupportedInPresentState();
1288 }
1289 // finish transfer based on URI
1290 if (!transferFirmwareFromUri(fwXferUriPath))
1291 {
1292 phosphor::logging::log<phosphor::logging::level::ERR>(
1293 "transferFirmwareFromUri failed.");
1294 return ipmi::responseUnspecifiedError();
1295 }
1296 // transfer complete
1297 if (xferHashCheck)
1298 {
1299 if (TransferHashCheck::HashCheck::sha2Success !=
1300 xferHashCheck->verify())
1301 {
1302 phosphor::logging::log<phosphor::logging::level::ERR>(
1303 "xferHashCheck failed.");
1304 return ipmi::responseUnspecifiedError();
1305 }
1306 }
1307 // Set state to verify and start the update
1308 fwUpdateStatus.setState(
1309 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
1310 // start the request
1311 if (!startFirmwareUpdate(firmwareBufferFile))
1312 {
1313 phosphor::logging::log<phosphor::logging::level::ERR>(
1314 "startFirmwareUpdate failed.");
1315 return ipmi::responseUnspecifiedError();
1316 }
1317 imageTransferCompleted = true;
1318 }
1319 break;
1320 case FwUpdateCtrlReq::imageTransferAbort:
1321 phosphor::logging::log<phosphor::logging::level::INFO>(
1322 "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
1323 if (usbAttached)
1324 {
1325 if (detachUsbDevice())
1326 {
1327 phosphor::logging::log<phosphor::logging::level::ERR>(
1328 "Detach USB device failed.");
1329 return ipmi::responseUsbAttachOrDetachFailed();
1330 }
1331 usbAttached = false;
1332 }
1333 // During abort request reset the state to Init by cleaning update
1334 // file.
1335 fwUpdateStatus.firmwareUpdateAbortState();
1336 imageTransferAborted = true;
1337 break;
1338 case FwUpdateCtrlReq::setFirmwareFilename:
1339 phosphor::logging::log<phosphor::logging::level::INFO>(
1340 "ipmiGetSetFirmwareUpdateControl: Set filename.");
1341 if (!fileName || ((*fileName).length() == 0))
1342 {
1343 phosphor::logging::log<phosphor::logging::level::ERR>(
1344 "Invalid Filename specified.");
1345 return ipmi::responseInvalidFieldRequest();
1346 }
1347
1348 fwXferUriPath = *fileName;
1349 break;
1350 case FwUpdateCtrlReq::attachUsbDevice:
1351 phosphor::logging::log<phosphor::logging::level::INFO>(
1352 "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
1353 if (usbAttached)
1354 {
1355 phosphor::logging::log<phosphor::logging::level::ERR>(
1356 "USB device is already attached.");
1357 return ipmi::responseInvalidFieldRequest();
1358 }
1359 if (attachUsbDevice())
1360 {
1361 phosphor::logging::log<phosphor::logging::level::ERR>(
1362 "Attach USB device failed.");
1363 return ipmi::responseUsbAttachOrDetachFailed();
1364 }
1365 usbAttached = true;
1366 break;
1367 case FwUpdateCtrlReq::detachUsbDevice:
1368 phosphor::logging::log<phosphor::logging::level::INFO>(
1369 "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
1370 if (!usbAttached)
1371 {
1372 phosphor::logging::log<phosphor::logging::level::ERR>(
1373 "USB device is not attached.");
1374 return ipmi::responseInvalidFieldRequest();
1375 }
1376 if (detachUsbDevice())
1377 {
1378 phosphor::logging::log<phosphor::logging::level::ERR>(
1379 "Detach USB device failed.");
1380 return ipmi::responseUsbAttachOrDetachFailed();
1381 }
1382 usbAttached = false;
1383 break;
1384 default:
1385 phosphor::logging::log<phosphor::logging::level::ERR>(
1386 "Invalid control option specified.");
1387 return ipmi::responseInvalidFieldRequest();
1388 }
1389
1390 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
1391 imageTransferAborted, usbAttached, uint4_t(0));
1392}
1393
1394/** @brief implements firmware get status command
1395 * @parameter
1396 * - none
1397 * @returns IPMI completion code plus response data
1398 * - status - processing status
1399 * - percentage - percentage completion
1400 * - check - channel integrity check status
1401 **/
1402ipmi::RspType<uint8_t, // status
1403 uint8_t, // percentage
1404 uint8_t // check
1405 >
1406 ipmiGetFirmwareUpdateStatus()
1407
1408{
1409 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1410 // 5=ready, f=error, 83=ac cycle required)
1411 // Byte 2 - percent
1412 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1413 uint8_t status = fwUpdateStatus.getState();
1414 uint8_t percent = fwUpdateStatus.percent();
1415 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
1416
1417 // Status code.
1418 return ipmi::responseSuccess(status, percent, check);
1419}
1420
1421ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
James Feistfcd2d3a2020-05-28 10:38:15 -07001422 const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask,
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301423 bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart,
1424 bool sha2Check, uint5_t reserved2,
1425 std::optional<std::vector<uint8_t>> integrityCheckVal)
AppaRao Puli28972062019-11-11 02:04:45 +05301426{
1427 phosphor::logging::log<phosphor::logging::level::INFO>(
1428 "Set firmware update options.");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301429 bool isIPMBChannel = false;
1430
1431 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1432 {
1433 return ipmi::responseUnspecifiedError();
1434 }
1435 if (isIPMBChannel)
1436 {
1437 phosphor::logging::log<phosphor::logging::level::INFO>(
1438 "Channel not supported. Failed to set firmware update options");
1439 return ipmi::responseCommandNotAvailable();
1440 }
AppaRao Puli28972062019-11-11 02:04:45 +05301441 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
1442 bool deferRestartState = fwUpdateStatus.getDeferRestart();
1443 bool sha2CheckState = xferHashCheck ? true : false;
1444
1445 if (noDowngradeMask && (noDowngradeState != noDowngrade))
1446 {
1447 fwUpdateStatus.setInhibitDowngrade(noDowngrade);
1448 noDowngradeState = noDowngrade;
1449 }
1450 if (deferRestartMask && (deferRestartState != deferRestart))
1451 {
1452 fwUpdateStatus.setDeferRestart(deferRestart);
1453 deferRestartState = deferRestart;
1454 }
1455 if (sha2CheckMask)
1456 {
1457 if (sha2Check)
1458 {
1459 auto hashSize = EVP_MD_size(EVP_sha256());
1460 if ((*integrityCheckVal).size() != hashSize)
1461 {
1462 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1463 "Invalid size of Hash specified.");
1464 return ipmi::responseInvalidFieldRequest();
1465 }
1466 xferHashCheck = std::make_shared<TransferHashCheck>();
1467 xferHashCheck->init(*integrityCheckVal);
1468 }
1469 else
1470 {
1471 // delete the xferHashCheck object
1472 xferHashCheck.reset();
1473 }
1474 sha2CheckState = sha2CheckMask;
1475 }
1476 return ipmi::responseSuccess(noDowngradeState, deferRestartState,
1477 sha2CheckState, reserved1);
1478}
Vernon Mauery52ce6622019-05-22 09:19:46 -07001479
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301480ipmi::RspType<uint32_t>
James Feistfcd2d3a2020-05-28 10:38:15 -07001481 ipmiFwImageWriteData(const std::vector<uint8_t>& writeData)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001482{
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301483 const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1484 size_t writeDataLen = writeData.size();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001485
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301486 if (!writeDataLen)
1487 {
1488 return ipmi::responseReqDataLenInvalid();
1489 }
1490
AppaRao Puli28972062019-11-11 02:04:45 +05301491 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301492 {
AppaRao Puli28972062019-11-11 02:04:45 +05301493 phosphor::logging::log<phosphor::logging::level::ERR>(
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301494 "Invalid firmware update state.");
1495 return ipmi::response(ccCmdNotSupportedInPresentState);
1496 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001497
AppaRao Puli28972062019-11-11 02:04:45 +05301498 std::ofstream out(firmwareBufferFile,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001499 std::ofstream::binary | std::ofstream::app);
1500 if (!out)
1501 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301502 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1503 "Error while opening file.");
1504 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001505 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301506
1507 uint64_t fileDataLen = out.tellp();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301508
AppaRao Puli28972062019-11-11 02:04:45 +05301509 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001510 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301511 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1512 "Firmware image size exceeds the limit");
1513 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001514 }
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301515
James Feistfcd2d3a2020-05-28 10:38:15 -07001516 const char* data = reinterpret_cast<const char*>(writeData.data());
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301517 out.write(data, writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001518 out.close();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301519
AppaRao Puli28972062019-11-11 02:04:45 +05301520 if (xferHashCheck)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001521 {
AppaRao Puli28972062019-11-11 02:04:45 +05301522 xferHashCheck->hash(writeData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001523 }
1524
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301525#ifdef INTEL_PFR_ENABLED
1526 /* PFR image block 0 - As defined in HAS */
1527 struct PFRImageBlock0
1528 {
1529 uint32_t tag;
1530 uint32_t pcLength;
1531 uint32_t pcType;
1532 uint32_t reserved1;
1533 uint8_t hash256[32];
1534 uint8_t hash384[48];
1535 uint8_t reserved2[32];
1536 } __attribute__((packed));
1537
1538 /* Get the PFR block 0 data and read the uploaded image
1539 * information( Image type, length etc) */
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301540 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1541 (!block0Mapped))
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301542 {
1543 struct PFRImageBlock0 block0Data = {0};
1544
AppaRao Puli28972062019-11-11 02:04:45 +05301545 std::ifstream inFile(firmwareBufferFile,
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301546 std::ios::binary | std::ios::in);
James Feistfcd2d3a2020-05-28 10:38:15 -07001547 inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data));
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301548 inFile.close();
1549
1550 uint32_t magicNum = block0Data.tag;
1551
1552 /* Validate the magic number */
1553 if (magicNum != perBlock0MagicNum)
1554 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301555 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1556 "PFR image magic number not matched");
1557 return ipmi::responseInvalidFieldRequest();
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301558 }
1559 // Note:imgLength, imgType and block0Mapped are in global scope, as
1560 // these are used in cascaded updates.
1561 imgLength = block0Data.pcLength;
1562 imgType = block0Data.pcType;
1563 block0Mapped = true;
1564 }
1565#endif // end of INTEL_PFR_ENABLED
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301566 return ipmi::responseSuccess(writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001567}
1568
AppaRao Puli28972062019-11-11 02:04:45 +05301569static void registerFirmwareFunctions()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001570{
1571 // guarantee that we start with an already timed out timestamp
AppaRao Puli28972062019-11-11 02:04:45 +05301572 fwRandomNumGenTs =
1573 std::chrono::steady_clock::now() - fwRandomNumExpirySeconds;
1574 fwUpdateStatus.setState(
1575 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
Vernon Mauery52ce6622019-05-22 09:19:46 -07001576
AppaRao Puli28972062019-11-11 02:04:45 +05301577 unlink(firmwareBufferFile);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001578
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301579#ifdef INTEL_PFR_ENABLED
1580 // Following commands are supported only for PFR enabled platforms
1581 // CMD:0x20 - Get Firmware Version Information
AppaRao Puli28972062019-11-11 02:04:45 +05301582 // CMD:0x21 - Get Firmware Security Version Information
1583 // CMD:0x25 - Get Root Certificate Data
Vernon Mauery52ce6622019-05-22 09:19:46 -07001584
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301585 // get firmware version information
1586 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1587 ipmi::firmware::cmdGetFwVersionInfo,
1588 ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
AppaRao Puli28972062019-11-11 02:04:45 +05301589
Vernon Mauery52ce6622019-05-22 09:19:46 -07001590 // get firmware security version information
AppaRao Puli28972062019-11-11 02:04:45 +05301591 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1592 ipmi::firmware::cmdGetFwSecurityVersionInfo,
1593 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001594
Vernon Mauery52ce6622019-05-22 09:19:46 -07001595 // get root certificate data
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301596 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1597 ipmi::firmware::cmdFwGetRootCertData,
1598 ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1599#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001600
AppaRao Puli28972062019-11-11 02:04:45 +05301601 // get firmware update channel information (max transfer sizes)
1602 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1603 ipmi::firmware::cmdGetFwUpdateChannelInfo,
1604 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001605
AppaRao Puli28972062019-11-11 02:04:45 +05301606 // get bmc execution context
1607 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1608 ipmi::firmware::cmdGetBmcExecutionContext,
1609 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
1610
1611 // Get Firmware Update Random number
1612 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1613 ipmi::firmware::cmdGetFwUpdateRandomNumber,
1614 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
1615
1616 // Set Firmware Update Mode
1617 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1618 ipmi::firmware::cmdSetFirmwareUpdateMode,
AppaRao Puli4b3e1c72019-10-16 20:53:09 +05301619 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001620
AppaRao Puli28972062019-11-11 02:04:45 +05301621 // Exit Firmware Update Mode
anil kumar appanab57098a2019-05-28 16:22:33 +00001622 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
AppaRao Puli28972062019-11-11 02:04:45 +05301623 ipmi::firmware::cmdExitFirmwareUpdateMode,
1624 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001625
AppaRao Puli28972062019-11-11 02:04:45 +05301626 // Get/Set Firmware Update Control
1627 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1628 ipmi::firmware::cmdGetSetFwUpdateControl,
1629 ipmi::Privilege::Admin,
1630 ipmiGetSetFirmwareUpdateControl);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001631
AppaRao Puli28972062019-11-11 02:04:45 +05301632 // Get Firmware Update Status
1633 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1634 ipmi::firmware::cmdGetFirmwareUpdateStatus,
1635 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001636
AppaRao Puli28972062019-11-11 02:04:45 +05301637 // Set Firmware Update Options
1638 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1639 ipmi::firmware::cmdSetFirmwareUpdateOptions,
1640 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001641 // write image data
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301642 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1643 ipmi::firmware::cmdFwImageWriteData,
1644 ipmi::Privilege::Admin, ipmiFwImageWriteData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001645 return;
1646}