blob: 3f17dacfe31796ac46a860675167905934180074 [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
Arun Lal K M153d4c12021-08-25 22:00:37 +000037static constexpr int openSslSuccess = 1;
AppaRao Puli28972062019-11-11 02:04:45 +053038static constexpr bool DEBUG = true;
39static void registerFirmwareFunctions() __attribute__((constructor));
40
AppaRao Puli37fde6b2019-10-25 16:37:50 +053041namespace ipmi
42{
43namespace firmware
44{
45constexpr Cmd cmdGetFwVersionInfo = 0x20;
AppaRao Puli28972062019-11-11 02:04:45 +053046constexpr Cmd cmdGetFwSecurityVersionInfo = 0x21;
47constexpr Cmd cmdGetFwUpdateChannelInfo = 0x22;
48constexpr Cmd cmdGetBmcExecutionContext = 0x23;
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +053049constexpr Cmd cmdFwGetRootCertData = 0x25;
AppaRao Puli28972062019-11-11 02:04:45 +053050constexpr Cmd cmdGetFwUpdateRandomNumber = 0x26;
51constexpr Cmd cmdSetFirmwareUpdateMode = 0x27;
52constexpr Cmd cmdExitFirmwareUpdateMode = 0x28;
53constexpr Cmd cmdGetSetFwUpdateControl = 0x29;
54constexpr Cmd cmdGetFirmwareUpdateStatus = 0x2A;
55constexpr Cmd cmdSetFirmwareUpdateOptions = 0x2B;
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +053056constexpr Cmd cmdFwImageWriteData = 0x2c;
AppaRao Puli37fde6b2019-10-25 16:37:50 +053057} // namespace firmware
58} // namespace ipmi
59
AppaRao Puli28972062019-11-11 02:04:45 +053060namespace ipmi
61{
62// Custom completion codes
63constexpr Cc ccUsbAttachOrDetachFailed = 0x80;
64constexpr Cc ccNotSupportedInPresentState = 0xD5;
65
66static inline auto responseUsbAttachOrDetachFailed()
67{
68 return response(ccUsbAttachOrDetachFailed);
69}
70static inline auto responseNotSupportedInPresentState()
71{
72 return response(ccNotSupportedInPresentState);
73}
74} // namespace ipmi
75
Punith Nadur Basavarajaiaha8dd1972020-08-19 18:05:44 +000076static constexpr size_t imageCount = 2;
77std::array<std::array<uint8_t, imageCount>, imageCount> imgFwSecurityVersion = {
78 (0, 0), (0, 0)};
79static constexpr size_t svnActiveVerOffsetInPfm = 0x404;
80static constexpr size_t bkcActiveVerOffsetInPfm = 0x405;
81static constexpr size_t svnRecoveryVerOffsetInPfm = 0x804;
82static constexpr size_t bkcRecoveryVerOffsetInPfm = 0x805;
James Feistfcd2d3a2020-05-28 10:38:15 -070083static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC";
84static constexpr const char* bmcStatePath = "/xyz/openbmc_project/state/bmc0";
85static constexpr const char* bmcStateReady =
AppaRao Puli28972062019-11-11 02:04:45 +053086 "xyz.openbmc_project.State.BMC.BMCState.Ready";
James Feistfcd2d3a2020-05-28 10:38:15 -070087static constexpr const char* bmcStateUpdateInProgress =
AppaRao Puli28972062019-11-11 02:04:45 +053088 "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
89
90static constexpr char firmwareBufferFile[] = "/tmp/fw-download.bin";
91static std::chrono::steady_clock::time_point fwRandomNumGenTs;
92static constexpr auto fwRandomNumExpirySeconds = std::chrono::seconds(30);
93static constexpr size_t fwRandomNumLength = 8;
94static std::array<uint8_t, fwRandomNumLength> fwRandomNum;
95constexpr char usbCtrlPath[] = "/usr/bin/usb-ctrl";
96constexpr char fwUpdateMountPoint[] = "/tmp/usb-fwupd.mnt";
97constexpr char fwUpdateUsbVolImage[] = "/tmp/usb-fwupd.img";
98constexpr char fwUpdateUSBDevName[] = "fw-usb-mass-storage-dev";
99constexpr size_t fwPathMaxLength = 255;
100
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +0530101#ifdef INTEL_PFR_ENABLED
102uint32_t imgLength = 0;
103uint32_t imgType = 0;
104bool block0Mapped = false;
105static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530106
James Feistfcd2d3a2020-05-28 10:38:15 -0700107static constexpr const char* bmcActivePfmMTDDev = "/dev/mtd/pfm";
108static constexpr const char* bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
AppaRao Puli28972062019-11-11 02:04:45 +0530109static constexpr size_t pfmBaseOffsetInImage = 0x400;
110static constexpr size_t rootkeyOffsetInPfm = 0xA0;
111static constexpr size_t cskKeyOffsetInPfm = 0x124;
112static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
113static constexpr size_t certKeyLen = 96;
114static constexpr size_t cskSignatureLen = 96;
115
James Feistfcd2d3a2020-05-28 10:38:15 -0700116static constexpr const char* versionIntf =
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530117 "xyz.openbmc_project.Software.Version";
118
AppaRao Puli28972062019-11-11 02:04:45 +0530119enum class FwGetRootCertDataTag : uint8_t
120{
121 activeRootKey = 1,
122 recoveryRootKey,
123 activeCSK,
124 recoveryCSK,
125};
126
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530127enum class FWDeviceIDTag : uint8_t
128{
129 bmcActiveImage = 1,
130 bmcRecoveryImage,
131};
132
James Feistfcd2d3a2020-05-28 10:38:15 -0700133const static boost::container::flat_map<FWDeviceIDTag, const char*>
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530134 fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
135 "/xyz/openbmc_project/software/bmc_active"},
136 {FWDeviceIDTag::bmcRecoveryImage,
137 "/xyz/openbmc_project/software/bmc_recovery"}};
AppaRao Puli28972062019-11-11 02:04:45 +0530138#endif // INTEL_PFR_ENABLED
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530139
AppaRao Puli28972062019-11-11 02:04:45 +0530140enum class ChannelIdTag : uint8_t
141{
142 reserved = 0,
143 kcs = 1,
144 ipmb = 2,
145 rmcpPlus = 3
146};
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +0530147
AppaRao Puli28972062019-11-11 02:04:45 +0530148enum class BmcExecutionContext : uint8_t
149{
150 reserved = 0,
151 linuxOs = 0x10,
152 bootLoader = 0x11,
153};
AppaRao Puli09a83142019-11-23 02:46:06 +0530154
AppaRao Puli28972062019-11-11 02:04:45 +0530155enum class FwUpdateCtrlReq : uint8_t
156{
157 getCurrentControlStatus = 0x00,
158 imageTransferStart = 0x01,
159 imageTransferComplete = 0x02,
160 imageTransferAbort = 0x03,
161 setFirmwareFilename = 0x04,
162 attachUsbDevice = 0x05,
163 detachUsbDevice = 0x06
164};
Vernon Mauery52ce6622019-05-22 09:19:46 -0700165
166constexpr std::size_t operator""_MB(unsigned long long v)
167{
168 return 1024u * 1024u * v;
169}
AppaRao Puli28972062019-11-11 02:04:45 +0530170static constexpr size_t maxFirmwareImageSize = 32_MB;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700171
AppaRao Puli28972062019-11-11 02:04:45 +0530172static bool localDownloadInProgress(void)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700173{
174 struct stat sb;
AppaRao Puli28972062019-11-11 02:04:45 +0530175 if (stat(firmwareBufferFile, &sb) < 0)
176 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700177 return false;
AppaRao Puli28972062019-11-11 02:04:45 +0530178 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700179 return true;
180}
181
AppaRao Puli28972062019-11-11 02:04:45 +0530182class TransferHashCheck
183{
184 public:
185 enum class HashCheck : uint8_t
186 {
187 notRequested = 0,
188 requested,
189 sha2Success,
190 sha2Failed = 0xe2,
191 };
192
193 protected:
Arun Lal K M153d4c12021-08-25 22:00:37 +0000194 std::unique_ptr<EVP_MD_CTX, std::function<void(EVP_MD_CTX*)>> ctx;
AppaRao Puli28972062019-11-11 02:04:45 +0530195 std::vector<uint8_t> expectedHash;
Arun Lal K M153d4c12021-08-25 22:00:37 +0000196 HashCheck check;
AppaRao Puli28972062019-11-11 02:04:45 +0530197
198 public:
Arun Lal K M153d4c12021-08-25 22:00:37 +0000199 TransferHashCheck(const std::vector<uint8_t>& expected) :
200 ctx(EVP_MD_CTX_new(), &EVP_MD_CTX_free), expectedHash(expected)
AppaRao Puli28972062019-11-11 02:04:45 +0530201 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000202 if (!ctx)
AppaRao Puli28972062019-11-11 02:04:45 +0530203 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000204 throw std::runtime_error("Unable to allocate for ctx.");
AppaRao Puli28972062019-11-11 02:04:45 +0530205 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000206
207 if (EVP_DigestInit(ctx.get(), EVP_sha256()) != openSslSuccess)
208 {
209 throw std::runtime_error("Unable to allocate for ctx.");
210 }
211
AppaRao Puli28972062019-11-11 02:04:45 +0530212 check = HashCheck::requested;
AppaRao Puli28972062019-11-11 02:04:45 +0530213 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000214
215 ~TransferHashCheck()
216 {}
217
218 bool hash(const std::vector<uint8_t>& data)
AppaRao Puli28972062019-11-11 02:04:45 +0530219 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000220 if (EVP_DigestUpdate(ctx.get(), data.data(), data.size()) !=
221 openSslSuccess)
AppaRao Puli28972062019-11-11 02:04:45 +0530222 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000223 return false;
AppaRao Puli28972062019-11-11 02:04:45 +0530224 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000225
226 return true;
AppaRao Puli28972062019-11-11 02:04:45 +0530227 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000228
229 bool clear()
AppaRao Puli28972062019-11-11 02:04:45 +0530230 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000231 /*
232 * EVP_DigestInit() always uses the default digest implementation and
233 * calls EVP_MD_CTX_reset().
234 */
235 if (EVP_DigestInit(ctx.get(), EVP_sha256()) != openSslSuccess)
AppaRao Puli28972062019-11-11 02:04:45 +0530236 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000237 return false;
AppaRao Puli28972062019-11-11 02:04:45 +0530238 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000239
240 return true;
AppaRao Puli28972062019-11-11 02:04:45 +0530241 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000242
AppaRao Puli28972062019-11-11 02:04:45 +0530243 enum HashCheck verify()
244 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000245 unsigned int len = 0;
246 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
247
248 check = HashCheck::sha2Failed;
249
250 if (EVP_DigestFinal(ctx.get(), digest.data(), &len) == openSslSuccess)
AppaRao Puli28972062019-11-11 02:04:45 +0530251 {
AppaRao Puli28972062019-11-11 02:04:45 +0530252 if (digest == expectedHash)
253 {
254 phosphor::logging::log<phosphor::logging::level::INFO>(
255 "Transfer sha2 verify passed.");
256 check = HashCheck::sha2Success;
257 }
258 else
259 {
260 phosphor::logging::log<phosphor::logging::level::ERR>(
261 "Transfer sha2 verify failed.");
262 check = HashCheck::sha2Failed;
263 }
264 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000265
AppaRao Puli28972062019-11-11 02:04:45 +0530266 return check;
267 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000268
AppaRao Puli28972062019-11-11 02:04:45 +0530269 uint8_t status() const
270 {
271 return static_cast<uint8_t>(check);
272 }
273};
274
275class MappedFile
276{
277 public:
James Feistfcd2d3a2020-05-28 10:38:15 -0700278 MappedFile(const std::string& fname) : addr(nullptr), fsize(0)
AppaRao Puli28972062019-11-11 02:04:45 +0530279 {
280 std::error_code ec;
281 size_t sz = std::filesystem::file_size(fname, ec);
P Dheeraj Srujan Kumarc08e9752021-07-14 19:38:32 +0530282 if (!ec)
283 {
284 return;
285 }
AppaRao Puli28972062019-11-11 02:04:45 +0530286 int fd = open(fname.c_str(), O_RDONLY);
P Dheeraj Srujan Kumarc08e9752021-07-14 19:38:32 +0530287 if (fd < 0)
AppaRao Puli28972062019-11-11 02:04:45 +0530288 {
289 return;
290 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700291 void* tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
AppaRao Puli28972062019-11-11 02:04:45 +0530292 close(fd);
293 if (tmp == MAP_FAILED)
294 {
295 return;
296 }
297 addr = tmp;
298 fsize = sz;
299 }
300
301 ~MappedFile()
302 {
303 if (addr)
304 {
305 munmap(addr, fsize);
306 }
307 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700308 const uint8_t* data() const
AppaRao Puli28972062019-11-11 02:04:45 +0530309 {
James Feistfcd2d3a2020-05-28 10:38:15 -0700310 return static_cast<const uint8_t*>(addr);
AppaRao Puli28972062019-11-11 02:04:45 +0530311 }
312 size_t size() const
313 {
314 return fsize;
315 }
316
317 private:
318 size_t fsize;
James Feistfcd2d3a2020-05-28 10:38:15 -0700319 void* addr;
AppaRao Puli28972062019-11-11 02:04:45 +0530320};
321
322class FwUpdateStatusCache
Vernon Mauery52ce6622019-05-22 09:19:46 -0700323{
324 public:
325 enum
326 {
AppaRao Puli28972062019-11-11 02:04:45 +0530327 fwStateInit = 0,
328 fwStateIdle,
329 fwStateDownload,
330 fwStateVerify,
331 fwStateProgram,
332 fwStateUpdateSuccess,
333 fwStateError = 0x0f,
334 fwStateAcCycleRequired = 0x83,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700335 };
AppaRao Puli28972062019-11-11 02:04:45 +0530336 uint8_t getState()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700337 {
AppaRao Puli28972062019-11-11 02:04:45 +0530338 if ((fwUpdateState == fwStateIdle || fwUpdateState == fwStateInit) &&
339 localDownloadInProgress())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700340 {
AppaRao Puli28972062019-11-11 02:04:45 +0530341 fwUpdateState = fwStateDownload;
342 progressPercent = 0;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700343 }
AppaRao Puli28972062019-11-11 02:04:45 +0530344 return fwUpdateState;
345 }
346 void resetStatusCache()
347 {
348 unlink(firmwareBufferFile);
349 }
350 void setState(const uint8_t state)
351 {
352 switch (state)
353 {
354 case fwStateInit:
355 case fwStateIdle:
356 case fwStateError:
357 resetStatusCache();
358 break;
359 case fwStateDownload:
360 case fwStateVerify:
361 case fwStateProgram:
362 case fwStateUpdateSuccess:
363 break;
364 default:
365 // Error
366 break;
367 }
368 fwUpdateState = state;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700369 }
370 uint8_t percent()
371 {
AppaRao Puli28972062019-11-11 02:04:45 +0530372 return progressPercent;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700373 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700374 void updateActivationPercent(const std::string& objPath)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700375 {
AppaRao Puli28972062019-11-11 02:04:45 +0530376 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
377 fwUpdateState = fwStateProgram;
378 progressPercent = 0;
379 match = std::make_shared<sdbusplus::bus::match::match>(
380 *busp,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700381 sdbusplus::bus::match::rules::propertiesChanged(
AppaRao Puli28972062019-11-11 02:04:45 +0530382 objPath, "xyz.openbmc_project.Software.ActivationProgress"),
James Feistfcd2d3a2020-05-28 10:38:15 -0700383 [&](sdbusplus::message::message& msg) {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700384 std::map<std::string, ipmi::DbusVariant> props;
AppaRao Puli28972062019-11-11 02:04:45 +0530385 std::vector<std::string> inVal;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700386 std::string iface;
AppaRao Puli28972062019-11-11 02:04:45 +0530387 try
388 {
389 msg.read(iface, props, inVal);
390 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700391 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +0530392 {
393 phosphor::logging::log<phosphor::logging::level::ERR>(
394 "Exception caught in get ActivationProgress");
395 return;
396 }
397
398 auto it = props.find("Progress");
399 if (it != props.end())
400 {
401 progressPercent = std::get<uint8_t>(it->second);
402 if (progressPercent == 100)
403 {
404 fwUpdateState = fwStateUpdateSuccess;
405 }
406 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700407 });
408 }
AppaRao Puli28972062019-11-11 02:04:45 +0530409 uint8_t activationTimerTimeout()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700410 {
AppaRao Puli28972062019-11-11 02:04:45 +0530411 phosphor::logging::log<phosphor::logging::level::INFO>(
412 "activationTimerTimeout: Increase percentage...",
413 phosphor::logging::entry("PERCENT:%d", progressPercent));
414 progressPercent = progressPercent + 5;
jayaprakash Mutyalab8f2bf92020-01-27 23:17:39 +0000415 if (progressPercent > 95)
anil kumar appana31f88872019-08-02 15:16:27 +0000416 {
417 /*changing the state to ready to update firmware utility */
AppaRao Puli28972062019-11-11 02:04:45 +0530418 fwUpdateState = fwStateUpdateSuccess;
anil kumar appana31f88872019-08-02 15:16:27 +0000419 }
AppaRao Puli28972062019-11-11 02:04:45 +0530420 return progressPercent;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700421 }
anil kumar appana31f88872019-08-02 15:16:27 +0000422 /* API for changing state to ERROR */
423 void firmwareUpdateAbortState()
424 {
AppaRao Puli28972062019-11-11 02:04:45 +0530425 unlink(firmwareBufferFile);
anil kumar appana31f88872019-08-02 15:16:27 +0000426 // changing the state to error
AppaRao Puli28972062019-11-11 02:04:45 +0530427 fwUpdateState = fwStateError;
anil kumar appana31f88872019-08-02 15:16:27 +0000428 }
429 void setDeferRestart(bool deferRestart)
430 {
AppaRao Puli28972062019-11-11 02:04:45 +0530431 deferRestartState = deferRestart;
anil kumar appana31f88872019-08-02 15:16:27 +0000432 }
433 void setInhibitDowngrade(bool inhibitDowngrade)
434 {
AppaRao Puli28972062019-11-11 02:04:45 +0530435 inhibitDowngradeState = inhibitDowngrade;
anil kumar appana31f88872019-08-02 15:16:27 +0000436 }
437 bool getDeferRestart()
438 {
AppaRao Puli28972062019-11-11 02:04:45 +0530439 return deferRestartState;
anil kumar appana31f88872019-08-02 15:16:27 +0000440 }
441 bool getInhibitDowngrade()
442 {
AppaRao Puli28972062019-11-11 02:04:45 +0530443 return inhibitDowngradeState;
anil kumar appana31f88872019-08-02 15:16:27 +0000444 }
445
Vernon Mauery52ce6622019-05-22 09:19:46 -0700446 protected:
AppaRao Puli28972062019-11-11 02:04:45 +0530447 std::shared_ptr<sdbusplus::asio::connection> busp;
448 std::shared_ptr<sdbusplus::bus::match::match> match;
449 uint8_t fwUpdateState = 0;
450 uint8_t progressPercent = 0;
451 bool deferRestartState = false;
452 bool inhibitDowngradeState = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700453};
454
AppaRao Puli28972062019-11-11 02:04:45 +0530455static FwUpdateStatusCache fwUpdateStatus;
Arun Lal K M153d4c12021-08-25 22:00:37 +0000456std::unique_ptr<TransferHashCheck> xferHashCheck;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700457
James Feistfcd2d3a2020-05-28 10:38:15 -0700458static void activateImage(const std::string& objPath)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700459{
AppaRao Puli28972062019-11-11 02:04:45 +0530460 // If flag is false means to reboot
461 if (fwUpdateStatus.getDeferRestart() == false)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700462 {
AppaRao Puli28972062019-11-11 02:04:45 +0530463 phosphor::logging::log<phosphor::logging::level::INFO>(
464 "activating Image: ",
465 phosphor::logging::entry("OBJPATH =%s", objPath.c_str()));
466 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
467 bus->async_method_call(
468 [](const boost::system::error_code ec) {
469 if (ec)
470 {
471 phosphor::logging::log<phosphor::logging::level::ERR>(
472 "async_method_call error: activateImage failed");
473 return;
474 }
475 },
476 "xyz.openbmc_project.Software.BMC.Updater", objPath,
477 "org.freedesktop.DBus.Properties", "Set",
478 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
479 std::variant<std::string>("xyz.openbmc_project.Software.Activation."
480 "RequestedActivations.Active"));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700481 }
AppaRao Puli28972062019-11-11 02:04:45 +0530482 else
483 {
484 phosphor::logging::log<phosphor::logging::level::INFO>(
485 "Firmware image activation is deferred.");
486 }
487 fwUpdateStatus.setState(
488 static_cast<uint8_t>(FwUpdateStatusCache::fwStateUpdateSuccess));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700489}
490
AppaRao Puli09a83142019-11-23 02:46:06 +0530491static bool getFirmwareUpdateMode()
492{
493 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
494 try
495 {
496 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
497 ipmi::Value state = ipmi::getDbusProperty(
498 *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState");
499 std::string bmcState = std::get<std::string>(state);
500 return (bmcState == bmcStateUpdateInProgress);
501 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700502 catch (const std::exception& e)
AppaRao Puli09a83142019-11-23 02:46:06 +0530503 {
504 phosphor::logging::log<phosphor::logging::level::ERR>(
505 "Exception caught while getting BMC state.",
506 phosphor::logging::entry("EXCEPTION=%s", e.what()));
507 throw;
508 }
509}
510
511static void setFirmwareUpdateMode(const bool mode)
512{
513
514 std::string bmcState(bmcStateReady);
515 if (mode)
516 {
517 bmcState = bmcStateUpdateInProgress;
518 }
519
520 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
521 try
522 {
523 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
524 ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf,
525 "CurrentBMCState", bmcState);
526 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700527 catch (const std::exception& e)
AppaRao Puli09a83142019-11-23 02:46:06 +0530528 {
529 phosphor::logging::log<phosphor::logging::level::ERR>(
530 "Exception caught while setting BMC state.",
531 phosphor::logging::entry("EXCEPTION=%s", e.what()));
532 throw;
533 }
534}
535
AppaRao Pulie2cddf62020-01-31 00:30:08 +0530536/** @brief check if channel IPMB
537 *
538 * This function checks if the command is from IPMB
539 *
540 * @param[in] ctx - context of current session.
541 * @returns true if the medium is IPMB else return true.
542 **/
James Feistfcd2d3a2020-05-28 10:38:15 -0700543ipmi::Cc checkIPMBChannel(const ipmi::Context::ptr& ctx, bool& isIPMBChannel)
AppaRao Pulie2cddf62020-01-31 00:30:08 +0530544{
545 ipmi::ChannelInfo chInfo;
546
547 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
548 {
549 phosphor::logging::log<phosphor::logging::level::ERR>(
550 "Failed to get Channel Info",
551 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
552 return ipmi::ccUnspecifiedError;
553 }
554
555 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
556 ipmi::EChannelMediumType::ipmb)
557 {
558 isIPMBChannel = true;
559 }
560 return ipmi::ccSuccess;
561}
562
AppaRao Puli28972062019-11-11 02:04:45 +0530563static void postTransferCompleteHandler(
James Feistfcd2d3a2020-05-28 10:38:15 -0700564 std::unique_ptr<sdbusplus::bus::match::match>& fwUpdateMatchSignal)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700565{
566 // Setup timer for watching signal
567 static phosphor::Timer timer(
AppaRao Puli28972062019-11-11 02:04:45 +0530568 [&fwUpdateMatchSignal]() { fwUpdateMatchSignal = nullptr; });
Vernon Mauery52ce6622019-05-22 09:19:46 -0700569
AppaRao Puli28972062019-11-11 02:04:45 +0530570 static phosphor::Timer activationStatusTimer([]() {
jayaprakash Mutyalab8f2bf92020-01-27 23:17:39 +0000571 if (fwUpdateStatus.activationTimerTimeout() > 95)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700572 {
AppaRao Puli28972062019-11-11 02:04:45 +0530573 activationStatusTimer.stop();
574 fwUpdateStatus.setState(
575 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700576 }
577 });
578
579 timer.start(std::chrono::microseconds(5000000), false);
580
581 // callback function for capturing signal
James Feistfcd2d3a2020-05-28 10:38:15 -0700582 auto callback = [&](sdbusplus::message::message& m) {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700583 bool flag = false;
584
585 std::vector<std::pair<
586 std::string,
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700587 std::vector<std::pair<std::string, std::variant<std::string>>>>>
AppaRao Puli28972062019-11-11 02:04:45 +0530588 intfPropsPair;
589 sdbusplus::message::object_path objPath;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700590
591 try
592 {
AppaRao Puli28972062019-11-11 02:04:45 +0530593 m.read(objPath, intfPropsPair); // Read in the object path
594 // that was just created
Vernon Mauery52ce6622019-05-22 09:19:46 -0700595 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700596 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700597 {
AppaRao Puli28972062019-11-11 02:04:45 +0530598 phosphor::logging::log<phosphor::logging::level::ERR>(
599 "Exception caught in reading created object path.");
600 return;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700601 }
602 // constructing response message
AppaRao Puli28972062019-11-11 02:04:45 +0530603 phosphor::logging::log<phosphor::logging::level::INFO>(
604 "New Interface Added.",
605 phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
James Feistfcd2d3a2020-05-28 10:38:15 -0700606 for (auto& interface : intfPropsPair)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700607 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700608 if (interface.first == "xyz.openbmc_project.Software.Activation")
609 {
AppaRao Puli28972062019-11-11 02:04:45 +0530610 // There are chances of getting two signals for
611 // InterfacesAdded. So cross check and discrad second instance.
612 if (fwUpdateMatchSignal == nullptr)
613 {
614 return;
615 }
616 // Found our interface, disable callbacks
617 fwUpdateMatchSignal = nullptr;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700618
AppaRao Puli28972062019-11-11 02:04:45 +0530619 phosphor::logging::log<phosphor::logging::level::INFO>(
620 "Start activationStatusTimer for status.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700621 try
622 {
623 timer.stop();
AppaRao Puli28972062019-11-11 02:04:45 +0530624 activationStatusTimer.start(
Vernon Mauery52ce6622019-05-22 09:19:46 -0700625 std::chrono::microseconds(3000000), true);
626 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700627 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700628 {
AppaRao Puli28972062019-11-11 02:04:45 +0530629 phosphor::logging::log<phosphor::logging::level::ERR>(
630 "Exception caught in start activationStatusTimer.",
631 phosphor::logging::entry("ERROR=%s", e.what()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700632 }
633
AppaRao Puli28972062019-11-11 02:04:45 +0530634 fwUpdateStatus.updateActivationPercent(objPath.str);
635 activateImage(objPath.str);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700636 }
637 }
638 };
639
640 // Adding matcher
AppaRao Puli28972062019-11-11 02:04:45 +0530641 fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700642 *getSdBus(),
Vernon Mauery52ce6622019-05-22 09:19:46 -0700643 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
644 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
645 callback);
646}
James Feistfcd2d3a2020-05-28 10:38:15 -0700647static bool startFirmwareUpdate(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700648{
AppaRao Puli28972062019-11-11 02:04:45 +0530649 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
650 // the code gets to this point, the file should be transferred start the
651 // request (creating a new file in /tmp/images causes the update manager to
652 // check if it is ready for activation)
653 static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatchSignal;
654 postTransferCompleteHandler(fwUpdateMatchSignal);
655 std::filesystem::rename(
656 uri, "/tmp/images/" +
657 boost::uuids::to_string(boost::uuids::random_generator()()));
658 return true;
659}
Vernon Mauery52ce6622019-05-22 09:19:46 -0700660
Arun Lal K M153d4c12021-08-25 22:00:37 +0000661static bool transferImageFromFile(const std::string& uri, bool move = true)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700662{
663 std::error_code ec;
AppaRao Puli28972062019-11-11 02:04:45 +0530664 phosphor::logging::log<phosphor::logging::level::INFO>(
665 "Transfer Image From File.",
666 phosphor::logging::entry("URI=%s", uri.c_str()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700667 if (move)
668 {
AppaRao Puli28972062019-11-11 02:04:45 +0530669 std::filesystem::rename(uri, firmwareBufferFile, ec);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700670 }
671 else
672 {
AppaRao Puli28972062019-11-11 02:04:45 +0530673 std::filesystem::copy(uri, firmwareBufferFile,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700674 std::filesystem::copy_options::overwrite_existing,
675 ec);
676 }
AppaRao Puli28972062019-11-11 02:04:45 +0530677 if (xferHashCheck)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700678 {
679 MappedFile mappedfw(uri);
Arun Lal K M153d4c12021-08-25 22:00:37 +0000680 if (!xferHashCheck->hash(
681 {mappedfw.data(), mappedfw.data() + mappedfw.size()}))
682 {
683 phosphor::logging::log<phosphor::logging::level::ERR>(
684 "transferImageFromFile: xferHashCheck->hash failed.");
685 return false;
686 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700687 }
688 if (ec.value())
689 {
AppaRao Puli28972062019-11-11 02:04:45 +0530690 phosphor::logging::log<phosphor::logging::level::ERR>(
691 "Image copy failed.");
Arun Lal K M153d4c12021-08-25 22:00:37 +0000692 return false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700693 }
Arun Lal K M153d4c12021-08-25 22:00:37 +0000694
695 return true;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700696}
697
698template <typename... ArgTypes>
James Feistfcd2d3a2020-05-28 10:38:15 -0700699static int executeCmd(const char* path, ArgTypes&&... tArgs)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700700{
James Feistfcd2d3a2020-05-28 10:38:15 -0700701 boost::process::child execProg(path, const_cast<char*>(tArgs)...);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700702 execProg.wait();
703 return execProg.exit_code();
704}
705
Arun Lal K M153d4c12021-08-25 22:00:37 +0000706static bool transferImageFromUsb(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700707{
Arun Lal K M153d4c12021-08-25 22:00:37 +0000708 bool ret = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700709 char fwpath[fwPathMaxLength];
Arun Lal K M153d4c12021-08-25 22:00:37 +0000710
AppaRao Puli28972062019-11-11 02:04:45 +0530711 phosphor::logging::log<phosphor::logging::level::INFO>(
712 "Transfer Image From USB.",
713 phosphor::logging::entry("URI=%s", uri.c_str()));
Arun Lal K M153d4c12021-08-25 22:00:37 +0000714
715 if (executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
716 fwUpdateMountPoint) == 0)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700717 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000718 std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
719 ret = transferImageFromFile(usb_path, false);
720
721 executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage,
722 fwUpdateMountPoint);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700723 }
724
Vernon Mauery52ce6622019-05-22 09:19:46 -0700725 return ret;
726}
727
James Feistfcd2d3a2020-05-28 10:38:15 -0700728static bool transferFirmwareFromUri(const std::string& uri)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700729{
AppaRao Puli28972062019-11-11 02:04:45 +0530730 static constexpr char fwUriFile[] = "file://";
731 static constexpr char fwUriUsb[] = "usb://";
732 phosphor::logging::log<phosphor::logging::level::INFO>(
733 "Transfer Image From URI.",
734 phosphor::logging::entry("URI=%s", uri.c_str()));
735 if (boost::algorithm::starts_with(uri, fwUriFile))
Vernon Mauery52ce6622019-05-22 09:19:46 -0700736 {
AppaRao Puli28972062019-11-11 02:04:45 +0530737 std::string fname = uri.substr(sizeof(fwUriFile) - 1);
738 if (fname != firmwareBufferFile)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700739 {
Arun Lal K M153d4c12021-08-25 22:00:37 +0000740 return transferImageFromFile(fname);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700741 }
742 return true;
743 }
AppaRao Puli28972062019-11-11 02:04:45 +0530744 if (boost::algorithm::starts_with(uri, fwUriUsb))
Vernon Mauery52ce6622019-05-22 09:19:46 -0700745 {
AppaRao Puli28972062019-11-11 02:04:45 +0530746 std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
Arun Lal K M153d4c12021-08-25 22:00:37 +0000747 return transferImageFromUsb(fname);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700748 }
749 return false;
750}
751
752/* Get USB-mass-storage device status: inserted => true, ejected => false */
AppaRao Puli28972062019-11-11 02:04:45 +0530753static bool getUsbStatus()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700754{
AppaRao Puli28972062019-11-11 02:04:45 +0530755 std::filesystem::path usbDevPath =
756 std::filesystem::path("/sys/kernel/config/usb_gadget") /
757 fwUpdateUSBDevName;
758 return (std::filesystem::exists(usbDevPath) ? true : false);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700759}
760
761/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
AppaRao Puli28972062019-11-11 02:04:45 +0530762static int attachUsbDevice()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700763{
AppaRao Puli28972062019-11-11 02:04:45 +0530764 if (getUsbStatus())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700765 {
766 return 1;
767 }
AppaRao Puli28972062019-11-11 02:04:45 +0530768 int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
769 std::to_string(maxFirmwareImageSize / 1_MB).c_str());
Vernon Mauery52ce6622019-05-22 09:19:46 -0700770 if (!ret)
771 {
AppaRao Puli28972062019-11-11 02:04:45 +0530772 ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
773 fwUpdateUsbVolImage);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700774 }
775 return ret;
776}
777
778/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
AppaRao Puli28972062019-11-11 02:04:45 +0530779static int detachUsbDevice()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700780{
AppaRao Puli28972062019-11-11 02:04:45 +0530781 if (!getUsbStatus())
Vernon Mauery52ce6622019-05-22 09:19:46 -0700782 {
783 return 1;
784 }
AppaRao Puli28972062019-11-11 02:04:45 +0530785 return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700786}
AppaRao Puli28972062019-11-11 02:04:45 +0530787static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700788{
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800789 constexpr uint8_t undefinedImage = 0x00;
AppaRao Puli28972062019-11-11 02:04:45 +0530790 constexpr uint8_t primaryImage = 0x01;
791 constexpr uint8_t secondaryImage = 0x02;
James Feistfcd2d3a2020-05-28 10:38:15 -0700792 constexpr const char* secondaryFitImageStartAddr = "22480000";
AppaRao Puli28972062019-11-11 02:04:45 +0530793
794 uint8_t bootImage = primaryImage;
795 boost::system::error_code ec;
796 std::string value = ctx->bus->yield_method_call<std::string>(
797 ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
798 "/xyz/openbmc_project/u_boot/environment/mgr",
799 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
800 if (ec)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700801 {
AppaRao Puli28972062019-11-11 02:04:45 +0530802 phosphor::logging::log<phosphor::logging::level::ERR>(
803 "Failed to read the bootcmd value");
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800804 /* don't fail, just give back undefined until it is ready */
805 bootImage = undefinedImage;
AppaRao Puli28972062019-11-11 02:04:45 +0530806 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700807
AppaRao Puli28972062019-11-11 02:04:45 +0530808 /* cheking for secondary FitImage Address 22480000 */
Terry S. Duncane319ecf2020-02-10 15:59:55 -0800809 else if (value.find(secondaryFitImageStartAddr) != std::string::npos)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700810 {
AppaRao Puli28972062019-11-11 02:04:45 +0530811 bootImage = secondaryImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700812 }
813 else
814 {
AppaRao Puli28972062019-11-11 02:04:45 +0530815 bootImage = primaryImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700816 }
817
AppaRao Puli28972062019-11-11 02:04:45 +0530818 return bootImage;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700819}
820
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530821#ifdef INTEL_PFR_ENABLED
822using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
823 uint8_t, // Major Version Number
824 uint8_t, // Minor Version Number
825 uint32_t, // Build Number
826 uint32_t, // Build Timestamp
827 uint32_t>; // Update Timestamp
828ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700829{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700830 // Byte 1 - Count (N) Number of devices data is being returned for.
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530831 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700832 // Bytes - 17:(15xN) - Repeat of 2 through 16
833
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530834 std::vector<fwVersionInfoType> fwVerInfoList;
835 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
James Feistfcd2d3a2020-05-28 10:38:15 -0700836 for (const auto& fwDev : fwVersionIdMap)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700837 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530838 std::string verStr;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700839 try
840 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530841 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700842
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530843 ipmi::Value result = ipmi::getDbusProperty(
844 *busp, service, fwDev.second, versionIntf, "Version");
845 verStr = std::get<std::string>(result);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700846 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700847 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700848 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530849 phosphor::logging::log<phosphor::logging::level::INFO>(
850 "Failed to fetch Version property",
851 phosphor::logging::entry("ERROR=%s", e.what()),
852 phosphor::logging::entry("PATH=%s", fwDev.second),
853 phosphor::logging::entry("INTERFACE=%s", versionIntf));
854 continue;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700855 }
856
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530857 if (verStr.empty())
858 {
859 phosphor::logging::log<phosphor::logging::level::INFO>(
860 "Version is empty.",
861 phosphor::logging::entry("PATH=%s", fwDev.second),
862 phosphor::logging::entry("INTERFACE=%s", versionIntf));
863 continue;
864 }
865
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530866 uint8_t majorNum = 0;
867 uint8_t minorNum = 0;
868 uint32_t buildNum = 0;
869 try
870 {
srikanta mondal52a292b2020-07-27 23:49:14 +0000871 std::optional<ipmi::MetaRevision> rev =
872 ipmi::convertIntelVersion(verStr);
873 if (rev.has_value())
874 {
875 ipmi::MetaRevision revision = rev.value();
876 majorNum = revision.major % 10 + (revision.major / 10) * 16;
877 minorNum = (revision.minor > 99 ? 99 : revision.minor);
878 minorNum = minorNum % 10 + (minorNum / 10) * 16;
879 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
880 hash = bswap_32(hash);
881 buildNum = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
882 }
883 else
884 {
885 std::vector<std::string> splitVer;
886 boost::split(splitVer, verStr, boost::is_any_of(".-"));
887 if (splitVer.size() < 3)
888 {
889 phosphor::logging::log<phosphor::logging::level::INFO>(
890 "Invalid Version format.",
891 phosphor::logging::entry("Version=%s", verStr.c_str()),
892 phosphor::logging::entry("PATH=%s", fwDev.second));
893 continue;
894 }
895 majorNum = std::stoul(splitVer[0], nullptr, 16);
896 minorNum = std::stoul(splitVer[1], nullptr, 16);
897 buildNum = std::stoul(splitVer[2], nullptr, 16);
898 }
899 // Build Timestamp - Not supported.
900 // Update Timestamp - TODO: Need to check with CPLD team.
901 fwVerInfoList.emplace_back(
902 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
903 minorNum, buildNum, 0, 0));
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530904 }
James Feistfcd2d3a2020-05-28 10:38:15 -0700905 catch (const std::exception& e)
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530906 {
907 phosphor::logging::log<phosphor::logging::level::INFO>(
908 "Failed to convert stoul.",
909 phosphor::logging::entry("ERROR=%s", e.what()));
910 continue;
911 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700912 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700913
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530914 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700915}
Punith Nadur Basavarajaiaha8dd1972020-08-19 18:05:44 +0000916
917std::array<uint8_t, imageCount> getSecurityVersionInfo(const char* mtdDevBuf,
918 size_t svnVerOffsetInPfm,
919 size_t bkcVerOffsetInPfm)
920{
921 constexpr size_t bufLength = 1;
922 std::array<uint8_t, imageCount> fwSecurityVersionBuf = {0}, temp;
923 constexpr uint8_t svnIndexValue = 0x00;
924 constexpr uint8_t bkcIndexValue = 0x01;
925 constexpr uint8_t tempIndexValue = 0x00;
926 try
927 {
928 SPIDev spiDev(mtdDevBuf);
929 spiDev.spiReadData(svnVerOffsetInPfm, bufLength, temp.data());
930 fwSecurityVersionBuf.at(svnIndexValue) = temp.at(tempIndexValue);
931 spiDev.spiReadData(bkcVerOffsetInPfm, bufLength, temp.data());
932 fwSecurityVersionBuf.at(bkcIndexValue) = temp.at(tempIndexValue);
933 }
934 catch (const std::exception& e)
935 {
936 phosphor::logging::log<phosphor::logging::level::ERR>(
937 "Exception caught in getSecurityVersionInfo",
938 phosphor::logging::entry("MSG=%s", e.what()));
939 fwSecurityVersionBuf = {0, 0};
940 }
941
942 return fwSecurityVersionBuf;
943}
944
945ipmi::RspType<
946 uint8_t, // device ID
947 uint8_t, // Active Image Value
948 std::array<uint8_t, imageCount>, // Security version for Active Image
949 uint8_t, // recovery Image Value
950 std::array<uint8_t, imageCount>> // Security version for Recovery Image
AppaRao Puli28972062019-11-11 02:04:45 +0530951 ipmiGetFwSecurityVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700952{
Punith Nadur Basavarajaiaha8dd1972020-08-19 18:05:44 +0000953 static bool cacheFlag = false;
954 constexpr std::array<const char*, imageCount> mtdDevBuf = {
955 bmcActivePfmMTDDev, bmcRecoveryImgMTDDev};
956
957 // To avoid multiple reading from SPI device
958 if (!cacheFlag)
959 {
960 imgFwSecurityVersion[0] = getSecurityVersionInfo(
961 mtdDevBuf[0], svnActiveVerOffsetInPfm, bkcActiveVerOffsetInPfm);
962 imgFwSecurityVersion[1] = getSecurityVersionInfo(
963 mtdDevBuf[1], svnRecoveryVerOffsetInPfm, bkcRecoveryVerOffsetInPfm);
964 cacheFlag = true;
965 }
966
967 constexpr uint8_t ActivePfmMTDDev = 0x00;
968 constexpr uint8_t RecoveryImgMTDDev = 0x01;
969
970 return ipmi::responseSuccess(
971 imageCount, static_cast<uint8_t>(FWDeviceIDTag::bmcActiveImage),
972 imgFwSecurityVersion[ActivePfmMTDDev],
973 static_cast<uint8_t>(FWDeviceIDTag::bmcRecoveryImage),
974 imgFwSecurityVersion[RecoveryImgMTDDev]);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700975}
976
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530977ipmi::RspType<std::array<uint8_t, certKeyLen>,
978 std::optional<std::array<uint8_t, cskSignatureLen>>>
James Feistfcd2d3a2020-05-28 10:38:15 -0700979 ipmiGetFwRootCertData(const ipmi::Context::ptr& ctx, uint8_t certId)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700980{
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +0530981 bool isIPMBChannel = false;
982
983 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
984 {
985 return ipmi::responseUnspecifiedError();
986 }
987 if (isIPMBChannel)
988 {
989 phosphor::logging::log<phosphor::logging::level::INFO>(
990 "Command not supported. Failed to get root certificate data.");
991 return ipmi::responseCommandNotAvailable();
992 }
993
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530994 size_t certKeyOffset = 0;
995 size_t cskSigOffset = 0;
996 std::string mtdDev;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700997
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +0530998 switch (static_cast<FwGetRootCertDataTag>(certId))
999 {
1000 case FwGetRootCertDataTag::activeRootKey:
1001 {
1002 mtdDev = bmcActivePfmMTDDev;
1003 certKeyOffset = rootkeyOffsetInPfm;
1004 break;
1005 }
1006 case FwGetRootCertDataTag::recoveryRootKey:
1007 {
1008 mtdDev = bmcRecoveryImgMTDDev;
1009 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1010 break;
1011 }
1012 case FwGetRootCertDataTag::activeCSK:
1013 {
1014 mtdDev = bmcActivePfmMTDDev;
1015 certKeyOffset = cskKeyOffsetInPfm;
1016 cskSigOffset = cskSignatureOffsetInPfm;
1017 break;
1018 }
1019 case FwGetRootCertDataTag::recoveryCSK:
1020 {
1021 mtdDev = bmcRecoveryImgMTDDev;
1022 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1023 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1024 break;
1025 }
1026 default:
1027 {
1028 return ipmi::responseInvalidFieldRequest();
1029 }
1030 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001031
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301032 std::array<uint8_t, certKeyLen> certKey = {0};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001033
Vernon Mauery52ce6622019-05-22 09:19:46 -07001034 try
1035 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301036 SPIDev spiDev(mtdDev);
1037 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1038
1039 if (cskSigOffset)
1040 {
1041 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1042 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1043 cskSignature.data());
1044 return ipmi::responseSuccess(certKey, cskSignature);
1045 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001046 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001047 catch (const std::exception& e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001048 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301049 phosphor::logging::log<phosphor::logging::level::ERR>(
1050 "Exception caught in ipmiGetFwRootCertData",
1051 phosphor::logging::entry("MSG=%s", e.what()));
1052 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001053 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001054
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301055 return ipmi::responseSuccess(certKey, std::nullopt);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001056}
AppaRao Puli28972062019-11-11 02:04:45 +05301057#endif // INTEL_PFR_ENABLED
1058
1059static constexpr uint8_t channelListSize = 3;
1060/** @brief implements Maximum Firmware Transfer size command
1061 * @parameter
1062 * - none
1063 * @returns IPMI completion code plus response data
1064 * - count - channel count
1065 * - channelList - channel list information
1066 */
1067ipmi::RspType<uint8_t, // channel count
1068 std::array<std::tuple<uint8_t, uint32_t>,
1069 channelListSize> // Channel List
1070 >
1071 ipmiFirmwareMaxTransferSize()
1072{
1073 constexpr size_t kcsMaxBufSize = 128;
1074 constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
AppaRao Puli28972062019-11-11 02:04:45 +05301075 // Byte 1 - Count (N) Number of devices data is being returned for.
1076 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
1077 // Byte 3-6 - transfer size (little endian)
1078 // Bytes - 7:(5xN) - Repeat of 2 through 6
1079 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1080 channelList = {
1081 {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
AppaRao Puli28972062019-11-11 02:04:45 +05301082 {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
1083 rmcpPlusMaxBufSize}}};
1084
1085 return ipmi::responseSuccess(channelListSize, channelList);
1086}
1087
1088ipmi::RspType<uint8_t, uint8_t>
1089 ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
1090{
1091 // Byte 1 - Current execution context
1092 // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
1093 // Byte 2 - Partition pointer
1094 // 0x01 - primary, 0x02 - secondary
1095 uint8_t partitionPtr = getActiveBootImage(ctx);
1096
1097 return ipmi::responseSuccess(
1098 static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
1099}
AppaRao Puli28972062019-11-11 02:04:45 +05301100/** @brief Get Firmware Update Random Number
1101 *
1102 * This function generate the random number used for
1103 * setting the firmware update mode as authentication key.
1104 *
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301105 * @param[in] ctx - context of current session
AppaRao Puli28972062019-11-11 02:04:45 +05301106 * @returns IPMI completion code along with
1107 * - random number
1108 **/
1109ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
James Feistfcd2d3a2020-05-28 10:38:15 -07001110 ipmiGetFwUpdateRandomNumber(const ipmi::Context::ptr& ctx)
AppaRao Puli28972062019-11-11 02:04:45 +05301111{
1112 phosphor::logging::log<phosphor::logging::level::INFO>(
1113 "Generate FW update random number");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301114 bool isIPMBChannel = false;
1115
1116 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1117 {
1118 return ipmi::responseUnspecifiedError();
1119 }
1120 if (isIPMBChannel)
1121 {
1122 phosphor::logging::log<phosphor::logging::level::INFO>(
1123 "Channel not supported. Failed to fetch FW update random number");
1124 return ipmi::responseCommandNotAvailable();
1125 }
AppaRao Puli28972062019-11-11 02:04:45 +05301126 std::random_device rd;
1127 std::default_random_engine gen(rd());
1128 std::uniform_int_distribution<> dist{0, 255};
1129
1130 fwRandomNumGenTs = std::chrono::steady_clock::now();
1131
1132 for (int i = 0; i < fwRandomNumLength; i++)
1133 {
1134 fwRandomNum[i] = dist(gen);
1135 }
1136
1137 return ipmi::responseSuccess(fwRandomNum);
1138}
1139
1140/** @brief Set Firmware Update Mode
1141 *
1142 * This function sets BMC into firmware update mode
1143 * after validating Random number obtained from the Get
1144 * Firmware Update Random Number command
1145 *
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301146 * @param[in] ctx - context of current session
1147 * @parameter randNum - Random number(token)
1148 * @returns IPMI completion code
AppaRao Puli28972062019-11-11 02:04:45 +05301149 **/
1150ipmi::RspType<>
James Feistfcd2d3a2020-05-28 10:38:15 -07001151 ipmiSetFirmwareUpdateMode(const ipmi::Context::ptr& ctx,
1152 std::array<uint8_t, fwRandomNumLength>& randNum)
AppaRao Puli28972062019-11-11 02:04:45 +05301153{
1154 phosphor::logging::log<phosphor::logging::level::INFO>(
1155 "Start FW update mode");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301156
1157 bool isIPMBChannel = false;
1158
1159 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1160 {
1161 return ipmi::responseUnspecifiedError();
1162 }
1163 if (isIPMBChannel)
1164 {
1165 phosphor::logging::log<phosphor::logging::level::INFO>(
1166 "Channel not supported. Failed to set FW update mode");
1167 return ipmi::responseCommandNotAvailable();
1168 }
AppaRao Puli28972062019-11-11 02:04:45 +05301169 /* Firmware Update Random number is valid for 30 seconds only */
1170 auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
1171 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
1172 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
1173 fwRandomNumExpirySeconds)
1174 .count())
1175 {
1176 phosphor::logging::log<phosphor::logging::level::INFO>(
1177 "Firmware update random number expired.");
1178 return ipmi::responseInvalidFieldRequest();
1179 }
1180
1181 /* Validate random number */
1182 for (int i = 0; i < fwRandomNumLength; i++)
1183 {
1184 if (fwRandomNum[i] != randNum[i])
1185 {
1186 phosphor::logging::log<phosphor::logging::level::INFO>(
1187 "Invalid random number specified.");
1188 return ipmi::responseInvalidFieldRequest();
1189 }
1190 }
1191
1192 try
1193 {
1194 if (getFirmwareUpdateMode())
1195 {
1196 phosphor::logging::log<phosphor::logging::level::INFO>(
1197 "Already firmware update is in progress.");
1198 return ipmi::responseBusy();
1199 }
1200 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001201 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301202 {
1203 return ipmi::responseUnspecifiedError();
1204 }
1205
1206 // FIXME? c++ doesn't off an option for exclusive file creation
James Feistfcd2d3a2020-05-28 10:38:15 -07001207 FILE* fp = fopen(firmwareBufferFile, "wx");
AppaRao Puli28972062019-11-11 02:04:45 +05301208 if (!fp)
1209 {
1210 phosphor::logging::log<phosphor::logging::level::INFO>(
1211 "Unable to open file.");
1212 return ipmi::responseUnspecifiedError();
1213 }
1214 fclose(fp);
1215
1216 try
1217 {
1218 setFirmwareUpdateMode(true);
1219 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001220 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301221 {
1222 unlink(firmwareBufferFile);
1223 return ipmi::responseUnspecifiedError();
1224 }
1225
1226 return ipmi::responseSuccess();
1227}
1228
1229/** @brief implements exit firmware update mode command
1230 * @param None
1231 *
1232 * @returns IPMI completion code
1233 */
James Feistfcd2d3a2020-05-28 10:38:15 -07001234ipmi::RspType<> ipmiExitFirmwareUpdateMode(const ipmi::Context::ptr& ctx)
AppaRao Puli28972062019-11-11 02:04:45 +05301235{
1236 phosphor::logging::log<phosphor::logging::level::INFO>(
1237 "Exit FW update mode");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301238 bool isIPMBChannel = false;
1239
1240 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1241 {
1242 return ipmi::responseUnspecifiedError();
1243 }
1244 if (isIPMBChannel)
1245 {
1246 phosphor::logging::log<phosphor::logging::level::INFO>(
1247 "Command not supported. Failed to exit firmware update mode");
1248 return ipmi::responseCommandNotAvailable();
1249 }
1250
AppaRao Puli28972062019-11-11 02:04:45 +05301251 switch (fwUpdateStatus.getState())
1252 {
1253 case FwUpdateStatusCache::fwStateInit:
1254 case FwUpdateStatusCache::fwStateIdle:
1255 return ipmi::responseInvalidFieldRequest();
1256 break;
1257 case FwUpdateStatusCache::fwStateDownload:
1258 case FwUpdateStatusCache::fwStateVerify:
1259 break;
1260 case FwUpdateStatusCache::fwStateProgram:
1261 break;
1262 case FwUpdateStatusCache::fwStateUpdateSuccess:
1263 case FwUpdateStatusCache::fwStateError:
1264 break;
1265 case FwUpdateStatusCache::fwStateAcCycleRequired:
1266 return ipmi::responseInvalidFieldRequest();
1267 break;
1268 }
1269 fwUpdateStatus.firmwareUpdateAbortState();
1270
1271 try
1272 {
1273 setFirmwareUpdateMode(false);
1274 }
James Feistfcd2d3a2020-05-28 10:38:15 -07001275 catch (const std::exception& e)
AppaRao Puli28972062019-11-11 02:04:45 +05301276 {
1277 return ipmi::responseUnspecifiedError();
1278 }
1279
1280 return ipmi::responseSuccess();
1281}
1282
1283/** @brief implements Get/Set Firmware Update Control
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301284 * @param[in] ctx - context of current session
AppaRao Puli28972062019-11-11 02:04:45 +05301285 * @parameter
1286 * - Byte 1: Control Byte
1287 * - Byte 2: Firmware filename length (Optional)
1288 * - Byte 3:N: Firmware filename data (Optional)
1289 * @returns IPMI completion code plus response data
1290 * - Byte 2: Current control status
1291 **/
1292ipmi::RspType<bool, bool, bool, bool, uint4_t>
James Feistfcd2d3a2020-05-28 10:38:15 -07001293 ipmiGetSetFirmwareUpdateControl(const ipmi::Context::ptr& ctx,
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301294 const uint8_t controlReq,
James Feistfcd2d3a2020-05-28 10:38:15 -07001295 const std::optional<std::string>& fileName)
AppaRao Puli28972062019-11-11 02:04:45 +05301296{
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301297 bool isIPMBChannel = false;
1298
1299 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1300 {
1301 return ipmi::responseUnspecifiedError();
1302 }
1303 if (isIPMBChannel)
1304 {
1305 phosphor::logging::log<phosphor::logging::level::INFO>(
1306 "Channel not supported. Failed to get or set FW update control");
1307 return ipmi::responseCommandNotAvailable();
1308 }
1309
AppaRao Puli28972062019-11-11 02:04:45 +05301310 static std::string fwXferUriPath;
1311 static bool imageTransferStarted = false;
1312 static bool imageTransferCompleted = false;
1313 static bool imageTransferAborted = false;
1314
1315 if ((controlReq !=
1316 static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
1317 (fileName))
1318 {
1319 phosphor::logging::log<phosphor::logging::level::ERR>(
1320 "Invalid request field (Filename).");
1321 return ipmi::responseInvalidFieldRequest();
1322 }
1323
1324 static bool usbAttached = getUsbStatus();
1325
1326 switch (static_cast<FwUpdateCtrlReq>(controlReq))
1327 {
1328 case FwUpdateCtrlReq::getCurrentControlStatus:
1329 phosphor::logging::log<phosphor::logging::level::INFO>(
1330 "ipmiGetSetFirmwareUpdateControl: Get status");
1331 break;
1332 case FwUpdateCtrlReq::imageTransferStart:
1333 {
1334 phosphor::logging::log<phosphor::logging::level::INFO>(
1335 "ipmiGetSetFirmwareUpdateControl: Set transfer start");
1336 imageTransferStarted = true;
1337 // reset buffer to empty (truncate file)
1338 std::ofstream out(firmwareBufferFile,
1339 std::ofstream::binary | std::ofstream::trunc);
1340 fwXferUriPath = std::string("file://") + firmwareBufferFile;
1341 if (xferHashCheck)
1342 {
Arun Lal K M153d4c12021-08-25 22:00:37 +00001343 if (!xferHashCheck->clear())
1344 {
1345 phosphor::logging::log<phosphor::logging::level::ERR>(
1346 "clear() for xferHashCheck failed");
1347 return ipmi::responseUnspecifiedError();
1348 }
AppaRao Puli28972062019-11-11 02:04:45 +05301349 }
1350 // Setting state to download
1351 fwUpdateStatus.setState(
1352 static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
1353#ifdef INTEL_PFR_ENABLED
1354 imgLength = 0;
1355 imgType = 0;
1356 block0Mapped = false;
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301357#endif
AppaRao Puli28972062019-11-11 02:04:45 +05301358 }
1359 break;
1360 case FwUpdateCtrlReq::imageTransferComplete:
1361 {
1362 phosphor::logging::log<phosphor::logging::level::INFO>(
1363 "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
1364 if (usbAttached)
1365 {
1366 phosphor::logging::log<phosphor::logging::level::ERR>(
1367 "USB should be detached to perform this operation.");
1368 return ipmi::responseNotSupportedInPresentState();
1369 }
1370 // finish transfer based on URI
1371 if (!transferFirmwareFromUri(fwXferUriPath))
1372 {
1373 phosphor::logging::log<phosphor::logging::level::ERR>(
1374 "transferFirmwareFromUri failed.");
1375 return ipmi::responseUnspecifiedError();
1376 }
1377 // transfer complete
1378 if (xferHashCheck)
1379 {
1380 if (TransferHashCheck::HashCheck::sha2Success !=
1381 xferHashCheck->verify())
1382 {
1383 phosphor::logging::log<phosphor::logging::level::ERR>(
1384 "xferHashCheck failed.");
1385 return ipmi::responseUnspecifiedError();
1386 }
1387 }
1388 // Set state to verify and start the update
1389 fwUpdateStatus.setState(
1390 static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
1391 // start the request
1392 if (!startFirmwareUpdate(firmwareBufferFile))
1393 {
1394 phosphor::logging::log<phosphor::logging::level::ERR>(
1395 "startFirmwareUpdate failed.");
1396 return ipmi::responseUnspecifiedError();
1397 }
1398 imageTransferCompleted = true;
1399 }
1400 break;
1401 case FwUpdateCtrlReq::imageTransferAbort:
1402 phosphor::logging::log<phosphor::logging::level::INFO>(
1403 "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
1404 if (usbAttached)
1405 {
1406 if (detachUsbDevice())
1407 {
1408 phosphor::logging::log<phosphor::logging::level::ERR>(
1409 "Detach USB device failed.");
1410 return ipmi::responseUsbAttachOrDetachFailed();
1411 }
1412 usbAttached = false;
1413 }
1414 // During abort request reset the state to Init by cleaning update
1415 // file.
1416 fwUpdateStatus.firmwareUpdateAbortState();
1417 imageTransferAborted = true;
1418 break;
1419 case FwUpdateCtrlReq::setFirmwareFilename:
1420 phosphor::logging::log<phosphor::logging::level::INFO>(
1421 "ipmiGetSetFirmwareUpdateControl: Set filename.");
1422 if (!fileName || ((*fileName).length() == 0))
1423 {
1424 phosphor::logging::log<phosphor::logging::level::ERR>(
1425 "Invalid Filename specified.");
1426 return ipmi::responseInvalidFieldRequest();
1427 }
1428
1429 fwXferUriPath = *fileName;
1430 break;
1431 case FwUpdateCtrlReq::attachUsbDevice:
1432 phosphor::logging::log<phosphor::logging::level::INFO>(
1433 "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
1434 if (usbAttached)
1435 {
1436 phosphor::logging::log<phosphor::logging::level::ERR>(
1437 "USB device is already attached.");
1438 return ipmi::responseInvalidFieldRequest();
1439 }
1440 if (attachUsbDevice())
1441 {
1442 phosphor::logging::log<phosphor::logging::level::ERR>(
1443 "Attach USB device failed.");
1444 return ipmi::responseUsbAttachOrDetachFailed();
1445 }
1446 usbAttached = true;
1447 break;
1448 case FwUpdateCtrlReq::detachUsbDevice:
1449 phosphor::logging::log<phosphor::logging::level::INFO>(
1450 "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
1451 if (!usbAttached)
1452 {
1453 phosphor::logging::log<phosphor::logging::level::ERR>(
1454 "USB device is not attached.");
1455 return ipmi::responseInvalidFieldRequest();
1456 }
1457 if (detachUsbDevice())
1458 {
1459 phosphor::logging::log<phosphor::logging::level::ERR>(
1460 "Detach USB device failed.");
1461 return ipmi::responseUsbAttachOrDetachFailed();
1462 }
1463 usbAttached = false;
1464 break;
1465 default:
1466 phosphor::logging::log<phosphor::logging::level::ERR>(
1467 "Invalid control option specified.");
1468 return ipmi::responseInvalidFieldRequest();
1469 }
1470
1471 return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
1472 imageTransferAborted, usbAttached, uint4_t(0));
1473}
1474
1475/** @brief implements firmware get status command
1476 * @parameter
1477 * - none
1478 * @returns IPMI completion code plus response data
1479 * - status - processing status
1480 * - percentage - percentage completion
1481 * - check - channel integrity check status
1482 **/
1483ipmi::RspType<uint8_t, // status
1484 uint8_t, // percentage
1485 uint8_t // check
1486 >
1487 ipmiGetFirmwareUpdateStatus()
1488
1489{
1490 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1491 // 5=ready, f=error, 83=ac cycle required)
1492 // Byte 2 - percent
1493 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1494 uint8_t status = fwUpdateStatus.getState();
1495 uint8_t percent = fwUpdateStatus.percent();
1496 uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
1497
1498 // Status code.
1499 return ipmi::responseSuccess(status, percent, check);
1500}
1501
1502ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
James Feistfcd2d3a2020-05-28 10:38:15 -07001503 const ipmi::Context::ptr& ctx, bool noDowngradeMask, bool deferRestartMask,
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301504 bool sha2CheckMask, uint5_t reserved1, bool noDowngrade, bool deferRestart,
1505 bool sha2Check, uint5_t reserved2,
1506 std::optional<std::vector<uint8_t>> integrityCheckVal)
AppaRao Puli28972062019-11-11 02:04:45 +05301507{
1508 phosphor::logging::log<phosphor::logging::level::INFO>(
1509 "Set firmware update options.");
Rajashekar Gade Reddy5b1988b2019-12-18 19:17:26 +05301510 bool isIPMBChannel = false;
1511
1512 if (checkIPMBChannel(ctx, isIPMBChannel) != ipmi::ccSuccess)
1513 {
1514 return ipmi::responseUnspecifiedError();
1515 }
1516 if (isIPMBChannel)
1517 {
1518 phosphor::logging::log<phosphor::logging::level::INFO>(
1519 "Channel not supported. Failed to set firmware update options");
1520 return ipmi::responseCommandNotAvailable();
1521 }
AppaRao Puli28972062019-11-11 02:04:45 +05301522 bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
1523 bool deferRestartState = fwUpdateStatus.getDeferRestart();
1524 bool sha2CheckState = xferHashCheck ? true : false;
1525
1526 if (noDowngradeMask && (noDowngradeState != noDowngrade))
1527 {
1528 fwUpdateStatus.setInhibitDowngrade(noDowngrade);
1529 noDowngradeState = noDowngrade;
1530 }
1531 if (deferRestartMask && (deferRestartState != deferRestart))
1532 {
1533 fwUpdateStatus.setDeferRestart(deferRestart);
1534 deferRestartState = deferRestart;
1535 }
1536 if (sha2CheckMask)
1537 {
1538 if (sha2Check)
1539 {
1540 auto hashSize = EVP_MD_size(EVP_sha256());
1541 if ((*integrityCheckVal).size() != hashSize)
1542 {
1543 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1544 "Invalid size of Hash specified.");
1545 return ipmi::responseInvalidFieldRequest();
1546 }
Arun Lal K M153d4c12021-08-25 22:00:37 +00001547
1548 try
1549 {
1550 xferHashCheck =
1551 std::make_unique<TransferHashCheck>(*integrityCheckVal);
1552 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001553 catch (const std::exception& ex)
Arun Lal K M153d4c12021-08-25 22:00:37 +00001554 {
1555 phosphor::logging::log<phosphor::logging::level::ERR>(
1556 ex.what());
1557 return ipmi::responseUnspecifiedError();
1558 }
AppaRao Puli28972062019-11-11 02:04:45 +05301559 }
1560 else
1561 {
1562 // delete the xferHashCheck object
1563 xferHashCheck.reset();
1564 }
1565 sha2CheckState = sha2CheckMask;
1566 }
1567 return ipmi::responseSuccess(noDowngradeState, deferRestartState,
1568 sha2CheckState, reserved1);
1569}
Vernon Mauery52ce6622019-05-22 09:19:46 -07001570
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301571ipmi::RspType<uint32_t>
James Feistfcd2d3a2020-05-28 10:38:15 -07001572 ipmiFwImageWriteData(const std::vector<uint8_t>& writeData)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001573{
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301574 const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1575 size_t writeDataLen = writeData.size();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001576
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301577 if (!writeDataLen)
1578 {
1579 return ipmi::responseReqDataLenInvalid();
1580 }
1581
AppaRao Puli28972062019-11-11 02:04:45 +05301582 if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301583 {
AppaRao Puli28972062019-11-11 02:04:45 +05301584 phosphor::logging::log<phosphor::logging::level::ERR>(
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301585 "Invalid firmware update state.");
1586 return ipmi::response(ccCmdNotSupportedInPresentState);
1587 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001588
AppaRao Puli28972062019-11-11 02:04:45 +05301589 std::ofstream out(firmwareBufferFile,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001590 std::ofstream::binary | std::ofstream::app);
1591 if (!out)
1592 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301593 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1594 "Error while opening file.");
1595 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001596 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301597
1598 uint64_t fileDataLen = out.tellp();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301599
AppaRao Puli28972062019-11-11 02:04:45 +05301600 if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001601 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301602 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1603 "Firmware image size exceeds the limit");
1604 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001605 }
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301606
James Feistfcd2d3a2020-05-28 10:38:15 -07001607 const char* data = reinterpret_cast<const char*>(writeData.data());
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301608 out.write(data, writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001609 out.close();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301610
AppaRao Puli28972062019-11-11 02:04:45 +05301611 if (xferHashCheck)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001612 {
Arun Lal K M153d4c12021-08-25 22:00:37 +00001613 if (!xferHashCheck->hash(writeData))
1614 {
1615 phosphor::logging::log<phosphor::logging::level::ERR>(
1616 "ipmiFwImageWriteData: xferHashCheck->hash failed.");
1617 return ipmi::responseUnspecifiedError();
1618 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001619 }
1620
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301621#ifdef INTEL_PFR_ENABLED
1622 /* PFR image block 0 - As defined in HAS */
1623 struct PFRImageBlock0
1624 {
1625 uint32_t tag;
1626 uint32_t pcLength;
1627 uint32_t pcType;
1628 uint32_t reserved1;
1629 uint8_t hash256[32];
1630 uint8_t hash384[48];
1631 uint8_t reserved2[32];
1632 } __attribute__((packed));
1633
1634 /* Get the PFR block 0 data and read the uploaded image
1635 * information( Image type, length etc) */
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301636 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1637 (!block0Mapped))
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301638 {
1639 struct PFRImageBlock0 block0Data = {0};
1640
AppaRao Puli28972062019-11-11 02:04:45 +05301641 std::ifstream inFile(firmwareBufferFile,
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301642 std::ios::binary | std::ios::in);
James Feistfcd2d3a2020-05-28 10:38:15 -07001643 inFile.read(reinterpret_cast<char*>(&block0Data), sizeof(block0Data));
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301644 inFile.close();
1645
1646 uint32_t magicNum = block0Data.tag;
1647
1648 /* Validate the magic number */
1649 if (magicNum != perBlock0MagicNum)
1650 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301651 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1652 "PFR image magic number not matched");
1653 return ipmi::responseInvalidFieldRequest();
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301654 }
1655 // Note:imgLength, imgType and block0Mapped are in global scope, as
1656 // these are used in cascaded updates.
1657 imgLength = block0Data.pcLength;
1658 imgType = block0Data.pcType;
1659 block0Mapped = true;
1660 }
1661#endif // end of INTEL_PFR_ENABLED
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301662 return ipmi::responseSuccess(writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001663}
1664
AppaRao Puli28972062019-11-11 02:04:45 +05301665static void registerFirmwareFunctions()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001666{
1667 // guarantee that we start with an already timed out timestamp
AppaRao Puli28972062019-11-11 02:04:45 +05301668 fwRandomNumGenTs =
1669 std::chrono::steady_clock::now() - fwRandomNumExpirySeconds;
1670 fwUpdateStatus.setState(
1671 static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
Vernon Mauery52ce6622019-05-22 09:19:46 -07001672
AppaRao Puli28972062019-11-11 02:04:45 +05301673 unlink(firmwareBufferFile);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001674
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301675#ifdef INTEL_PFR_ENABLED
1676 // Following commands are supported only for PFR enabled platforms
1677 // CMD:0x20 - Get Firmware Version Information
AppaRao Puli28972062019-11-11 02:04:45 +05301678 // CMD:0x21 - Get Firmware Security Version Information
1679 // CMD:0x25 - Get Root Certificate Data
Vernon Mauery52ce6622019-05-22 09:19:46 -07001680
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301681 // get firmware version information
1682 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1683 ipmi::firmware::cmdGetFwVersionInfo,
1684 ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
AppaRao Puli28972062019-11-11 02:04:45 +05301685
Vernon Mauery52ce6622019-05-22 09:19:46 -07001686 // get firmware security version information
AppaRao Puli28972062019-11-11 02:04:45 +05301687 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1688 ipmi::firmware::cmdGetFwSecurityVersionInfo,
1689 ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001690
Vernon Mauery52ce6622019-05-22 09:19:46 -07001691 // get root certificate data
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301692 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1693 ipmi::firmware::cmdFwGetRootCertData,
1694 ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1695#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001696
AppaRao Puli28972062019-11-11 02:04:45 +05301697 // get firmware update channel information (max transfer sizes)
1698 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1699 ipmi::firmware::cmdGetFwUpdateChannelInfo,
1700 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001701
AppaRao Puli28972062019-11-11 02:04:45 +05301702 // get bmc execution context
1703 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1704 ipmi::firmware::cmdGetBmcExecutionContext,
1705 ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
1706
1707 // Get Firmware Update Random number
1708 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1709 ipmi::firmware::cmdGetFwUpdateRandomNumber,
1710 ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
1711
1712 // Set Firmware Update Mode
1713 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1714 ipmi::firmware::cmdSetFirmwareUpdateMode,
AppaRao Puli4b3e1c72019-10-16 20:53:09 +05301715 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001716
AppaRao Puli28972062019-11-11 02:04:45 +05301717 // Exit Firmware Update Mode
anil kumar appanab57098a2019-05-28 16:22:33 +00001718 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
AppaRao Puli28972062019-11-11 02:04:45 +05301719 ipmi::firmware::cmdExitFirmwareUpdateMode,
1720 ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001721
AppaRao Puli28972062019-11-11 02:04:45 +05301722 // Get/Set Firmware Update Control
1723 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1724 ipmi::firmware::cmdGetSetFwUpdateControl,
1725 ipmi::Privilege::Admin,
1726 ipmiGetSetFirmwareUpdateControl);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001727
AppaRao Puli28972062019-11-11 02:04:45 +05301728 // Get Firmware Update Status
1729 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1730 ipmi::firmware::cmdGetFirmwareUpdateStatus,
1731 ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001732
AppaRao Puli28972062019-11-11 02:04:45 +05301733 // Set Firmware Update Options
1734 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1735 ipmi::firmware::cmdSetFirmwareUpdateOptions,
1736 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001737 // write image data
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301738 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1739 ipmi::firmware::cmdFwImageWriteData,
1740 ipmi::Privilege::Admin, ipmiFwImageWriteData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001741 return;
1742}