blob: b4c7608da3ee469bdbf8c912649a1ecb8e3c3119 [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 Reddyc8864062019-10-15 16:52:30 +053038constexpr ipmi::Cmd cmdFwGetRootCertData = 0x25;
AppaRao Puli37fde6b2019-10-25 16:37:50 +053039} // namespace firmware
40} // namespace ipmi
41
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +053042#ifdef INTEL_PFR_ENABLED
43uint32_t imgLength = 0;
44uint32_t imgType = 0;
45bool block0Mapped = false;
46static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
AppaRao Puli37fde6b2019-10-25 16:37:50 +053047
48static constexpr const char *versionIntf =
49 "xyz.openbmc_project.Software.Version";
50
51enum class FWDeviceIDTag : uint8_t
52{
53 bmcActiveImage = 1,
54 bmcRecoveryImage,
55};
56
57const static boost::container::flat_map<FWDeviceIDTag, const char *>
58 fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
59 "/xyz/openbmc_project/software/bmc_active"},
60 {FWDeviceIDTag::bmcRecoveryImage,
61 "/xyz/openbmc_project/software/bmc_recovery"}};
62
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +053063#endif
64
James Feistbeaa2eb2019-08-22 10:59:24 -070065static constexpr const char *secondaryFitImageStartAddr = "22480000";
anil kumar appana31f88872019-08-02 15:16:27 +000066static uint8_t getActiveBootImage(void);
Vernon Mauery52ce6622019-05-22 09:19:46 -070067static void register_netfn_firmware_functions() __attribute__((constructor));
68
69// oem return code for firmware update control
70constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5;
71constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80;
72
Vernon Mauery52ce6622019-05-22 09:19:46 -070073static constexpr bool DEBUG = false;
74
75static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] =
76 "xyz.openbmc_project.fwupdate1.server";
77
78static constexpr char FW_UPDATE_SERVER_PATH[] =
79 "/xyz/openbmc_project/fwupdate1";
80static constexpr char FW_UPDATE_SERVER_INFO_PATH[] =
81 "/xyz/openbmc_project/fwupdate1/info";
82static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] =
83 "/xyz/openbmc_project/fwupdate1/info/bmc_active";
84static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] =
85 "/xyz/openbmc_project/fwupdate1/info/bmc_backup";
86
87static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1";
88static constexpr char FW_UPDATE_INFO_INTERFACE[] =
89 "xyz.openbmc_project.fwupdate1.fwinfo";
90static constexpr char FW_UPDATE_SECURITY_INTERFACE[] =
91 "xyz.openbmc_project.fwupdate1.security";
92
93constexpr std::size_t operator""_MB(unsigned long long v)
94{
95 return 1024u * 1024u * v;
96}
97static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB;
98
99static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin";
100static bool local_download_is_active(void)
101{
102 struct stat sb;
103 if (stat(FIRMWARE_BUFFER_FILE, &sb) < 0)
104 return false;
105 return true;
106}
107
108class fw_update_status_cache
109{
110 public:
111 enum
112 {
113 FW_STATE_INIT = 0,
114 FW_STATE_IDLE,
115 FW_STATE_DOWNLOAD,
116 FW_STATE_VERIFY,
117 FW_STATE_WRITE,
118 FW_STATE_READY,
119 FW_STATE_ERROR = 0x0f,
120 FW_STATE_AC_CYCLE_REQUIRED = 0x83,
121 };
Vernon Mauery52ce6622019-05-22 09:19:46 -0700122 uint8_t state()
123 {
124 if (DEBUG)
125 std::cerr << "fw-state: 0x" << std::hex << (int)_state << '\n';
126 if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
127 local_download_is_active())
128 {
129 _state = FW_STATE_DOWNLOAD;
130 _percent = 0;
131 }
132 return _state;
133 }
134 uint8_t percent()
135 {
136 return _percent;
137 }
138 std::string msg()
139 {
140 return _msg;
141 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700142 std::string get_software_obj_path()
143 {
144 return _software_obj_path;
145 }
146 void set_software_obj_path(std::string &obj_path)
147 {
148 _software_obj_path = obj_path;
149 _state = FW_STATE_WRITE;
150 _percent = 0;
151 _match = std::make_shared<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700152 *_bus,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700153 sdbusplus::bus::match::rules::propertiesChanged(
154 _software_obj_path,
155 "xyz.openbmc_project.Software.ActivationProgress"),
156 [&](sdbusplus::message::message &msg) {
157 if (DEBUG)
158 std::cerr << "propertiesChanged lambda\n";
159 std::map<std::string, ipmi::DbusVariant> props;
160 std::vector<std::string> inval;
161 std::string iface;
162 msg.read(iface, props, inval);
163 _parse_props(props);
164 });
165 }
166 uint8_t activation_timer_timeout()
167 {
168 std::cerr << "activation_timer_timout(): increase percentage...\n";
169 _percent = _percent + 5;
anil kumar appana31f88872019-08-02 15:16:27 +0000170 if (_percent >= 95)
171 {
172 /*changing the state to ready to update firmware utility */
173 _state = FW_STATE_READY;
174 }
175 std::cerr << " _percent = " << (int)_percent << "\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -0700176 return _percent;
177 }
anil kumar appana31f88872019-08-02 15:16:27 +0000178 /* API for changing state to ERROR */
179 void firmwareUpdateAbortState()
180 {
181 unlink(FIRMWARE_BUFFER_FILE);
182 // changing the state to error
183 _state = FW_STATE_ERROR;
184 }
185 void setDeferRestart(bool deferRestart)
186 {
187 _deferRestart = deferRestart;
188 }
189 void setInhibitDowngrade(bool inhibitDowngrade)
190 {
191 _inhibitDowngrade = inhibitDowngrade;
192 }
193 bool getDeferRestart()
194 {
195 return _deferRestart;
196 }
197 bool getInhibitDowngrade()
198 {
199 return _inhibitDowngrade;
200 }
201
Vernon Mauery52ce6622019-05-22 09:19:46 -0700202 protected:
203 void _parse_props(std::map<std::string, ipmi::DbusVariant> &properties)
204 {
205 if (DEBUG)
206 std::cerr << "propertiesChanged (" << properties.size()
207 << " elements)";
208 for (const auto &t : properties)
209 {
210 auto key = t.first;
211 auto value = t.second;
212 if (key == "state")
213 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700214 auto state = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700215 if (DEBUG)
216 std::cerr << ", state=" << state;
217 if (state == "INIT")
218 _state = FW_STATE_INIT;
219 else if (state == "IDLE")
220 _state = FW_STATE_IDLE;
221 else if (state == "DOWNLOAD")
222 _state = FW_STATE_DOWNLOAD;
223 else if (state == "VERIFY")
224 _state = FW_STATE_VERIFY;
225 else if (state == "WRITE")
226 _state = FW_STATE_WRITE;
227 else if (state == "READY")
228 _state = FW_STATE_READY;
229 else if (state == "ERROR")
230 _state = FW_STATE_ERROR;
231 else if (state == "AC_CYCLE_REQUIRED")
232 _state = FW_STATE_AC_CYCLE_REQUIRED;
233 else
234 {
235 _state = FW_STATE_ERROR;
236 _msg = "internal error";
237 }
238 }
239 else if (key == "percent")
240 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700241 _percent = std::get<int32_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700242 if (DEBUG)
243 std::cerr << ", pct=" << (int)_percent;
244 }
245 else if (key == "msg")
246 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700247 _msg = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700248 if (DEBUG)
249 std::cerr << ", msg='" << _msg << '\'';
250 }
251 else if (key == "Progress")
252 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700253 _percent = std::get<uint8_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700254 ;
255 if (_percent == 100)
256 _state = FW_STATE_READY;
257 }
258 }
259 if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
260 local_download_is_active())
261 {
262 _state = FW_STATE_DOWNLOAD;
263 _percent = 0;
264 }
265 if (DEBUG)
266 std::cerr << '\n';
267 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700268
Vernon Mauery15419dd2019-05-24 09:40:30 -0700269 std::shared_ptr<sdbusplus::asio::connection> _bus;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700270 std::shared_ptr<sdbusplus::bus::match::match> _match;
271 uint8_t _state = 0;
272 uint8_t _percent = 0;
anil kumar appana31f88872019-08-02 15:16:27 +0000273 bool _deferRestart = false;
274 bool _inhibitDowngrade = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700275 std::string _msg;
276
277 private:
278 std::string _software_obj_path;
279};
280
281static fw_update_status_cache fw_update_status;
282
283static std::chrono::steady_clock::time_point fw_random_number_timestamp;
284static constexpr int FW_RANDOM_NUMBER_LENGTH = 8;
285static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30);
286static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH];
287
288static ipmi_ret_t ipmi_firmware_get_fw_random_number(
289 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
290 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
291{
292 std::random_device rd;
293 std::default_random_engine gen(rd());
294 std::uniform_int_distribution<> dist{0, 255};
295
296 if (*data_len != 0)
297 {
298 *data_len = 0;
299 return IPMI_CC_REQ_DATA_LEN_INVALID;
300 }
301
302 fw_random_number_timestamp = std::chrono::steady_clock::now();
303
304 uint8_t *msg_reply = static_cast<uint8_t *>(response);
305 for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
306 fw_random_number[i] = msg_reply[i] = dist(gen);
307
308 if (DEBUG)
309 std::cerr << "FW Rand Num: 0x" << std::hex << (int)msg_reply[0] << " 0x"
310 << (int)msg_reply[1] << " 0x" << (int)msg_reply[2] << " 0x"
311 << (int)msg_reply[3] << " 0x" << (int)msg_reply[4] << " 0x"
312 << (int)msg_reply[5] << " 0x" << (int)msg_reply[6] << " 0x"
313 << (int)msg_reply[7] << '\n';
314
315 *data_len = FW_RANDOM_NUMBER_LENGTH;
316
317 return IPMI_CC_OK;
318}
319
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530320/** @brief Set Firmware Update Mode
321 *
322 * This function sets BMC into firmware update mode
323 * after validating Random number obtained from the Get
324 * Firmware Update Random Number command
325 *
326 * @parameter
327 * - randNum - Random number(token)
328 * @returns IPMI completion code
329 **/
330ipmi::RspType<> ipmiSetFirmwareUpdateMode(
331 std::array<uint8_t, FW_RANDOM_NUMBER_LENGTH> &randNum)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700332{
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530333 /* Firmware Update Random number is valid for 30 seconds only */
334 auto timeElapsed =
335 (std::chrono::steady_clock::now() - fw_random_number_timestamp);
336 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700337 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
338 FW_RANDOM_NUMBER_TTL)
339 .count())
340 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530341 phosphor::logging::log<phosphor::logging::level::INFO>(
342 "Firmware update random number expired.");
343 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700344 }
345
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530346 /* Validate random number */
Vernon Mauery52ce6622019-05-22 09:19:46 -0700347 for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
348 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530349 if (fw_random_number[i] != randNum[i])
Vernon Mauery52ce6622019-05-22 09:19:46 -0700350 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530351 phosphor::logging::log<phosphor::logging::level::INFO>(
352 "Invalid random number specified.");
353 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700354 }
355 }
356
357 if (fw_update_status.state() != fw_update_status_cache::FW_STATE_IDLE
358 // TODO: Allowing FW_STATE_INIT here to let image activation available
359 // without being in FW_STATE_IDLE, need to fix/adjust the state machine
360 // to match xyz.openbmc_project.Software.BMC.Updater service activation
361 // mechanism at finer grain
362 && fw_update_status.state() != fw_update_status_cache::FW_STATE_INIT)
363 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530364 phosphor::logging::log<phosphor::logging::level::INFO>(
365 "Already firmware update is in progress.");
366 return ipmi::responseBusy();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700367 }
368 // FIXME? c++ doesn't off an option for exclusive file creation
369 FILE *fp = fopen(FIRMWARE_BUFFER_FILE, "wx");
370 if (!fp)
371 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530372 phosphor::logging::log<phosphor::logging::level::INFO>(
373 "Unable to open file.");
374 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700375 }
376 fclose(fp);
377
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530378 return ipmi::responseSuccess();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700379}
380
anil kumar appanab57098a2019-05-28 16:22:33 +0000381/** @brief implements exit firmware update mode command
382 * @param None
383 *
384 * @returns IPMI completion code
385 */
386ipmi::RspType<> ipmiFirmwareExitFwUpdateMode()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700387{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700388
anil kumar appanab57098a2019-05-28 16:22:33 +0000389 if (DEBUG)
390 {
391 std::cerr << "Exit FW update mode \n";
392 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700393 switch (fw_update_status.state())
394 {
395 case fw_update_status_cache::FW_STATE_INIT:
396 case fw_update_status_cache::FW_STATE_IDLE:
anil kumar appanab57098a2019-05-28 16:22:33 +0000397 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700398 break;
399 case fw_update_status_cache::FW_STATE_DOWNLOAD:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700400 case fw_update_status_cache::FW_STATE_VERIFY:
401 break;
402 case fw_update_status_cache::FW_STATE_WRITE:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700403 break;
404 case fw_update_status_cache::FW_STATE_READY:
405 case fw_update_status_cache::FW_STATE_ERROR:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700406 break;
407 case fw_update_status_cache::FW_STATE_AC_CYCLE_REQUIRED:
anil kumar appanab57098a2019-05-28 16:22:33 +0000408 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700409 break;
410 }
anil kumar appanab57098a2019-05-28 16:22:33 +0000411 fw_update_status.firmwareUpdateAbortState();
412 return ipmi::responseSuccess();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700413}
414
415static void post_transfer_complete_handler(
416 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher);
417static bool request_start_firmware_update(const std::string &uri)
418{
419 if (DEBUG)
420 std::cerr << "request start firmware update()\n";
421
Vernon Mauery52ce6622019-05-22 09:19:46 -0700422 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
423 // the code gets to this point, the file should be transferred start the
424 // request (creating a new file in /tmp/images causes the update manager to
425 // check if it is ready for activation)
426 static std::unique_ptr<sdbusplus::bus::match::match> fw_update_matcher;
427 post_transfer_complete_handler(fw_update_matcher);
428 std::filesystem::rename(
429 uri, "/tmp/images/" +
430 boost::uuids::to_string(boost::uuids::random_generator()()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700431 return true;
432}
433
434class transfer_hash_check
435{
436 public:
437 enum hash_check
438 {
439 CHECK_NOT_REQUESTED = 0,
440 CHECK_REQUESTED,
441 CHECK_PASSED_SHA2,
442 CHECK_RESVD1,
443 CHECK_FAILED_SHA2 = 0xe2,
444 CHECK_RESVD2 = 0xe3,
445 };
446
447 protected:
448 EVP_MD_CTX *_ctx;
449 std::vector<uint8_t> _expected;
450 enum hash_check _check;
451 bool _started;
452
453 public:
454 transfer_hash_check() : _check(CHECK_NOT_REQUESTED), _started(false)
455 {
456 }
457 ~transfer_hash_check()
458 {
459 if (_ctx)
460 {
461 EVP_MD_CTX_destroy(_ctx);
462 _ctx = NULL;
463 }
464 }
465 void init(const std::vector<uint8_t> &expected)
466 {
467 _expected = expected;
468 _check = CHECK_REQUESTED;
469 _ctx = EVP_MD_CTX_create();
470 EVP_DigestInit(_ctx, EVP_sha256());
471 }
472 void hash(const std::vector<uint8_t> &data)
473 {
474 if (!_started)
475 _started = true;
476 EVP_DigestUpdate(_ctx, data.data(), data.size());
477 }
478 void clear()
479 {
480 // if not started, nothing to clear
481 if (_started)
482 {
483 if (_ctx)
484 EVP_MD_CTX_destroy(_ctx);
485 if (_check != CHECK_NOT_REQUESTED)
486 _check = CHECK_REQUESTED;
487 _ctx = EVP_MD_CTX_create();
488 EVP_DigestInit(_ctx, EVP_sha256());
489 }
490 }
491 enum hash_check check()
492 {
493 if (_check == CHECK_REQUESTED)
494 {
495 unsigned int len;
496 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
497 EVP_DigestFinal(_ctx, digest.data(), &len);
498 if (digest == _expected)
499 {
500 if (DEBUG)
501 std::cerr << "transfer sha2 check passed\n";
502 _check = CHECK_PASSED_SHA2;
503 }
504 else
505 {
506 if (DEBUG)
507 std::cerr << "transfer sha2 check failed\n";
508 _check = CHECK_FAILED_SHA2;
509 }
510 }
511 return _check;
512 }
513 uint8_t status() const
514 {
515 return static_cast<uint8_t>(_check);
516 }
517};
518
519std::shared_ptr<transfer_hash_check> xfer_hash_check;
520
Vernon Mauery52ce6622019-05-22 09:19:46 -0700521static void activate_image(const char *obj_path)
522{
anil kumar appana31f88872019-08-02 15:16:27 +0000523 // If flag is false means to reboot
524 if (fw_update_status.getDeferRestart() == false)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700525 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700526
anil kumar appana31f88872019-08-02 15:16:27 +0000527 if (DEBUG)
528 {
529 std::cerr << "activateImage()...\n";
530 std::cerr << "obj_path = " << obj_path << "\n";
531 }
532 phosphor::logging::log<phosphor::logging::level::INFO>(
533 "activating Image: ",
534 phosphor::logging::entry("OBJPATH =%s", obj_path));
535 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
536 bus->async_method_call(
537 [](const boost::system::error_code ec) {
538 if (ec)
539 {
540 phosphor::logging::log<phosphor::logging::level::ERR>(
541 "async_method_call error: activate_image failed");
542 return;
543 }
544 },
545 "xyz.openbmc_project.Software.BMC.Updater", obj_path,
546 "org.freedesktop.DBus.Properties", "Set",
547 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
548 std::variant<std::string>("xyz.openbmc_project.Software.Activation."
549 "RequestedActivations.Active"));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700550 }
anil kumar appana31f88872019-08-02 15:16:27 +0000551 else
Vernon Mauery52ce6622019-05-22 09:19:46 -0700552 {
anil kumar appana31f88872019-08-02 15:16:27 +0000553 phosphor::logging::log<phosphor::logging::level::INFO>(
554 "Firmware image activation is deferred.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700555 }
556}
557
558static void post_transfer_complete_handler(
559 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher)
560{
561 // Setup timer for watching signal
562 static phosphor::Timer timer(
563 [&fw_update_matcher]() { fw_update_matcher = nullptr; });
564
565 static phosphor::Timer activation_status_timer([]() {
566 if (fw_update_status.activation_timer_timeout() >= 95)
567 {
568 activation_status_timer.stop();
569 }
570 });
571
572 timer.start(std::chrono::microseconds(5000000), false);
573
574 // callback function for capturing signal
575 auto callback = [&fw_update_matcher](sdbusplus::message::message &m) {
576 if (DEBUG)
577 std::cerr << "[complete] Match fired\n";
578 bool flag = false;
579
580 std::vector<std::pair<
581 std::string,
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700582 std::vector<std::pair<std::string, std::variant<std::string>>>>>
Vernon Mauery52ce6622019-05-22 09:19:46 -0700583 interfaces_properties;
584
585 sdbusplus::message::object_path obj_path;
586
587 try
588 {
589 m.read(obj_path, interfaces_properties); // Read in the object path
590 // that was just created
591 }
592 catch (std::exception &e)
593 {
594 std::cerr
595 << "[complete] Failed at post_transfer_complete-handler : "
596 << e.what() << "\n";
597 }
598 // constructing response message
599 if (DEBUG)
600 std::cerr << "[complete] obj path = " << obj_path.str << "\n";
601 for (auto &interface : interfaces_properties)
602 {
603 if (DEBUG)
604 std::cerr << "[complete] interface = " << interface.first
605 << "\n";
606
607 if (interface.first == "xyz.openbmc_project.Software.Activation")
608 {
609 // cancel timer only when
610 // xyz.openbmc_project.Software.Activation interface is
611 // added
612
613 if (DEBUG)
614 std::cerr << "[complete] Attempt to cancel timer...\n";
615 try
616 {
617 timer.stop();
618 activation_status_timer.start(
619 std::chrono::microseconds(3000000), true);
620 }
621 catch (std::exception &e)
622 {
623 std::cerr << "[complete] cancel timer error: " << e.what()
624 << "\n";
625 }
626
627 fw_update_status.set_software_obj_path(obj_path.str);
628 activate_image(obj_path.str.c_str());
629 if (DEBUG)
630 std::cerr << "[complete] returned from activeImage()\n";
631
632 fw_update_matcher = nullptr;
633 }
634 }
635 };
636
637 // Adding matcher
638 fw_update_matcher = std::make_unique<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700639 *getSdBus(),
Vernon Mauery52ce6622019-05-22 09:19:46 -0700640 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
641 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
642 callback);
643}
Vernon Mauery52ce6622019-05-22 09:19:46 -0700644
645class MappedFile
646{
647 public:
648 MappedFile(const std::string &fname) : addr(nullptr), fsize(0)
649 {
650 std::error_code ec;
651 size_t sz = std::filesystem::file_size(fname, ec);
652 int fd = open(fname.c_str(), O_RDONLY);
653 if (!ec || fd < 0)
654 {
655 return;
656 }
657 void *tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
658 close(fd);
659 if (tmp == MAP_FAILED)
660 {
661 return;
662 }
663 addr = tmp;
664 fsize = sz;
665 }
666
667 ~MappedFile()
668 {
669 if (addr)
670 {
671 munmap(addr, fsize);
672 }
673 }
674 const uint8_t *data() const
675 {
676 return static_cast<const uint8_t *>(addr);
677 }
678 size_t size() const
679 {
680 return fsize;
681 }
682
683 private:
684 size_t fsize;
685 void *addr;
686};
687
688static int transfer_from_file(const std::string &uri, bool move = true)
689{
690 std::error_code ec;
691 if (DEBUG)
692 std::cerr << "transfer_from_file(" << uri << ")\n";
693 if (move)
694 {
695 std::filesystem::rename(uri, FIRMWARE_BUFFER_FILE, ec);
696 }
697 else
698 {
699 std::filesystem::copy(uri, FIRMWARE_BUFFER_FILE,
700 std::filesystem::copy_options::overwrite_existing,
701 ec);
702 }
703 if (xfer_hash_check)
704 {
705 MappedFile mappedfw(uri);
706 xfer_hash_check->hash(
707 {mappedfw.data(), mappedfw.data() + mappedfw.size()});
708 }
709 if (ec.value())
710 {
711 std::cerr << "cp/mv returns: " << ec.message() << "(" << ec.value()
712 << ")\n";
713 }
714 return ec.value();
715}
716
717template <typename... ArgTypes>
718static int executeCmd(const char *path, ArgTypes &&... tArgs)
719{
720 boost::process::child execProg(path, const_cast<char *>(tArgs)...);
721 execProg.wait();
722 return execProg.exit_code();
723}
724
725constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl";
726constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt";
727constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img";
728constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev";
729constexpr size_t fwPathMaxLength = 255;
730static int transfer_from_usb(const std::string &uri)
731{
732 int ret, sysret;
733 char fwpath[fwPathMaxLength];
734 if (DEBUG)
735 std::cerr << "transfer_from_usb(" << uri << ")\n";
736 ret = executeCmd(USB_CTRL_PATH, "mount", FWUPDATE_USB_VOL_IMG,
737 FWUPDATE_MOUNT_POINT);
738 if (ret)
739 {
740 return ret;
741 }
742
743 std::string usb_path = std::string(FWUPDATE_MOUNT_POINT) + "/" + uri;
744 ret = transfer_from_file(usb_path, false);
745
746 executeCmd(USB_CTRL_PATH, "cleanup", FWUPDATE_USB_VOL_IMG,
747 FWUPDATE_MOUNT_POINT);
748 return ret;
749}
750
751static bool transfer_firmware_from_uri(const std::string &uri)
752{
753 static constexpr char FW_URI_FILE[] = "file://";
754 static constexpr char FW_URI_USB[] = "usb://";
755 if (DEBUG)
756 std::cerr << "transfer_firmware_from_uri(" << uri << ")\n";
757 if (boost::algorithm::starts_with(uri, FW_URI_FILE))
758 {
759 std::string fname = uri.substr(sizeof(FW_URI_FILE) - 1);
760 if (fname != FIRMWARE_BUFFER_FILE)
761 {
762 return 0 == transfer_from_file(fname);
763 }
764 return true;
765 }
766 if (boost::algorithm::starts_with(uri, FW_URI_USB))
767 {
768 std::string fname = uri.substr(sizeof(FW_URI_USB) - 1);
769 return 0 == transfer_from_usb(fname);
770 }
771 return false;
772}
773
774/* Get USB-mass-storage device status: inserted => true, ejected => false */
775static int usb_get_status()
776{
777 static constexpr char usb_gadget_base[] = "/sys/kernel/config/usb_gadget/";
778 auto usb_device =
779 std::filesystem::path(usb_gadget_base) / FWUPDATE_USB_DEV_NAME;
780 std::error_code ec;
781 return std::filesystem::exists(usb_device, ec) && !ec;
782}
783
784/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
785static int usb_attach_device()
786{
787 if (usb_get_status())
788 {
789 return 1;
790 }
791 int ret =
792 executeCmd(USB_CTRL_PATH, "setup", FWUPDATE_USB_VOL_IMG,
793 std::to_string(FIRMWARE_BUFFER_MAX_SIZE / 1_MB).c_str());
794 if (!ret)
795 {
796 ret = executeCmd(USB_CTRL_PATH, "insert", FWUPDATE_USB_DEV_NAME,
797 FWUPDATE_USB_VOL_IMG);
798 }
799 return ret;
800}
801
802/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
803static int usb_detach_device()
804{
805 if (!usb_get_status())
806 {
807 return 1;
808 }
809 return executeCmd(USB_CTRL_PATH, "eject", FWUPDATE_USB_DEV_NAME);
810}
811
812constexpr uint8_t controls_init = 0x00;
813constexpr uint8_t controls_transfer_started = 0x01;
814constexpr uint8_t controls_transfer_completed = 0x02;
815constexpr uint8_t controls_transfer_aborted = 0x04;
816constexpr uint8_t controls_usb_attached = 0x08;
817
818struct fw_update_control_request
819{
820 enum knob
821 {
822 CTRL_GET = 0,
823 CTRL_XFER_START,
824 CTRL_XFER_COMPLETE,
825 CTRL_XFER_ABORT,
826 CTRL_SET_FILENAME,
827 CTRL_USB_ATTACH,
828 CTRL_USB_DETACH,
829 } __attribute__((packed));
830 enum knob control;
831 uint8_t nlen;
832 char filename[fwPathMaxLength];
833} __attribute__((packed));
834
835static ipmi_ret_t ipmi_firmware_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
836 ipmi_request_t request,
837 ipmi_response_t response,
838 ipmi_data_len_t data_len,
839 ipmi_context_t context)
840{
841 static std::string fw_xfer_uri;
842
843 if (DEBUG)
844 std::cerr << "FW update control\n";
845 *data_len = 0;
846
847 static uint8_t controls = controls_init;
848 ipmi_ret_t rc = IPMI_CC_OK;
849 auto ctrl_req = reinterpret_cast<fw_update_control_request *>(request);
850 auto ctrl_resp = reinterpret_cast<uint8_t *>(response);
851
852 if (usb_get_status())
853 {
854 controls |= controls_usb_attached;
855 }
856 else
857 {
858 controls &= ~controls_usb_attached;
859 }
860
861 switch (ctrl_req->control)
862 {
863 case fw_update_control_request::CTRL_GET:
864 break;
865 case fw_update_control_request::CTRL_XFER_START:
866 {
867 controls |= controls_transfer_started;
868 // reset buffer to empty (truncate file)
869 std::ofstream out(FIRMWARE_BUFFER_FILE,
870 std::ofstream::binary | std::ofstream::trunc);
871 fw_xfer_uri = std::string("file://") + FIRMWARE_BUFFER_FILE;
872 if (xfer_hash_check)
873 {
874 xfer_hash_check->clear();
875 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +0530876#ifdef INTEL_PFR_ENABLED
877 imgLength = 0;
878 imgType = 0;
879 block0Mapped = false;
880#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -0700881 if (DEBUG)
882 std::cerr << "transfer start\n";
883 }
884 break;
885 case fw_update_control_request::CTRL_XFER_COMPLETE:
886 {
887 if (usb_get_status())
888 {
889 rc = IPMI_CC_REQ_INVALID_PHASE;
890 }
891 // finish transfer based on URI
892 if (!transfer_firmware_from_uri(fw_xfer_uri))
893 {
894 rc = IPMI_CC_UNSPECIFIED_ERROR;
895 break;
896 }
897 // transfer complete
898 if (xfer_hash_check)
899 {
900 if (transfer_hash_check::CHECK_PASSED_SHA2 !=
901 xfer_hash_check->check())
902 {
903 if (DEBUG)
904 std::cerr << "xfer_hash_check returns not "
905 "CHECK_PASSED_SHA2\n";
906 rc = IPMI_CC_UNSPECIFIED_ERROR;
907 break;
908 }
909 }
910 // start the request
911 if (!request_start_firmware_update(FIRMWARE_BUFFER_FILE))
912 {
913 if (DEBUG)
914 std::cerr
915 << "request_start_firmware_update returns failure\n";
916 rc = IPMI_CC_UNSPECIFIED_ERROR;
917 }
918 if (rc == IPMI_CC_OK)
919 {
920 controls |= controls_transfer_completed;
921 }
922 }
923 break;
924 case fw_update_control_request::CTRL_XFER_ABORT:
925 if (DEBUG)
926 std::cerr << "send abort request\n";
927 if (usb_get_status())
928 {
929 if (0 != usb_detach_device())
930 {
931 rc = IPMI_CC_USB_ATTACH_FAIL;
932 }
933 }
anil kumar appana31f88872019-08-02 15:16:27 +0000934 fw_update_status.firmwareUpdateAbortState();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700935 controls |= controls_transfer_aborted;
936 break;
937 case fw_update_control_request::CTRL_SET_FILENAME:
938 fw_xfer_uri.clear();
939 fw_xfer_uri.insert(0, ctrl_req->filename, ctrl_req->nlen);
940 break;
941 case fw_update_control_request::CTRL_USB_ATTACH:
942 if (usb_get_status())
943 {
944 rc = IPMI_CC_INVALID_FIELD_REQUEST;
945 }
946 else if (0 != usb_attach_device())
947 {
948 rc = IPMI_CC_USB_ATTACH_FAIL;
949 }
950 else
951 {
952 rc = IPMI_CC_OK;
953 }
954 break;
955 case fw_update_control_request::CTRL_USB_DETACH:
956 if (!usb_get_status())
957 {
958 rc = IPMI_CC_INVALID_FIELD_REQUEST;
959 }
960 if (0 != usb_detach_device())
961 {
962 rc = IPMI_CC_USB_ATTACH_FAIL;
963 }
964 else
965 {
966 rc = IPMI_CC_OK;
967 }
968 break;
969 default:
970 if (DEBUG)
971 std::cerr << "control byte " << std::hex << ctrl_req->control
972 << " unknown\n";
973 rc = IPMI_CC_INVALID_FIELD_REQUEST;
974 break;
975 }
976
977 if (rc == IPMI_CC_OK)
978 {
979 *ctrl_resp = controls;
980 *data_len = sizeof(*ctrl_resp);
981 }
982
983 return rc;
984}
985
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530986#ifdef INTEL_PFR_ENABLED
987using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
988 uint8_t, // Major Version Number
989 uint8_t, // Minor Version Number
990 uint32_t, // Build Number
991 uint32_t, // Build Timestamp
992 uint32_t>; // Update Timestamp
993ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700994{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700995 // Byte 1 - Count (N) Number of devices data is being returned for.
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530996 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700997 // Bytes - 17:(15xN) - Repeat of 2 through 16
998
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530999 std::vector<fwVersionInfoType> fwVerInfoList;
1000 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
1001 for (const auto &fwDev : fwVersionIdMap)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001002 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301003 std::string verStr;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001004 try
1005 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301006 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001007
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301008 ipmi::Value result = ipmi::getDbusProperty(
1009 *busp, service, fwDev.second, versionIntf, "Version");
1010 verStr = std::get<std::string>(result);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001011 }
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301012 catch (const std::exception &e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001013 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301014 phosphor::logging::log<phosphor::logging::level::INFO>(
1015 "Failed to fetch Version property",
1016 phosphor::logging::entry("ERROR=%s", e.what()),
1017 phosphor::logging::entry("PATH=%s", fwDev.second),
1018 phosphor::logging::entry("INTERFACE=%s", versionIntf));
1019 continue;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001020 }
1021
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301022 if (verStr.empty())
1023 {
1024 phosphor::logging::log<phosphor::logging::level::INFO>(
1025 "Version is empty.",
1026 phosphor::logging::entry("PATH=%s", fwDev.second),
1027 phosphor::logging::entry("INTERFACE=%s", versionIntf));
1028 continue;
1029 }
1030
1031 // BMC Version format: <major>.<minor>-<build bum>-<build hash>
1032 std::vector<std::string> splitVer;
1033 boost::split(splitVer, verStr, boost::is_any_of(".-"));
1034 if (splitVer.size() < 3)
1035 {
1036 phosphor::logging::log<phosphor::logging::level::INFO>(
1037 "Invalid Version format.",
1038 phosphor::logging::entry("Version=%s", verStr.c_str()),
1039 phosphor::logging::entry("PATH=%s", fwDev.second));
1040 continue;
1041 }
1042
1043 uint8_t majorNum = 0;
1044 uint8_t minorNum = 0;
1045 uint32_t buildNum = 0;
1046 try
1047 {
1048 majorNum = std::stoul(splitVer[0], nullptr, 16);
1049 minorNum = std::stoul(splitVer[1], nullptr, 16);
1050 buildNum = std::stoul(splitVer[2], nullptr, 16);
1051 }
1052 catch (const std::exception &e)
1053 {
1054 phosphor::logging::log<phosphor::logging::level::INFO>(
1055 "Failed to convert stoul.",
1056 phosphor::logging::entry("ERROR=%s", e.what()));
1057 continue;
1058 }
1059
1060 // Build Timestamp - Not supported.
1061 // Update Timestamp - TODO: Need to check with CPLD team.
1062 fwVerInfoList.emplace_back(
1063 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
1064 minorNum, buildNum, 0, 0));
Vernon Mauery52ce6622019-05-22 09:19:46 -07001065 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001066
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301067 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001068}
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301069#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001070
1071struct fw_security_revision_info
1072{
1073 uint8_t id_tag;
1074 uint16_t sec_rev;
1075} __attribute__((packed));
1076
1077static ipmi_ret_t ipmi_firmware_get_fw_security_revision(
1078 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1079 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1080{
1081 if (DEBUG)
1082 std::cerr << "Get FW security revision info\n";
1083
1084 // Byte 1 - Count (N) Number of devices data is being returned for.
1085 // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image
1086 // 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR
1087 // Image
1088 // Byte 3 - Major Version Number
1089 // Byte 4 - Minor Version Number
1090 // Bytes 5:8 - Build Number
1091 // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL
1092 // timestamp
1093 // Bytes 13:16 - Update Timestamp
1094 // Bytes - 17:(15xN) - Repeat of 2 through 16
1095
1096 uint8_t count = 0;
1097 auto ret_count = reinterpret_cast<uint8_t *>(response);
1098 auto info =
1099 reinterpret_cast<struct fw_security_revision_info *>(ret_count + 1);
1100
Vernon Mauery15419dd2019-05-24 09:40:30 -07001101 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001102 for (uint8_t id_tag = 1; id_tag < 6; id_tag++)
1103 {
1104 const char *fw_path;
1105 switch (id_tag)
1106 {
1107 case 1:
1108 fw_path = FW_UPDATE_ACTIVE_INFO_PATH;
1109 break;
1110 case 2:
1111 fw_path = FW_UPDATE_BACKUP_INFO_PATH;
1112 break;
1113 case 3:
1114 case 4:
1115 case 5:
1116 continue; // skip for now
1117 break;
1118 }
1119 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001120 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001121 "org.freedesktop.DBus.Properties", "GetAll");
1122 method.append(FW_UPDATE_INFO_INTERFACE, "security_version");
1123 ipmi::DbusVariant sec_rev;
1124 try
1125 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001126 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001127
1128 if (reply.is_method_error())
1129 continue;
1130
1131 reply.read(sec_rev);
1132 }
1133 catch (sdbusplus::exception::SdBusError &e)
1134 {
1135 std::cerr << "SDBus Error: " << e.what();
1136 return IPMI_CC_UNSPECIFIED_ERROR;
1137 }
1138
1139 info->id_tag = id_tag;
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001140 info->sec_rev = std::get<int>(sec_rev);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001141 count++;
1142 info++;
1143 }
1144 *ret_count = count;
1145
1146 // Status code.
1147 ipmi_ret_t rc = IPMI_CC_OK;
1148 *data_len = sizeof(count) + count * sizeof(*info);
1149
1150 return rc;
1151}
1152
1153struct fw_channel_size
1154{
1155 uint8_t channel_id;
1156 uint32_t channel_size;
1157} __attribute__((packed));
1158
1159enum
1160{
1161 CHANNEL_RESVD = 0,
1162 CHANNEL_KCS,
1163 CHANNEL_RMCP_PLUS,
1164 CHANNEL_USB_DATA,
1165 CHANNEL_USB_MASS_STORAGE,
1166} channel_transfer_type;
1167
anil kumar appana159547c2019-05-31 16:08:34 +00001168static constexpr uint8_t channelListSize = 2;
1169/** @brief implements Maximum Firmware Transfer size command
1170 * @parameter
1171 * - none
1172 * @returns IPMI completion code plus response data
1173 * - count - channel count
1174 * - channelList - channel list information
1175 */
1176ipmi::RspType<uint8_t, // channel count
1177 std::array<std::tuple<uint8_t, uint32_t>,
1178 channelListSize> // channel
1179 // list
1180 >
1181 ipmiFirmwareMaxTransferSize()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001182{
anil kumar appana159547c2019-05-31 16:08:34 +00001183 constexpr uint8_t KCSMaxBufSize = 128;
1184 constexpr uint32_t RMCPPLUSMaxBufSize = 50 * 1024;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001185 if (DEBUG)
1186 std::cerr << "Get FW max transfer size\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -07001187 // Byte 1 - Count (N) Number of devices data is being returned for.
1188 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+,
1189 // 03 – usb data, 04 – usb mass storage
1190 // Byte 3-6 - transfer size (little endian)
1191 // Bytes - 7:(5xN) - Repeat of 2 through 6
anil kumar appana159547c2019-05-31 16:08:34 +00001192 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1193 channelList = {{{CHANNEL_KCS, KCSMaxBufSize},
1194 {CHANNEL_RMCP_PLUS, RMCPPLUSMaxBufSize}}};
1195 return ipmi::responseSuccess(channelListSize, channelList);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001196}
1197
1198enum
1199{
1200 EXEC_CTX_RESVD = 0,
1201 EXEC_CTX_FULL_LINUX = 0x10,
1202 EXEC_CTX_SAFE_MODE_LINUX = 0x11,
1203} bmc_execution_context;
1204
1205struct fw_execution_context
1206{
1207 uint8_t context;
1208 uint8_t image_selection;
1209} __attribute__((packed));
1210
1211static ipmi_ret_t ipmi_firmware_get_fw_execution_context(
1212 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1213 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1214{
1215 if (DEBUG)
1216 std::cerr << "Get FW execution context\n";
1217
1218 // Byte 1 - execution context
1219 // 0x10 - full linux stack, 0x11 - safe-mode linux stack
1220 // Byte 2 - current image selection
1221 // 1 - primary, 2 - secondary
1222
1223 auto info = reinterpret_cast<struct fw_execution_context *>(response);
anil kumar appana31f88872019-08-02 15:16:27 +00001224 info->context = EXEC_CTX_FULL_LINUX;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001225
anil kumar appana31f88872019-08-02 15:16:27 +00001226 info->image_selection = getActiveBootImage();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001227
1228 // Status code.
1229 ipmi_ret_t rc = IPMI_CC_OK;
1230 *data_len = sizeof(*info);
1231
1232 return rc;
1233}
1234
anil kumar appana31f88872019-08-02 15:16:27 +00001235uint8_t getActiveBootImage(void)
1236{
1237 // 0x01 - primaryImage
1238 constexpr uint8_t primaryImage = 0x01;
1239 // 0x02 - secondaryImage
1240 constexpr uint8_t secondaryImage = 0x02;
1241 uint8_t bootImage = primaryImage;
1242
1243 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1244 auto method = bus->new_method_call(
1245 "xyz.openbmc_project.U_Boot.Environment.Manager",
1246 "/xyz/openbmc_project/u_boot/environment/mgr",
1247 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read");
1248 method.append("bootcmd");
1249 std::string value;
1250 try
1251 {
1252 auto reply = bus->call(method);
1253 reply.read(value);
1254 }
1255 catch (sdbusplus::exception::SdBusError &e)
1256 {
1257 std::cerr << "SDBus Error: " << e.what();
1258 return IPMI_CC_UNSPECIFIED_ERROR;
1259 }
1260 /* cheking for secondary FitImage Address 22480000 */
1261 if (value.find(secondaryFitImageStartAddr) != std::string::npos)
1262 {
1263 bootImage = secondaryImage;
1264 }
1265 else
1266 {
1267 bootImage = primaryImage;
1268 }
1269
1270 return bootImage;
1271}
anil kumar appana6c7d9382019-05-31 14:33:13 +00001272/** @brief implements firmware get status command
1273 * @parameter
1274 * - none
1275 * @returns IPMI completion code plus response data
1276 * - status - processing status
1277 * - percentage - percentage completion
1278 * - check - channel integrity check status
1279 **/
1280ipmi::RspType<uint8_t, // status
1281 uint8_t, // percentage
1282 uint8_t // check
1283 >
1284 ipmiFrmwareGetStatus()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001285
Vernon Mauery52ce6622019-05-22 09:19:46 -07001286{
1287 if (DEBUG)
1288 std::cerr << "Get FW update status\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -07001289 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1290 // 5=ready, f=error, 83=ac cycle required)
1291 // Byte 2 - percent
1292 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
anil kumar appana6c7d9382019-05-31 14:33:13 +00001293 uint8_t status = fw_update_status.state();
1294 uint8_t percent = fw_update_status.percent();
1295 uint8_t check = xfer_hash_check ? xfer_hash_check->status() : 0;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001296
1297 // Status code.
anil kumar appana6c7d9382019-05-31 14:33:13 +00001298 return ipmi::responseSuccess(status, percent, check);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001299}
1300
1301static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0);
1302static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1);
1303static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2);
1304static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3);
1305struct fw_update_options_request
1306{
1307 uint8_t mask;
1308 uint8_t options;
1309} __attribute__((packed));
1310
Vernon Mauery52ce6622019-05-22 09:19:46 -07001311uint32_t fw_update_options = 0;
1312static ipmi_ret_t ipmi_firmware_update_options(
1313 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1314 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1315{
1316 if (DEBUG)
1317 std::cerr << "Get/set FW update options\n";
1318
1319 // request:
1320 // Byte 1 - mask
1321 // Byte 2 - options
1322 // Byte 3-34 - optional integrity check expected value
1323 // response:
1324 // Byte 1 - set options
1325
1326 auto fw_options =
1327 reinterpret_cast<struct fw_update_options_request *>(request);
1328
1329 const char *path = FW_UPDATE_SERVER_INFO_PATH;
1330 const char *iface = FW_UPDATE_SECURITY_INTERFACE;
1331 if ((fw_options->mask & FW_UPDATE_OPTIONS_NO_DOWNREV) &&
1332 (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) !=
1333 (fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV))
1334 {
1335 if (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV)
1336 {
1337 fw_update_options |= FW_UPDATE_OPTIONS_NO_DOWNREV;
anil kumar appana31f88872019-08-02 15:16:27 +00001338 /*setting flag to flase for deferring downgrade support*/
1339 fw_update_status.setInhibitDowngrade(true);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001340 }
1341 else
1342 {
1343 fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV;
anil kumar appana31f88872019-08-02 15:16:27 +00001344 /*setting flag to true for downgrade support*/
1345 fw_update_status.setInhibitDowngrade(false);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001346 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001347 }
1348 if ((fw_options->mask & FW_UPDATE_OPTIONS_DEFER_RESTART) &&
1349 (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) !=
1350 (fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART))
1351 {
1352 if (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART)
1353 {
1354 fw_update_options |= FW_UPDATE_OPTIONS_DEFER_RESTART;
anil kumar appana31f88872019-08-02 15:16:27 +00001355 /* setting flag to true to stop image activation */
1356 fw_update_status.setDeferRestart(true);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001357 }
1358 else
1359 {
anil kumar appana31f88872019-08-02 15:16:27 +00001360 /* setting flag to false for image activation */
Vernon Mauery52ce6622019-05-22 09:19:46 -07001361 fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART;
anil kumar appana31f88872019-08-02 15:16:27 +00001362 fw_update_status.setDeferRestart(false);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001363 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001364 }
1365 if (fw_options->mask & FW_UPDATE_OPTIONS_SHA2_CHECK)
1366 {
1367 auto hash_size = EVP_MD_size(EVP_sha256());
1368 if (fw_options->options & FW_UPDATE_OPTIONS_SHA2_CHECK)
1369 {
1370 if (*data_len != (sizeof(*fw_options) + hash_size))
1371 {
1372 *data_len = 0;
1373 return IPMI_CC_REQ_DATA_LEN_INVALID;
1374 }
1375 xfer_hash_check = std::make_shared<transfer_hash_check>();
1376 auto exp_hash = reinterpret_cast<uint8_t *>(fw_options + 1);
1377 xfer_hash_check->init({exp_hash, exp_hash + hash_size});
1378 fw_update_options |= FW_UPDATE_OPTIONS_SHA2_CHECK;
1379 }
1380 else
1381 {
1382 fw_update_options &= ~FW_UPDATE_OPTIONS_SHA2_CHECK;
1383 // delete the xfer_hash_check object
1384 xfer_hash_check.reset();
1385 }
1386 }
1387 auto options_rsp = reinterpret_cast<uint8_t *>(response);
1388 *options_rsp = fw_update_options;
1389
1390 if (DEBUG)
1391 std::cerr << "current fw_update_options = " << std::hex
1392 << fw_update_options << '\n';
1393 // Status code.
1394 *data_len = sizeof(*options_rsp);
1395 return IPMI_CC_OK;
1396}
1397
1398struct fw_cert_info
1399{
1400 uint16_t cert_len;
1401 uint64_t serial;
1402 uint8_t subject_len;
1403 char subject[255];
1404} __attribute__((packed));
1405
1406static ipmi_ret_t ipmi_firmware_get_root_cert_info(
1407 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1408 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1409{
1410 if (DEBUG)
1411 std::cerr << "Get FW root cert info\n";
1412
1413 // request:
1414 // Byte 1 - certificate ID: request which certificate (ignored)
1415
1416 // response:
1417 // Byte 1-2 - certificate length (little endian)
1418 // Byte 3-10 - serial number (little endian)
1419 // Byte 11 - subject length
1420 // Byte 12-N - subject data
1421
1422 auto cert_info = reinterpret_cast<struct fw_cert_info *>(response);
Vernon Mauery15419dd2019-05-24 09:40:30 -07001423 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1424 auto method = bus->new_method_call(
Vernon Mauery52ce6622019-05-22 09:19:46 -07001425 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH,
1426 "org.freedesktop.DBus.Properties", "GetAll");
1427 method.append(FW_UPDATE_SECURITY_INTERFACE);
1428 std::string subject;
1429 uint64_t serial;
1430 std::string cert;
1431 try
1432 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001433 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001434
1435 std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
1436 reply.read(properties);
1437
1438 for (const auto &t : properties)
1439 {
1440 auto key = t.first;
1441 auto value = t.second;
1442 if (key == "certificate_subject")
1443 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001444 subject = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001445 }
1446 else if (key == "cetificate_serial")
1447 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001448 serial = std::get<uint64_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001449 }
1450 else if (key == "certificate")
1451 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001452 cert = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001453 }
1454 }
1455 }
1456 catch (sdbusplus::exception::SdBusError &e)
1457 {
1458 std::cerr << "SDBus Error: " << e.what();
1459 return IPMI_CC_UNSPECIFIED_ERROR;
1460 }
1461
1462 cert_info->cert_len = cert.size();
1463 cert_info->serial = serial;
1464 // truncate subject so it fits in the 255-byte array (if necessary)
1465 if (subject.size() > sizeof(cert_info->subject))
1466 subject.resize(sizeof(cert_info->subject));
1467 cert_info->subject_len = subject.size();
1468 std::copy(subject.begin(), subject.end(), cert_info->subject);
1469
1470 // Status code.
1471 ipmi_ret_t rc = IPMI_CC_OK;
1472 // make sure to account for the *actual* size of the subject
1473 *data_len = sizeof(*cert_info) - sizeof(cert_info->subject) +
1474 cert_info->subject_len;
1475
1476 return rc;
1477}
1478
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301479#ifdef INTEL_PFR_ENABLED
1480enum class FwGetRootCertDataTag : uint8_t
Vernon Mauery52ce6622019-05-22 09:19:46 -07001481{
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301482 activeRootKey = 1,
1483 recoveryRootKey,
1484 activeCSK,
1485 recoveryCSK,
1486};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001487
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301488static constexpr char *bmcActivePfmMTDDev = "/dev/mtd/pfm";
1489static constexpr char *bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
1490static constexpr size_t pfmBaseOffsetInImage = 0x400;
1491static constexpr size_t rootkeyOffsetInPfm = 0xA0;
1492static constexpr size_t cskKeyOffsetInPfm = 0x124;
1493static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
1494static constexpr size_t certKeyLen = 96;
1495static constexpr size_t cskSignatureLen = 96;
1496
1497ipmi::RspType<std::array<uint8_t, certKeyLen>,
1498 std::optional<std::array<uint8_t, cskSignatureLen>>>
1499 ipmiGetFwRootCertData(uint8_t certId)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001500{
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301501 size_t certKeyOffset = 0;
1502 size_t cskSigOffset = 0;
1503 std::string mtdDev;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001504
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301505 switch (static_cast<FwGetRootCertDataTag>(certId))
1506 {
1507 case FwGetRootCertDataTag::activeRootKey:
1508 {
1509 mtdDev = bmcActivePfmMTDDev;
1510 certKeyOffset = rootkeyOffsetInPfm;
1511 break;
1512 }
1513 case FwGetRootCertDataTag::recoveryRootKey:
1514 {
1515 mtdDev = bmcRecoveryImgMTDDev;
1516 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1517 break;
1518 }
1519 case FwGetRootCertDataTag::activeCSK:
1520 {
1521 mtdDev = bmcActivePfmMTDDev;
1522 certKeyOffset = cskKeyOffsetInPfm;
1523 cskSigOffset = cskSignatureOffsetInPfm;
1524 break;
1525 }
1526 case FwGetRootCertDataTag::recoveryCSK:
1527 {
1528 mtdDev = bmcRecoveryImgMTDDev;
1529 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1530 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1531 break;
1532 }
1533 default:
1534 {
1535 return ipmi::responseInvalidFieldRequest();
1536 }
1537 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001538
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301539 std::array<uint8_t, certKeyLen> certKey = {0};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001540
Vernon Mauery52ce6622019-05-22 09:19:46 -07001541 try
1542 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301543 SPIDev spiDev(mtdDev);
1544 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1545
1546 if (cskSigOffset)
1547 {
1548 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1549 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1550 cskSignature.data());
1551 return ipmi::responseSuccess(certKey, cskSignature);
1552 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001553 }
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301554 catch (const std::exception &e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001555 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301556 phosphor::logging::log<phosphor::logging::level::ERR>(
1557 "Exception caught in ipmiGetFwRootCertData",
1558 phosphor::logging::entry("MSG=%s", e.what()));
1559 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001560 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001561
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301562 return ipmi::responseSuccess(certKey, std::nullopt);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001563}
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301564#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001565
1566static ipmi_ret_t ipmi_firmware_write_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1567 ipmi_request_t request,
1568 ipmi_response_t response,
1569 ipmi_data_len_t data_len,
1570 ipmi_context_t context)
1571{
1572 if (DEBUG)
1573 std::cerr << "write fw data (" << *data_len << " bytes)\n";
1574
1575 auto bytes_in = *data_len;
1576 *data_len = 0;
1577 if (fw_update_status.state() != fw_update_status_cache::FW_STATE_DOWNLOAD)
1578 return IPMI_CC_INVALID;
1579
1580 std::ofstream out(FIRMWARE_BUFFER_FILE,
1581 std::ofstream::binary | std::ofstream::app);
1582 if (!out)
1583 {
1584 return IPMI_CC_UNSPECIFIED_ERROR;
1585 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301586
1587 uint64_t fileDataLen = out.tellp();
1588 if (fileDataLen > FIRMWARE_BUFFER_MAX_SIZE)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001589 {
1590 return IPMI_CC_INVALID_FIELD_REQUEST;
1591 }
1592 auto data = reinterpret_cast<uint8_t *>(request);
1593 out.write(reinterpret_cast<char *>(data), bytes_in);
1594 out.close();
1595 if (xfer_hash_check)
1596 {
1597 xfer_hash_check->hash({data, data + bytes_in});
1598 }
1599
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301600#ifdef INTEL_PFR_ENABLED
1601 /* PFR image block 0 - As defined in HAS */
1602 struct PFRImageBlock0
1603 {
1604 uint32_t tag;
1605 uint32_t pcLength;
1606 uint32_t pcType;
1607 uint32_t reserved1;
1608 uint8_t hash256[32];
1609 uint8_t hash384[48];
1610 uint8_t reserved2[32];
1611 } __attribute__((packed));
1612
1613 /* Get the PFR block 0 data and read the uploaded image
1614 * information( Image type, length etc) */
1615 if ((fileDataLen >= sizeof(PFRImageBlock0)) && (!block0Mapped))
1616 {
1617 struct PFRImageBlock0 block0Data = {0};
1618
1619 std::ifstream inFile(FIRMWARE_BUFFER_FILE,
1620 std::ios::binary | std::ios::in);
1621 inFile.read(reinterpret_cast<char *>(&block0Data), sizeof(block0Data));
1622 inFile.close();
1623
1624 uint32_t magicNum = block0Data.tag;
1625
1626 /* Validate the magic number */
1627 if (magicNum != perBlock0MagicNum)
1628 {
1629 return IPMI_CC_INVALID_FIELD_REQUEST;
1630 }
1631 // Note:imgLength, imgType and block0Mapped are in global scope, as
1632 // these are used in cascaded updates.
1633 imgLength = block0Data.pcLength;
1634 imgType = block0Data.pcType;
1635 block0Mapped = true;
1636 }
1637#endif // end of INTEL_PFR_ENABLED
Vernon Mauery52ce6622019-05-22 09:19:46 -07001638 return IPMI_CC_OK;
1639}
1640
Vernon Mauery52ce6622019-05-22 09:19:46 -07001641struct intc_app_get_buffer_size_resp
1642{
1643 uint8_t kcs_size;
1644 uint8_t ipmb_size;
1645} __attribute__((packed));
1646
1647static constexpr int KCS_MAX_BUFFER_SIZE = 63;
1648static constexpr int IPMB_MAX_BUFFER_SIZE = 128;
1649static ipmi_ret_t ipmi_intel_app_get_buffer_size(
1650 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1651 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1652{
1653 auto msg_reply =
1654 reinterpret_cast<intc_app_get_buffer_size_resp *>(response);
1655 // for now this is hard coded; really this number is dependent on
1656 // the BMC kcs driver as well as the host kcs driver....
1657 // we can't know the latter.
1658 msg_reply->kcs_size = KCS_MAX_BUFFER_SIZE / 4;
1659 msg_reply->ipmb_size = IPMB_MAX_BUFFER_SIZE / 4;
1660 *data_len = sizeof(*msg_reply);
1661
1662 return IPMI_CC_OK;
1663}
1664
1665static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20;
1666static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21;
1667static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22;
1668static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23;
1669static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001670static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26;
1671static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27;
anil kumar appanab57098a2019-05-28 16:22:33 +00001672static constexpr ipmi_cmd_t cmdFirmwareExitFirmwareUpdateMode = 0x28;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001673static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29;
1674static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a;
1675static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b;
1676static constexpr ipmi_cmd_t IPMI_CMD_FW_IMAGE_WRITE = 0x2c;
1677static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d;
1678static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0;
1679static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0;
1680
1681static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30;
1682static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66;
1683
1684static void register_netfn_firmware_functions()
1685{
1686 // guarantee that we start with an already timed out timestamp
1687 fw_random_number_timestamp =
1688 std::chrono::steady_clock::now() - FW_RANDOM_NUMBER_TTL;
1689
1690 unlink(FIRMWARE_BUFFER_FILE);
1691
1692 // <Get BT Interface Capabilities>
1693 if (DEBUG)
1694 std::cerr << "Registering firmware update commands\n";
1695
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301696#ifdef INTEL_PFR_ENABLED
1697 // Following commands are supported only for PFR enabled platforms
1698 // CMD:0x20 - Get Firmware Version Information
Vernon Mauery52ce6622019-05-22 09:19:46 -07001699
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301700 // get firmware version information
1701 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1702 ipmi::firmware::cmdGetFwVersionInfo,
1703 ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
1704#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001705 // get firmware security version information
1706 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO,
1707 NULL, ipmi_firmware_get_fw_security_revision,
1708 PRIVILEGE_ADMIN);
1709
1710 // get channel information (max transfer sizes)
anil kumar appana159547c2019-05-31 16:08:34 +00001711 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1712 IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO,
1713 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001714
1715 // get bmc execution context
1716 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_BMC_EXEC_CTX, NULL,
1717 ipmi_firmware_get_fw_execution_context,
1718 PRIVILEGE_ADMIN);
1719
1720 // get root certificate information
1721 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO,
1722 NULL, ipmi_firmware_get_root_cert_info,
1723 PRIVILEGE_ADMIN);
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301724#ifdef INTEL_PFR_ENABLED
Vernon Mauery52ce6622019-05-22 09:19:46 -07001725 // get root certificate data
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301726 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1727 ipmi::firmware::cmdFwGetRootCertData,
1728 ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1729#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001730
1731 // generate bmc fw update random number (for enter fw tranfer mode)
1732 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM,
1733 NULL, ipmi_firmware_get_fw_random_number,
1734 PRIVILEGE_ADMIN);
1735
AppaRao Puli4b3e1c72019-10-16 20:53:09 +05301736 // Set Firmware Update Mode(0x27)
1737 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1738 IPMI_CMD_FW_SET_FW_UPDATE_MODE,
1739 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001740
1741 // exit firmware update mode
anil kumar appanab57098a2019-05-28 16:22:33 +00001742 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1743 cmdFirmwareExitFirmwareUpdateMode,
1744 ipmi::Privilege::Admin, ipmiFirmwareExitFwUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001745
1746 // firmware control mechanism (set filename, usb, etc.)
1747 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_UPDATE_CONTROL, NULL,
1748 ipmi_firmware_control, PRIVILEGE_ADMIN);
1749
1750 // get firmware update status
anil kumar appana6c7d9382019-05-31 14:33:13 +00001751 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1752 IPMI_CMD_FW_GET_STATUS, ipmi::Privilege::Admin,
1753 ipmiFrmwareGetStatus);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001754 // set firmware update options (no downgrade, etc.)
1755 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS,
1756 NULL, ipmi_firmware_update_options, PRIVILEGE_ADMIN);
1757
1758 // write image data
1759 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_IMAGE_WRITE, NULL,
1760 ipmi_firmware_write_data, PRIVILEGE_ADMIN);
1761
Vernon Mauery52ce6622019-05-22 09:19:46 -07001762 // get buffer size is used by fw update (exclusively?)
1763 ipmi_register_callback(NETFUN_INTC_APP, IPMI_CMD_INTC_GET_BUFFER_SIZE, NULL,
1764 ipmi_intel_app_get_buffer_size, PRIVILEGE_USER);
1765 return;
1766}