blob: 809b87a5f427d7193a52289d293af6c8f00c0976 [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
Punith Nadur Basavarajaiaha8dd1972020-08-19 18:05:44 +000075static constexpr size_t imageCount = 2;
76std::array<std::array<uint8_t, imageCount>, imageCount> imgFwSecurityVersion = {
77 (0, 0), (0, 0)};
78static constexpr size_t svnActiveVerOffsetInPfm = 0x404;
79static constexpr size_t bkcActiveVerOffsetInPfm = 0x405;
80static constexpr size_t svnRecoveryVerOffsetInPfm = 0x804;
81static constexpr size_t bkcRecoveryVerOffsetInPfm = 0x805;
James Feistfcd2d3a2020-05-28 10:38:15 -070082static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC";
83static constexpr const char* bmcStatePath = "/xyz/openbmc_project/state/bmc0";
84static constexpr const char* bmcStateReady =
AppaRao Puli28972062019-11-11 02:04:45 +053085 "xyz.openbmc_project.State.BMC.BMCState.Ready";
James Feistfcd2d3a2020-05-28 10:38:15 -070086static constexpr const char* bmcStateUpdateInProgress =
AppaRao Puli28972062019-11-11 02:04:45 +053087 "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
88
89static constexpr char firmwareBufferFile[] = "/tmp/fw-download.bin";
90static std::chrono::steady_clock::time_point fwRandomNumGenTs;
91static constexpr auto fwRandomNumExpirySeconds = std::chrono::seconds(30);
92static constexpr size_t fwRandomNumLength = 8;
93static std::array<uint8_t, fwRandomNumLength> fwRandomNum;
94constexpr char usbCtrlPath[] = "/usr/bin/usb-ctrl";
95constexpr char fwUpdateMountPoint[] = "/tmp/usb-fwupd.mnt";
96constexpr char fwUpdateUsbVolImage[] = "/tmp/usb-fwupd.img";
97constexpr char fwUpdateUSBDevName[] = "fw-usb-mass-storage-dev";
98constexpr size_t fwPathMaxLength = 255;
99
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +0530100#ifdef INTEL_PFR_ENABLED
101uint32_t imgLength = 0;
102uint32_t imgType = 0;
103bool block0Mapped = false;
104static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530105
James Feistfcd2d3a2020-05-28 10:38:15 -0700106static constexpr const char* bmcActivePfmMTDDev = "/dev/mtd/pfm";
107static constexpr const char* bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
AppaRao Puli28972062019-11-11 02:04:45 +0530108static constexpr size_t pfmBaseOffsetInImage = 0x400;
109static constexpr size_t rootkeyOffsetInPfm = 0xA0;
110static constexpr size_t cskKeyOffsetInPfm = 0x124;
111static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
112static constexpr size_t certKeyLen = 96;
113static constexpr size_t cskSignatureLen = 96;
114
James Feistfcd2d3a2020-05-28 10:38:15 -0700115static constexpr const char* versionIntf =
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530116 "xyz.openbmc_project.Software.Version";
117
AppaRao Puli28972062019-11-11 02:04:45 +0530118enum class FwGetRootCertDataTag : uint8_t
119{
120 activeRootKey = 1,
121 recoveryRootKey,
122 activeCSK,
123 recoveryCSK,
124};
125
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530126enum class FWDeviceIDTag : uint8_t
127{
128 bmcActiveImage = 1,
129 bmcRecoveryImage,
130};
131
James Feistfcd2d3a2020-05-28 10:38:15 -0700132const static boost::container::flat_map<FWDeviceIDTag, const char*>
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530133 fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
134 "/xyz/openbmc_project/software/bmc_active"},
135 {FWDeviceIDTag::bmcRecoveryImage,
136 "/xyz/openbmc_project/software/bmc_recovery"}};
AppaRao Puli28972062019-11-11 02:04:45 +0530137#endif // INTEL_PFR_ENABLED
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530138
AppaRao Puli28972062019-11-11 02:04:45 +0530139enum class ChannelIdTag : uint8_t
140{
141 reserved = 0,
142 kcs = 1,
143 ipmb = 2,
144 rmcpPlus = 3
145};
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +0530146
AppaRao Puli28972062019-11-11 02:04:45 +0530147enum class BmcExecutionContext : uint8_t
148{
149 reserved = 0,
150 linuxOs = 0x10,
151 bootLoader = 0x11,
152};
AppaRao Puli09a83142019-11-23 02:46:06 +0530153
AppaRao Puli28972062019-11-11 02:04:45 +0530154enum class FwUpdateCtrlReq : uint8_t
155{
156 getCurrentControlStatus = 0x00,
157 imageTransferStart = 0x01,
158 imageTransferComplete = 0x02,
159 imageTransferAbort = 0x03,
160 setFirmwareFilename = 0x04,
161 attachUsbDevice = 0x05,
162 detachUsbDevice = 0x06
163};
Vernon Mauery52ce6622019-05-22 09:19:46 -0700164
165constexpr std::size_t operator""_MB(unsigned long long v)
166{
167 return 1024u * 1024u * v;
168}
AppaRao Puli28972062019-11-11 02:04:45 +0530169static constexpr size_t maxFirmwareImageSize = 32_MB;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700170
AppaRao Puli28972062019-11-11 02:04:45 +0530171static bool localDownloadInProgress(void)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700172{
173 struct stat sb;
AppaRao Puli28972062019-11-11 02:04:45 +0530174 if (stat(firmwareBufferFile, &sb) < 0)
175 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700176 return false;
AppaRao Puli28972062019-11-11 02:04:45 +0530177 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700178 return true;
179}
180
AppaRao Puli28972062019-11-11 02:04:45 +0530181class TransferHashCheck
182{
183 public:
184 enum class HashCheck : uint8_t
185 {
186 notRequested = 0,
187 requested,
188 sha2Success,
189 sha2Failed = 0xe2,
190 };
191
192 protected:
James Feistfcd2d3a2020-05-28 10:38:15 -0700193 EVP_MD_CTX* ctx;
AppaRao Puli28972062019-11-11 02:04:45 +0530194 std::vector<uint8_t> expectedHash;
195 enum HashCheck check;
196 bool started;
197
198 public:
199 TransferHashCheck() : check(HashCheck::notRequested), started(false)
James Feistfcd2d3a2020-05-28 10:38:15 -0700200 {}
AppaRao Puli28972062019-11-11 02:04:45 +0530201 ~TransferHashCheck()
202 {
203 if (ctx)
204 {
205 EVP_MD_CTX_destroy(ctx);
206 ctx = NULL;
207 }
208 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700209 void init(const std::vector<uint8_t>& expected)
AppaRao Puli28972062019-11-11 02:04:45 +0530210 {
211 expectedHash = expected;
212 check = HashCheck::requested;
213 ctx = EVP_MD_CTX_create();
214 EVP_DigestInit(ctx, EVP_sha256());
215 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700216 void hash(const std::vector<uint8_t>& data)
AppaRao Puli28972062019-11-11 02:04:45 +0530217 {
218 if (!started)
219 {
220 started = true;
221 }
222 EVP_DigestUpdate(ctx, data.data(), data.size());
223 }
224 void clear()
225 {
226 // if not started, nothing to clear
227 if (started)
228 {
229 if (ctx)
230 {
231 EVP_MD_CTX_destroy(ctx);
232 }
233 if (check != HashCheck::notRequested)
234 {
235 check = HashCheck::requested;
236 }
237 ctx = EVP_MD_CTX_create();
238 EVP_DigestInit(ctx, EVP_sha256());
239 }
240 }
241 enum HashCheck verify()
242 {
243 if (check == HashCheck::requested)
244 {
245 unsigned int len = 0;
246 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
247 EVP_DigestFinal(ctx, digest.data(), &len);
248 if (digest == expectedHash)
249 {
250 phosphor::logging::log<phosphor::logging::level::INFO>(
251 "Transfer sha2 verify passed.");
252 check = HashCheck::sha2Success;
253 }
254 else
255 {
256 phosphor::logging::log<phosphor::logging::level::ERR>(
257 "Transfer sha2 verify failed.");
258 check = HashCheck::sha2Failed;
259 }
260 }
261 return check;
262 }
263 uint8_t status() const
264 {
265 return static_cast<uint8_t>(check);
266 }
267};
268
269class MappedFile
270{
271 public:
James Feistfcd2d3a2020-05-28 10:38:15 -0700272 MappedFile(const std::string& fname) : addr(nullptr), fsize(0)
AppaRao Puli28972062019-11-11 02:04:45 +0530273 {
274 std::error_code ec;
275 size_t sz = std::filesystem::file_size(fname, ec);
P Dheeraj Srujan Kumarc08e9752021-07-14 19:38:32 +0530276 if (!ec)
277 {
278 return;
279 }
AppaRao Puli28972062019-11-11 02:04:45 +0530280 int fd = open(fname.c_str(), O_RDONLY);
P Dheeraj Srujan Kumarc08e9752021-07-14 19:38:32 +0530281 if (fd < 0)
AppaRao Puli28972062019-11-11 02:04:45 +0530282 {
283 return;
284 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700285 void* tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
AppaRao Puli28972062019-11-11 02:04:45 +0530286 close(fd);
287 if (tmp == MAP_FAILED)
288 {
289 return;
290 }
291 addr = tmp;
292 fsize = sz;
293 }
294
295 ~MappedFile()
296 {
297 if (addr)
298 {
299 munmap(addr, fsize);
300 }
301 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700302 const uint8_t* data() const
AppaRao Puli28972062019-11-11 02:04:45 +0530303 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700304 return static_cast<const uint8_t*>(addr);
AppaRao Puli28972062019-11-11 02:04:45 +0530305 }
306 size_t size() const
307 {
308 return fsize;
309 }
310
311 private:
312 size_t fsize;
James Feistfcd2d3a2020-05-28 10:38:15 -0700313 void* addr;
AppaRao Puli28972062019-11-11 02:04:45 +0530314};
315
316class FwUpdateStatusCache
Vernon Mauery52ce6622019-05-22 09:19:46 -0700317{
318 public:
319 enum
320 {
AppaRao Puli28972062019-11-11 02:04:45 +0530321 fwStateInit = 0,
322 fwStateIdle,
323 fwStateDownload,
324 fwStateVerify,
325 fwStateProgram,
326 fwStateUpdateSuccess,
327 fwStateError = 0x0f,
328 fwStateAcCycleRequired = 0x83,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700329 };
AppaRao Puli28972062019-11-11 02:04:45 +0530330 uint8_t getState()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700331 {
AppaRao Puli28972062019-11-11 02:04:45 +0530332 if ((fwUpdateState == fwStateIdle || fwUpdateState == fwStateInit) &&
333 localDownloadInProgress())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700334 {
AppaRao Puli28972062019-11-11 02:04:45 +0530335 fwUpdateState = fwStateDownload;
336 progressPercent = 0;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700337 }
AppaRao Puli28972062019-11-11 02:04:45 +0530338 return fwUpdateState;
339 }
340 void resetStatusCache()
341 {
342 unlink(firmwareBufferFile);
343 }
344 void setState(const uint8_t state)
345 {
346 switch (state)
347 {
348 case fwStateInit:
349 case fwStateIdle:
350 case fwStateError:
351 resetStatusCache();
352 break;
353 case fwStateDownload:
354 case fwStateVerify:
355 case fwStateProgram:
356 case fwStateUpdateSuccess:
357 break;
358 default:
359 // Error
360 break;
361 }
362 fwUpdateState = state;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700363 }
364 uint8_t percent()
365 {
AppaRao Puli28972062019-11-11 02:04:45 +0530366 return progressPercent;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700367 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700368 void updateActivationPercent(const std::string& objPath)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700369 {
AppaRao Puli28972062019-11-11 02:04:45 +0530370 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
371 fwUpdateState = fwStateProgram;
372 progressPercent = 0;
373 match = std::make_shared<sdbusplus::bus::match::match>(
374 *busp,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700375 sdbusplus::bus::match::rules::propertiesChanged(
AppaRao Puli28972062019-11-11 02:04:45 +0530376 objPath, "xyz.openbmc_project.Software.ActivationProgress"),
James Feistfcd2d3a2020-05-28 10:38:15 -0700377 [&](sdbusplus::message::message& msg) {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700378 std::map<std::string, ipmi::DbusVariant> props;
AppaRao Puli28972062019-11-11 02:04:45 +0530379 std::vector<std::string> inVal;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700380 std::string iface;
AppaRao Puli28972062019-11-11 02:04:45 +0530381 try
382 {
383 msg.read(iface, props, inVal);
384 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700385 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +0530386 {
387 phosphor::logging::log<phosphor::logging::level::ERR>(
388 "Exception caught in get ActivationProgress");
389 return;
390 }
391
392 auto it = props.find("Progress");
393 if (it != props.end())
394 {
395 progressPercent = std::get<uint8_t>(it->second);
396 if (progressPercent == 100)
397 {
398 fwUpdateState = fwStateUpdateSuccess;
399 }
400 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700401 });
402 }
AppaRao Puli28972062019-11-11 02:04:45 +0530403 uint8_t activationTimerTimeout()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700404 {
AppaRao Puli28972062019-11-11 02:04:45 +0530405 phosphor::logging::log<phosphor::logging::level::INFO>(
406 "activationTimerTimeout: Increase percentage...",
407 phosphor::logging::entry("PERCENT:%d", progressPercent));
408 progressPercent = progressPercent + 5;
jayaprakash Mutyalab8f2bf92020-01-27 23:17:39 +0000409 if (progressPercent > 95)
anil kumar appana31f88872019-08-02 15:16:27 +0000410 {
411 /*changing the state to ready to update firmware utility */
AppaRao Puli28972062019-11-11 02:04:45 +0530412 fwUpdateState = fwStateUpdateSuccess;
anil kumar appana31f88872019-08-02 15:16:27 +0000413 }
AppaRao Puli28972062019-11-11 02:04:45 +0530414 return progressPercent;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700415 }
anil kumar appana31f88872019-08-02 15:16:27 +0000416 /* API for changing state to ERROR */
417 void firmwareUpdateAbortState()
418 {
AppaRao Puli28972062019-11-11 02:04:45 +0530419 unlink(firmwareBufferFile);
anil kumar appana31f88872019-08-02 15:16:27 +0000420 // changing the state to error
AppaRao Puli28972062019-11-11 02:04:45 +0530421 fwUpdateState = fwStateError;
anil kumar appana31f88872019-08-02 15:16:27 +0000422 }
423 void setDeferRestart(bool deferRestart)
424 {
AppaRao Puli28972062019-11-11 02:04:45 +0530425 deferRestartState = deferRestart;
anil kumar appana31f88872019-08-02 15:16:27 +0000426 }
427 void setInhibitDowngrade(bool inhibitDowngrade)
428 {
AppaRao Puli28972062019-11-11 02:04:45 +0530429 inhibitDowngradeState = inhibitDowngrade;
anil kumar appana31f88872019-08-02 15:16:27 +0000430 }
431 bool getDeferRestart()
432 {
AppaRao Puli28972062019-11-11 02:04:45 +0530433 return deferRestartState;
anil kumar appana31f88872019-08-02 15:16:27 +0000434 }
435 bool getInhibitDowngrade()
436 {
AppaRao Puli28972062019-11-11 02:04:45 +0530437 return inhibitDowngradeState;
anil kumar appana31f88872019-08-02 15:16:27 +0000438 }
439
Vernon Mauery52ce6622019-05-22 09:19:46 -0700440 protected:
AppaRao Puli28972062019-11-11 02:04:45 +0530441 std::shared_ptr<sdbusplus::asio::connection> busp;
442 std::shared_ptr<sdbusplus::bus::match::match> match;
443 uint8_t fwUpdateState = 0;
444 uint8_t progressPercent = 0;
445 bool deferRestartState = false;
446 bool inhibitDowngradeState = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700447};
448
AppaRao Puli28972062019-11-11 02:04:45 +0530449static FwUpdateStatusCache fwUpdateStatus;
450std::shared_ptr<TransferHashCheck> xferHashCheck;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700451
James Feistfcd2d3a2020-05-28 10:38:15 -0700452static void activateImage(const std::string& objPath)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700453{
AppaRao Puli28972062019-11-11 02:04:45 +0530454 // If flag is false means to reboot
455 if (fwUpdateStatus.getDeferRestart() == false)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700456 {
AppaRao Puli28972062019-11-11 02:04:45 +0530457 phosphor::logging::log<phosphor::logging::level::INFO>(
458 "activating Image: ",
459 phosphor::logging::entry("OBJPATH =%s", objPath.c_str()));
460 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
461 bus->async_method_call(
462 [](const boost::system::error_code ec) {
463 if (ec)
464 {
465 phosphor::logging::log<phosphor::logging::level::ERR>(
466 "async_method_call error: activateImage failed");
467 return;
468 }
469 },
470 "xyz.openbmc_project.Software.BMC.Updater", objPath,
471 "org.freedesktop.DBus.Properties", "Set",
472 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
473 std::variant<std::string>("xyz.openbmc_project.Software.Activation."
474 "RequestedActivations.Active"));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700475 }
AppaRao Puli28972062019-11-11 02:04:45 +0530476 else
477 {
478 phosphor::logging::log<phosphor::logging::level::INFO>(
479 "Firmware image activation is deferred.");
480 }
481 fwUpdateStatus.setState(
482 static_cast<uint8_t>(FwUpdateStatusCache::fwStateUpdateSuccess));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700483}
484
AppaRao Puli09a83142019-11-23 02:46:06 +0530485static bool getFirmwareUpdateMode()
486{
487 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
488 try
489 {
490 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
491 ipmi::Value state = ipmi::getDbusProperty(
492 *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState");
493 std::string bmcState = std::get<std::string>(state);
494 return (bmcState == bmcStateUpdateInProgress);
495 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700496 catch (const std::exception& e)
AppaRao Puli09a83142019-11-23 02:46:06 +0530497 {
498 phosphor::logging::log<phosphor::logging::level::ERR>(
499 "Exception caught while getting BMC state.",
500 phosphor::logging::entry("EXCEPTION=%s", e.what()));
501 throw;
502 }
503}
504
505static void setFirmwareUpdateMode(const bool mode)
506{
507
508 std::string bmcState(bmcStateReady);
509 if (mode)
510 {
511 bmcState = bmcStateUpdateInProgress;
512 }
513
514 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
515 try
516 {
517 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
518 ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf,
519 "CurrentBMCState", bmcState);
520 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700521 catch (const std::exception& e)
AppaRao Puli09a83142019-11-23 02:46:06 +0530522 {
523 phosphor::logging::log<phosphor::logging::level::ERR>(
524 "Exception caught while setting BMC state.",
525 phosphor::logging::entry("EXCEPTION=%s", e.what()));
526 throw;
527 }
528}
529
AppaRao Pulie2cddf62020-01-31 00:30:08 +0530530/** @brief check if channel IPMB
531 *
532 * This function checks if the command is from IPMB
533 *
534 * @param[in] ctx - context of current session.
535 * @returns true if the medium is IPMB else return true.
536 **/
James Feistfcd2d3a2020-05-28 10:38:15 -0700537ipmi::Cc checkIPMBChannel(const ipmi::Context::ptr& ctx, bool& isIPMBChannel)
AppaRao Pulie2cddf62020-01-31 00:30:08 +0530538{
539 ipmi::ChannelInfo chInfo;
540
541 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
542 {
543 phosphor::logging::log<phosphor::logging::level::ERR>(
544 "Failed to get Channel Info",
545 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
546 return ipmi::ccUnspecifiedError;
547 }
548
549 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
550 ipmi::EChannelMediumType::ipmb)
551 {
552 isIPMBChannel = true;
553 }
554 return ipmi::ccSuccess;
555}
556
AppaRao Puli28972062019-11-11 02:04:45 +0530557static void postTransferCompleteHandler(
James Feistfcd2d3a2020-05-28 10:38:15 -0700558 std::unique_ptr<sdbusplus::bus::match::match>& fwUpdateMatchSignal)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700559{
560 // Setup timer for watching signal
561 static phosphor::Timer timer(
AppaRao Puli28972062019-11-11 02:04:45 +0530562 [&fwUpdateMatchSignal]() { fwUpdateMatchSignal = nullptr; });
Vernon Mauery52ce6622019-05-22 09:19:46 -0700563
AppaRao Puli28972062019-11-11 02:04:45 +0530564 static phosphor::Timer activationStatusTimer([]() {
jayaprakash Mutyalab8f2bf92020-01-27 23:17:39 +0000565 if (fwUpdateStatus.activationTimerTimeout() > 95)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700566 {
AppaRao Puli28972062019-11-11 02:04:45 +0530567 activationStatusTimer.stop();
568 fwUpdateStatus.setState(
569 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700570 }
571 });
572
573 timer.start(std::chrono::microseconds(5000000), false);
574
575 // callback function for capturing signal
James Feistfcd2d3a2020-05-28 10:38:15 -0700576 auto callback = [&](sdbusplus::message::message& m) {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700577 bool flag = false;
578
579 std::vector<std::pair<
580 std::string,
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700581 std::vector<std::pair<std::string, std::variant<std::string>>>>>
AppaRao Puli28972062019-11-11 02:04:45 +0530582 intfPropsPair;
583 sdbusplus::message::object_path objPath;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700584
585 try
586 {
AppaRao Puli28972062019-11-11 02:04:45 +0530587 m.read(objPath, intfPropsPair); // Read in the object path
588 // that was just created
Vernon Mauery52ce6622019-05-22 09:19:46 -0700589 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700590 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700591 {
AppaRao Puli28972062019-11-11 02:04:45 +0530592 phosphor::logging::log<phosphor::logging::level::ERR>(
593 "Exception caught in reading created object path.");
594 return;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700595 }
596 // constructing response message
AppaRao Puli28972062019-11-11 02:04:45 +0530597 phosphor::logging::log<phosphor::logging::level::INFO>(
598 "New Interface Added.",
599 phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
James Feistfcd2d3a2020-05-28 10:38:15 -0700600 for (auto& interface : intfPropsPair)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700601 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700602 if (interface.first == "xyz.openbmc_project.Software.Activation")
603 {
AppaRao Puli28972062019-11-11 02:04:45 +0530604 // There are chances of getting two signals for
605 // InterfacesAdded. So cross check and discrad second instance.
606 if (fwUpdateMatchSignal == nullptr)
607 {
608 return;
609 }
610 // Found our interface, disable callbacks
611 fwUpdateMatchSignal = nullptr;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700612
AppaRao Puli28972062019-11-11 02:04:45 +0530613 phosphor::logging::log<phosphor::logging::level::INFO>(
614 "Start activationStatusTimer for status.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700615 try
616 {
617 timer.stop();
AppaRao Puli28972062019-11-11 02:04:45 +0530618 activationStatusTimer.start(
Vernon Mauery52ce6622019-05-22 09:19:46 -0700619 std::chrono::microseconds(3000000), true);
620 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700621 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700622 {
AppaRao Puli28972062019-11-11 02:04:45 +0530623 phosphor::logging::log<phosphor::logging::level::ERR>(
624 "Exception caught in start activationStatusTimer.",
625 phosphor::logging::entry("ERROR=%s", e.what()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700626 }
627
AppaRao Puli28972062019-11-11 02:04:45 +0530628 fwUpdateStatus.updateActivationPercent(objPath.str);
629 activateImage(objPath.str);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700630 }
631 }
632 };
633
634 // Adding matcher
AppaRao Puli28972062019-11-11 02:04:45 +0530635 fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700636 *getSdBus(),
Vernon Mauery52ce6622019-05-22 09:19:46 -0700637 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
638 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
639 callback);
640}
James Feistfcd2d3a2020-05-28 10:38:15 -0700641static bool startFirmwareUpdate(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700642{
AppaRao Puli28972062019-11-11 02:04:45 +0530643 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
644 // the code gets to this point, the file should be transferred start the
645 // request (creating a new file in /tmp/images causes the update manager to
646 // check if it is ready for activation)
647 static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatchSignal;
648 postTransferCompleteHandler(fwUpdateMatchSignal);
649 std::filesystem::rename(
650 uri, "/tmp/images/" +
651 boost::uuids::to_string(boost::uuids::random_generator()()));
652 return true;
653}
Vernon Mauery52ce6622019-05-22 09:19:46 -0700654
James Feistfcd2d3a2020-05-28 10:38:15 -0700655static int transferImageFromFile(const std::string& uri, bool move = true)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700656{
657 std::error_code ec;
AppaRao Puli28972062019-11-11 02:04:45 +0530658 phosphor::logging::log<phosphor::logging::level::INFO>(
659 "Transfer Image From File.",
660 phosphor::logging::entry("URI=%s", uri.c_str()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700661 if (move)
662 {
AppaRao Puli28972062019-11-11 02:04:45 +0530663 std::filesystem::rename(uri, firmwareBufferFile, ec);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700664 }
665 else
666 {
AppaRao Puli28972062019-11-11 02:04:45 +0530667 std::filesystem::copy(uri, firmwareBufferFile,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700668 std::filesystem::copy_options::overwrite_existing,
669 ec);
670 }
AppaRao Puli28972062019-11-11 02:04:45 +0530671 if (xferHashCheck)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700672 {
673 MappedFile mappedfw(uri);
AppaRao Puli28972062019-11-11 02:04:45 +0530674 xferHashCheck->hash(
Vernon Mauery52ce6622019-05-22 09:19:46 -0700675 {mappedfw.data(), mappedfw.data() + mappedfw.size()});
676 }
677 if (ec.value())
678 {
AppaRao Puli28972062019-11-11 02:04:45 +0530679 phosphor::logging::log<phosphor::logging::level::ERR>(
680 "Image copy failed.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700681 }
682 return ec.value();
683}
684
685template <typename... ArgTypes>
James Feistfcd2d3a2020-05-28 10:38:15 -0700686static int executeCmd(const char* path, ArgTypes&&... tArgs)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700687{
James Feistfcd2d3a2020-05-28 10:38:15 -0700688 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700689 execProg.wait();
690 return execProg.exit_code();
691}
692
James Feistfcd2d3a2020-05-28 10:38:15 -0700693static int transferImageFromUsb(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700694{
695 int ret, sysret;
696 char fwpath[fwPathMaxLength];
AppaRao Puli28972062019-11-11 02:04:45 +0530697 phosphor::logging::log<phosphor::logging::level::INFO>(
698 "Transfer Image From USB.",
699 phosphor::logging::entry("URI=%s", uri.c_str()));
700 ret = executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
701 fwUpdateMountPoint);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700702 if (ret)
703 {
704 return ret;
705 }
706
AppaRao Puli28972062019-11-11 02:04:45 +0530707 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
708 ret = transferImageFromFile(usb_path, false);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700709
AppaRao Puli28972062019-11-11 02:04:45 +0530710 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage, fwUpdateMountPoint);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700711 return ret;
712}
713
James Feistfcd2d3a2020-05-28 10:38:15 -0700714static bool transferFirmwareFromUri(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700715{
AppaRao Puli28972062019-11-11 02:04:45 +0530716 static constexpr char fwUriFile[] = "file://";
717 static constexpr char fwUriUsb[] = "usb://";
718 phosphor::logging::log<phosphor::logging::level::INFO>(
719 "Transfer Image From URI.",
720 phosphor::logging::entry("URI=%s", uri.c_str()));
721 if (boost::algorithm::starts_with(uri, fwUriFile))
Vernon Mauery52ce6622019-05-22 09:19:46 -0700722 {
AppaRao Puli28972062019-11-11 02:04:45 +0530723 std::string fname = uri.substr(sizeof(fwUriFile) - 1);
724 if (fname != firmwareBufferFile)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700725 {
AppaRao Puli28972062019-11-11 02:04:45 +0530726 return 0 == transferImageFromFile(fname);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700727 }
728 return true;
729 }
AppaRao Puli28972062019-11-11 02:04:45 +0530730 if (boost::algorithm::starts_with(uri, fwUriUsb))
Vernon Mauery52ce6622019-05-22 09:19:46 -0700731 {
AppaRao Puli28972062019-11-11 02:04:45 +0530732 std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
733 return 0 == transferImageFromUsb(fname);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700734 }
735 return false;
736}
737
738/* Get USB-mass-storage device status: inserted => true, ejected => false */
AppaRao Puli28972062019-11-11 02:04:45 +0530739static bool getUsbStatus()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700740{
AppaRao Puli28972062019-11-11 02:04:45 +0530741 std::filesystem::path usbDevPath =
742 std::filesystem::path("/sys/kernel/config/usb_gadget") /
743 fwUpdateUSBDevName;
744 return (std::filesystem::exists(usbDevPath) ? true : false);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700745}
746
747/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
AppaRao Puli28972062019-11-11 02:04:45 +0530748static int attachUsbDevice()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700749{
AppaRao Puli28972062019-11-11 02:04:45 +0530750 if (getUsbStatus())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700751 {
752 return 1;
753 }
AppaRao Puli28972062019-11-11 02:04:45 +0530754 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
755 std::to_string(maxFirmwareImageSize / 1_MB).c_str());
Vernon Mauery52ce6622019-05-22 09:19:46 -0700756 if (!ret)
757 {
AppaRao Puli28972062019-11-11 02:04:45 +0530758 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
759 fwUpdateUsbVolImage);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700760 }
761 return ret;
762}
763
764/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
AppaRao Puli28972062019-11-11 02:04:45 +0530765static int detachUsbDevice()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700766{
AppaRao Puli28972062019-11-11 02:04:45 +0530767 if (!getUsbStatus())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700768 {
769 return 1;
770 }
AppaRao Puli28972062019-11-11 02:04:45 +0530771 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700772}
AppaRao Puli28972062019-11-11 02:04:45 +0530773static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700774{
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800775 constexpr uint8_t undefinedImage = 0x00;
AppaRao Puli28972062019-11-11 02:04:45 +0530776 constexpr uint8_t primaryImage = 0x01;
777 constexpr uint8_t secondaryImage = 0x02;
James Feistfcd2d3a2020-05-28 10:38:15 -0700778 constexpr const char* secondaryFitImageStartAddr = "22480000";
AppaRao Puli28972062019-11-11 02:04:45 +0530779
780 uint8_t bootImage = primaryImage;
781 boost::system::error_code ec;
782 std::string value = ctx->bus->yield_method_call<std::string>(
783 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
784 "/xyz/openbmc_project/u_boot/environment/mgr",
785 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
786 if (ec)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700787 {
AppaRao Puli28972062019-11-11 02:04:45 +0530788 phosphor::logging::log<phosphor::logging::level::ERR>(
789 "Failed to read the bootcmd value");
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800790 /* don't fail, just give back undefined until it is ready */
791 bootImage = undefinedImage;
AppaRao Puli28972062019-11-11 02:04:45 +0530792 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700793
AppaRao Puli28972062019-11-11 02:04:45 +0530794 /* cheking for secondary FitImage Address 22480000 */
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800795 else if (value.find(secondaryFitImageStartAddr) != std::string::npos)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700796 {
AppaRao Puli28972062019-11-11 02:04:45 +0530797 bootImage = secondaryImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700798 }
799 else
800 {
AppaRao Puli28972062019-11-11 02:04:45 +0530801 bootImage = primaryImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700802 }
803
AppaRao Puli28972062019-11-11 02:04:45 +0530804 return bootImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700805}
806
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530807#ifdef INTEL_PFR_ENABLED
808using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
809 uint8_t, // Major Version Number
810 uint8_t, // Minor Version Number
811 uint32_t, // Build Number
812 uint32_t, // Build Timestamp
813 uint32_t>; // Update Timestamp
814ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700815{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700816 // Byte 1 - Count (N) Number of devices data is being returned for.
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530817 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700818 // Bytes - 17:(15xN) - Repeat of 2 through 16
819
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530820 std::vector<fwVersionInfoType> fwVerInfoList;
821 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
James Feistfcd2d3a2020-05-28 10:38:15 -0700822 for (const auto& fwDev : fwVersionIdMap)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700823 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530824 std::string verStr;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700825 try
826 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530827 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700828
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530829 ipmi::Value result = ipmi::getDbusProperty(
830 *busp, service, fwDev.second, versionIntf, "Version");
831 verStr = std::get<std::string>(result);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700832 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700833 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700834 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530835 phosphor::logging::log<phosphor::logging::level::INFO>(
836 "Failed to fetch Version property",
837 phosphor::logging::entry("ERROR=%s", e.what()),
838 phosphor::logging::entry("PATH=%s", fwDev.second),
839 phosphor::logging::entry("INTERFACE=%s", versionIntf));
840 continue;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700841 }
842
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530843 if (verStr.empty())
844 {
845 phosphor::logging::log<phosphor::logging::level::INFO>(
846 "Version is empty.",
847 phosphor::logging::entry("PATH=%s", fwDev.second),
848 phosphor::logging::entry("INTERFACE=%s", versionIntf));
849 continue;
850 }
851
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530852 uint8_t majorNum = 0;
853 uint8_t minorNum = 0;
854 uint32_t buildNum = 0;
855 try
856 {
srikanta mondal52a292b2020-07-27 23:49:14 +0000857 std::optional<ipmi::MetaRevision> rev =
858 ipmi::convertIntelVersion(verStr);
859 if (rev.has_value())
860 {
861 ipmi::MetaRevision revision = rev.value();
862 majorNum = revision.major % 10 + (revision.major / 10) * 16;
863 minorNum = (revision.minor > 99 ? 99 : revision.minor);
864 minorNum = minorNum % 10 + (minorNum / 10) * 16;
865 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
866 hash = bswap_32(hash);
867 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
868 }
869 else
870 {
871 std::vector<std::string> splitVer;
872 boost::split(splitVer, verStr, boost::is_any_of(".-"));
873 if (splitVer.size() < 3)
874 {
875 phosphor::logging::log<phosphor::logging::level::INFO>(
876 "Invalid Version format.",
877 phosphor::logging::entry("Version=%s", verStr.c_str()),
878 phosphor::logging::entry("PATH=%s", fwDev.second));
879 continue;
880 }
881 majorNum = std::stoul(splitVer[0], nullptr, 16);
882 minorNum = std::stoul(splitVer[1], nullptr, 16);
883 buildNum = std::stoul(splitVer[2], nullptr, 16);
884 }
885 // Build Timestamp - Not supported.
886 // Update Timestamp - TODO: Need to check with CPLD team.
887 fwVerInfoList.emplace_back(
888 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
889 minorNum, buildNum, 0, 0));
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530890 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700891 catch (const std::exception& e)
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530892 {
893 phosphor::logging::log<phosphor::logging::level::INFO>(
894 "Failed to convert stoul.",
895 phosphor::logging::entry("ERROR=%s", e.what()));
896 continue;
897 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700898 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700899
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530900 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700901}
Punith Nadur Basavarajaiaha8dd1972020-08-19 18:05:44 +0000902
903std::array<uint8_t, imageCount> getSecurityVersionInfo(const char* mtdDevBuf,
904 size_t svnVerOffsetInPfm,
905 size_t bkcVerOffsetInPfm)
906{
907 constexpr size_t bufLength = 1;
908 std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp;
909 constexpr uint8_t svnIndexValue = 0x00;
910 constexpr uint8_t bkcIndexValue = 0x01;
911 constexpr uint8_t tempIndexValue = 0x00;
912 try
913 {
914 SPIDev spiDev(mtdDevBuf);
915 spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data());
916 fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue);
917 spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data());
918 fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue);
919 }
920 catch (const std::exception& e)
921 {
922 phosphor::logging::log<phosphor::logging::level::ERR>(
923 "Exception caught in getSecurityVersionInfo",
924 phosphor::logging::entry("MSG=%s", e.what()));
925 fwSecurityVersionBuf = {0, 0};
926 }
927
928 return fwSecurityVersionBuf;
929}
930
931ipmi::RspType<
932 uint8_t, // device ID
933 uint8_t, // Active Image Value
934 std::array<uint8_t, imageCount>, // Security version for Active Image
935 uint8_t, // recovery Image Value
936 std::array<uint8_t, imageCount>> // Security version for Recovery Image
AppaRao Puli28972062019-11-11 02:04:45 +0530937 ipmiGetFwSecurityVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700938{
Punith Nadur Basavarajaiaha8dd1972020-08-19 18:05:44 +0000939 static bool cacheFlag = false;
940 constexpr std::array<const char*, imageCount> mtdDevBuf = {
941 bmcActivePfmMTDDev, bmcRecoveryImgMTDDev};
942
943 // To avoid multiple reading from SPI device
944 if (!cacheFlag)
945 {
946 imgFwSecurityVersion[0] = getSecurityVersionInfo(
947 mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm);
948 imgFwSecurityVersion[1] = getSecurityVersionInfo(
949 mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm);
950 cacheFlag = true;
951 }
952
953 constexpr uint8_t ActivePfmMTDDev = 0x00;
954 constexpr uint8_t RecoveryImgMTDDev = 0x01;
955
956 return ipmi::responseSuccess(
957 imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage),
958 imgFwSecurityVersion[ActivePfmMTDDev],
959 static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage),
960 imgFwSecurityVersion[RecoveryImgMTDDev]);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700961}
962
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530963ipmi::RspType<std::array<uint8_t, certKeyLen>,
964 std::optional<std::array<uint8_t, cskSignatureLen>>>
James Feistfcd2d3a2020-05-28 10:38:15 -0700965 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700966{
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +0530967 bool isIPMBChannel = false;
968
969 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
970 {
971 return ipmi::responseUnspecifiedError();
972 }
973 if (isIPMBChannel)
974 {
975 phosphor::logging::log<phosphor::logging::level::INFO>(
976 "Command not supported. Failed to get root certificate data.");
977 return ipmi::responseCommandNotAvailable();
978 }
979
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530980 size_t certKeyOffset = 0;
981 size_t cskSigOffset = 0;
982 std::string mtdDev;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700983
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530984 switch (static_cast<FwGetRootCertDataTag>(certId))
985 {
986 case FwGetRootCertDataTag::activeRootKey:
987 {
988 mtdDev = bmcActivePfmMTDDev;
989 certKeyOffset = rootkeyOffsetInPfm;
990 break;
991 }
992 case FwGetRootCertDataTag::recoveryRootKey:
993 {
994 mtdDev = bmcRecoveryImgMTDDev;
995 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
996 break;
997 }
998 case FwGetRootCertDataTag::activeCSK:
999 {
1000 mtdDev = bmcActivePfmMTDDev;
1001 certKeyOffset = cskKeyOffsetInPfm;
1002 cskSigOffset = cskSignatureOffsetInPfm;
1003 break;
1004 }
1005 case FwGetRootCertDataTag::recoveryCSK:
1006 {
1007 mtdDev = bmcRecoveryImgMTDDev;
1008 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1009 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1010 break;
1011 }
1012 default:
1013 {
1014 return ipmi::responseInvalidFieldRequest();
1015 }
1016 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001017
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301018 std::array<uint8_t, certKeyLen> certKey = {0};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001019
Vernon Mauery52ce6622019-05-22 09:19:46 -07001020 try
1021 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301022 SPIDev spiDev(mtdDev);
1023 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1024
1025 if (cskSigOffset)
1026 {
1027 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1028 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1029 cskSignature.data());
1030 return ipmi::responseSuccess(certKey, cskSignature);
1031 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001032 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001033 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001034 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301035 phosphor::logging::log<phosphor::logging::level::ERR>(
1036 "Exception caught in ipmiGetFwRootCertData",
1037 phosphor::logging::entry("MSG=%s", e.what()));
1038 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001039 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001040
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301041 return ipmi::responseSuccess(certKey, std::nullopt);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001042}
AppaRao Puli28972062019-11-11 02:04:45 +05301043#endif // INTEL_PFR_ENABLED
1044
1045static constexpr uint8_t channelListSize = 3;
1046/** @brief implements Maximum Firmware Transfer size command
1047 * @parameter
1048 * - none
1049 * @returns IPMI completion code plus response data
1050 * - count - channel count
1051 * - channelList - channel list information
1052 */
1053ipmi::RspType<uint8_t, // channel count
1054 std::array<std::tuple<uint8_t, uint32_t>,
1055 channelListSize> // Channel List
1056 >
1057 ipmiFirmwareMaxTransferSize()
1058{
1059 constexpr size_t kcsMaxBufSize = 128;
1060 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
AppaRao Puli28972062019-11-11 02:04:45 +05301061 // Byte 1 - Count (N) Number of devices data is being returned for.
1062 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
1063 // Byte 3-6 - transfer size (little endian)
1064 // Bytes - 7:(5xN) - Repeat of 2 through 6
1065 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1066 channelList = {
1067 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
AppaRao Puli28972062019-11-11 02:04:45 +05301068 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
1069 rmcpPlusMaxBufSize}}};
1070
1071 return ipmi::responseSuccess(channelListSize, channelList);
1072}
1073
1074ipmi::RspType<uint8_t, uint8_t>
1075 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
1076{
1077 // Byte 1 - Current execution context
1078 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
1079 // Byte 2 - Partition pointer
1080 // 0x01 - primary, 0x02 - secondary
1081 uint8_t partitionPtr = getActiveBootImage(ctx);
1082
1083 return ipmi::responseSuccess(
1084 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
1085}
AppaRao Puli28972062019-11-11 02:04:45 +05301086/** @brief Get Firmware Update Random Number
1087 *
1088 * This function generate the random number used for
1089 * setting the firmware update mode as authentication key.
1090 *
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301091 * @param[in] ctx - context of current session
AppaRao Puli28972062019-11-11 02:04:45 +05301092 * @returns IPMI completion code along with
1093 * - random number
1094 **/
1095ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
James Feistfcd2d3a2020-05-28 10:38:15 -07001096 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx)
AppaRao Puli28972062019-11-11 02:04:45 +05301097{
1098 phosphor::logging::log<phosphor::logging::level::INFO>(
1099 "Generate FW update random number");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301100 bool isIPMBChannel = false;
1101
1102 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1103 {
1104 return ipmi::responseUnspecifiedError();
1105 }
1106 if (isIPMBChannel)
1107 {
1108 phosphor::logging::log<phosphor::logging::level::INFO>(
1109 "Channel not supported. Failed to fetch FW update random number");
1110 return ipmi::responseCommandNotAvailable();
1111 }
AppaRao Puli28972062019-11-11 02:04:45 +05301112 std::random_device rd;
1113 std::default_random_engine gen(rd());
1114 std::uniform_int_distribution<> dist{0, 255};
1115
1116 fwRandomNumGenTs = std::chrono::steady_clock::now();
1117
1118 for (int i = 0; i < fwRandomNumLength; i++)
1119 {
1120 fwRandomNum[i] = dist(gen);
1121 }
1122
1123 return ipmi::responseSuccess(fwRandomNum);
1124}
1125
1126/** @brief Set Firmware Update Mode
1127 *
1128 * This function sets BMC into firmware update mode
1129 * after validating Random number obtained from the Get
1130 * Firmware Update Random Number command
1131 *
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301132 * @param[in] ctx - context of current session
1133 * @parameter randNum - Random number(token)
1134 * @returns IPMI completion code
AppaRao Puli28972062019-11-11 02:04:45 +05301135 **/
1136ipmi::RspType<>
James Feistfcd2d3a2020-05-28 10:38:15 -07001137 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx,
1138 std::array<uint8_t, fwRandomNumLength>& randNum)
AppaRao Puli28972062019-11-11 02:04:45 +05301139{
1140 phosphor::logging::log<phosphor::logging::level::INFO>(
1141 "Start FW update mode");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301142
1143 bool isIPMBChannel = false;
1144
1145 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1146 {
1147 return ipmi::responseUnspecifiedError();
1148 }
1149 if (isIPMBChannel)
1150 {
1151 phosphor::logging::log<phosphor::logging::level::INFO>(
1152 "Channel not supported. Failed to set FW update mode");
1153 return ipmi::responseCommandNotAvailable();
1154 }
AppaRao Puli28972062019-11-11 02:04:45 +05301155 /* Firmware Update Random number is valid for 30 seconds only */
1156 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1157 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1158 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1159 fwRandomNumExpirySeconds)
1160 .count())
1161 {
1162 phosphor::logging::log<phosphor::logging::level::INFO>(
1163 "Firmware update random number expired.");
1164 return ipmi::responseInvalidFieldRequest();
1165 }
1166
1167 /* Validate random number */
1168 for (int i = 0; i < fwRandomNumLength; i++)
1169 {
1170 if (fwRandomNum[i] != randNum[i])
1171 {
1172 phosphor::logging::log<phosphor::logging::level::INFO>(
1173 "Invalid random number specified.");
1174 return ipmi::responseInvalidFieldRequest();
1175 }
1176 }
1177
1178 try
1179 {
1180 if (getFirmwareUpdateMode())
1181 {
1182 phosphor::logging::log<phosphor::logging::level::INFO>(
1183 "Already firmware update is in progress.");
1184 return ipmi::responseBusy();
1185 }
1186 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001187 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301188 {
1189 return ipmi::responseUnspecifiedError();
1190 }
1191
1192 // FIXME? c++ doesn't off an option for exclusive file creation
James Feistfcd2d3a2020-05-28 10:38:15 -07001193 FILE* fp = fopen(firmwareBufferFile, "wx");
AppaRao Puli28972062019-11-11 02:04:45 +05301194 if (!fp)
1195 {
1196 phosphor::logging::log<phosphor::logging::level::INFO>(
1197 "Unable to open file.");
1198 return ipmi::responseUnspecifiedError();
1199 }
1200 fclose(fp);
1201
1202 try
1203 {
1204 setFirmwareUpdateMode(true);
1205 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001206 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301207 {
1208 unlink(firmwareBufferFile);
1209 return ipmi::responseUnspecifiedError();
1210 }
1211
1212 return ipmi::responseSuccess();
1213}
1214
1215/** @brief implements exit firmware update mode command
1216 * @param None
1217 *
1218 * @returns IPMI completion code
1219 */
James Feistfcd2d3a2020-05-28 10:38:15 -07001220ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx)
AppaRao Puli28972062019-11-11 02:04:45 +05301221{
1222 phosphor::logging::log<phosphor::logging::level::INFO>(
1223 "Exit FW update mode");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301224 bool isIPMBChannel = false;
1225
1226 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1227 {
1228 return ipmi::responseUnspecifiedError();
1229 }
1230 if (isIPMBChannel)
1231 {
1232 phosphor::logging::log<phosphor::logging::level::INFO>(
1233 "Command not supported. Failed to exit firmware update mode");
1234 return ipmi::responseCommandNotAvailable();
1235 }
1236
AppaRao Puli28972062019-11-11 02:04:45 +05301237 switch (fwUpdateStatus.getState())
1238 {
1239 case FwUpdateStatusCache::fwStateInit:
1240 case FwUpdateStatusCache::fwStateIdle:
1241 return ipmi::responseInvalidFieldRequest();
1242 break;
1243 case FwUpdateStatusCache::fwStateDownload:
1244 case FwUpdateStatusCache::fwStateVerify:
1245 break;
1246 case FwUpdateStatusCache::fwStateProgram:
1247 break;
1248 case FwUpdateStatusCache::fwStateUpdateSuccess:
1249 case FwUpdateStatusCache::fwStateError:
1250 break;
1251 case FwUpdateStatusCache::fwStateAcCycleRequired:
1252 return ipmi::responseInvalidFieldRequest();
1253 break;
1254 }
1255 fwUpdateStatus.firmwareUpdateAbortState();
1256
1257 try
1258 {
1259 setFirmwareUpdateMode(false);
1260 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001261 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301262 {
1263 return ipmi::responseUnspecifiedError();
1264 }
1265
1266 return ipmi::responseSuccess();
1267}
1268
1269/** @brief implements Get/Set Firmware Update Control
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301270 * @param[in] ctx - context of current session
AppaRao Puli28972062019-11-11 02:04:45 +05301271 * @parameter
1272 * - Byte 1: Control Byte
1273 * - Byte 2: Firmware filename length (Optional)
1274 * - Byte 3:N: Firmware filename data (Optional)
1275 * @returns IPMI completion code plus response data
1276 * - Byte 2: Current control status
1277 **/
1278ipmi::RspType<bool, bool, bool, bool, uint4_t>
James Feistfcd2d3a2020-05-28 10:38:15 -07001279 ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr& ctx,
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301280 const uint8_t controlReq,
James Feistfcd2d3a2020-05-28 10:38:15 -07001281 const std::optional<std::string>& fileName)
AppaRao Puli28972062019-11-11 02:04:45 +05301282{
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301283 bool isIPMBChannel = false;
1284
1285 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1286 {
1287 return ipmi::responseUnspecifiedError();
1288 }
1289 if (isIPMBChannel)
1290 {
1291 phosphor::logging::log<phosphor::logging::level::INFO>(
1292 "Channel not supported. Failed to get or set FW update control");
1293 return ipmi::responseCommandNotAvailable();
1294 }
1295
AppaRao Puli28972062019-11-11 02:04:45 +05301296 static std::string fwXferUriPath;
1297 static bool imageTransferStarted = false;
1298 static bool imageTransferCompleted = false;
1299 static bool imageTransferAborted = false;
1300
1301 if ((controlReq !=
1302 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
1303 (fileName))
1304 {
1305 phosphor::logging::log<phosphor::logging::level::ERR>(
1306 "Invalid request field (Filename).");
1307 return ipmi::responseInvalidFieldRequest();
1308 }
1309
1310 static bool usbAttached = getUsbStatus();
1311
1312 switch (static_cast<FwUpdateCtrlReq>(controlReq))
1313 {
1314 case FwUpdateCtrlReq::getCurrentControlStatus:
1315 phosphor::logging::log<phosphor::logging::level::INFO>(
1316 "ipmiGetSetFirmwareUpdateControl: Get status");
1317 break;
1318 case FwUpdateCtrlReq::imageTransferStart:
1319 {
1320 phosphor::logging::log<phosphor::logging::level::INFO>(
1321 "ipmiGetSetFirmwareUpdateControl: Set transfer start");
1322 imageTransferStarted = true;
1323 // reset buffer to empty (truncate file)
1324 std::ofstream out(firmwareBufferFile,
1325 std::ofstream::binary | std::ofstream::trunc);
1326 fwXferUriPath = std::string("file://") + firmwareBufferFile;
1327 if (xferHashCheck)
1328 {
1329 xferHashCheck->clear();
1330 }
1331 // Setting state to download
1332 fwUpdateStatus.setState(
1333 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
1334#ifdef INTEL_PFR_ENABLED
1335 imgLength = 0;
1336 imgType = 0;
1337 block0Mapped = false;
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301338#endif
AppaRao Puli28972062019-11-11 02:04:45 +05301339 }
1340 break;
1341 case FwUpdateCtrlReq::imageTransferComplete:
1342 {
1343 phosphor::logging::log<phosphor::logging::level::INFO>(
1344 "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
1345 if (usbAttached)
1346 {
1347 phosphor::logging::log<phosphor::logging::level::ERR>(
1348 "USB should be detached to perform this operation.");
1349 return ipmi::responseNotSupportedInPresentState();
1350 }
1351 // finish transfer based on URI
1352 if (!transferFirmwareFromUri(fwXferUriPath))
1353 {
1354 phosphor::logging::log<phosphor::logging::level::ERR>(
1355 "transferFirmwareFromUri failed.");
1356 return ipmi::responseUnspecifiedError();
1357 }
1358 // transfer complete
1359 if (xferHashCheck)
1360 {
1361 if (TransferHashCheck::HashCheck::sha2Success !=
1362 xferHashCheck->verify())
1363 {
1364 phosphor::logging::log<phosphor::logging::level::ERR>(
1365 "xferHashCheck failed.");
1366 return ipmi::responseUnspecifiedError();
1367 }
1368 }
1369 // Set state to verify and start the update
1370 fwUpdateStatus.setState(
1371 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
1372 // start the request
1373 if (!startFirmwareUpdate(firmwareBufferFile))
1374 {
1375 phosphor::logging::log<phosphor::logging::level::ERR>(
1376 "startFirmwareUpdate failed.");
1377 return ipmi::responseUnspecifiedError();
1378 }
1379 imageTransferCompleted = true;
1380 }
1381 break;
1382 case FwUpdateCtrlReq::imageTransferAbort:
1383 phosphor::logging::log<phosphor::logging::level::INFO>(
1384 "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
1385 if (usbAttached)
1386 {
1387 if (detachUsbDevice())
1388 {
1389 phosphor::logging::log<phosphor::logging::level::ERR>(
1390 "Detach USB device failed.");
1391 return ipmi::responseUsbAttachOrDetachFailed();
1392 }
1393 usbAttached = false;
1394 }
1395 // During abort request reset the state to Init by cleaning update
1396 // file.
1397 fwUpdateStatus.firmwareUpdateAbortState();
1398 imageTransferAborted = true;
1399 break;
1400 case FwUpdateCtrlReq::setFirmwareFilename:
1401 phosphor::logging::log<phosphor::logging::level::INFO>(
1402 "ipmiGetSetFirmwareUpdateControl: Set filename.");
1403 if (!fileName || ((*fileName).length() == 0))
1404 {
1405 phosphor::logging::log<phosphor::logging::level::ERR>(
1406 "Invalid Filename specified.");
1407 return ipmi::responseInvalidFieldRequest();
1408 }
1409
1410 fwXferUriPath = *fileName;
1411 break;
1412 case FwUpdateCtrlReq::attachUsbDevice:
1413 phosphor::logging::log<phosphor::logging::level::INFO>(
1414 "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
1415 if (usbAttached)
1416 {
1417 phosphor::logging::log<phosphor::logging::level::ERR>(
1418 "USB device is already attached.");
1419 return ipmi::responseInvalidFieldRequest();
1420 }
1421 if (attachUsbDevice())
1422 {
1423 phosphor::logging::log<phosphor::logging::level::ERR>(
1424 "Attach USB device failed.");
1425 return ipmi::responseUsbAttachOrDetachFailed();
1426 }
1427 usbAttached = true;
1428 break;
1429 case FwUpdateCtrlReq::detachUsbDevice:
1430 phosphor::logging::log<phosphor::logging::level::INFO>(
1431 "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
1432 if (!usbAttached)
1433 {
1434 phosphor::logging::log<phosphor::logging::level::ERR>(
1435 "USB device is not attached.");
1436 return ipmi::responseInvalidFieldRequest();
1437 }
1438 if (detachUsbDevice())
1439 {
1440 phosphor::logging::log<phosphor::logging::level::ERR>(
1441 "Detach USB device failed.");
1442 return ipmi::responseUsbAttachOrDetachFailed();
1443 }
1444 usbAttached = false;
1445 break;
1446 default:
1447 phosphor::logging::log<phosphor::logging::level::ERR>(
1448 "Invalid control option specified.");
1449 return ipmi::responseInvalidFieldRequest();
1450 }
1451
1452 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
1453 imageTransferAborted, usbAttached, uint4_t(0));
1454}
1455
1456/** @brief implements firmware get status command
1457 * @parameter
1458 * - none
1459 * @returns IPMI completion code plus response data
1460 * - status - processing status
1461 * - percentage - percentage completion
1462 * - check - channel integrity check status
1463 **/
1464ipmi::RspType<uint8_t, // status
1465 uint8_t, // percentage
1466 uint8_t // check
1467 >
1468 ipmiGetFirmwareUpdateStatus()
1469
1470{
1471 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1472 // 5=ready, f=error, 83=ac cycle required)
1473 // Byte 2 - percent
1474 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1475 uint8_t status = fwUpdateStatus.getState();
1476 uint8_t percent = fwUpdateStatus.percent();
1477 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
1478
1479 // Status code.
1480 return ipmi::responseSuccess(status, percent, check);
1481}
1482
1483ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
James Feistfcd2d3a2020-05-28 10:38:15 -07001484 const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask,
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301485 bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart,
1486 bool sha2Check, uint5_t reserved2,
1487 std::optional<std::vector<uint8_t>> integrityCheckVal)
AppaRao Puli28972062019-11-11 02:04:45 +05301488{
1489 phosphor::logging::log<phosphor::logging::level::INFO>(
1490 "Set firmware update options.");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301491 bool isIPMBChannel = false;
1492
1493 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1494 {
1495 return ipmi::responseUnspecifiedError();
1496 }
1497 if (isIPMBChannel)
1498 {
1499 phosphor::logging::log<phosphor::logging::level::INFO>(
1500 "Channel not supported. Failed to set firmware update options");
1501 return ipmi::responseCommandNotAvailable();
1502 }
AppaRao Puli28972062019-11-11 02:04:45 +05301503 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
1504 bool deferRestartState = fwUpdateStatus.getDeferRestart();
1505 bool sha2CheckState = xferHashCheck ? true : false;
1506
1507 if (noDowngradeMask && (noDowngradeState != noDowngrade))
1508 {
1509 fwUpdateStatus.setInhibitDowngrade(noDowngrade);
1510 noDowngradeState = noDowngrade;
1511 }
1512 if (deferRestartMask && (deferRestartState != deferRestart))
1513 {
1514 fwUpdateStatus.setDeferRestart(deferRestart);
1515 deferRestartState = deferRestart;
1516 }
1517 if (sha2CheckMask)
1518 {
1519 if (sha2Check)
1520 {
1521 auto hashSize = EVP_MD_size(EVP_sha256());
1522 if ((*integrityCheckVal).size() != hashSize)
1523 {
1524 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1525 "Invalid size of Hash specified.");
1526 return ipmi::responseInvalidFieldRequest();
1527 }
1528 xferHashCheck = std::make_shared<TransferHashCheck>();
1529 xferHashCheck->init(*integrityCheckVal);
1530 }
1531 else
1532 {
1533 // delete the xferHashCheck object
1534 xferHashCheck.reset();
1535 }
1536 sha2CheckState = sha2CheckMask;
1537 }
1538 return ipmi::responseSuccess(noDowngradeState, deferRestartState,
1539 sha2CheckState, reserved1);
1540}
Vernon Mauery52ce6622019-05-22 09:19:46 -07001541
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301542ipmi::RspType<uint32_t>
James Feistfcd2d3a2020-05-28 10:38:15 -07001543 ipmiFwImageWriteData(const std::vector<uint8_t>& writeData)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001544{
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301545 const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1546 size_t writeDataLen = writeData.size();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001547
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301548 if (!writeDataLen)
1549 {
1550 return ipmi::responseReqDataLenInvalid();
1551 }
1552
AppaRao Puli28972062019-11-11 02:04:45 +05301553 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301554 {
AppaRao Puli28972062019-11-11 02:04:45 +05301555 phosphor::logging::log<phosphor::logging::level::ERR>(
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301556 "Invalid firmware update state.");
1557 return ipmi::response(ccCmdNotSupportedInPresentState);
1558 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001559
AppaRao Puli28972062019-11-11 02:04:45 +05301560 std::ofstream out(firmwareBufferFile,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001561 std::ofstream::binary | std::ofstream::app);
1562 if (!out)
1563 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301564 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1565 "Error while opening file.");
1566 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001567 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301568
1569 uint64_t fileDataLen = out.tellp();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301570
AppaRao Puli28972062019-11-11 02:04:45 +05301571 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001572 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301573 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1574 "Firmware image size exceeds the limit");
1575 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001576 }
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301577
James Feistfcd2d3a2020-05-28 10:38:15 -07001578 const char* data = reinterpret_cast<const char*>(writeData.data());
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301579 out.write(data, writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001580 out.close();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301581
AppaRao Puli28972062019-11-11 02:04:45 +05301582 if (xferHashCheck)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001583 {
AppaRao Puli28972062019-11-11 02:04:45 +05301584 xferHashCheck->hash(writeData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001585 }
1586
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301587#ifdef INTEL_PFR_ENABLED
1588 /* PFR image block 0 - As defined in HAS */
1589 struct PFRImageBlock0
1590 {
1591 uint32_t tag;
1592 uint32_t pcLength;
1593 uint32_t pcType;
1594 uint32_t reserved1;
1595 uint8_t hash256[32];
1596 uint8_t hash384[48];
1597 uint8_t reserved2[32];
1598 } __attribute__((packed));
1599
1600 /* Get the PFR block 0 data and read the uploaded image
1601 * information( Image type, length etc) */
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301602 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1603 (!block0Mapped))
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301604 {
1605 struct PFRImageBlock0 block0Data = {0};
1606
AppaRao Puli28972062019-11-11 02:04:45 +05301607 std::ifstream inFile(firmwareBufferFile,
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301608 std::ios::binary | std::ios::in);
James Feistfcd2d3a2020-05-28 10:38:15 -07001609 inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data));
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301610 inFile.close();
1611
1612 uint32_t magicNum = block0Data.tag;
1613
1614 /* Validate the magic number */
1615 if (magicNum != perBlock0MagicNum)
1616 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301617 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1618 "PFR image magic number not matched");
1619 return ipmi::responseInvalidFieldRequest();
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301620 }
1621 // Note:imgLength, imgType and block0Mapped are in global scope, as
1622 // these are used in cascaded updates.
1623 imgLength = block0Data.pcLength;
1624 imgType = block0Data.pcType;
1625 block0Mapped = true;
1626 }
1627#endif // end of INTEL_PFR_ENABLED
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301628 return ipmi::responseSuccess(writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001629}
1630
AppaRao Puli28972062019-11-11 02:04:45 +05301631static void registerFirmwareFunctions()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001632{
1633 // guarantee that we start with an already timed out timestamp
AppaRao Puli28972062019-11-11 02:04:45 +05301634 fwRandomNumGenTs =
1635 std::chrono::steady_clock::now() - fwRandomNumExpirySeconds;
1636 fwUpdateStatus.setState(
1637 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
Vernon Mauery52ce6622019-05-22 09:19:46 -07001638
AppaRao Puli28972062019-11-11 02:04:45 +05301639 unlink(firmwareBufferFile);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001640
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301641#ifdef INTEL_PFR_ENABLED
1642 // Following commands are supported only for PFR enabled platforms
1643 // CMD:0x20 - Get Firmware Version Information
AppaRao Puli28972062019-11-11 02:04:45 +05301644 // CMD:0x21 - Get Firmware Security Version Information
1645 // CMD:0x25 - Get Root Certificate Data
Vernon Mauery52ce6622019-05-22 09:19:46 -07001646
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301647 // get firmware version information
1648 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1649 ipmi::firmware::cmdGetFwVersionInfo,
1650 ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
AppaRao Puli28972062019-11-11 02:04:45 +05301651
Vernon Mauery52ce6622019-05-22 09:19:46 -07001652 // get firmware security version information
AppaRao Puli28972062019-11-11 02:04:45 +05301653 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1654 ipmi::firmware::cmdGetFwSecurityVersionInfo,
1655 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001656
Vernon Mauery52ce6622019-05-22 09:19:46 -07001657 // get root certificate data
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301658 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1659 ipmi::firmware::cmdFwGetRootCertData,
1660 ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1661#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001662
AppaRao Puli28972062019-11-11 02:04:45 +05301663 // get firmware update channel information (max transfer sizes)
1664 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1665 ipmi::firmware::cmdGetFwUpdateChannelInfo,
1666 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001667
AppaRao Puli28972062019-11-11 02:04:45 +05301668 // get bmc execution context
1669 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1670 ipmi::firmware::cmdGetBmcExecutionContext,
1671 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
1672
1673 // Get Firmware Update Random number
1674 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1675 ipmi::firmware::cmdGetFwUpdateRandomNumber,
1676 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
1677
1678 // Set Firmware Update Mode
1679 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1680 ipmi::firmware::cmdSetFirmwareUpdateMode,
AppaRao Puli4b3e1c72019-10-16 20:53:09 +05301681 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001682
AppaRao Puli28972062019-11-11 02:04:45 +05301683 // Exit Firmware Update Mode
anil kumar appanab57098a2019-05-28 16:22:33 +00001684 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
AppaRao Puli28972062019-11-11 02:04:45 +05301685 ipmi::firmware::cmdExitFirmwareUpdateMode,
1686 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001687
AppaRao Puli28972062019-11-11 02:04:45 +05301688 // Get/Set Firmware Update Control
1689 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1690 ipmi::firmware::cmdGetSetFwUpdateControl,
1691 ipmi::Privilege::Admin,
1692 ipmiGetSetFirmwareUpdateControl);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001693
AppaRao Puli28972062019-11-11 02:04:45 +05301694 // Get Firmware Update Status
1695 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1696 ipmi::firmware::cmdGetFirmwareUpdateStatus,
1697 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001698
AppaRao Puli28972062019-11-11 02:04:45 +05301699 // Set Firmware Update Options
1700 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1701 ipmi::firmware::cmdSetFirmwareUpdateOptions,
1702 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001703 // write image data
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301704 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1705 ipmi::firmware::cmdFwImageWriteData,
1706 ipmi::Privilege::Admin, ipmiFwImageWriteData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001707 return;
1708}