blob: d949a335eb6a9affc7051de11e48cac1851cbcbf [file] [log] [blame]
Vernon Mauery52ce6622019-05-22 09:19:46 -07001#include <ipmid/api.h>
2#include <openssl/evp.h>
3#include <openssl/sha.h>
4#include <sys/mman.h>
5#include <sys/stat.h>
6#include <sys/types.h>
7#include <unistd.h>
8
9#include <boost/algorithm/string.hpp>
10#include <boost/asio.hpp>
11#include <boost/process/child.hpp>
12#include <boost/uuid/random_generator.hpp>
13#include <boost/uuid/uuid_io.hpp>
14#include <chrono>
15#include <commandutils.hpp>
16#include <cstdint>
17#include <filesystem>
18#include <fstream>
19#include <iostream>
anil kumar appana6c7d9382019-05-31 14:33:13 +000020#include <ipmid/api.hpp>
AppaRao Puli37fde6b2019-10-25 16:37:50 +053021#include <ipmid/utils.hpp>
Vernon Mauery52ce6622019-05-22 09:19:46 -070022#include <map>
23#include <random>
24#include <sdbusplus/bus.hpp>
25#include <sdbusplus/bus/match.hpp>
26#include <sdbusplus/server/object.hpp>
27#include <sdbusplus/timer.hpp>
28#include <sstream>
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +053029#ifdef INTEL_PFR_ENABLED
30#include <spiDev.hpp>
31#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -070032
AppaRao Puli37fde6b2019-10-25 16:37:50 +053033namespace ipmi
34{
35namespace firmware
36{
37constexpr Cmd cmdGetFwVersionInfo = 0x20;
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +053038constexpr Cmd cmdFwGetRootCertData = 0x25;
39constexpr Cmd cmdFwImageWriteData = 0x2c;
AppaRao Puli37fde6b2019-10-25 16:37:50 +053040} // namespace firmware
41} // namespace ipmi
42
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +053043#ifdef INTEL_PFR_ENABLED
44uint32_t imgLength = 0;
45uint32_t imgType = 0;
46bool block0Mapped = false;
47static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
AppaRao Puli37fde6b2019-10-25 16:37:50 +053048
49static constexpr const char *versionIntf =
50 "xyz.openbmc_project.Software.Version";
51
52enum class FWDeviceIDTag : uint8_t
53{
54 bmcActiveImage = 1,
55 bmcRecoveryImage,
56};
57
58const static boost::container::flat_map<FWDeviceIDTag, const char *>
59 fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
60 "/xyz/openbmc_project/software/bmc_active"},
61 {FWDeviceIDTag::bmcRecoveryImage,
62 "/xyz/openbmc_project/software/bmc_recovery"}};
63
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +053064#endif
65
AppaRao Puli09a83142019-11-23 02:46:06 +053066static constexpr const char *bmcStateIntf = "xyz.openbmc_project.State.BMC";
67static constexpr const char *bmcStatePath = "/xyz/openbmc_project/state/bmc0";
68static constexpr const char *bmcStateReady =
69 "xyz.openbmc_project.State.BMC.BMCState.Ready";
70static constexpr const char *bmcStateUpdateInProgress =
71 "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
72
James Feistbeaa2eb2019-08-22 10:59:24 -070073static constexpr const char *secondaryFitImageStartAddr = "22480000";
anil kumar appana31f88872019-08-02 15:16:27 +000074static uint8_t getActiveBootImage(void);
Vernon Mauery52ce6622019-05-22 09:19:46 -070075static void register_netfn_firmware_functions() __attribute__((constructor));
76
77// oem return code for firmware update control
78constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5;
79constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80;
80
Vernon Mauery52ce6622019-05-22 09:19:46 -070081static constexpr bool DEBUG = false;
82
83static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] =
84 "xyz.openbmc_project.fwupdate1.server";
85
86static constexpr char FW_UPDATE_SERVER_PATH[] =
87 "/xyz/openbmc_project/fwupdate1";
88static constexpr char FW_UPDATE_SERVER_INFO_PATH[] =
89 "/xyz/openbmc_project/fwupdate1/info";
90static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] =
91 "/xyz/openbmc_project/fwupdate1/info/bmc_active";
92static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] =
93 "/xyz/openbmc_project/fwupdate1/info/bmc_backup";
94
95static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1";
96static constexpr char FW_UPDATE_INFO_INTERFACE[] =
97 "xyz.openbmc_project.fwupdate1.fwinfo";
98static constexpr char FW_UPDATE_SECURITY_INTERFACE[] =
99 "xyz.openbmc_project.fwupdate1.security";
100
101constexpr std::size_t operator""_MB(unsigned long long v)
102{
103 return 1024u * 1024u * v;
104}
105static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB;
106
107static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin";
108static bool local_download_is_active(void)
109{
110 struct stat sb;
111 if (stat(FIRMWARE_BUFFER_FILE, &sb) < 0)
112 return false;
113 return true;
114}
115
116class fw_update_status_cache
117{
118 public:
119 enum
120 {
121 FW_STATE_INIT = 0,
122 FW_STATE_IDLE,
123 FW_STATE_DOWNLOAD,
124 FW_STATE_VERIFY,
125 FW_STATE_WRITE,
126 FW_STATE_READY,
127 FW_STATE_ERROR = 0x0f,
128 FW_STATE_AC_CYCLE_REQUIRED = 0x83,
129 };
Vernon Mauery52ce6622019-05-22 09:19:46 -0700130 uint8_t state()
131 {
132 if (DEBUG)
133 std::cerr << "fw-state: 0x" << std::hex << (int)_state << '\n';
134 if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
135 local_download_is_active())
136 {
137 _state = FW_STATE_DOWNLOAD;
138 _percent = 0;
139 }
140 return _state;
141 }
142 uint8_t percent()
143 {
144 return _percent;
145 }
146 std::string msg()
147 {
148 return _msg;
149 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700150 std::string get_software_obj_path()
151 {
152 return _software_obj_path;
153 }
154 void set_software_obj_path(std::string &obj_path)
155 {
156 _software_obj_path = obj_path;
157 _state = FW_STATE_WRITE;
158 _percent = 0;
159 _match = std::make_shared<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700160 *_bus,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700161 sdbusplus::bus::match::rules::propertiesChanged(
162 _software_obj_path,
163 "xyz.openbmc_project.Software.ActivationProgress"),
164 [&](sdbusplus::message::message &msg) {
165 if (DEBUG)
166 std::cerr << "propertiesChanged lambda\n";
167 std::map<std::string, ipmi::DbusVariant> props;
168 std::vector<std::string> inval;
169 std::string iface;
170 msg.read(iface, props, inval);
171 _parse_props(props);
172 });
173 }
174 uint8_t activation_timer_timeout()
175 {
176 std::cerr << "activation_timer_timout(): increase percentage...\n";
177 _percent = _percent + 5;
anil kumar appana31f88872019-08-02 15:16:27 +0000178 if (_percent >= 95)
179 {
180 /*changing the state to ready to update firmware utility */
181 _state = FW_STATE_READY;
182 }
183 std::cerr << " _percent = " << (int)_percent << "\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -0700184 return _percent;
185 }
anil kumar appana31f88872019-08-02 15:16:27 +0000186 /* API for changing state to ERROR */
187 void firmwareUpdateAbortState()
188 {
189 unlink(FIRMWARE_BUFFER_FILE);
190 // changing the state to error
191 _state = FW_STATE_ERROR;
192 }
193 void setDeferRestart(bool deferRestart)
194 {
195 _deferRestart = deferRestart;
196 }
197 void setInhibitDowngrade(bool inhibitDowngrade)
198 {
199 _inhibitDowngrade = inhibitDowngrade;
200 }
201 bool getDeferRestart()
202 {
203 return _deferRestart;
204 }
205 bool getInhibitDowngrade()
206 {
207 return _inhibitDowngrade;
208 }
209
Vernon Mauery52ce6622019-05-22 09:19:46 -0700210 protected:
211 void _parse_props(std::map<std::string, ipmi::DbusVariant> &properties)
212 {
213 if (DEBUG)
214 std::cerr << "propertiesChanged (" << properties.size()
215 << " elements)";
216 for (const auto &t : properties)
217 {
218 auto key = t.first;
219 auto value = t.second;
220 if (key == "state")
221 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700222 auto state = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700223 if (DEBUG)
224 std::cerr << ", state=" << state;
225 if (state == "INIT")
226 _state = FW_STATE_INIT;
227 else if (state == "IDLE")
228 _state = FW_STATE_IDLE;
229 else if (state == "DOWNLOAD")
230 _state = FW_STATE_DOWNLOAD;
231 else if (state == "VERIFY")
232 _state = FW_STATE_VERIFY;
233 else if (state == "WRITE")
234 _state = FW_STATE_WRITE;
235 else if (state == "READY")
236 _state = FW_STATE_READY;
237 else if (state == "ERROR")
238 _state = FW_STATE_ERROR;
239 else if (state == "AC_CYCLE_REQUIRED")
240 _state = FW_STATE_AC_CYCLE_REQUIRED;
241 else
242 {
243 _state = FW_STATE_ERROR;
244 _msg = "internal error";
245 }
246 }
247 else if (key == "percent")
248 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700249 _percent = std::get<int32_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700250 if (DEBUG)
251 std::cerr << ", pct=" << (int)_percent;
252 }
253 else if (key == "msg")
254 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700255 _msg = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700256 if (DEBUG)
257 std::cerr << ", msg='" << _msg << '\'';
258 }
259 else if (key == "Progress")
260 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700261 _percent = std::get<uint8_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700262 ;
263 if (_percent == 100)
264 _state = FW_STATE_READY;
265 }
266 }
267 if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
268 local_download_is_active())
269 {
270 _state = FW_STATE_DOWNLOAD;
271 _percent = 0;
272 }
273 if (DEBUG)
274 std::cerr << '\n';
275 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700276
Vernon Mauery15419dd2019-05-24 09:40:30 -0700277 std::shared_ptr<sdbusplus::asio::connection> _bus;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700278 std::shared_ptr<sdbusplus::bus::match::match> _match;
279 uint8_t _state = 0;
280 uint8_t _percent = 0;
anil kumar appana31f88872019-08-02 15:16:27 +0000281 bool _deferRestart = false;
282 bool _inhibitDowngrade = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700283 std::string _msg;
284
285 private:
286 std::string _software_obj_path;
287};
288
289static fw_update_status_cache fw_update_status;
290
291static std::chrono::steady_clock::time_point fw_random_number_timestamp;
292static constexpr int FW_RANDOM_NUMBER_LENGTH = 8;
293static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30);
294static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH];
295
296static ipmi_ret_t ipmi_firmware_get_fw_random_number(
297 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
298 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
299{
300 std::random_device rd;
301 std::default_random_engine gen(rd());
302 std::uniform_int_distribution<> dist{0, 255};
303
304 if (*data_len != 0)
305 {
306 *data_len = 0;
307 return IPMI_CC_REQ_DATA_LEN_INVALID;
308 }
309
310 fw_random_number_timestamp = std::chrono::steady_clock::now();
311
312 uint8_t *msg_reply = static_cast<uint8_t *>(response);
313 for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
314 fw_random_number[i] = msg_reply[i] = dist(gen);
315
316 if (DEBUG)
317 std::cerr << "FW Rand Num: 0x" << std::hex << (int)msg_reply[0] << " 0x"
318 << (int)msg_reply[1] << " 0x" << (int)msg_reply[2] << " 0x"
319 << (int)msg_reply[3] << " 0x" << (int)msg_reply[4] << " 0x"
320 << (int)msg_reply[5] << " 0x" << (int)msg_reply[6] << " 0x"
321 << (int)msg_reply[7] << '\n';
322
323 *data_len = FW_RANDOM_NUMBER_LENGTH;
324
325 return IPMI_CC_OK;
326}
327
AppaRao Puli09a83142019-11-23 02:46:06 +0530328static bool getFirmwareUpdateMode()
329{
330 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
331 try
332 {
333 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
334 ipmi::Value state = ipmi::getDbusProperty(
335 *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState");
336 std::string bmcState = std::get<std::string>(state);
337 return (bmcState == bmcStateUpdateInProgress);
338 }
339 catch (const std::exception &e)
340 {
341 phosphor::logging::log<phosphor::logging::level::ERR>(
342 "Exception caught while getting BMC state.",
343 phosphor::logging::entry("EXCEPTION=%s", e.what()));
344 throw;
345 }
346}
347
348static void setFirmwareUpdateMode(const bool mode)
349{
350
351 std::string bmcState(bmcStateReady);
352 if (mode)
353 {
354 bmcState = bmcStateUpdateInProgress;
355 }
356
357 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
358 try
359 {
360 auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
361 ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf,
362 "CurrentBMCState", bmcState);
363 }
364 catch (const std::exception &e)
365 {
366 phosphor::logging::log<phosphor::logging::level::ERR>(
367 "Exception caught while setting BMC state.",
368 phosphor::logging::entry("EXCEPTION=%s", e.what()));
369 throw;
370 }
371}
372
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530373/** @brief Set Firmware Update Mode
374 *
375 * This function sets BMC into firmware update mode
376 * after validating Random number obtained from the Get
377 * Firmware Update Random Number command
378 *
379 * @parameter
380 * - randNum - Random number(token)
381 * @returns IPMI completion code
382 **/
383ipmi::RspType<> ipmiSetFirmwareUpdateMode(
384 std::array<uint8_t, FW_RANDOM_NUMBER_LENGTH> &randNum)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700385{
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530386 /* Firmware Update Random number is valid for 30 seconds only */
387 auto timeElapsed =
388 (std::chrono::steady_clock::now() - fw_random_number_timestamp);
389 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700390 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
391 FW_RANDOM_NUMBER_TTL)
392 .count())
393 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530394 phosphor::logging::log<phosphor::logging::level::INFO>(
395 "Firmware update random number expired.");
396 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700397 }
398
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530399 /* Validate random number */
Vernon Mauery52ce6622019-05-22 09:19:46 -0700400 for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
401 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530402 if (fw_random_number[i] != randNum[i])
Vernon Mauery52ce6622019-05-22 09:19:46 -0700403 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530404 phosphor::logging::log<phosphor::logging::level::INFO>(
405 "Invalid random number specified.");
406 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700407 }
408 }
409
AppaRao Puli09a83142019-11-23 02:46:06 +0530410 try
Vernon Mauery52ce6622019-05-22 09:19:46 -0700411 {
AppaRao Puli09a83142019-11-23 02:46:06 +0530412 if (getFirmwareUpdateMode())
413 {
414 phosphor::logging::log<phosphor::logging::level::INFO>(
415 "Already firmware update is in progress.");
416 return ipmi::responseBusy();
417 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700418 }
AppaRao Puli09a83142019-11-23 02:46:06 +0530419 catch (const std::exception &e)
420 {
421 return ipmi::responseUnspecifiedError();
422 }
423
Vernon Mauery52ce6622019-05-22 09:19:46 -0700424 // FIXME? c++ doesn't off an option for exclusive file creation
425 FILE *fp = fopen(FIRMWARE_BUFFER_FILE, "wx");
426 if (!fp)
427 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530428 phosphor::logging::log<phosphor::logging::level::INFO>(
429 "Unable to open file.");
430 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700431 }
432 fclose(fp);
433
AppaRao Puli09a83142019-11-23 02:46:06 +0530434 try
435 {
436 setFirmwareUpdateMode(true);
437 }
438 catch (const std::exception &e)
439 {
440 unlink(FIRMWARE_BUFFER_FILE);
441 return ipmi::responseUnspecifiedError();
442 }
443
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530444 return ipmi::responseSuccess();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700445}
446
anil kumar appanab57098a2019-05-28 16:22:33 +0000447/** @brief implements exit firmware update mode command
448 * @param None
449 *
450 * @returns IPMI completion code
451 */
452ipmi::RspType<> ipmiFirmwareExitFwUpdateMode()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700453{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700454
anil kumar appanab57098a2019-05-28 16:22:33 +0000455 if (DEBUG)
456 {
457 std::cerr << "Exit FW update mode \n";
458 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700459 switch (fw_update_status.state())
460 {
461 case fw_update_status_cache::FW_STATE_INIT:
462 case fw_update_status_cache::FW_STATE_IDLE:
anil kumar appanab57098a2019-05-28 16:22:33 +0000463 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700464 break;
465 case fw_update_status_cache::FW_STATE_DOWNLOAD:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700466 case fw_update_status_cache::FW_STATE_VERIFY:
467 break;
468 case fw_update_status_cache::FW_STATE_WRITE:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700469 break;
470 case fw_update_status_cache::FW_STATE_READY:
471 case fw_update_status_cache::FW_STATE_ERROR:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700472 break;
473 case fw_update_status_cache::FW_STATE_AC_CYCLE_REQUIRED:
anil kumar appanab57098a2019-05-28 16:22:33 +0000474 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700475 break;
476 }
anil kumar appanab57098a2019-05-28 16:22:33 +0000477 fw_update_status.firmwareUpdateAbortState();
AppaRao Puli09a83142019-11-23 02:46:06 +0530478
479 try
480 {
481 setFirmwareUpdateMode(false);
482 }
483 catch (const std::exception &e)
484 {
485 return ipmi::responseUnspecifiedError();
486 }
487
anil kumar appanab57098a2019-05-28 16:22:33 +0000488 return ipmi::responseSuccess();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700489}
490
491static void post_transfer_complete_handler(
492 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher);
493static bool request_start_firmware_update(const std::string &uri)
494{
495 if (DEBUG)
496 std::cerr << "request start firmware update()\n";
497
Vernon Mauery52ce6622019-05-22 09:19:46 -0700498 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
499 // the code gets to this point, the file should be transferred start the
500 // request (creating a new file in /tmp/images causes the update manager to
501 // check if it is ready for activation)
502 static std::unique_ptr<sdbusplus::bus::match::match> fw_update_matcher;
503 post_transfer_complete_handler(fw_update_matcher);
504 std::filesystem::rename(
505 uri, "/tmp/images/" +
506 boost::uuids::to_string(boost::uuids::random_generator()()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700507 return true;
508}
509
510class transfer_hash_check
511{
512 public:
513 enum hash_check
514 {
515 CHECK_NOT_REQUESTED = 0,
516 CHECK_REQUESTED,
517 CHECK_PASSED_SHA2,
518 CHECK_RESVD1,
519 CHECK_FAILED_SHA2 = 0xe2,
520 CHECK_RESVD2 = 0xe3,
521 };
522
523 protected:
524 EVP_MD_CTX *_ctx;
525 std::vector<uint8_t> _expected;
526 enum hash_check _check;
527 bool _started;
528
529 public:
530 transfer_hash_check() : _check(CHECK_NOT_REQUESTED), _started(false)
531 {
532 }
533 ~transfer_hash_check()
534 {
535 if (_ctx)
536 {
537 EVP_MD_CTX_destroy(_ctx);
538 _ctx = NULL;
539 }
540 }
541 void init(const std::vector<uint8_t> &expected)
542 {
543 _expected = expected;
544 _check = CHECK_REQUESTED;
545 _ctx = EVP_MD_CTX_create();
546 EVP_DigestInit(_ctx, EVP_sha256());
547 }
548 void hash(const std::vector<uint8_t> &data)
549 {
550 if (!_started)
551 _started = true;
552 EVP_DigestUpdate(_ctx, data.data(), data.size());
553 }
554 void clear()
555 {
556 // if not started, nothing to clear
557 if (_started)
558 {
559 if (_ctx)
560 EVP_MD_CTX_destroy(_ctx);
561 if (_check != CHECK_NOT_REQUESTED)
562 _check = CHECK_REQUESTED;
563 _ctx = EVP_MD_CTX_create();
564 EVP_DigestInit(_ctx, EVP_sha256());
565 }
566 }
567 enum hash_check check()
568 {
569 if (_check == CHECK_REQUESTED)
570 {
571 unsigned int len;
572 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
573 EVP_DigestFinal(_ctx, digest.data(), &len);
574 if (digest == _expected)
575 {
576 if (DEBUG)
577 std::cerr << "transfer sha2 check passed\n";
578 _check = CHECK_PASSED_SHA2;
579 }
580 else
581 {
582 if (DEBUG)
583 std::cerr << "transfer sha2 check failed\n";
584 _check = CHECK_FAILED_SHA2;
585 }
586 }
587 return _check;
588 }
589 uint8_t status() const
590 {
591 return static_cast<uint8_t>(_check);
592 }
593};
594
595std::shared_ptr<transfer_hash_check> xfer_hash_check;
596
Vernon Mauery52ce6622019-05-22 09:19:46 -0700597static void activate_image(const char *obj_path)
598{
anil kumar appana31f88872019-08-02 15:16:27 +0000599 // If flag is false means to reboot
600 if (fw_update_status.getDeferRestart() == false)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700601 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700602
anil kumar appana31f88872019-08-02 15:16:27 +0000603 if (DEBUG)
604 {
605 std::cerr << "activateImage()...\n";
606 std::cerr << "obj_path = " << obj_path << "\n";
607 }
608 phosphor::logging::log<phosphor::logging::level::INFO>(
609 "activating Image: ",
610 phosphor::logging::entry("OBJPATH =%s", obj_path));
611 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
612 bus->async_method_call(
613 [](const boost::system::error_code ec) {
614 if (ec)
615 {
616 phosphor::logging::log<phosphor::logging::level::ERR>(
617 "async_method_call error: activate_image failed");
618 return;
619 }
620 },
621 "xyz.openbmc_project.Software.BMC.Updater", obj_path,
622 "org.freedesktop.DBus.Properties", "Set",
623 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
624 std::variant<std::string>("xyz.openbmc_project.Software.Activation."
625 "RequestedActivations.Active"));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700626 }
anil kumar appana31f88872019-08-02 15:16:27 +0000627 else
Vernon Mauery52ce6622019-05-22 09:19:46 -0700628 {
anil kumar appana31f88872019-08-02 15:16:27 +0000629 phosphor::logging::log<phosphor::logging::level::INFO>(
630 "Firmware image activation is deferred.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700631 }
632}
633
634static void post_transfer_complete_handler(
635 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher)
636{
637 // Setup timer for watching signal
638 static phosphor::Timer timer(
639 [&fw_update_matcher]() { fw_update_matcher = nullptr; });
640
641 static phosphor::Timer activation_status_timer([]() {
642 if (fw_update_status.activation_timer_timeout() >= 95)
643 {
644 activation_status_timer.stop();
645 }
646 });
647
648 timer.start(std::chrono::microseconds(5000000), false);
649
650 // callback function for capturing signal
651 auto callback = [&fw_update_matcher](sdbusplus::message::message &m) {
652 if (DEBUG)
653 std::cerr << "[complete] Match fired\n";
654 bool flag = false;
655
656 std::vector<std::pair<
657 std::string,
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700658 std::vector<std::pair<std::string, std::variant<std::string>>>>>
Vernon Mauery52ce6622019-05-22 09:19:46 -0700659 interfaces_properties;
660
661 sdbusplus::message::object_path obj_path;
662
663 try
664 {
665 m.read(obj_path, interfaces_properties); // Read in the object path
666 // that was just created
667 }
668 catch (std::exception &e)
669 {
670 std::cerr
671 << "[complete] Failed at post_transfer_complete-handler : "
672 << e.what() << "\n";
673 }
674 // constructing response message
675 if (DEBUG)
676 std::cerr << "[complete] obj path = " << obj_path.str << "\n";
677 for (auto &interface : interfaces_properties)
678 {
679 if (DEBUG)
680 std::cerr << "[complete] interface = " << interface.first
681 << "\n";
682
683 if (interface.first == "xyz.openbmc_project.Software.Activation")
684 {
685 // cancel timer only when
686 // xyz.openbmc_project.Software.Activation interface is
687 // added
688
689 if (DEBUG)
690 std::cerr << "[complete] Attempt to cancel timer...\n";
691 try
692 {
693 timer.stop();
694 activation_status_timer.start(
695 std::chrono::microseconds(3000000), true);
696 }
697 catch (std::exception &e)
698 {
699 std::cerr << "[complete] cancel timer error: " << e.what()
700 << "\n";
701 }
702
703 fw_update_status.set_software_obj_path(obj_path.str);
704 activate_image(obj_path.str.c_str());
705 if (DEBUG)
706 std::cerr << "[complete] returned from activeImage()\n";
707
708 fw_update_matcher = nullptr;
709 }
710 }
711 };
712
713 // Adding matcher
714 fw_update_matcher = std::make_unique<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700715 *getSdBus(),
Vernon Mauery52ce6622019-05-22 09:19:46 -0700716 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
717 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
718 callback);
719}
Vernon Mauery52ce6622019-05-22 09:19:46 -0700720
721class MappedFile
722{
723 public:
724 MappedFile(const std::string &fname) : addr(nullptr), fsize(0)
725 {
726 std::error_code ec;
727 size_t sz = std::filesystem::file_size(fname, ec);
728 int fd = open(fname.c_str(), O_RDONLY);
729 if (!ec || fd < 0)
730 {
731 return;
732 }
733 void *tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
734 close(fd);
735 if (tmp == MAP_FAILED)
736 {
737 return;
738 }
739 addr = tmp;
740 fsize = sz;
741 }
742
743 ~MappedFile()
744 {
745 if (addr)
746 {
747 munmap(addr, fsize);
748 }
749 }
750 const uint8_t *data() const
751 {
752 return static_cast<const uint8_t *>(addr);
753 }
754 size_t size() const
755 {
756 return fsize;
757 }
758
759 private:
760 size_t fsize;
761 void *addr;
762};
763
764static int transfer_from_file(const std::string &uri, bool move = true)
765{
766 std::error_code ec;
767 if (DEBUG)
768 std::cerr << "transfer_from_file(" << uri << ")\n";
769 if (move)
770 {
771 std::filesystem::rename(uri, FIRMWARE_BUFFER_FILE, ec);
772 }
773 else
774 {
775 std::filesystem::copy(uri, FIRMWARE_BUFFER_FILE,
776 std::filesystem::copy_options::overwrite_existing,
777 ec);
778 }
779 if (xfer_hash_check)
780 {
781 MappedFile mappedfw(uri);
782 xfer_hash_check->hash(
783 {mappedfw.data(), mappedfw.data() + mappedfw.size()});
784 }
785 if (ec.value())
786 {
787 std::cerr << "cp/mv returns: " << ec.message() << "(" << ec.value()
788 << ")\n";
789 }
790 return ec.value();
791}
792
793template <typename... ArgTypes>
794static int executeCmd(const char *path, ArgTypes &&... tArgs)
795{
796 boost::process::child execProg(path, const_cast<char *>(tArgs)...);
797 execProg.wait();
798 return execProg.exit_code();
799}
800
801constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl";
802constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt";
803constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img";
804constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev";
805constexpr size_t fwPathMaxLength = 255;
806static int transfer_from_usb(const std::string &uri)
807{
808 int ret, sysret;
809 char fwpath[fwPathMaxLength];
810 if (DEBUG)
811 std::cerr << "transfer_from_usb(" << uri << ")\n";
812 ret = executeCmd(USB_CTRL_PATH, "mount", FWUPDATE_USB_VOL_IMG,
813 FWUPDATE_MOUNT_POINT);
814 if (ret)
815 {
816 return ret;
817 }
818
819 std::string usb_path = std::string(FWUPDATE_MOUNT_POINT) + "/" + uri;
820 ret = transfer_from_file(usb_path, false);
821
822 executeCmd(USB_CTRL_PATH, "cleanup", FWUPDATE_USB_VOL_IMG,
823 FWUPDATE_MOUNT_POINT);
824 return ret;
825}
826
827static bool transfer_firmware_from_uri(const std::string &uri)
828{
829 static constexpr char FW_URI_FILE[] = "file://";
830 static constexpr char FW_URI_USB[] = "usb://";
831 if (DEBUG)
832 std::cerr << "transfer_firmware_from_uri(" << uri << ")\n";
833 if (boost::algorithm::starts_with(uri, FW_URI_FILE))
834 {
835 std::string fname = uri.substr(sizeof(FW_URI_FILE) - 1);
836 if (fname != FIRMWARE_BUFFER_FILE)
837 {
838 return 0 == transfer_from_file(fname);
839 }
840 return true;
841 }
842 if (boost::algorithm::starts_with(uri, FW_URI_USB))
843 {
844 std::string fname = uri.substr(sizeof(FW_URI_USB) - 1);
845 return 0 == transfer_from_usb(fname);
846 }
847 return false;
848}
849
850/* Get USB-mass-storage device status: inserted => true, ejected => false */
851static int usb_get_status()
852{
853 static constexpr char usb_gadget_base[] = "/sys/kernel/config/usb_gadget/";
854 auto usb_device =
855 std::filesystem::path(usb_gadget_base) / FWUPDATE_USB_DEV_NAME;
856 std::error_code ec;
857 return std::filesystem::exists(usb_device, ec) && !ec;
858}
859
860/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
861static int usb_attach_device()
862{
863 if (usb_get_status())
864 {
865 return 1;
866 }
867 int ret =
868 executeCmd(USB_CTRL_PATH, "setup", FWUPDATE_USB_VOL_IMG,
869 std::to_string(FIRMWARE_BUFFER_MAX_SIZE / 1_MB).c_str());
870 if (!ret)
871 {
872 ret = executeCmd(USB_CTRL_PATH, "insert", FWUPDATE_USB_DEV_NAME,
873 FWUPDATE_USB_VOL_IMG);
874 }
875 return ret;
876}
877
878/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
879static int usb_detach_device()
880{
881 if (!usb_get_status())
882 {
883 return 1;
884 }
885 return executeCmd(USB_CTRL_PATH, "eject", FWUPDATE_USB_DEV_NAME);
886}
887
888constexpr uint8_t controls_init = 0x00;
889constexpr uint8_t controls_transfer_started = 0x01;
890constexpr uint8_t controls_transfer_completed = 0x02;
891constexpr uint8_t controls_transfer_aborted = 0x04;
892constexpr uint8_t controls_usb_attached = 0x08;
893
894struct fw_update_control_request
895{
896 enum knob
897 {
898 CTRL_GET = 0,
899 CTRL_XFER_START,
900 CTRL_XFER_COMPLETE,
901 CTRL_XFER_ABORT,
902 CTRL_SET_FILENAME,
903 CTRL_USB_ATTACH,
904 CTRL_USB_DETACH,
905 } __attribute__((packed));
906 enum knob control;
907 uint8_t nlen;
908 char filename[fwPathMaxLength];
909} __attribute__((packed));
910
911static ipmi_ret_t ipmi_firmware_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
912 ipmi_request_t request,
913 ipmi_response_t response,
914 ipmi_data_len_t data_len,
915 ipmi_context_t context)
916{
917 static std::string fw_xfer_uri;
918
919 if (DEBUG)
920 std::cerr << "FW update control\n";
921 *data_len = 0;
922
923 static uint8_t controls = controls_init;
924 ipmi_ret_t rc = IPMI_CC_OK;
925 auto ctrl_req = reinterpret_cast<fw_update_control_request *>(request);
926 auto ctrl_resp = reinterpret_cast<uint8_t *>(response);
927
928 if (usb_get_status())
929 {
930 controls |= controls_usb_attached;
931 }
932 else
933 {
934 controls &= ~controls_usb_attached;
935 }
936
937 switch (ctrl_req->control)
938 {
939 case fw_update_control_request::CTRL_GET:
940 break;
941 case fw_update_control_request::CTRL_XFER_START:
942 {
943 controls |= controls_transfer_started;
944 // reset buffer to empty (truncate file)
945 std::ofstream out(FIRMWARE_BUFFER_FILE,
946 std::ofstream::binary | std::ofstream::trunc);
947 fw_xfer_uri = std::string("file://") + FIRMWARE_BUFFER_FILE;
948 if (xfer_hash_check)
949 {
950 xfer_hash_check->clear();
951 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +0530952#ifdef INTEL_PFR_ENABLED
953 imgLength = 0;
954 imgType = 0;
955 block0Mapped = false;
956#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -0700957 if (DEBUG)
958 std::cerr << "transfer start\n";
959 }
960 break;
961 case fw_update_control_request::CTRL_XFER_COMPLETE:
962 {
963 if (usb_get_status())
964 {
965 rc = IPMI_CC_REQ_INVALID_PHASE;
966 }
967 // finish transfer based on URI
968 if (!transfer_firmware_from_uri(fw_xfer_uri))
969 {
970 rc = IPMI_CC_UNSPECIFIED_ERROR;
971 break;
972 }
973 // transfer complete
974 if (xfer_hash_check)
975 {
976 if (transfer_hash_check::CHECK_PASSED_SHA2 !=
977 xfer_hash_check->check())
978 {
979 if (DEBUG)
980 std::cerr << "xfer_hash_check returns not "
981 "CHECK_PASSED_SHA2\n";
982 rc = IPMI_CC_UNSPECIFIED_ERROR;
983 break;
984 }
985 }
986 // start the request
987 if (!request_start_firmware_update(FIRMWARE_BUFFER_FILE))
988 {
989 if (DEBUG)
990 std::cerr
991 << "request_start_firmware_update returns failure\n";
992 rc = IPMI_CC_UNSPECIFIED_ERROR;
993 }
994 if (rc == IPMI_CC_OK)
995 {
996 controls |= controls_transfer_completed;
997 }
998 }
999 break;
1000 case fw_update_control_request::CTRL_XFER_ABORT:
1001 if (DEBUG)
1002 std::cerr << "send abort request\n";
1003 if (usb_get_status())
1004 {
1005 if (0 != usb_detach_device())
1006 {
1007 rc = IPMI_CC_USB_ATTACH_FAIL;
1008 }
1009 }
anil kumar appana31f88872019-08-02 15:16:27 +00001010 fw_update_status.firmwareUpdateAbortState();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001011 controls |= controls_transfer_aborted;
1012 break;
1013 case fw_update_control_request::CTRL_SET_FILENAME:
1014 fw_xfer_uri.clear();
1015 fw_xfer_uri.insert(0, ctrl_req->filename, ctrl_req->nlen);
1016 break;
1017 case fw_update_control_request::CTRL_USB_ATTACH:
1018 if (usb_get_status())
1019 {
1020 rc = IPMI_CC_INVALID_FIELD_REQUEST;
1021 }
1022 else if (0 != usb_attach_device())
1023 {
1024 rc = IPMI_CC_USB_ATTACH_FAIL;
1025 }
1026 else
1027 {
1028 rc = IPMI_CC_OK;
1029 }
1030 break;
1031 case fw_update_control_request::CTRL_USB_DETACH:
1032 if (!usb_get_status())
1033 {
1034 rc = IPMI_CC_INVALID_FIELD_REQUEST;
1035 }
1036 if (0 != usb_detach_device())
1037 {
1038 rc = IPMI_CC_USB_ATTACH_FAIL;
1039 }
1040 else
1041 {
1042 rc = IPMI_CC_OK;
1043 }
1044 break;
1045 default:
1046 if (DEBUG)
1047 std::cerr << "control byte " << std::hex << ctrl_req->control
1048 << " unknown\n";
1049 rc = IPMI_CC_INVALID_FIELD_REQUEST;
1050 break;
1051 }
1052
1053 if (rc == IPMI_CC_OK)
1054 {
1055 *ctrl_resp = controls;
1056 *data_len = sizeof(*ctrl_resp);
1057 }
1058
1059 return rc;
1060}
1061
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301062#ifdef INTEL_PFR_ENABLED
1063using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
1064 uint8_t, // Major Version Number
1065 uint8_t, // Minor Version Number
1066 uint32_t, // Build Number
1067 uint32_t, // Build Timestamp
1068 uint32_t>; // Update Timestamp
1069ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001070{
Vernon Mauery52ce6622019-05-22 09:19:46 -07001071 // Byte 1 - Count (N) Number of devices data is being returned for.
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301072 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001073 // Bytes - 17:(15xN) - Repeat of 2 through 16
1074
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301075 std::vector<fwVersionInfoType> fwVerInfoList;
1076 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
1077 for (const auto &fwDev : fwVersionIdMap)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001078 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301079 std::string verStr;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001080 try
1081 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301082 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001083
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301084 ipmi::Value result = ipmi::getDbusProperty(
1085 *busp, service, fwDev.second, versionIntf, "Version");
1086 verStr = std::get<std::string>(result);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001087 }
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301088 catch (const std::exception &e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001089 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301090 phosphor::logging::log<phosphor::logging::level::INFO>(
1091 "Failed to fetch Version property",
1092 phosphor::logging::entry("ERROR=%s", e.what()),
1093 phosphor::logging::entry("PATH=%s", fwDev.second),
1094 phosphor::logging::entry("INTERFACE=%s", versionIntf));
1095 continue;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001096 }
1097
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301098 if (verStr.empty())
1099 {
1100 phosphor::logging::log<phosphor::logging::level::INFO>(
1101 "Version is empty.",
1102 phosphor::logging::entry("PATH=%s", fwDev.second),
1103 phosphor::logging::entry("INTERFACE=%s", versionIntf));
1104 continue;
1105 }
1106
1107 // BMC Version format: <major>.<minor>-<build bum>-<build hash>
1108 std::vector<std::string> splitVer;
1109 boost::split(splitVer, verStr, boost::is_any_of(".-"));
1110 if (splitVer.size() < 3)
1111 {
1112 phosphor::logging::log<phosphor::logging::level::INFO>(
1113 "Invalid Version format.",
1114 phosphor::logging::entry("Version=%s", verStr.c_str()),
1115 phosphor::logging::entry("PATH=%s", fwDev.second));
1116 continue;
1117 }
1118
1119 uint8_t majorNum = 0;
1120 uint8_t minorNum = 0;
1121 uint32_t buildNum = 0;
1122 try
1123 {
1124 majorNum = std::stoul(splitVer[0], nullptr, 16);
1125 minorNum = std::stoul(splitVer[1], nullptr, 16);
1126 buildNum = std::stoul(splitVer[2], nullptr, 16);
1127 }
1128 catch (const std::exception &e)
1129 {
1130 phosphor::logging::log<phosphor::logging::level::INFO>(
1131 "Failed to convert stoul.",
1132 phosphor::logging::entry("ERROR=%s", e.what()));
1133 continue;
1134 }
1135
1136 // Build Timestamp - Not supported.
1137 // Update Timestamp - TODO: Need to check with CPLD team.
1138 fwVerInfoList.emplace_back(
1139 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
1140 minorNum, buildNum, 0, 0));
Vernon Mauery52ce6622019-05-22 09:19:46 -07001141 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001142
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301143 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001144}
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301145#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001146
1147struct fw_security_revision_info
1148{
1149 uint8_t id_tag;
1150 uint16_t sec_rev;
1151} __attribute__((packed));
1152
1153static ipmi_ret_t ipmi_firmware_get_fw_security_revision(
1154 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1155 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1156{
1157 if (DEBUG)
1158 std::cerr << "Get FW security revision info\n";
1159
1160 // Byte 1 - Count (N) Number of devices data is being returned for.
1161 // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image
1162 // 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR
1163 // Image
1164 // Byte 3 - Major Version Number
1165 // Byte 4 - Minor Version Number
1166 // Bytes 5:8 - Build Number
1167 // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL
1168 // timestamp
1169 // Bytes 13:16 - Update Timestamp
1170 // Bytes - 17:(15xN) - Repeat of 2 through 16
1171
1172 uint8_t count = 0;
1173 auto ret_count = reinterpret_cast<uint8_t *>(response);
1174 auto info =
1175 reinterpret_cast<struct fw_security_revision_info *>(ret_count + 1);
1176
Vernon Mauery15419dd2019-05-24 09:40:30 -07001177 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001178 for (uint8_t id_tag = 1; id_tag < 6; id_tag++)
1179 {
1180 const char *fw_path;
1181 switch (id_tag)
1182 {
1183 case 1:
1184 fw_path = FW_UPDATE_ACTIVE_INFO_PATH;
1185 break;
1186 case 2:
1187 fw_path = FW_UPDATE_BACKUP_INFO_PATH;
1188 break;
1189 case 3:
1190 case 4:
1191 case 5:
1192 continue; // skip for now
1193 break;
1194 }
1195 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001196 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001197 "org.freedesktop.DBus.Properties", "GetAll");
1198 method.append(FW_UPDATE_INFO_INTERFACE, "security_version");
1199 ipmi::DbusVariant sec_rev;
1200 try
1201 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001202 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001203
1204 if (reply.is_method_error())
1205 continue;
1206
1207 reply.read(sec_rev);
1208 }
1209 catch (sdbusplus::exception::SdBusError &e)
1210 {
1211 std::cerr << "SDBus Error: " << e.what();
1212 return IPMI_CC_UNSPECIFIED_ERROR;
1213 }
1214
1215 info->id_tag = id_tag;
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001216 info->sec_rev = std::get<int>(sec_rev);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001217 count++;
1218 info++;
1219 }
1220 *ret_count = count;
1221
1222 // Status code.
1223 ipmi_ret_t rc = IPMI_CC_OK;
1224 *data_len = sizeof(count) + count * sizeof(*info);
1225
1226 return rc;
1227}
1228
1229struct fw_channel_size
1230{
1231 uint8_t channel_id;
1232 uint32_t channel_size;
1233} __attribute__((packed));
1234
1235enum
1236{
1237 CHANNEL_RESVD = 0,
1238 CHANNEL_KCS,
1239 CHANNEL_RMCP_PLUS,
1240 CHANNEL_USB_DATA,
1241 CHANNEL_USB_MASS_STORAGE,
1242} channel_transfer_type;
1243
anil kumar appana159547c2019-05-31 16:08:34 +00001244static constexpr uint8_t channelListSize = 2;
1245/** @brief implements Maximum Firmware Transfer size command
1246 * @parameter
1247 * - none
1248 * @returns IPMI completion code plus response data
1249 * - count - channel count
1250 * - channelList - channel list information
1251 */
1252ipmi::RspType<uint8_t, // channel count
1253 std::array<std::tuple<uint8_t, uint32_t>,
1254 channelListSize> // channel
1255 // list
1256 >
1257 ipmiFirmwareMaxTransferSize()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001258{
anil kumar appana159547c2019-05-31 16:08:34 +00001259 constexpr uint8_t KCSMaxBufSize = 128;
1260 constexpr uint32_t RMCPPLUSMaxBufSize = 50 * 1024;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001261 if (DEBUG)
1262 std::cerr << "Get FW max transfer size\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -07001263 // Byte 1 - Count (N) Number of devices data is being returned for.
1264 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+,
1265 // 03 – usb data, 04 – usb mass storage
1266 // Byte 3-6 - transfer size (little endian)
1267 // Bytes - 7:(5xN) - Repeat of 2 through 6
anil kumar appana159547c2019-05-31 16:08:34 +00001268 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1269 channelList = {{{CHANNEL_KCS, KCSMaxBufSize},
1270 {CHANNEL_RMCP_PLUS, RMCPPLUSMaxBufSize}}};
1271 return ipmi::responseSuccess(channelListSize, channelList);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001272}
1273
1274enum
1275{
1276 EXEC_CTX_RESVD = 0,
1277 EXEC_CTX_FULL_LINUX = 0x10,
1278 EXEC_CTX_SAFE_MODE_LINUX = 0x11,
1279} bmc_execution_context;
1280
1281struct fw_execution_context
1282{
1283 uint8_t context;
1284 uint8_t image_selection;
1285} __attribute__((packed));
1286
1287static ipmi_ret_t ipmi_firmware_get_fw_execution_context(
1288 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1289 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1290{
1291 if (DEBUG)
1292 std::cerr << "Get FW execution context\n";
1293
1294 // Byte 1 - execution context
1295 // 0x10 - full linux stack, 0x11 - safe-mode linux stack
1296 // Byte 2 - current image selection
1297 // 1 - primary, 2 - secondary
1298
1299 auto info = reinterpret_cast<struct fw_execution_context *>(response);
anil kumar appana31f88872019-08-02 15:16:27 +00001300 info->context = EXEC_CTX_FULL_LINUX;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001301
anil kumar appana31f88872019-08-02 15:16:27 +00001302 info->image_selection = getActiveBootImage();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001303
1304 // Status code.
1305 ipmi_ret_t rc = IPMI_CC_OK;
1306 *data_len = sizeof(*info);
1307
1308 return rc;
1309}
1310
anil kumar appana31f88872019-08-02 15:16:27 +00001311uint8_t getActiveBootImage(void)
1312{
1313 // 0x01 - primaryImage
1314 constexpr uint8_t primaryImage = 0x01;
1315 // 0x02 - secondaryImage
1316 constexpr uint8_t secondaryImage = 0x02;
1317 uint8_t bootImage = primaryImage;
1318
1319 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1320 auto method = bus->new_method_call(
1321 "xyz.openbmc_project.U_Boot.Environment.Manager",
1322 "/xyz/openbmc_project/u_boot/environment/mgr",
1323 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read");
1324 method.append("bootcmd");
1325 std::string value;
1326 try
1327 {
1328 auto reply = bus->call(method);
1329 reply.read(value);
1330 }
1331 catch (sdbusplus::exception::SdBusError &e)
1332 {
1333 std::cerr << "SDBus Error: " << e.what();
1334 return IPMI_CC_UNSPECIFIED_ERROR;
1335 }
1336 /* cheking for secondary FitImage Address 22480000 */
1337 if (value.find(secondaryFitImageStartAddr) != std::string::npos)
1338 {
1339 bootImage = secondaryImage;
1340 }
1341 else
1342 {
1343 bootImage = primaryImage;
1344 }
1345
1346 return bootImage;
1347}
anil kumar appana6c7d9382019-05-31 14:33:13 +00001348/** @brief implements firmware get status command
1349 * @parameter
1350 * - none
1351 * @returns IPMI completion code plus response data
1352 * - status - processing status
1353 * - percentage - percentage completion
1354 * - check - channel integrity check status
1355 **/
1356ipmi::RspType<uint8_t, // status
1357 uint8_t, // percentage
1358 uint8_t // check
1359 >
1360 ipmiFrmwareGetStatus()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001361
Vernon Mauery52ce6622019-05-22 09:19:46 -07001362{
1363 if (DEBUG)
1364 std::cerr << "Get FW update status\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -07001365 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1366 // 5=ready, f=error, 83=ac cycle required)
1367 // Byte 2 - percent
1368 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
anil kumar appana6c7d9382019-05-31 14:33:13 +00001369 uint8_t status = fw_update_status.state();
1370 uint8_t percent = fw_update_status.percent();
1371 uint8_t check = xfer_hash_check ? xfer_hash_check->status() : 0;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001372
1373 // Status code.
anil kumar appana6c7d9382019-05-31 14:33:13 +00001374 return ipmi::responseSuccess(status, percent, check);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001375}
1376
1377static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0);
1378static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1);
1379static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2);
1380static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3);
1381struct fw_update_options_request
1382{
1383 uint8_t mask;
1384 uint8_t options;
1385} __attribute__((packed));
1386
Vernon Mauery52ce6622019-05-22 09:19:46 -07001387uint32_t fw_update_options = 0;
1388static ipmi_ret_t ipmi_firmware_update_options(
1389 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1390 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1391{
1392 if (DEBUG)
1393 std::cerr << "Get/set FW update options\n";
1394
1395 // request:
1396 // Byte 1 - mask
1397 // Byte 2 - options
1398 // Byte 3-34 - optional integrity check expected value
1399 // response:
1400 // Byte 1 - set options
1401
1402 auto fw_options =
1403 reinterpret_cast<struct fw_update_options_request *>(request);
1404
1405 const char *path = FW_UPDATE_SERVER_INFO_PATH;
1406 const char *iface = FW_UPDATE_SECURITY_INTERFACE;
1407 if ((fw_options->mask & FW_UPDATE_OPTIONS_NO_DOWNREV) &&
1408 (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) !=
1409 (fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV))
1410 {
1411 if (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV)
1412 {
1413 fw_update_options |= FW_UPDATE_OPTIONS_NO_DOWNREV;
anil kumar appana31f88872019-08-02 15:16:27 +00001414 /*setting flag to flase for deferring downgrade support*/
1415 fw_update_status.setInhibitDowngrade(true);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001416 }
1417 else
1418 {
1419 fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV;
anil kumar appana31f88872019-08-02 15:16:27 +00001420 /*setting flag to true for downgrade support*/
1421 fw_update_status.setInhibitDowngrade(false);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001422 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001423 }
1424 if ((fw_options->mask & FW_UPDATE_OPTIONS_DEFER_RESTART) &&
1425 (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) !=
1426 (fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART))
1427 {
1428 if (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART)
1429 {
1430 fw_update_options |= FW_UPDATE_OPTIONS_DEFER_RESTART;
anil kumar appana31f88872019-08-02 15:16:27 +00001431 /* setting flag to true to stop image activation */
1432 fw_update_status.setDeferRestart(true);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001433 }
1434 else
1435 {
anil kumar appana31f88872019-08-02 15:16:27 +00001436 /* setting flag to false for image activation */
Vernon Mauery52ce6622019-05-22 09:19:46 -07001437 fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART;
anil kumar appana31f88872019-08-02 15:16:27 +00001438 fw_update_status.setDeferRestart(false);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001439 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001440 }
1441 if (fw_options->mask & FW_UPDATE_OPTIONS_SHA2_CHECK)
1442 {
1443 auto hash_size = EVP_MD_size(EVP_sha256());
1444 if (fw_options->options & FW_UPDATE_OPTIONS_SHA2_CHECK)
1445 {
1446 if (*data_len != (sizeof(*fw_options) + hash_size))
1447 {
1448 *data_len = 0;
1449 return IPMI_CC_REQ_DATA_LEN_INVALID;
1450 }
1451 xfer_hash_check = std::make_shared<transfer_hash_check>();
1452 auto exp_hash = reinterpret_cast<uint8_t *>(fw_options + 1);
1453 xfer_hash_check->init({exp_hash, exp_hash + hash_size});
1454 fw_update_options |= FW_UPDATE_OPTIONS_SHA2_CHECK;
1455 }
1456 else
1457 {
1458 fw_update_options &= ~FW_UPDATE_OPTIONS_SHA2_CHECK;
1459 // delete the xfer_hash_check object
1460 xfer_hash_check.reset();
1461 }
1462 }
1463 auto options_rsp = reinterpret_cast<uint8_t *>(response);
1464 *options_rsp = fw_update_options;
1465
1466 if (DEBUG)
1467 std::cerr << "current fw_update_options = " << std::hex
1468 << fw_update_options << '\n';
1469 // Status code.
1470 *data_len = sizeof(*options_rsp);
1471 return IPMI_CC_OK;
1472}
1473
1474struct fw_cert_info
1475{
1476 uint16_t cert_len;
1477 uint64_t serial;
1478 uint8_t subject_len;
1479 char subject[255];
1480} __attribute__((packed));
1481
1482static ipmi_ret_t ipmi_firmware_get_root_cert_info(
1483 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1484 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1485{
1486 if (DEBUG)
1487 std::cerr << "Get FW root cert info\n";
1488
1489 // request:
1490 // Byte 1 - certificate ID: request which certificate (ignored)
1491
1492 // response:
1493 // Byte 1-2 - certificate length (little endian)
1494 // Byte 3-10 - serial number (little endian)
1495 // Byte 11 - subject length
1496 // Byte 12-N - subject data
1497
1498 auto cert_info = reinterpret_cast<struct fw_cert_info *>(response);
Vernon Mauery15419dd2019-05-24 09:40:30 -07001499 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1500 auto method = bus->new_method_call(
Vernon Mauery52ce6622019-05-22 09:19:46 -07001501 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH,
1502 "org.freedesktop.DBus.Properties", "GetAll");
1503 method.append(FW_UPDATE_SECURITY_INTERFACE);
1504 std::string subject;
1505 uint64_t serial;
1506 std::string cert;
1507 try
1508 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001509 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001510
1511 std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
1512 reply.read(properties);
1513
1514 for (const auto &t : properties)
1515 {
1516 auto key = t.first;
1517 auto value = t.second;
1518 if (key == "certificate_subject")
1519 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001520 subject = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001521 }
1522 else if (key == "cetificate_serial")
1523 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001524 serial = std::get<uint64_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001525 }
1526 else if (key == "certificate")
1527 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001528 cert = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001529 }
1530 }
1531 }
1532 catch (sdbusplus::exception::SdBusError &e)
1533 {
1534 std::cerr << "SDBus Error: " << e.what();
1535 return IPMI_CC_UNSPECIFIED_ERROR;
1536 }
1537
1538 cert_info->cert_len = cert.size();
1539 cert_info->serial = serial;
1540 // truncate subject so it fits in the 255-byte array (if necessary)
1541 if (subject.size() > sizeof(cert_info->subject))
1542 subject.resize(sizeof(cert_info->subject));
1543 cert_info->subject_len = subject.size();
1544 std::copy(subject.begin(), subject.end(), cert_info->subject);
1545
1546 // Status code.
1547 ipmi_ret_t rc = IPMI_CC_OK;
1548 // make sure to account for the *actual* size of the subject
1549 *data_len = sizeof(*cert_info) - sizeof(cert_info->subject) +
1550 cert_info->subject_len;
1551
1552 return rc;
1553}
1554
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301555#ifdef INTEL_PFR_ENABLED
1556enum class FwGetRootCertDataTag : uint8_t
Vernon Mauery52ce6622019-05-22 09:19:46 -07001557{
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301558 activeRootKey = 1,
1559 recoveryRootKey,
1560 activeCSK,
1561 recoveryCSK,
1562};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001563
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301564static constexpr char *bmcActivePfmMTDDev = "/dev/mtd/pfm";
1565static constexpr char *bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
1566static constexpr size_t pfmBaseOffsetInImage = 0x400;
1567static constexpr size_t rootkeyOffsetInPfm = 0xA0;
1568static constexpr size_t cskKeyOffsetInPfm = 0x124;
1569static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
1570static constexpr size_t certKeyLen = 96;
1571static constexpr size_t cskSignatureLen = 96;
1572
1573ipmi::RspType<std::array<uint8_t, certKeyLen>,
1574 std::optional<std::array<uint8_t, cskSignatureLen>>>
1575 ipmiGetFwRootCertData(uint8_t certId)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001576{
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301577 size_t certKeyOffset = 0;
1578 size_t cskSigOffset = 0;
1579 std::string mtdDev;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001580
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301581 switch (static_cast<FwGetRootCertDataTag>(certId))
1582 {
1583 case FwGetRootCertDataTag::activeRootKey:
1584 {
1585 mtdDev = bmcActivePfmMTDDev;
1586 certKeyOffset = rootkeyOffsetInPfm;
1587 break;
1588 }
1589 case FwGetRootCertDataTag::recoveryRootKey:
1590 {
1591 mtdDev = bmcRecoveryImgMTDDev;
1592 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1593 break;
1594 }
1595 case FwGetRootCertDataTag::activeCSK:
1596 {
1597 mtdDev = bmcActivePfmMTDDev;
1598 certKeyOffset = cskKeyOffsetInPfm;
1599 cskSigOffset = cskSignatureOffsetInPfm;
1600 break;
1601 }
1602 case FwGetRootCertDataTag::recoveryCSK:
1603 {
1604 mtdDev = bmcRecoveryImgMTDDev;
1605 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1606 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1607 break;
1608 }
1609 default:
1610 {
1611 return ipmi::responseInvalidFieldRequest();
1612 }
1613 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001614
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301615 std::array<uint8_t, certKeyLen> certKey = {0};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001616
Vernon Mauery52ce6622019-05-22 09:19:46 -07001617 try
1618 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301619 SPIDev spiDev(mtdDev);
1620 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1621
1622 if (cskSigOffset)
1623 {
1624 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1625 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1626 cskSignature.data());
1627 return ipmi::responseSuccess(certKey, cskSignature);
1628 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001629 }
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301630 catch (const std::exception &e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001631 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301632 phosphor::logging::log<phosphor::logging::level::ERR>(
1633 "Exception caught in ipmiGetFwRootCertData",
1634 phosphor::logging::entry("MSG=%s", e.what()));
1635 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001636 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001637
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301638 return ipmi::responseSuccess(certKey, std::nullopt);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001639}
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301640#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001641
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301642ipmi::RspType<uint32_t>
1643 ipmiFwImageWriteData(const std::vector<uint8_t> &writeData)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001644{
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301645 const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1646 size_t writeDataLen = writeData.size();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001647
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301648 if (!writeDataLen)
1649 {
1650 return ipmi::responseReqDataLenInvalid();
1651 }
1652
Vernon Mauery52ce6622019-05-22 09:19:46 -07001653 if (fw_update_status.state() != fw_update_status_cache::FW_STATE_DOWNLOAD)
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301654 {
1655 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1656 "Invalid firmware update state.");
1657 return ipmi::response(ccCmdNotSupportedInPresentState);
1658 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001659
1660 std::ofstream out(FIRMWARE_BUFFER_FILE,
1661 std::ofstream::binary | std::ofstream::app);
1662 if (!out)
1663 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301664 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1665 "Error while opening file.");
1666 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001667 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301668
1669 uint64_t fileDataLen = out.tellp();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301670
1671 if ((fileDataLen + writeDataLen) > FIRMWARE_BUFFER_MAX_SIZE)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001672 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301673 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1674 "Firmware image size exceeds the limit");
1675 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001676 }
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301677
1678 const char *data = reinterpret_cast<const char *>(writeData.data());
1679 out.write(data, writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001680 out.close();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301681
Vernon Mauery52ce6622019-05-22 09:19:46 -07001682 if (xfer_hash_check)
1683 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301684 xfer_hash_check->hash(writeData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001685 }
1686
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301687#ifdef INTEL_PFR_ENABLED
1688 /* PFR image block 0 - As defined in HAS */
1689 struct PFRImageBlock0
1690 {
1691 uint32_t tag;
1692 uint32_t pcLength;
1693 uint32_t pcType;
1694 uint32_t reserved1;
1695 uint8_t hash256[32];
1696 uint8_t hash384[48];
1697 uint8_t reserved2[32];
1698 } __attribute__((packed));
1699
1700 /* Get the PFR block 0 data and read the uploaded image
1701 * information( Image type, length etc) */
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301702 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1703 (!block0Mapped))
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301704 {
1705 struct PFRImageBlock0 block0Data = {0};
1706
1707 std::ifstream inFile(FIRMWARE_BUFFER_FILE,
1708 std::ios::binary | std::ios::in);
1709 inFile.read(reinterpret_cast<char *>(&block0Data), sizeof(block0Data));
1710 inFile.close();
1711
1712 uint32_t magicNum = block0Data.tag;
1713
1714 /* Validate the magic number */
1715 if (magicNum != perBlock0MagicNum)
1716 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301717 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1718 "PFR image magic number not matched");
1719 return ipmi::responseInvalidFieldRequest();
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301720 }
1721 // Note:imgLength, imgType and block0Mapped are in global scope, as
1722 // these are used in cascaded updates.
1723 imgLength = block0Data.pcLength;
1724 imgType = block0Data.pcType;
1725 block0Mapped = true;
1726 }
1727#endif // end of INTEL_PFR_ENABLED
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301728 return ipmi::responseSuccess(writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001729}
1730
Vernon Mauery52ce6622019-05-22 09:19:46 -07001731struct intc_app_get_buffer_size_resp
1732{
1733 uint8_t kcs_size;
1734 uint8_t ipmb_size;
1735} __attribute__((packed));
1736
1737static constexpr int KCS_MAX_BUFFER_SIZE = 63;
1738static constexpr int IPMB_MAX_BUFFER_SIZE = 128;
1739static ipmi_ret_t ipmi_intel_app_get_buffer_size(
1740 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1741 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1742{
1743 auto msg_reply =
1744 reinterpret_cast<intc_app_get_buffer_size_resp *>(response);
1745 // for now this is hard coded; really this number is dependent on
1746 // the BMC kcs driver as well as the host kcs driver....
1747 // we can't know the latter.
1748 msg_reply->kcs_size = KCS_MAX_BUFFER_SIZE / 4;
1749 msg_reply->ipmb_size = IPMB_MAX_BUFFER_SIZE / 4;
1750 *data_len = sizeof(*msg_reply);
1751
1752 return IPMI_CC_OK;
1753}
1754
1755static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20;
1756static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21;
1757static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22;
1758static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23;
1759static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001760static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26;
1761static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27;
anil kumar appanab57098a2019-05-28 16:22:33 +00001762static constexpr ipmi_cmd_t cmdFirmwareExitFirmwareUpdateMode = 0x28;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001763static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29;
1764static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a;
1765static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001766static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d;
1767static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0;
1768static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0;
1769
1770static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30;
1771static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66;
1772
1773static void register_netfn_firmware_functions()
1774{
1775 // guarantee that we start with an already timed out timestamp
1776 fw_random_number_timestamp =
1777 std::chrono::steady_clock::now() - FW_RANDOM_NUMBER_TTL;
1778
1779 unlink(FIRMWARE_BUFFER_FILE);
1780
1781 // <Get BT Interface Capabilities>
1782 if (DEBUG)
1783 std::cerr << "Registering firmware update commands\n";
1784
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301785#ifdef INTEL_PFR_ENABLED
1786 // Following commands are supported only for PFR enabled platforms
1787 // CMD:0x20 - Get Firmware Version Information
Vernon Mauery52ce6622019-05-22 09:19:46 -07001788
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301789 // get firmware version information
1790 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1791 ipmi::firmware::cmdGetFwVersionInfo,
1792 ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
1793#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001794 // get firmware security version information
1795 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO,
1796 NULL, ipmi_firmware_get_fw_security_revision,
1797 PRIVILEGE_ADMIN);
1798
1799 // get channel information (max transfer sizes)
anil kumar appana159547c2019-05-31 16:08:34 +00001800 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1801 IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO,
1802 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001803
1804 // get bmc execution context
1805 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_BMC_EXEC_CTX, NULL,
1806 ipmi_firmware_get_fw_execution_context,
1807 PRIVILEGE_ADMIN);
1808
1809 // get root certificate information
1810 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO,
1811 NULL, ipmi_firmware_get_root_cert_info,
1812 PRIVILEGE_ADMIN);
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301813#ifdef INTEL_PFR_ENABLED
Vernon Mauery52ce6622019-05-22 09:19:46 -07001814 // get root certificate data
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301815 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1816 ipmi::firmware::cmdFwGetRootCertData,
1817 ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1818#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001819
1820 // generate bmc fw update random number (for enter fw tranfer mode)
1821 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM,
1822 NULL, ipmi_firmware_get_fw_random_number,
1823 PRIVILEGE_ADMIN);
1824
AppaRao Puli4b3e1c72019-10-16 20:53:09 +05301825 // Set Firmware Update Mode(0x27)
1826 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1827 IPMI_CMD_FW_SET_FW_UPDATE_MODE,
1828 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001829
1830 // exit firmware update mode
anil kumar appanab57098a2019-05-28 16:22:33 +00001831 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1832 cmdFirmwareExitFirmwareUpdateMode,
1833 ipmi::Privilege::Admin, ipmiFirmwareExitFwUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001834
1835 // firmware control mechanism (set filename, usb, etc.)
1836 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_UPDATE_CONTROL, NULL,
1837 ipmi_firmware_control, PRIVILEGE_ADMIN);
1838
1839 // get firmware update status
anil kumar appana6c7d9382019-05-31 14:33:13 +00001840 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1841 IPMI_CMD_FW_GET_STATUS, ipmi::Privilege::Admin,
1842 ipmiFrmwareGetStatus);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001843 // set firmware update options (no downgrade, etc.)
1844 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS,
1845 NULL, ipmi_firmware_update_options, PRIVILEGE_ADMIN);
1846
1847 // write image data
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301848 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1849 ipmi::firmware::cmdFwImageWriteData,
1850 ipmi::Privilege::Admin, ipmiFwImageWriteData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001851
Vernon Mauery52ce6622019-05-22 09:19:46 -07001852 // get buffer size is used by fw update (exclusively?)
1853 ipmi_register_callback(NETFUN_INTC_APP, IPMI_CMD_INTC_GET_BUFFER_SIZE, NULL,
1854 ipmi_intel_app_get_buffer_size, PRIVILEGE_USER);
1855 return;
1856}