blob: d27b8c6cf9ea8fd3bbb072d1cbf642f687b27ee1 [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);
276 int fd = open(fname.c_str(), O_RDONLY);
277 if (!ec || fd < 0)
278 {
279 return;
280 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700281 void* tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
AppaRao Puli28972062019-11-11 02:04:45 +0530282 close(fd);
283 if (tmp == MAP_FAILED)
284 {
285 return;
286 }
287 addr = tmp;
288 fsize = sz;
289 }
290
291 ~MappedFile()
292 {
293 if (addr)
294 {
295 munmap(addr, fsize);
296 }
297 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700298 const uint8_t* data() const
AppaRao Puli28972062019-11-11 02:04:45 +0530299 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700300 return static_cast<const uint8_t*>(addr);
AppaRao Puli28972062019-11-11 02:04:45 +0530301 }
302 size_t size() const
303 {
304 return fsize;
305 }
306
307 private:
308 size_t fsize;
James Feistfcd2d3a2020-05-28 10:38:15 -0700309 void* addr;
AppaRao Puli28972062019-11-11 02:04:45 +0530310};
311
312class FwUpdateStatusCache
Vernon Mauery52ce6622019-05-22 09:19:46 -0700313{
314 public:
315 enum
316 {
AppaRao Puli28972062019-11-11 02:04:45 +0530317 fwStateInit = 0,
318 fwStateIdle,
319 fwStateDownload,
320 fwStateVerify,
321 fwStateProgram,
322 fwStateUpdateSuccess,
323 fwStateError = 0x0f,
324 fwStateAcCycleRequired = 0x83,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700325 };
AppaRao Puli28972062019-11-11 02:04:45 +0530326 uint8_t getState()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700327 {
AppaRao Puli28972062019-11-11 02:04:45 +0530328 if ((fwUpdateState == fwStateIdle || fwUpdateState == fwStateInit) &&
329 localDownloadInProgress())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700330 {
AppaRao Puli28972062019-11-11 02:04:45 +0530331 fwUpdateState = fwStateDownload;
332 progressPercent = 0;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700333 }
AppaRao Puli28972062019-11-11 02:04:45 +0530334 return fwUpdateState;
335 }
336 void resetStatusCache()
337 {
338 unlink(firmwareBufferFile);
339 }
340 void setState(const uint8_t state)
341 {
342 switch (state)
343 {
344 case fwStateInit:
345 case fwStateIdle:
346 case fwStateError:
347 resetStatusCache();
348 break;
349 case fwStateDownload:
350 case fwStateVerify:
351 case fwStateProgram:
352 case fwStateUpdateSuccess:
353 break;
354 default:
355 // Error
356 break;
357 }
358 fwUpdateState = state;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700359 }
360 uint8_t percent()
361 {
AppaRao Puli28972062019-11-11 02:04:45 +0530362 return progressPercent;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700363 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700364 void updateActivationPercent(const std::string& objPath)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700365 {
AppaRao Puli28972062019-11-11 02:04:45 +0530366 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
367 fwUpdateState = fwStateProgram;
368 progressPercent = 0;
369 match = std::make_shared<sdbusplus::bus::match::match>(
370 *busp,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700371 sdbusplus::bus::match::rules::propertiesChanged(
AppaRao Puli28972062019-11-11 02:04:45 +0530372 objPath, "xyz.openbmc_project.Software.ActivationProgress"),
James Feistfcd2d3a2020-05-28 10:38:15 -0700373 [&](sdbusplus::message::message& msg) {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700374 std::map<std::string, ipmi::DbusVariant> props;
AppaRao Puli28972062019-11-11 02:04:45 +0530375 std::vector<std::string> inVal;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700376 std::string iface;
AppaRao Puli28972062019-11-11 02:04:45 +0530377 try
378 {
379 msg.read(iface, props, inVal);
380 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700381 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +0530382 {
383 phosphor::logging::log<phosphor::logging::level::ERR>(
384 "Exception caught in get ActivationProgress");
385 return;
386 }
387
388 auto it = props.find("Progress");
389 if (it != props.end())
390 {
391 progressPercent = std::get<uint8_t>(it->second);
392 if (progressPercent == 100)
393 {
394 fwUpdateState = fwStateUpdateSuccess;
395 }
396 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700397 });
398 }
AppaRao Puli28972062019-11-11 02:04:45 +0530399 uint8_t activationTimerTimeout()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700400 {
AppaRao Puli28972062019-11-11 02:04:45 +0530401 phosphor::logging::log<phosphor::logging::level::INFO>(
402 "activationTimerTimeout: Increase percentage...",
403 phosphor::logging::entry("PERCENT:%d", progressPercent));
404 progressPercent = progressPercent + 5;
jayaprakash Mutyalab8f2bf92020-01-27 23:17:39 +0000405 if (progressPercent > 95)
anil kumar appana31f88872019-08-02 15:16:27 +0000406 {
407 /*changing the state to ready to update firmware utility */
AppaRao Puli28972062019-11-11 02:04:45 +0530408 fwUpdateState = fwStateUpdateSuccess;
anil kumar appana31f88872019-08-02 15:16:27 +0000409 }
AppaRao Puli28972062019-11-11 02:04:45 +0530410 return progressPercent;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700411 }
anil kumar appana31f88872019-08-02 15:16:27 +0000412 /* API for changing state to ERROR */
413 void firmwareUpdateAbortState()
414 {
AppaRao Puli28972062019-11-11 02:04:45 +0530415 unlink(firmwareBufferFile);
anil kumar appana31f88872019-08-02 15:16:27 +0000416 // changing the state to error
AppaRao Puli28972062019-11-11 02:04:45 +0530417 fwUpdateState = fwStateError;
anil kumar appana31f88872019-08-02 15:16:27 +0000418 }
419 void setDeferRestart(bool deferRestart)
420 {
AppaRao Puli28972062019-11-11 02:04:45 +0530421 deferRestartState = deferRestart;
anil kumar appana31f88872019-08-02 15:16:27 +0000422 }
423 void setInhibitDowngrade(bool inhibitDowngrade)
424 {
AppaRao Puli28972062019-11-11 02:04:45 +0530425 inhibitDowngradeState = inhibitDowngrade;
anil kumar appana31f88872019-08-02 15:16:27 +0000426 }
427 bool getDeferRestart()
428 {
AppaRao Puli28972062019-11-11 02:04:45 +0530429 return deferRestartState;
anil kumar appana31f88872019-08-02 15:16:27 +0000430 }
431 bool getInhibitDowngrade()
432 {
AppaRao Puli28972062019-11-11 02:04:45 +0530433 return inhibitDowngradeState;
anil kumar appana31f88872019-08-02 15:16:27 +0000434 }
435
Vernon Mauery52ce6622019-05-22 09:19:46 -0700436 protected:
AppaRao Puli28972062019-11-11 02:04:45 +0530437 std::shared_ptr<sdbusplus::asio::connection> busp;
438 std::shared_ptr<sdbusplus::bus::match::match> match;
439 uint8_t fwUpdateState = 0;
440 uint8_t progressPercent = 0;
441 bool deferRestartState = false;
442 bool inhibitDowngradeState = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700443};
444
AppaRao Puli28972062019-11-11 02:04:45 +0530445static FwUpdateStatusCache fwUpdateStatus;
446std::shared_ptr<TransferHashCheck> xferHashCheck;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700447
James Feistfcd2d3a2020-05-28 10:38:15 -0700448static void activateImage(const std::string& objPath)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700449{
AppaRao Puli28972062019-11-11 02:04:45 +0530450 // If flag is false means to reboot
451 if (fwUpdateStatus.getDeferRestart() == false)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700452 {
AppaRao Puli28972062019-11-11 02:04:45 +0530453 phosphor::logging::log<phosphor::logging::level::INFO>(
454 "activating Image: ",
455 phosphor::logging::entry("OBJPATH =%s", objPath.c_str()));
456 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
457 bus->async_method_call(
458 [](const boost::system::error_code ec) {
459 if (ec)
460 {
461 phosphor::logging::log<phosphor::logging::level::ERR>(
462 "async_method_call error: activateImage failed");
463 return;
464 }
465 },
466 "xyz.openbmc_project.Software.BMC.Updater", objPath,
467 "org.freedesktop.DBus.Properties", "Set",
468 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
469 std::variant<std::string>("xyz.openbmc_project.Software.Activation."
470 "RequestedActivations.Active"));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700471 }
AppaRao Puli28972062019-11-11 02:04:45 +0530472 else
473 {
474 phosphor::logging::log<phosphor::logging::level::INFO>(
475 "Firmware image activation is deferred.");
476 }
477 fwUpdateStatus.setState(
478 static_cast<uint8_t>(FwUpdateStatusCache::fwStateUpdateSuccess));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700479}
480
AppaRao Puli09a83142019-11-23 02:46:06 +0530481static bool getFirmwareUpdateMode()
482{
483 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
484 try
485 {
486 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
487 ipmi::Value state = ipmi::getDbusProperty(
488 *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState");
489 std::string bmcState = std::get<std::string>(state);
490 return (bmcState == bmcStateUpdateInProgress);
491 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700492 catch (const std::exception& e)
AppaRao Puli09a83142019-11-23 02:46:06 +0530493 {
494 phosphor::logging::log<phosphor::logging::level::ERR>(
495 "Exception caught while getting BMC state.",
496 phosphor::logging::entry("EXCEPTION=%s", e.what()));
497 throw;
498 }
499}
500
501static void setFirmwareUpdateMode(const bool mode)
502{
503
504 std::string bmcState(bmcStateReady);
505 if (mode)
506 {
507 bmcState = bmcStateUpdateInProgress;
508 }
509
510 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
511 try
512 {
513 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
514 ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf,
515 "CurrentBMCState", bmcState);
516 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700517 catch (const std::exception& e)
AppaRao Puli09a83142019-11-23 02:46:06 +0530518 {
519 phosphor::logging::log<phosphor::logging::level::ERR>(
520 "Exception caught while setting BMC state.",
521 phosphor::logging::entry("EXCEPTION=%s", e.what()));
522 throw;
523 }
524}
525
AppaRao Pulie2cddf62020-01-31 00:30:08 +0530526/** @brief check if channel IPMB
527 *
528 * This function checks if the command is from IPMB
529 *
530 * @param[in] ctx - context of current session.
531 * @returns true if the medium is IPMB else return true.
532 **/
James Feistfcd2d3a2020-05-28 10:38:15 -0700533ipmi::Cc checkIPMBChannel(const ipmi::Context::ptr& ctx, bool& isIPMBChannel)
AppaRao Pulie2cddf62020-01-31 00:30:08 +0530534{
535 ipmi::ChannelInfo chInfo;
536
537 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
538 {
539 phosphor::logging::log<phosphor::logging::level::ERR>(
540 "Failed to get Channel Info",
541 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
542 return ipmi::ccUnspecifiedError;
543 }
544
545 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
546 ipmi::EChannelMediumType::ipmb)
547 {
548 isIPMBChannel = true;
549 }
550 return ipmi::ccSuccess;
551}
552
AppaRao Puli28972062019-11-11 02:04:45 +0530553static void postTransferCompleteHandler(
James Feistfcd2d3a2020-05-28 10:38:15 -0700554 std::unique_ptr<sdbusplus::bus::match::match>& fwUpdateMatchSignal)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700555{
556 // Setup timer for watching signal
557 static phosphor::Timer timer(
AppaRao Puli28972062019-11-11 02:04:45 +0530558 [&fwUpdateMatchSignal]() { fwUpdateMatchSignal = nullptr; });
Vernon Mauery52ce6622019-05-22 09:19:46 -0700559
AppaRao Puli28972062019-11-11 02:04:45 +0530560 static phosphor::Timer activationStatusTimer([]() {
jayaprakash Mutyalab8f2bf92020-01-27 23:17:39 +0000561 if (fwUpdateStatus.activationTimerTimeout() > 95)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700562 {
AppaRao Puli28972062019-11-11 02:04:45 +0530563 activationStatusTimer.stop();
564 fwUpdateStatus.setState(
565 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700566 }
567 });
568
569 timer.start(std::chrono::microseconds(5000000), false);
570
571 // callback function for capturing signal
James Feistfcd2d3a2020-05-28 10:38:15 -0700572 auto callback = [&](sdbusplus::message::message& m) {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700573 bool flag = false;
574
575 std::vector<std::pair<
576 std::string,
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700577 std::vector<std::pair<std::string, std::variant<std::string>>>>>
AppaRao Puli28972062019-11-11 02:04:45 +0530578 intfPropsPair;
579 sdbusplus::message::object_path objPath;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700580
581 try
582 {
AppaRao Puli28972062019-11-11 02:04:45 +0530583 m.read(objPath, intfPropsPair); // Read in the object path
584 // that was just created
Vernon Mauery52ce6622019-05-22 09:19:46 -0700585 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700586 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700587 {
AppaRao Puli28972062019-11-11 02:04:45 +0530588 phosphor::logging::log<phosphor::logging::level::ERR>(
589 "Exception caught in reading created object path.");
590 return;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700591 }
592 // constructing response message
AppaRao Puli28972062019-11-11 02:04:45 +0530593 phosphor::logging::log<phosphor::logging::level::INFO>(
594 "New Interface Added.",
595 phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
James Feistfcd2d3a2020-05-28 10:38:15 -0700596 for (auto& interface : intfPropsPair)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700597 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700598 if (interface.first == "xyz.openbmc_project.Software.Activation")
599 {
AppaRao Puli28972062019-11-11 02:04:45 +0530600 // There are chances of getting two signals for
601 // InterfacesAdded. So cross check and discrad second instance.
602 if (fwUpdateMatchSignal == nullptr)
603 {
604 return;
605 }
606 // Found our interface, disable callbacks
607 fwUpdateMatchSignal = nullptr;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700608
AppaRao Puli28972062019-11-11 02:04:45 +0530609 phosphor::logging::log<phosphor::logging::level::INFO>(
610 "Start activationStatusTimer for status.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700611 try
612 {
613 timer.stop();
AppaRao Puli28972062019-11-11 02:04:45 +0530614 activationStatusTimer.start(
Vernon Mauery52ce6622019-05-22 09:19:46 -0700615 std::chrono::microseconds(3000000), true);
616 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700617 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700618 {
AppaRao Puli28972062019-11-11 02:04:45 +0530619 phosphor::logging::log<phosphor::logging::level::ERR>(
620 "Exception caught in start activationStatusTimer.",
621 phosphor::logging::entry("ERROR=%s", e.what()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700622 }
623
AppaRao Puli28972062019-11-11 02:04:45 +0530624 fwUpdateStatus.updateActivationPercent(objPath.str);
625 activateImage(objPath.str);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700626 }
627 }
628 };
629
630 // Adding matcher
AppaRao Puli28972062019-11-11 02:04:45 +0530631 fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700632 *getSdBus(),
Vernon Mauery52ce6622019-05-22 09:19:46 -0700633 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
634 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
635 callback);
636}
James Feistfcd2d3a2020-05-28 10:38:15 -0700637static bool startFirmwareUpdate(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700638{
AppaRao Puli28972062019-11-11 02:04:45 +0530639 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
640 // the code gets to this point, the file should be transferred start the
641 // request (creating a new file in /tmp/images causes the update manager to
642 // check if it is ready for activation)
643 static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatchSignal;
644 postTransferCompleteHandler(fwUpdateMatchSignal);
645 std::filesystem::rename(
646 uri, "/tmp/images/" +
647 boost::uuids::to_string(boost::uuids::random_generator()()));
648 return true;
649}
Vernon Mauery52ce6622019-05-22 09:19:46 -0700650
James Feistfcd2d3a2020-05-28 10:38:15 -0700651static int transferImageFromFile(const std::string& uri, bool move = true)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700652{
653 std::error_code ec;
AppaRao Puli28972062019-11-11 02:04:45 +0530654 phosphor::logging::log<phosphor::logging::level::INFO>(
655 "Transfer Image From File.",
656 phosphor::logging::entry("URI=%s", uri.c_str()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700657 if (move)
658 {
AppaRao Puli28972062019-11-11 02:04:45 +0530659 std::filesystem::rename(uri, firmwareBufferFile, ec);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700660 }
661 else
662 {
AppaRao Puli28972062019-11-11 02:04:45 +0530663 std::filesystem::copy(uri, firmwareBufferFile,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700664 std::filesystem::copy_options::overwrite_existing,
665 ec);
666 }
AppaRao Puli28972062019-11-11 02:04:45 +0530667 if (xferHashCheck)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700668 {
669 MappedFile mappedfw(uri);
AppaRao Puli28972062019-11-11 02:04:45 +0530670 xferHashCheck->hash(
Vernon Mauery52ce6622019-05-22 09:19:46 -0700671 {mappedfw.data(), mappedfw.data() + mappedfw.size()});
672 }
673 if (ec.value())
674 {
AppaRao Puli28972062019-11-11 02:04:45 +0530675 phosphor::logging::log<phosphor::logging::level::ERR>(
676 "Image copy failed.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700677 }
678 return ec.value();
679}
680
681template <typename... ArgTypes>
James Feistfcd2d3a2020-05-28 10:38:15 -0700682static int executeCmd(const char* path, ArgTypes&&... tArgs)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700683{
James Feistfcd2d3a2020-05-28 10:38:15 -0700684 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700685 execProg.wait();
686 return execProg.exit_code();
687}
688
James Feistfcd2d3a2020-05-28 10:38:15 -0700689static int transferImageFromUsb(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700690{
691 int ret, sysret;
692 char fwpath[fwPathMaxLength];
AppaRao Puli28972062019-11-11 02:04:45 +0530693 phosphor::logging::log<phosphor::logging::level::INFO>(
694 "Transfer Image From USB.",
695 phosphor::logging::entry("URI=%s", uri.c_str()));
696 ret = executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
697 fwUpdateMountPoint);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700698 if (ret)
699 {
700 return ret;
701 }
702
AppaRao Puli28972062019-11-11 02:04:45 +0530703 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
704 ret = transferImageFromFile(usb_path, false);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700705
AppaRao Puli28972062019-11-11 02:04:45 +0530706 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage, fwUpdateMountPoint);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700707 return ret;
708}
709
James Feistfcd2d3a2020-05-28 10:38:15 -0700710static bool transferFirmwareFromUri(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700711{
AppaRao Puli28972062019-11-11 02:04:45 +0530712 static constexpr char fwUriFile[] = "file://";
713 static constexpr char fwUriUsb[] = "usb://";
714 phosphor::logging::log<phosphor::logging::level::INFO>(
715 "Transfer Image From URI.",
716 phosphor::logging::entry("URI=%s", uri.c_str()));
717 if (boost::algorithm::starts_with(uri, fwUriFile))
Vernon Mauery52ce6622019-05-22 09:19:46 -0700718 {
AppaRao Puli28972062019-11-11 02:04:45 +0530719 std::string fname = uri.substr(sizeof(fwUriFile) - 1);
720 if (fname != firmwareBufferFile)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700721 {
AppaRao Puli28972062019-11-11 02:04:45 +0530722 return 0 == transferImageFromFile(fname);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700723 }
724 return true;
725 }
AppaRao Puli28972062019-11-11 02:04:45 +0530726 if (boost::algorithm::starts_with(uri, fwUriUsb))
Vernon Mauery52ce6622019-05-22 09:19:46 -0700727 {
AppaRao Puli28972062019-11-11 02:04:45 +0530728 std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
729 return 0 == transferImageFromUsb(fname);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700730 }
731 return false;
732}
733
734/* Get USB-mass-storage device status: inserted => true, ejected => false */
AppaRao Puli28972062019-11-11 02:04:45 +0530735static bool getUsbStatus()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700736{
AppaRao Puli28972062019-11-11 02:04:45 +0530737 std::filesystem::path usbDevPath =
738 std::filesystem::path("/sys/kernel/config/usb_gadget") /
739 fwUpdateUSBDevName;
740 return (std::filesystem::exists(usbDevPath) ? true : false);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700741}
742
743/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
AppaRao Puli28972062019-11-11 02:04:45 +0530744static int attachUsbDevice()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700745{
AppaRao Puli28972062019-11-11 02:04:45 +0530746 if (getUsbStatus())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700747 {
748 return 1;
749 }
AppaRao Puli28972062019-11-11 02:04:45 +0530750 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
751 std::to_string(maxFirmwareImageSize / 1_MB).c_str());
Vernon Mauery52ce6622019-05-22 09:19:46 -0700752 if (!ret)
753 {
AppaRao Puli28972062019-11-11 02:04:45 +0530754 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
755 fwUpdateUsbVolImage);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700756 }
757 return ret;
758}
759
760/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
AppaRao Puli28972062019-11-11 02:04:45 +0530761static int detachUsbDevice()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700762{
AppaRao Puli28972062019-11-11 02:04:45 +0530763 if (!getUsbStatus())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700764 {
765 return 1;
766 }
AppaRao Puli28972062019-11-11 02:04:45 +0530767 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700768}
AppaRao Puli28972062019-11-11 02:04:45 +0530769static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700770{
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800771 constexpr uint8_t undefinedImage = 0x00;
AppaRao Puli28972062019-11-11 02:04:45 +0530772 constexpr uint8_t primaryImage = 0x01;
773 constexpr uint8_t secondaryImage = 0x02;
James Feistfcd2d3a2020-05-28 10:38:15 -0700774 constexpr const char* secondaryFitImageStartAddr = "22480000";
AppaRao Puli28972062019-11-11 02:04:45 +0530775
776 uint8_t bootImage = primaryImage;
777 boost::system::error_code ec;
778 std::string value = ctx->bus->yield_method_call<std::string>(
779 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
780 "/xyz/openbmc_project/u_boot/environment/mgr",
781 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
782 if (ec)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700783 {
AppaRao Puli28972062019-11-11 02:04:45 +0530784 phosphor::logging::log<phosphor::logging::level::ERR>(
785 "Failed to read the bootcmd value");
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800786 /* don't fail, just give back undefined until it is ready */
787 bootImage = undefinedImage;
AppaRao Puli28972062019-11-11 02:04:45 +0530788 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700789
AppaRao Puli28972062019-11-11 02:04:45 +0530790 /* cheking for secondary FitImage Address 22480000 */
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800791 else if (value.find(secondaryFitImageStartAddr) != std::string::npos)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700792 {
AppaRao Puli28972062019-11-11 02:04:45 +0530793 bootImage = secondaryImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700794 }
795 else
796 {
AppaRao Puli28972062019-11-11 02:04:45 +0530797 bootImage = primaryImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700798 }
799
AppaRao Puli28972062019-11-11 02:04:45 +0530800 return bootImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700801}
802
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530803#ifdef INTEL_PFR_ENABLED
804using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
805 uint8_t, // Major Version Number
806 uint8_t, // Minor Version Number
807 uint32_t, // Build Number
808 uint32_t, // Build Timestamp
809 uint32_t>; // Update Timestamp
810ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700811{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700812 // Byte 1 - Count (N) Number of devices data is being returned for.
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530813 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700814 // Bytes - 17:(15xN) - Repeat of 2 through 16
815
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530816 std::vector<fwVersionInfoType> fwVerInfoList;
817 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
James Feistfcd2d3a2020-05-28 10:38:15 -0700818 for (const auto& fwDev : fwVersionIdMap)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700819 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530820 std::string verStr;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700821 try
822 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530823 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700824
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530825 ipmi::Value result = ipmi::getDbusProperty(
826 *busp, service, fwDev.second, versionIntf, "Version");
827 verStr = std::get<std::string>(result);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700828 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700829 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700830 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530831 phosphor::logging::log<phosphor::logging::level::INFO>(
832 "Failed to fetch Version property",
833 phosphor::logging::entry("ERROR=%s", e.what()),
834 phosphor::logging::entry("PATH=%s", fwDev.second),
835 phosphor::logging::entry("INTERFACE=%s", versionIntf));
836 continue;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700837 }
838
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530839 if (verStr.empty())
840 {
841 phosphor::logging::log<phosphor::logging::level::INFO>(
842 "Version is empty.",
843 phosphor::logging::entry("PATH=%s", fwDev.second),
844 phosphor::logging::entry("INTERFACE=%s", versionIntf));
845 continue;
846 }
847
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530848 uint8_t majorNum = 0;
849 uint8_t minorNum = 0;
850 uint32_t buildNum = 0;
851 try
852 {
srikanta mondal52a292b2020-07-27 23:49:14 +0000853 std::optional<ipmi::MetaRevision> rev =
854 ipmi::convertIntelVersion(verStr);
855 if (rev.has_value())
856 {
857 ipmi::MetaRevision revision = rev.value();
858 majorNum = revision.major % 10 + (revision.major / 10) * 16;
859 minorNum = (revision.minor > 99 ? 99 : revision.minor);
860 minorNum = minorNum % 10 + (minorNum / 10) * 16;
861 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
862 hash = bswap_32(hash);
863 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
864 }
865 else
866 {
867 std::vector<std::string> splitVer;
868 boost::split(splitVer, verStr, boost::is_any_of(".-"));
869 if (splitVer.size() < 3)
870 {
871 phosphor::logging::log<phosphor::logging::level::INFO>(
872 "Invalid Version format.",
873 phosphor::logging::entry("Version=%s", verStr.c_str()),
874 phosphor::logging::entry("PATH=%s", fwDev.second));
875 continue;
876 }
877 majorNum = std::stoul(splitVer[0], nullptr, 16);
878 minorNum = std::stoul(splitVer[1], nullptr, 16);
879 buildNum = std::stoul(splitVer[2], nullptr, 16);
880 }
881 // Build Timestamp - Not supported.
882 // Update Timestamp - TODO: Need to check with CPLD team.
883 fwVerInfoList.emplace_back(
884 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
885 minorNum, buildNum, 0, 0));
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530886 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700887 catch (const std::exception& e)
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530888 {
889 phosphor::logging::log<phosphor::logging::level::INFO>(
890 "Failed to convert stoul.",
891 phosphor::logging::entry("ERROR=%s", e.what()));
892 continue;
893 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700894 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700895
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530896 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700897}
Punith Nadur Basavarajaiaha8dd1972020-08-19 18:05:44 +0000898
899std::array<uint8_t, imageCount> getSecurityVersionInfo(const char* mtdDevBuf,
900 size_t svnVerOffsetInPfm,
901 size_t bkcVerOffsetInPfm)
902{
903 constexpr size_t bufLength = 1;
904 std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp;
905 constexpr uint8_t svnIndexValue = 0x00;
906 constexpr uint8_t bkcIndexValue = 0x01;
907 constexpr uint8_t tempIndexValue = 0x00;
908 try
909 {
910 SPIDev spiDev(mtdDevBuf);
911 spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data());
912 fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue);
913 spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data());
914 fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue);
915 }
916 catch (const std::exception& e)
917 {
918 phosphor::logging::log<phosphor::logging::level::ERR>(
919 "Exception caught in getSecurityVersionInfo",
920 phosphor::logging::entry("MSG=%s", e.what()));
921 fwSecurityVersionBuf = {0, 0};
922 }
923
924 return fwSecurityVersionBuf;
925}
926
927ipmi::RspType<
928 uint8_t, // device ID
929 uint8_t, // Active Image Value
930 std::array<uint8_t, imageCount>, // Security version for Active Image
931 uint8_t, // recovery Image Value
932 std::array<uint8_t, imageCount>> // Security version for Recovery Image
AppaRao Puli28972062019-11-11 02:04:45 +0530933 ipmiGetFwSecurityVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700934{
Punith Nadur Basavarajaiaha8dd1972020-08-19 18:05:44 +0000935 static bool cacheFlag = false;
936 constexpr std::array<const char*, imageCount> mtdDevBuf = {
937 bmcActivePfmMTDDev, bmcRecoveryImgMTDDev};
938
939 // To avoid multiple reading from SPI device
940 if (!cacheFlag)
941 {
942 imgFwSecurityVersion[0] = getSecurityVersionInfo(
943 mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm);
944 imgFwSecurityVersion[1] = getSecurityVersionInfo(
945 mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm);
946 cacheFlag = true;
947 }
948
949 constexpr uint8_t ActivePfmMTDDev = 0x00;
950 constexpr uint8_t RecoveryImgMTDDev = 0x01;
951
952 return ipmi::responseSuccess(
953 imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage),
954 imgFwSecurityVersion[ActivePfmMTDDev],
955 static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage),
956 imgFwSecurityVersion[RecoveryImgMTDDev]);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700957}
958
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530959ipmi::RspType<std::array<uint8_t, certKeyLen>,
960 std::optional<std::array<uint8_t, cskSignatureLen>>>
James Feistfcd2d3a2020-05-28 10:38:15 -0700961 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700962{
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +0530963 bool isIPMBChannel = false;
964
965 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
966 {
967 return ipmi::responseUnspecifiedError();
968 }
969 if (isIPMBChannel)
970 {
971 phosphor::logging::log<phosphor::logging::level::INFO>(
972 "Command not supported. Failed to get root certificate data.");
973 return ipmi::responseCommandNotAvailable();
974 }
975
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530976 size_t certKeyOffset = 0;
977 size_t cskSigOffset = 0;
978 std::string mtdDev;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700979
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530980 switch (static_cast<FwGetRootCertDataTag>(certId))
981 {
982 case FwGetRootCertDataTag::activeRootKey:
983 {
984 mtdDev = bmcActivePfmMTDDev;
985 certKeyOffset = rootkeyOffsetInPfm;
986 break;
987 }
988 case FwGetRootCertDataTag::recoveryRootKey:
989 {
990 mtdDev = bmcRecoveryImgMTDDev;
991 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
992 break;
993 }
994 case FwGetRootCertDataTag::activeCSK:
995 {
996 mtdDev = bmcActivePfmMTDDev;
997 certKeyOffset = cskKeyOffsetInPfm;
998 cskSigOffset = cskSignatureOffsetInPfm;
999 break;
1000 }
1001 case FwGetRootCertDataTag::recoveryCSK:
1002 {
1003 mtdDev = bmcRecoveryImgMTDDev;
1004 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1005 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1006 break;
1007 }
1008 default:
1009 {
1010 return ipmi::responseInvalidFieldRequest();
1011 }
1012 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001013
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301014 std::array<uint8_t, certKeyLen> certKey = {0};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001015
Vernon Mauery52ce6622019-05-22 09:19:46 -07001016 try
1017 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301018 SPIDev spiDev(mtdDev);
1019 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1020
1021 if (cskSigOffset)
1022 {
1023 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1024 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1025 cskSignature.data());
1026 return ipmi::responseSuccess(certKey, cskSignature);
1027 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001028 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001029 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001030 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301031 phosphor::logging::log<phosphor::logging::level::ERR>(
1032 "Exception caught in ipmiGetFwRootCertData",
1033 phosphor::logging::entry("MSG=%s", e.what()));
1034 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001035 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001036
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301037 return ipmi::responseSuccess(certKey, std::nullopt);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001038}
AppaRao Puli28972062019-11-11 02:04:45 +05301039#endif // INTEL_PFR_ENABLED
1040
1041static constexpr uint8_t channelListSize = 3;
1042/** @brief implements Maximum Firmware Transfer size command
1043 * @parameter
1044 * - none
1045 * @returns IPMI completion code plus response data
1046 * - count - channel count
1047 * - channelList - channel list information
1048 */
1049ipmi::RspType<uint8_t, // channel count
1050 std::array<std::tuple<uint8_t, uint32_t>,
1051 channelListSize> // Channel List
1052 >
1053 ipmiFirmwareMaxTransferSize()
1054{
1055 constexpr size_t kcsMaxBufSize = 128;
1056 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
AppaRao Puli28972062019-11-11 02:04:45 +05301057 // Byte 1 - Count (N) Number of devices data is being returned for.
1058 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
1059 // Byte 3-6 - transfer size (little endian)
1060 // Bytes - 7:(5xN) - Repeat of 2 through 6
1061 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1062 channelList = {
1063 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
AppaRao Puli28972062019-11-11 02:04:45 +05301064 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
1065 rmcpPlusMaxBufSize}}};
1066
1067 return ipmi::responseSuccess(channelListSize, channelList);
1068}
1069
1070ipmi::RspType<uint8_t, uint8_t>
1071 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
1072{
1073 // Byte 1 - Current execution context
1074 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
1075 // Byte 2 - Partition pointer
1076 // 0x01 - primary, 0x02 - secondary
1077 uint8_t partitionPtr = getActiveBootImage(ctx);
1078
1079 return ipmi::responseSuccess(
1080 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
1081}
AppaRao Puli28972062019-11-11 02:04:45 +05301082/** @brief Get Firmware Update Random Number
1083 *
1084 * This function generate the random number used for
1085 * setting the firmware update mode as authentication key.
1086 *
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301087 * @param[in] ctx - context of current session
AppaRao Puli28972062019-11-11 02:04:45 +05301088 * @returns IPMI completion code along with
1089 * - random number
1090 **/
1091ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
James Feistfcd2d3a2020-05-28 10:38:15 -07001092 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx)
AppaRao Puli28972062019-11-11 02:04:45 +05301093{
1094 phosphor::logging::log<phosphor::logging::level::INFO>(
1095 "Generate FW update random number");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301096 bool isIPMBChannel = false;
1097
1098 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1099 {
1100 return ipmi::responseUnspecifiedError();
1101 }
1102 if (isIPMBChannel)
1103 {
1104 phosphor::logging::log<phosphor::logging::level::INFO>(
1105 "Channel not supported. Failed to fetch FW update random number");
1106 return ipmi::responseCommandNotAvailable();
1107 }
AppaRao Puli28972062019-11-11 02:04:45 +05301108 std::random_device rd;
1109 std::default_random_engine gen(rd());
1110 std::uniform_int_distribution<> dist{0, 255};
1111
1112 fwRandomNumGenTs = std::chrono::steady_clock::now();
1113
1114 for (int i = 0; i < fwRandomNumLength; i++)
1115 {
1116 fwRandomNum[i] = dist(gen);
1117 }
1118
1119 return ipmi::responseSuccess(fwRandomNum);
1120}
1121
1122/** @brief Set Firmware Update Mode
1123 *
1124 * This function sets BMC into firmware update mode
1125 * after validating Random number obtained from the Get
1126 * Firmware Update Random Number command
1127 *
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301128 * @param[in] ctx - context of current session
1129 * @parameter randNum - Random number(token)
1130 * @returns IPMI completion code
AppaRao Puli28972062019-11-11 02:04:45 +05301131 **/
1132ipmi::RspType<>
James Feistfcd2d3a2020-05-28 10:38:15 -07001133 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx,
1134 std::array<uint8_t, fwRandomNumLength>& randNum)
AppaRao Puli28972062019-11-11 02:04:45 +05301135{
1136 phosphor::logging::log<phosphor::logging::level::INFO>(
1137 "Start FW update mode");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301138
1139 bool isIPMBChannel = false;
1140
1141 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1142 {
1143 return ipmi::responseUnspecifiedError();
1144 }
1145 if (isIPMBChannel)
1146 {
1147 phosphor::logging::log<phosphor::logging::level::INFO>(
1148 "Channel not supported. Failed to set FW update mode");
1149 return ipmi::responseCommandNotAvailable();
1150 }
AppaRao Puli28972062019-11-11 02:04:45 +05301151 /* Firmware Update Random number is valid for 30 seconds only */
1152 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1153 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1154 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1155 fwRandomNumExpirySeconds)
1156 .count())
1157 {
1158 phosphor::logging::log<phosphor::logging::level::INFO>(
1159 "Firmware update random number expired.");
1160 return ipmi::responseInvalidFieldRequest();
1161 }
1162
1163 /* Validate random number */
1164 for (int i = 0; i < fwRandomNumLength; i++)
1165 {
1166 if (fwRandomNum[i] != randNum[i])
1167 {
1168 phosphor::logging::log<phosphor::logging::level::INFO>(
1169 "Invalid random number specified.");
1170 return ipmi::responseInvalidFieldRequest();
1171 }
1172 }
1173
1174 try
1175 {
1176 if (getFirmwareUpdateMode())
1177 {
1178 phosphor::logging::log<phosphor::logging::level::INFO>(
1179 "Already firmware update is in progress.");
1180 return ipmi::responseBusy();
1181 }
1182 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001183 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301184 {
1185 return ipmi::responseUnspecifiedError();
1186 }
1187
1188 // FIXME? c++ doesn't off an option for exclusive file creation
James Feistfcd2d3a2020-05-28 10:38:15 -07001189 FILE* fp = fopen(firmwareBufferFile, "wx");
AppaRao Puli28972062019-11-11 02:04:45 +05301190 if (!fp)
1191 {
1192 phosphor::logging::log<phosphor::logging::level::INFO>(
1193 "Unable to open file.");
1194 return ipmi::responseUnspecifiedError();
1195 }
1196 fclose(fp);
1197
1198 try
1199 {
1200 setFirmwareUpdateMode(true);
1201 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001202 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301203 {
1204 unlink(firmwareBufferFile);
1205 return ipmi::responseUnspecifiedError();
1206 }
1207
1208 return ipmi::responseSuccess();
1209}
1210
1211/** @brief implements exit firmware update mode command
1212 * @param None
1213 *
1214 * @returns IPMI completion code
1215 */
James Feistfcd2d3a2020-05-28 10:38:15 -07001216ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx)
AppaRao Puli28972062019-11-11 02:04:45 +05301217{
1218 phosphor::logging::log<phosphor::logging::level::INFO>(
1219 "Exit FW update mode");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301220 bool isIPMBChannel = false;
1221
1222 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1223 {
1224 return ipmi::responseUnspecifiedError();
1225 }
1226 if (isIPMBChannel)
1227 {
1228 phosphor::logging::log<phosphor::logging::level::INFO>(
1229 "Command not supported. Failed to exit firmware update mode");
1230 return ipmi::responseCommandNotAvailable();
1231 }
1232
AppaRao Puli28972062019-11-11 02:04:45 +05301233 switch (fwUpdateStatus.getState())
1234 {
1235 case FwUpdateStatusCache::fwStateInit:
1236 case FwUpdateStatusCache::fwStateIdle:
1237 return ipmi::responseInvalidFieldRequest();
1238 break;
1239 case FwUpdateStatusCache::fwStateDownload:
1240 case FwUpdateStatusCache::fwStateVerify:
1241 break;
1242 case FwUpdateStatusCache::fwStateProgram:
1243 break;
1244 case FwUpdateStatusCache::fwStateUpdateSuccess:
1245 case FwUpdateStatusCache::fwStateError:
1246 break;
1247 case FwUpdateStatusCache::fwStateAcCycleRequired:
1248 return ipmi::responseInvalidFieldRequest();
1249 break;
1250 }
1251 fwUpdateStatus.firmwareUpdateAbortState();
1252
1253 try
1254 {
1255 setFirmwareUpdateMode(false);
1256 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001257 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301258 {
1259 return ipmi::responseUnspecifiedError();
1260 }
1261
1262 return ipmi::responseSuccess();
1263}
1264
1265/** @brief implements Get/Set Firmware Update Control
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301266 * @param[in] ctx - context of current session
AppaRao Puli28972062019-11-11 02:04:45 +05301267 * @parameter
1268 * - Byte 1: Control Byte
1269 * - Byte 2: Firmware filename length (Optional)
1270 * - Byte 3:N: Firmware filename data (Optional)
1271 * @returns IPMI completion code plus response data
1272 * - Byte 2: Current control status
1273 **/
1274ipmi::RspType<bool, bool, bool, bool, uint4_t>
James Feistfcd2d3a2020-05-28 10:38:15 -07001275 ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr& ctx,
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301276 const uint8_t controlReq,
James Feistfcd2d3a2020-05-28 10:38:15 -07001277 const std::optional<std::string>& fileName)
AppaRao Puli28972062019-11-11 02:04:45 +05301278{
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301279 bool isIPMBChannel = false;
1280
1281 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1282 {
1283 return ipmi::responseUnspecifiedError();
1284 }
1285 if (isIPMBChannel)
1286 {
1287 phosphor::logging::log<phosphor::logging::level::INFO>(
1288 "Channel not supported. Failed to get or set FW update control");
1289 return ipmi::responseCommandNotAvailable();
1290 }
1291
AppaRao Puli28972062019-11-11 02:04:45 +05301292 static std::string fwXferUriPath;
1293 static bool imageTransferStarted = false;
1294 static bool imageTransferCompleted = false;
1295 static bool imageTransferAborted = false;
1296
1297 if ((controlReq !=
1298 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
1299 (fileName))
1300 {
1301 phosphor::logging::log<phosphor::logging::level::ERR>(
1302 "Invalid request field (Filename).");
1303 return ipmi::responseInvalidFieldRequest();
1304 }
1305
1306 static bool usbAttached = getUsbStatus();
1307
1308 switch (static_cast<FwUpdateCtrlReq>(controlReq))
1309 {
1310 case FwUpdateCtrlReq::getCurrentControlStatus:
1311 phosphor::logging::log<phosphor::logging::level::INFO>(
1312 "ipmiGetSetFirmwareUpdateControl: Get status");
1313 break;
1314 case FwUpdateCtrlReq::imageTransferStart:
1315 {
1316 phosphor::logging::log<phosphor::logging::level::INFO>(
1317 "ipmiGetSetFirmwareUpdateControl: Set transfer start");
1318 imageTransferStarted = true;
1319 // reset buffer to empty (truncate file)
1320 std::ofstream out(firmwareBufferFile,
1321 std::ofstream::binary | std::ofstream::trunc);
1322 fwXferUriPath = std::string("file://") + firmwareBufferFile;
1323 if (xferHashCheck)
1324 {
1325 xferHashCheck->clear();
1326 }
1327 // Setting state to download
1328 fwUpdateStatus.setState(
1329 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
1330#ifdef INTEL_PFR_ENABLED
1331 imgLength = 0;
1332 imgType = 0;
1333 block0Mapped = false;
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301334#endif
AppaRao Puli28972062019-11-11 02:04:45 +05301335 }
1336 break;
1337 case FwUpdateCtrlReq::imageTransferComplete:
1338 {
1339 phosphor::logging::log<phosphor::logging::level::INFO>(
1340 "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
1341 if (usbAttached)
1342 {
1343 phosphor::logging::log<phosphor::logging::level::ERR>(
1344 "USB should be detached to perform this operation.");
1345 return ipmi::responseNotSupportedInPresentState();
1346 }
1347 // finish transfer based on URI
1348 if (!transferFirmwareFromUri(fwXferUriPath))
1349 {
1350 phosphor::logging::log<phosphor::logging::level::ERR>(
1351 "transferFirmwareFromUri failed.");
1352 return ipmi::responseUnspecifiedError();
1353 }
1354 // transfer complete
1355 if (xferHashCheck)
1356 {
1357 if (TransferHashCheck::HashCheck::sha2Success !=
1358 xferHashCheck->verify())
1359 {
1360 phosphor::logging::log<phosphor::logging::level::ERR>(
1361 "xferHashCheck failed.");
1362 return ipmi::responseUnspecifiedError();
1363 }
1364 }
1365 // Set state to verify and start the update
1366 fwUpdateStatus.setState(
1367 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
1368 // start the request
1369 if (!startFirmwareUpdate(firmwareBufferFile))
1370 {
1371 phosphor::logging::log<phosphor::logging::level::ERR>(
1372 "startFirmwareUpdate failed.");
1373 return ipmi::responseUnspecifiedError();
1374 }
1375 imageTransferCompleted = true;
1376 }
1377 break;
1378 case FwUpdateCtrlReq::imageTransferAbort:
1379 phosphor::logging::log<phosphor::logging::level::INFO>(
1380 "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
1381 if (usbAttached)
1382 {
1383 if (detachUsbDevice())
1384 {
1385 phosphor::logging::log<phosphor::logging::level::ERR>(
1386 "Detach USB device failed.");
1387 return ipmi::responseUsbAttachOrDetachFailed();
1388 }
1389 usbAttached = false;
1390 }
1391 // During abort request reset the state to Init by cleaning update
1392 // file.
1393 fwUpdateStatus.firmwareUpdateAbortState();
1394 imageTransferAborted = true;
1395 break;
1396 case FwUpdateCtrlReq::setFirmwareFilename:
1397 phosphor::logging::log<phosphor::logging::level::INFO>(
1398 "ipmiGetSetFirmwareUpdateControl: Set filename.");
1399 if (!fileName || ((*fileName).length() == 0))
1400 {
1401 phosphor::logging::log<phosphor::logging::level::ERR>(
1402 "Invalid Filename specified.");
1403 return ipmi::responseInvalidFieldRequest();
1404 }
1405
1406 fwXferUriPath = *fileName;
1407 break;
1408 case FwUpdateCtrlReq::attachUsbDevice:
1409 phosphor::logging::log<phosphor::logging::level::INFO>(
1410 "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
1411 if (usbAttached)
1412 {
1413 phosphor::logging::log<phosphor::logging::level::ERR>(
1414 "USB device is already attached.");
1415 return ipmi::responseInvalidFieldRequest();
1416 }
1417 if (attachUsbDevice())
1418 {
1419 phosphor::logging::log<phosphor::logging::level::ERR>(
1420 "Attach USB device failed.");
1421 return ipmi::responseUsbAttachOrDetachFailed();
1422 }
1423 usbAttached = true;
1424 break;
1425 case FwUpdateCtrlReq::detachUsbDevice:
1426 phosphor::logging::log<phosphor::logging::level::INFO>(
1427 "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
1428 if (!usbAttached)
1429 {
1430 phosphor::logging::log<phosphor::logging::level::ERR>(
1431 "USB device is not attached.");
1432 return ipmi::responseInvalidFieldRequest();
1433 }
1434 if (detachUsbDevice())
1435 {
1436 phosphor::logging::log<phosphor::logging::level::ERR>(
1437 "Detach USB device failed.");
1438 return ipmi::responseUsbAttachOrDetachFailed();
1439 }
1440 usbAttached = false;
1441 break;
1442 default:
1443 phosphor::logging::log<phosphor::logging::level::ERR>(
1444 "Invalid control option specified.");
1445 return ipmi::responseInvalidFieldRequest();
1446 }
1447
1448 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
1449 imageTransferAborted, usbAttached, uint4_t(0));
1450}
1451
1452/** @brief implements firmware get status command
1453 * @parameter
1454 * - none
1455 * @returns IPMI completion code plus response data
1456 * - status - processing status
1457 * - percentage - percentage completion
1458 * - check - channel integrity check status
1459 **/
1460ipmi::RspType<uint8_t, // status
1461 uint8_t, // percentage
1462 uint8_t // check
1463 >
1464 ipmiGetFirmwareUpdateStatus()
1465
1466{
1467 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1468 // 5=ready, f=error, 83=ac cycle required)
1469 // Byte 2 - percent
1470 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1471 uint8_t status = fwUpdateStatus.getState();
1472 uint8_t percent = fwUpdateStatus.percent();
1473 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
1474
1475 // Status code.
1476 return ipmi::responseSuccess(status, percent, check);
1477}
1478
1479ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
James Feistfcd2d3a2020-05-28 10:38:15 -07001480 const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask,
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301481 bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart,
1482 bool sha2Check, uint5_t reserved2,
1483 std::optional<std::vector<uint8_t>> integrityCheckVal)
AppaRao Puli28972062019-11-11 02:04:45 +05301484{
1485 phosphor::logging::log<phosphor::logging::level::INFO>(
1486 "Set firmware update options.");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301487 bool isIPMBChannel = false;
1488
1489 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1490 {
1491 return ipmi::responseUnspecifiedError();
1492 }
1493 if (isIPMBChannel)
1494 {
1495 phosphor::logging::log<phosphor::logging::level::INFO>(
1496 "Channel not supported. Failed to set firmware update options");
1497 return ipmi::responseCommandNotAvailable();
1498 }
AppaRao Puli28972062019-11-11 02:04:45 +05301499 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
1500 bool deferRestartState = fwUpdateStatus.getDeferRestart();
1501 bool sha2CheckState = xferHashCheck ? true : false;
1502
1503 if (noDowngradeMask && (noDowngradeState != noDowngrade))
1504 {
1505 fwUpdateStatus.setInhibitDowngrade(noDowngrade);
1506 noDowngradeState = noDowngrade;
1507 }
1508 if (deferRestartMask && (deferRestartState != deferRestart))
1509 {
1510 fwUpdateStatus.setDeferRestart(deferRestart);
1511 deferRestartState = deferRestart;
1512 }
1513 if (sha2CheckMask)
1514 {
1515 if (sha2Check)
1516 {
1517 auto hashSize = EVP_MD_size(EVP_sha256());
1518 if ((*integrityCheckVal).size() != hashSize)
1519 {
1520 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1521 "Invalid size of Hash specified.");
1522 return ipmi::responseInvalidFieldRequest();
1523 }
1524 xferHashCheck = std::make_shared<TransferHashCheck>();
1525 xferHashCheck->init(*integrityCheckVal);
1526 }
1527 else
1528 {
1529 // delete the xferHashCheck object
1530 xferHashCheck.reset();
1531 }
1532 sha2CheckState = sha2CheckMask;
1533 }
1534 return ipmi::responseSuccess(noDowngradeState, deferRestartState,
1535 sha2CheckState, reserved1);
1536}
Vernon Mauery52ce6622019-05-22 09:19:46 -07001537
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301538ipmi::RspType<uint32_t>
James Feistfcd2d3a2020-05-28 10:38:15 -07001539 ipmiFwImageWriteData(const std::vector<uint8_t>& writeData)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001540{
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301541 const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1542 size_t writeDataLen = writeData.size();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001543
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301544 if (!writeDataLen)
1545 {
1546 return ipmi::responseReqDataLenInvalid();
1547 }
1548
AppaRao Puli28972062019-11-11 02:04:45 +05301549 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301550 {
AppaRao Puli28972062019-11-11 02:04:45 +05301551 phosphor::logging::log<phosphor::logging::level::ERR>(
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301552 "Invalid firmware update state.");
1553 return ipmi::response(ccCmdNotSupportedInPresentState);
1554 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001555
AppaRao Puli28972062019-11-11 02:04:45 +05301556 std::ofstream out(firmwareBufferFile,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001557 std::ofstream::binary | std::ofstream::app);
1558 if (!out)
1559 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301560 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1561 "Error while opening file.");
1562 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001563 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301564
1565 uint64_t fileDataLen = out.tellp();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301566
AppaRao Puli28972062019-11-11 02:04:45 +05301567 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001568 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301569 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1570 "Firmware image size exceeds the limit");
1571 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001572 }
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301573
James Feistfcd2d3a2020-05-28 10:38:15 -07001574 const char* data = reinterpret_cast<const char*>(writeData.data());
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301575 out.write(data, writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001576 out.close();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301577
AppaRao Puli28972062019-11-11 02:04:45 +05301578 if (xferHashCheck)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001579 {
AppaRao Puli28972062019-11-11 02:04:45 +05301580 xferHashCheck->hash(writeData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001581 }
1582
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301583#ifdef INTEL_PFR_ENABLED
1584 /* PFR image block 0 - As defined in HAS */
1585 struct PFRImageBlock0
1586 {
1587 uint32_t tag;
1588 uint32_t pcLength;
1589 uint32_t pcType;
1590 uint32_t reserved1;
1591 uint8_t hash256[32];
1592 uint8_t hash384[48];
1593 uint8_t reserved2[32];
1594 } __attribute__((packed));
1595
1596 /* Get the PFR block 0 data and read the uploaded image
1597 * information( Image type, length etc) */
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301598 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1599 (!block0Mapped))
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301600 {
1601 struct PFRImageBlock0 block0Data = {0};
1602
AppaRao Puli28972062019-11-11 02:04:45 +05301603 std::ifstream inFile(firmwareBufferFile,
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301604 std::ios::binary | std::ios::in);
James Feistfcd2d3a2020-05-28 10:38:15 -07001605 inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data));
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301606 inFile.close();
1607
1608 uint32_t magicNum = block0Data.tag;
1609
1610 /* Validate the magic number */
1611 if (magicNum != perBlock0MagicNum)
1612 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301613 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1614 "PFR image magic number not matched");
1615 return ipmi::responseInvalidFieldRequest();
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301616 }
1617 // Note:imgLength, imgType and block0Mapped are in global scope, as
1618 // these are used in cascaded updates.
1619 imgLength = block0Data.pcLength;
1620 imgType = block0Data.pcType;
1621 block0Mapped = true;
1622 }
1623#endif // end of INTEL_PFR_ENABLED
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301624 return ipmi::responseSuccess(writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001625}
1626
AppaRao Puli28972062019-11-11 02:04:45 +05301627static void registerFirmwareFunctions()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001628{
1629 // guarantee that we start with an already timed out timestamp
AppaRao Puli28972062019-11-11 02:04:45 +05301630 fwRandomNumGenTs =
1631 std::chrono::steady_clock::now() - fwRandomNumExpirySeconds;
1632 fwUpdateStatus.setState(
1633 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
Vernon Mauery52ce6622019-05-22 09:19:46 -07001634
AppaRao Puli28972062019-11-11 02:04:45 +05301635 unlink(firmwareBufferFile);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001636
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301637#ifdef INTEL_PFR_ENABLED
1638 // Following commands are supported only for PFR enabled platforms
1639 // CMD:0x20 - Get Firmware Version Information
AppaRao Puli28972062019-11-11 02:04:45 +05301640 // CMD:0x21 - Get Firmware Security Version Information
1641 // CMD:0x25 - Get Root Certificate Data
Vernon Mauery52ce6622019-05-22 09:19:46 -07001642
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301643 // get firmware version information
1644 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1645 ipmi::firmware::cmdGetFwVersionInfo,
1646 ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
AppaRao Puli28972062019-11-11 02:04:45 +05301647
Vernon Mauery52ce6622019-05-22 09:19:46 -07001648 // get firmware security version information
AppaRao Puli28972062019-11-11 02:04:45 +05301649 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1650 ipmi::firmware::cmdGetFwSecurityVersionInfo,
1651 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001652
Vernon Mauery52ce6622019-05-22 09:19:46 -07001653 // get root certificate data
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301654 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1655 ipmi::firmware::cmdFwGetRootCertData,
1656 ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1657#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001658
AppaRao Puli28972062019-11-11 02:04:45 +05301659 // get firmware update channel information (max transfer sizes)
1660 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1661 ipmi::firmware::cmdGetFwUpdateChannelInfo,
1662 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001663
AppaRao Puli28972062019-11-11 02:04:45 +05301664 // get bmc execution context
1665 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1666 ipmi::firmware::cmdGetBmcExecutionContext,
1667 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
1668
1669 // Get Firmware Update Random number
1670 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1671 ipmi::firmware::cmdGetFwUpdateRandomNumber,
1672 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
1673
1674 // Set Firmware Update Mode
1675 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1676 ipmi::firmware::cmdSetFirmwareUpdateMode,
AppaRao Puli4b3e1c72019-10-16 20:53:09 +05301677 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001678
AppaRao Puli28972062019-11-11 02:04:45 +05301679 // Exit Firmware Update Mode
anil kumar appanab57098a2019-05-28 16:22:33 +00001680 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
AppaRao Puli28972062019-11-11 02:04:45 +05301681 ipmi::firmware::cmdExitFirmwareUpdateMode,
1682 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001683
AppaRao Puli28972062019-11-11 02:04:45 +05301684 // Get/Set Firmware Update Control
1685 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1686 ipmi::firmware::cmdGetSetFwUpdateControl,
1687 ipmi::Privilege::Admin,
1688 ipmiGetSetFirmwareUpdateControl);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001689
AppaRao Puli28972062019-11-11 02:04:45 +05301690 // Get Firmware Update Status
1691 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1692 ipmi::firmware::cmdGetFirmwareUpdateStatus,
1693 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001694
AppaRao Puli28972062019-11-11 02:04:45 +05301695 // Set Firmware Update Options
1696 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1697 ipmi::firmware::cmdSetFirmwareUpdateOptions,
1698 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001699 // write image data
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301700 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1701 ipmi::firmware::cmdFwImageWriteData,
1702 ipmi::Privilege::Admin, ipmiFwImageWriteData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001703 return;
1704}