blob: 6bd057a43106a5301c61cd74735c8e1ab972c3c6 [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
James Feistbeaa2eb2019-08-22 10:59:24 -070066static constexpr const char *secondaryFitImageStartAddr = "22480000";
anil kumar appana31f88872019-08-02 15:16:27 +000067static uint8_t getActiveBootImage(void);
Vernon Mauery52ce6622019-05-22 09:19:46 -070068static void register_netfn_firmware_functions() __attribute__((constructor));
69
70// oem return code for firmware update control
71constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5;
72constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80;
73
Vernon Mauery52ce6622019-05-22 09:19:46 -070074static constexpr bool DEBUG = false;
75
76static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] =
77 "xyz.openbmc_project.fwupdate1.server";
78
79static constexpr char FW_UPDATE_SERVER_PATH[] =
80 "/xyz/openbmc_project/fwupdate1";
81static constexpr char FW_UPDATE_SERVER_INFO_PATH[] =
82 "/xyz/openbmc_project/fwupdate1/info";
83static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] =
84 "/xyz/openbmc_project/fwupdate1/info/bmc_active";
85static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] =
86 "/xyz/openbmc_project/fwupdate1/info/bmc_backup";
87
88static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1";
89static constexpr char FW_UPDATE_INFO_INTERFACE[] =
90 "xyz.openbmc_project.fwupdate1.fwinfo";
91static constexpr char FW_UPDATE_SECURITY_INTERFACE[] =
92 "xyz.openbmc_project.fwupdate1.security";
93
94constexpr std::size_t operator""_MB(unsigned long long v)
95{
96 return 1024u * 1024u * v;
97}
98static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB;
99
100static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin";
101static bool local_download_is_active(void)
102{
103 struct stat sb;
104 if (stat(FIRMWARE_BUFFER_FILE, &sb) < 0)
105 return false;
106 return true;
107}
108
109class fw_update_status_cache
110{
111 public:
112 enum
113 {
114 FW_STATE_INIT = 0,
115 FW_STATE_IDLE,
116 FW_STATE_DOWNLOAD,
117 FW_STATE_VERIFY,
118 FW_STATE_WRITE,
119 FW_STATE_READY,
120 FW_STATE_ERROR = 0x0f,
121 FW_STATE_AC_CYCLE_REQUIRED = 0x83,
122 };
Vernon Mauery52ce6622019-05-22 09:19:46 -0700123 uint8_t state()
124 {
125 if (DEBUG)
126 std::cerr << "fw-state: 0x" << std::hex << (int)_state << '\n';
127 if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
128 local_download_is_active())
129 {
130 _state = FW_STATE_DOWNLOAD;
131 _percent = 0;
132 }
133 return _state;
134 }
135 uint8_t percent()
136 {
137 return _percent;
138 }
139 std::string msg()
140 {
141 return _msg;
142 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700143 std::string get_software_obj_path()
144 {
145 return _software_obj_path;
146 }
147 void set_software_obj_path(std::string &obj_path)
148 {
149 _software_obj_path = obj_path;
150 _state = FW_STATE_WRITE;
151 _percent = 0;
152 _match = std::make_shared<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700153 *_bus,
Vernon Mauery52ce6622019-05-22 09:19:46 -0700154 sdbusplus::bus::match::rules::propertiesChanged(
155 _software_obj_path,
156 "xyz.openbmc_project.Software.ActivationProgress"),
157 [&](sdbusplus::message::message &msg) {
158 if (DEBUG)
159 std::cerr << "propertiesChanged lambda\n";
160 std::map<std::string, ipmi::DbusVariant> props;
161 std::vector<std::string> inval;
162 std::string iface;
163 msg.read(iface, props, inval);
164 _parse_props(props);
165 });
166 }
167 uint8_t activation_timer_timeout()
168 {
169 std::cerr << "activation_timer_timout(): increase percentage...\n";
170 _percent = _percent + 5;
anil kumar appana31f88872019-08-02 15:16:27 +0000171 if (_percent >= 95)
172 {
173 /*changing the state to ready to update firmware utility */
174 _state = FW_STATE_READY;
175 }
176 std::cerr << " _percent = " << (int)_percent << "\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -0700177 return _percent;
178 }
anil kumar appana31f88872019-08-02 15:16:27 +0000179 /* API for changing state to ERROR */
180 void firmwareUpdateAbortState()
181 {
182 unlink(FIRMWARE_BUFFER_FILE);
183 // changing the state to error
184 _state = FW_STATE_ERROR;
185 }
186 void setDeferRestart(bool deferRestart)
187 {
188 _deferRestart = deferRestart;
189 }
190 void setInhibitDowngrade(bool inhibitDowngrade)
191 {
192 _inhibitDowngrade = inhibitDowngrade;
193 }
194 bool getDeferRestart()
195 {
196 return _deferRestart;
197 }
198 bool getInhibitDowngrade()
199 {
200 return _inhibitDowngrade;
201 }
202
Vernon Mauery52ce6622019-05-22 09:19:46 -0700203 protected:
204 void _parse_props(std::map<std::string, ipmi::DbusVariant> &properties)
205 {
206 if (DEBUG)
207 std::cerr << "propertiesChanged (" << properties.size()
208 << " elements)";
209 for (const auto &t : properties)
210 {
211 auto key = t.first;
212 auto value = t.second;
213 if (key == "state")
214 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700215 auto state = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700216 if (DEBUG)
217 std::cerr << ", state=" << state;
218 if (state == "INIT")
219 _state = FW_STATE_INIT;
220 else if (state == "IDLE")
221 _state = FW_STATE_IDLE;
222 else if (state == "DOWNLOAD")
223 _state = FW_STATE_DOWNLOAD;
224 else if (state == "VERIFY")
225 _state = FW_STATE_VERIFY;
226 else if (state == "WRITE")
227 _state = FW_STATE_WRITE;
228 else if (state == "READY")
229 _state = FW_STATE_READY;
230 else if (state == "ERROR")
231 _state = FW_STATE_ERROR;
232 else if (state == "AC_CYCLE_REQUIRED")
233 _state = FW_STATE_AC_CYCLE_REQUIRED;
234 else
235 {
236 _state = FW_STATE_ERROR;
237 _msg = "internal error";
238 }
239 }
240 else if (key == "percent")
241 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700242 _percent = std::get<int32_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700243 if (DEBUG)
244 std::cerr << ", pct=" << (int)_percent;
245 }
246 else if (key == "msg")
247 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700248 _msg = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700249 if (DEBUG)
250 std::cerr << ", msg='" << _msg << '\'';
251 }
252 else if (key == "Progress")
253 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700254 _percent = std::get<uint8_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -0700255 ;
256 if (_percent == 100)
257 _state = FW_STATE_READY;
258 }
259 }
260 if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
261 local_download_is_active())
262 {
263 _state = FW_STATE_DOWNLOAD;
264 _percent = 0;
265 }
266 if (DEBUG)
267 std::cerr << '\n';
268 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700269
Vernon Mauery15419dd2019-05-24 09:40:30 -0700270 std::shared_ptr<sdbusplus::asio::connection> _bus;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700271 std::shared_ptr<sdbusplus::bus::match::match> _match;
272 uint8_t _state = 0;
273 uint8_t _percent = 0;
anil kumar appana31f88872019-08-02 15:16:27 +0000274 bool _deferRestart = false;
275 bool _inhibitDowngrade = false;
Vernon Mauery52ce6622019-05-22 09:19:46 -0700276 std::string _msg;
277
278 private:
279 std::string _software_obj_path;
280};
281
282static fw_update_status_cache fw_update_status;
283
284static std::chrono::steady_clock::time_point fw_random_number_timestamp;
285static constexpr int FW_RANDOM_NUMBER_LENGTH = 8;
286static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30);
287static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH];
288
289static ipmi_ret_t ipmi_firmware_get_fw_random_number(
290 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
291 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
292{
293 std::random_device rd;
294 std::default_random_engine gen(rd());
295 std::uniform_int_distribution<> dist{0, 255};
296
297 if (*data_len != 0)
298 {
299 *data_len = 0;
300 return IPMI_CC_REQ_DATA_LEN_INVALID;
301 }
302
303 fw_random_number_timestamp = std::chrono::steady_clock::now();
304
305 uint8_t *msg_reply = static_cast<uint8_t *>(response);
306 for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
307 fw_random_number[i] = msg_reply[i] = dist(gen);
308
309 if (DEBUG)
310 std::cerr << "FW Rand Num: 0x" << std::hex << (int)msg_reply[0] << " 0x"
311 << (int)msg_reply[1] << " 0x" << (int)msg_reply[2] << " 0x"
312 << (int)msg_reply[3] << " 0x" << (int)msg_reply[4] << " 0x"
313 << (int)msg_reply[5] << " 0x" << (int)msg_reply[6] << " 0x"
314 << (int)msg_reply[7] << '\n';
315
316 *data_len = FW_RANDOM_NUMBER_LENGTH;
317
318 return IPMI_CC_OK;
319}
320
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530321/** @brief Set Firmware Update Mode
322 *
323 * This function sets BMC into firmware update mode
324 * after validating Random number obtained from the Get
325 * Firmware Update Random Number command
326 *
327 * @parameter
328 * - randNum - Random number(token)
329 * @returns IPMI completion code
330 **/
331ipmi::RspType<> ipmiSetFirmwareUpdateMode(
332 std::array<uint8_t, FW_RANDOM_NUMBER_LENGTH> &randNum)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700333{
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530334 /* Firmware Update Random number is valid for 30 seconds only */
335 auto timeElapsed =
336 (std::chrono::steady_clock::now() - fw_random_number_timestamp);
337 if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700338 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
339 FW_RANDOM_NUMBER_TTL)
340 .count())
341 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530342 phosphor::logging::log<phosphor::logging::level::INFO>(
343 "Firmware update random number expired.");
344 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700345 }
346
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530347 /* Validate random number */
Vernon Mauery52ce6622019-05-22 09:19:46 -0700348 for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
349 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530350 if (fw_random_number[i] != randNum[i])
Vernon Mauery52ce6622019-05-22 09:19:46 -0700351 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530352 phosphor::logging::log<phosphor::logging::level::INFO>(
353 "Invalid random number specified.");
354 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700355 }
356 }
357
358 if (fw_update_status.state() != fw_update_status_cache::FW_STATE_IDLE
359 // TODO: Allowing FW_STATE_INIT here to let image activation available
360 // without being in FW_STATE_IDLE, need to fix/adjust the state machine
361 // to match xyz.openbmc_project.Software.BMC.Updater service activation
362 // mechanism at finer grain
363 && fw_update_status.state() != fw_update_status_cache::FW_STATE_INIT)
364 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530365 phosphor::logging::log<phosphor::logging::level::INFO>(
366 "Already firmware update is in progress.");
367 return ipmi::responseBusy();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700368 }
369 // FIXME? c++ doesn't off an option for exclusive file creation
370 FILE *fp = fopen(FIRMWARE_BUFFER_FILE, "wx");
371 if (!fp)
372 {
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530373 phosphor::logging::log<phosphor::logging::level::INFO>(
374 "Unable to open file.");
375 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700376 }
377 fclose(fp);
378
AppaRao Puli4b3e1c72019-10-16 20:53:09 +0530379 return ipmi::responseSuccess();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700380}
381
anil kumar appanab57098a2019-05-28 16:22:33 +0000382/** @brief implements exit firmware update mode command
383 * @param None
384 *
385 * @returns IPMI completion code
386 */
387ipmi::RspType<> ipmiFirmwareExitFwUpdateMode()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700388{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700389
anil kumar appanab57098a2019-05-28 16:22:33 +0000390 if (DEBUG)
391 {
392 std::cerr << "Exit FW update mode \n";
393 }
Vernon Mauery52ce6622019-05-22 09:19:46 -0700394 switch (fw_update_status.state())
395 {
396 case fw_update_status_cache::FW_STATE_INIT:
397 case fw_update_status_cache::FW_STATE_IDLE:
anil kumar appanab57098a2019-05-28 16:22:33 +0000398 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700399 break;
400 case fw_update_status_cache::FW_STATE_DOWNLOAD:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700401 case fw_update_status_cache::FW_STATE_VERIFY:
402 break;
403 case fw_update_status_cache::FW_STATE_WRITE:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700404 break;
405 case fw_update_status_cache::FW_STATE_READY:
406 case fw_update_status_cache::FW_STATE_ERROR:
Vernon Mauery52ce6622019-05-22 09:19:46 -0700407 break;
408 case fw_update_status_cache::FW_STATE_AC_CYCLE_REQUIRED:
anil kumar appanab57098a2019-05-28 16:22:33 +0000409 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700410 break;
411 }
anil kumar appanab57098a2019-05-28 16:22:33 +0000412 fw_update_status.firmwareUpdateAbortState();
413 return ipmi::responseSuccess();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700414}
415
416static void post_transfer_complete_handler(
417 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher);
418static bool request_start_firmware_update(const std::string &uri)
419{
420 if (DEBUG)
421 std::cerr << "request start firmware update()\n";
422
Vernon Mauery52ce6622019-05-22 09:19:46 -0700423 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
424 // the code gets to this point, the file should be transferred start the
425 // request (creating a new file in /tmp/images causes the update manager to
426 // check if it is ready for activation)
427 static std::unique_ptr<sdbusplus::bus::match::match> fw_update_matcher;
428 post_transfer_complete_handler(fw_update_matcher);
429 std::filesystem::rename(
430 uri, "/tmp/images/" +
431 boost::uuids::to_string(boost::uuids::random_generator()()));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700432 return true;
433}
434
435class transfer_hash_check
436{
437 public:
438 enum hash_check
439 {
440 CHECK_NOT_REQUESTED = 0,
441 CHECK_REQUESTED,
442 CHECK_PASSED_SHA2,
443 CHECK_RESVD1,
444 CHECK_FAILED_SHA2 = 0xe2,
445 CHECK_RESVD2 = 0xe3,
446 };
447
448 protected:
449 EVP_MD_CTX *_ctx;
450 std::vector<uint8_t> _expected;
451 enum hash_check _check;
452 bool _started;
453
454 public:
455 transfer_hash_check() : _check(CHECK_NOT_REQUESTED), _started(false)
456 {
457 }
458 ~transfer_hash_check()
459 {
460 if (_ctx)
461 {
462 EVP_MD_CTX_destroy(_ctx);
463 _ctx = NULL;
464 }
465 }
466 void init(const std::vector<uint8_t> &expected)
467 {
468 _expected = expected;
469 _check = CHECK_REQUESTED;
470 _ctx = EVP_MD_CTX_create();
471 EVP_DigestInit(_ctx, EVP_sha256());
472 }
473 void hash(const std::vector<uint8_t> &data)
474 {
475 if (!_started)
476 _started = true;
477 EVP_DigestUpdate(_ctx, data.data(), data.size());
478 }
479 void clear()
480 {
481 // if not started, nothing to clear
482 if (_started)
483 {
484 if (_ctx)
485 EVP_MD_CTX_destroy(_ctx);
486 if (_check != CHECK_NOT_REQUESTED)
487 _check = CHECK_REQUESTED;
488 _ctx = EVP_MD_CTX_create();
489 EVP_DigestInit(_ctx, EVP_sha256());
490 }
491 }
492 enum hash_check check()
493 {
494 if (_check == CHECK_REQUESTED)
495 {
496 unsigned int len;
497 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
498 EVP_DigestFinal(_ctx, digest.data(), &len);
499 if (digest == _expected)
500 {
501 if (DEBUG)
502 std::cerr << "transfer sha2 check passed\n";
503 _check = CHECK_PASSED_SHA2;
504 }
505 else
506 {
507 if (DEBUG)
508 std::cerr << "transfer sha2 check failed\n";
509 _check = CHECK_FAILED_SHA2;
510 }
511 }
512 return _check;
513 }
514 uint8_t status() const
515 {
516 return static_cast<uint8_t>(_check);
517 }
518};
519
520std::shared_ptr<transfer_hash_check> xfer_hash_check;
521
Vernon Mauery52ce6622019-05-22 09:19:46 -0700522static void activate_image(const char *obj_path)
523{
anil kumar appana31f88872019-08-02 15:16:27 +0000524 // If flag is false means to reboot
525 if (fw_update_status.getDeferRestart() == false)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700526 {
Vernon Mauery52ce6622019-05-22 09:19:46 -0700527
anil kumar appana31f88872019-08-02 15:16:27 +0000528 if (DEBUG)
529 {
530 std::cerr << "activateImage()...\n";
531 std::cerr << "obj_path = " << obj_path << "\n";
532 }
533 phosphor::logging::log<phosphor::logging::level::INFO>(
534 "activating Image: ",
535 phosphor::logging::entry("OBJPATH =%s", obj_path));
536 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
537 bus->async_method_call(
538 [](const boost::system::error_code ec) {
539 if (ec)
540 {
541 phosphor::logging::log<phosphor::logging::level::ERR>(
542 "async_method_call error: activate_image failed");
543 return;
544 }
545 },
546 "xyz.openbmc_project.Software.BMC.Updater", obj_path,
547 "org.freedesktop.DBus.Properties", "Set",
548 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
549 std::variant<std::string>("xyz.openbmc_project.Software.Activation."
550 "RequestedActivations.Active"));
Vernon Mauery52ce6622019-05-22 09:19:46 -0700551 }
anil kumar appana31f88872019-08-02 15:16:27 +0000552 else
Vernon Mauery52ce6622019-05-22 09:19:46 -0700553 {
anil kumar appana31f88872019-08-02 15:16:27 +0000554 phosphor::logging::log<phosphor::logging::level::INFO>(
555 "Firmware image activation is deferred.");
Vernon Mauery52ce6622019-05-22 09:19:46 -0700556 }
557}
558
559static void post_transfer_complete_handler(
560 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher)
561{
562 // Setup timer for watching signal
563 static phosphor::Timer timer(
564 [&fw_update_matcher]() { fw_update_matcher = nullptr; });
565
566 static phosphor::Timer activation_status_timer([]() {
567 if (fw_update_status.activation_timer_timeout() >= 95)
568 {
569 activation_status_timer.stop();
570 }
571 });
572
573 timer.start(std::chrono::microseconds(5000000), false);
574
575 // callback function for capturing signal
576 auto callback = [&fw_update_matcher](sdbusplus::message::message &m) {
577 if (DEBUG)
578 std::cerr << "[complete] Match fired\n";
579 bool flag = false;
580
581 std::vector<std::pair<
582 std::string,
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700583 std::vector<std::pair<std::string, std::variant<std::string>>>>>
Vernon Mauery52ce6622019-05-22 09:19:46 -0700584 interfaces_properties;
585
586 sdbusplus::message::object_path obj_path;
587
588 try
589 {
590 m.read(obj_path, interfaces_properties); // Read in the object path
591 // that was just created
592 }
593 catch (std::exception &e)
594 {
595 std::cerr
596 << "[complete] Failed at post_transfer_complete-handler : "
597 << e.what() << "\n";
598 }
599 // constructing response message
600 if (DEBUG)
601 std::cerr << "[complete] obj path = " << obj_path.str << "\n";
602 for (auto &interface : interfaces_properties)
603 {
604 if (DEBUG)
605 std::cerr << "[complete] interface = " << interface.first
606 << "\n";
607
608 if (interface.first == "xyz.openbmc_project.Software.Activation")
609 {
610 // cancel timer only when
611 // xyz.openbmc_project.Software.Activation interface is
612 // added
613
614 if (DEBUG)
615 std::cerr << "[complete] Attempt to cancel timer...\n";
616 try
617 {
618 timer.stop();
619 activation_status_timer.start(
620 std::chrono::microseconds(3000000), true);
621 }
622 catch (std::exception &e)
623 {
624 std::cerr << "[complete] cancel timer error: " << e.what()
625 << "\n";
626 }
627
628 fw_update_status.set_software_obj_path(obj_path.str);
629 activate_image(obj_path.str.c_str());
630 if (DEBUG)
631 std::cerr << "[complete] returned from activeImage()\n";
632
633 fw_update_matcher = nullptr;
634 }
635 }
636 };
637
638 // Adding matcher
639 fw_update_matcher = std::make_unique<sdbusplus::bus::match::match>(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700640 *getSdBus(),
Vernon Mauery52ce6622019-05-22 09:19:46 -0700641 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
642 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
643 callback);
644}
Vernon Mauery52ce6622019-05-22 09:19:46 -0700645
646class MappedFile
647{
648 public:
649 MappedFile(const std::string &fname) : addr(nullptr), fsize(0)
650 {
651 std::error_code ec;
652 size_t sz = std::filesystem::file_size(fname, ec);
653 int fd = open(fname.c_str(), O_RDONLY);
654 if (!ec || fd < 0)
655 {
656 return;
657 }
658 void *tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
659 close(fd);
660 if (tmp == MAP_FAILED)
661 {
662 return;
663 }
664 addr = tmp;
665 fsize = sz;
666 }
667
668 ~MappedFile()
669 {
670 if (addr)
671 {
672 munmap(addr, fsize);
673 }
674 }
675 const uint8_t *data() const
676 {
677 return static_cast<const uint8_t *>(addr);
678 }
679 size_t size() const
680 {
681 return fsize;
682 }
683
684 private:
685 size_t fsize;
686 void *addr;
687};
688
689static int transfer_from_file(const std::string &uri, bool move = true)
690{
691 std::error_code ec;
692 if (DEBUG)
693 std::cerr << "transfer_from_file(" << uri << ")\n";
694 if (move)
695 {
696 std::filesystem::rename(uri, FIRMWARE_BUFFER_FILE, ec);
697 }
698 else
699 {
700 std::filesystem::copy(uri, FIRMWARE_BUFFER_FILE,
701 std::filesystem::copy_options::overwrite_existing,
702 ec);
703 }
704 if (xfer_hash_check)
705 {
706 MappedFile mappedfw(uri);
707 xfer_hash_check->hash(
708 {mappedfw.data(), mappedfw.data() + mappedfw.size()});
709 }
710 if (ec.value())
711 {
712 std::cerr << "cp/mv returns: " << ec.message() << "(" << ec.value()
713 << ")\n";
714 }
715 return ec.value();
716}
717
718template <typename... ArgTypes>
719static int executeCmd(const char *path, ArgTypes &&... tArgs)
720{
721 boost::process::child execProg(path, const_cast<char *>(tArgs)...);
722 execProg.wait();
723 return execProg.exit_code();
724}
725
726constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl";
727constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt";
728constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img";
729constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev";
730constexpr size_t fwPathMaxLength = 255;
731static int transfer_from_usb(const std::string &uri)
732{
733 int ret, sysret;
734 char fwpath[fwPathMaxLength];
735 if (DEBUG)
736 std::cerr << "transfer_from_usb(" << uri << ")\n";
737 ret = executeCmd(USB_CTRL_PATH, "mount", FWUPDATE_USB_VOL_IMG,
738 FWUPDATE_MOUNT_POINT);
739 if (ret)
740 {
741 return ret;
742 }
743
744 std::string usb_path = std::string(FWUPDATE_MOUNT_POINT) + "/" + uri;
745 ret = transfer_from_file(usb_path, false);
746
747 executeCmd(USB_CTRL_PATH, "cleanup", FWUPDATE_USB_VOL_IMG,
748 FWUPDATE_MOUNT_POINT);
749 return ret;
750}
751
752static bool transfer_firmware_from_uri(const std::string &uri)
753{
754 static constexpr char FW_URI_FILE[] = "file://";
755 static constexpr char FW_URI_USB[] = "usb://";
756 if (DEBUG)
757 std::cerr << "transfer_firmware_from_uri(" << uri << ")\n";
758 if (boost::algorithm::starts_with(uri, FW_URI_FILE))
759 {
760 std::string fname = uri.substr(sizeof(FW_URI_FILE) - 1);
761 if (fname != FIRMWARE_BUFFER_FILE)
762 {
763 return 0 == transfer_from_file(fname);
764 }
765 return true;
766 }
767 if (boost::algorithm::starts_with(uri, FW_URI_USB))
768 {
769 std::string fname = uri.substr(sizeof(FW_URI_USB) - 1);
770 return 0 == transfer_from_usb(fname);
771 }
772 return false;
773}
774
775/* Get USB-mass-storage device status: inserted => true, ejected => false */
776static int usb_get_status()
777{
778 static constexpr char usb_gadget_base[] = "/sys/kernel/config/usb_gadget/";
779 auto usb_device =
780 std::filesystem::path(usb_gadget_base) / FWUPDATE_USB_DEV_NAME;
781 std::error_code ec;
782 return std::filesystem::exists(usb_device, ec) && !ec;
783}
784
785/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
786static int usb_attach_device()
787{
788 if (usb_get_status())
789 {
790 return 1;
791 }
792 int ret =
793 executeCmd(USB_CTRL_PATH, "setup", FWUPDATE_USB_VOL_IMG,
794 std::to_string(FIRMWARE_BUFFER_MAX_SIZE / 1_MB).c_str());
795 if (!ret)
796 {
797 ret = executeCmd(USB_CTRL_PATH, "insert", FWUPDATE_USB_DEV_NAME,
798 FWUPDATE_USB_VOL_IMG);
799 }
800 return ret;
801}
802
803/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
804static int usb_detach_device()
805{
806 if (!usb_get_status())
807 {
808 return 1;
809 }
810 return executeCmd(USB_CTRL_PATH, "eject", FWUPDATE_USB_DEV_NAME);
811}
812
813constexpr uint8_t controls_init = 0x00;
814constexpr uint8_t controls_transfer_started = 0x01;
815constexpr uint8_t controls_transfer_completed = 0x02;
816constexpr uint8_t controls_transfer_aborted = 0x04;
817constexpr uint8_t controls_usb_attached = 0x08;
818
819struct fw_update_control_request
820{
821 enum knob
822 {
823 CTRL_GET = 0,
824 CTRL_XFER_START,
825 CTRL_XFER_COMPLETE,
826 CTRL_XFER_ABORT,
827 CTRL_SET_FILENAME,
828 CTRL_USB_ATTACH,
829 CTRL_USB_DETACH,
830 } __attribute__((packed));
831 enum knob control;
832 uint8_t nlen;
833 char filename[fwPathMaxLength];
834} __attribute__((packed));
835
836static ipmi_ret_t ipmi_firmware_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
837 ipmi_request_t request,
838 ipmi_response_t response,
839 ipmi_data_len_t data_len,
840 ipmi_context_t context)
841{
842 static std::string fw_xfer_uri;
843
844 if (DEBUG)
845 std::cerr << "FW update control\n";
846 *data_len = 0;
847
848 static uint8_t controls = controls_init;
849 ipmi_ret_t rc = IPMI_CC_OK;
850 auto ctrl_req = reinterpret_cast<fw_update_control_request *>(request);
851 auto ctrl_resp = reinterpret_cast<uint8_t *>(response);
852
853 if (usb_get_status())
854 {
855 controls |= controls_usb_attached;
856 }
857 else
858 {
859 controls &= ~controls_usb_attached;
860 }
861
862 switch (ctrl_req->control)
863 {
864 case fw_update_control_request::CTRL_GET:
865 break;
866 case fw_update_control_request::CTRL_XFER_START:
867 {
868 controls |= controls_transfer_started;
869 // reset buffer to empty (truncate file)
870 std::ofstream out(FIRMWARE_BUFFER_FILE,
871 std::ofstream::binary | std::ofstream::trunc);
872 fw_xfer_uri = std::string("file://") + FIRMWARE_BUFFER_FILE;
873 if (xfer_hash_check)
874 {
875 xfer_hash_check->clear();
876 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +0530877#ifdef INTEL_PFR_ENABLED
878 imgLength = 0;
879 imgType = 0;
880 block0Mapped = false;
881#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -0700882 if (DEBUG)
883 std::cerr << "transfer start\n";
884 }
885 break;
886 case fw_update_control_request::CTRL_XFER_COMPLETE:
887 {
888 if (usb_get_status())
889 {
890 rc = IPMI_CC_REQ_INVALID_PHASE;
891 }
892 // finish transfer based on URI
893 if (!transfer_firmware_from_uri(fw_xfer_uri))
894 {
895 rc = IPMI_CC_UNSPECIFIED_ERROR;
896 break;
897 }
898 // transfer complete
899 if (xfer_hash_check)
900 {
901 if (transfer_hash_check::CHECK_PASSED_SHA2 !=
902 xfer_hash_check->check())
903 {
904 if (DEBUG)
905 std::cerr << "xfer_hash_check returns not "
906 "CHECK_PASSED_SHA2\n";
907 rc = IPMI_CC_UNSPECIFIED_ERROR;
908 break;
909 }
910 }
911 // start the request
912 if (!request_start_firmware_update(FIRMWARE_BUFFER_FILE))
913 {
914 if (DEBUG)
915 std::cerr
916 << "request_start_firmware_update returns failure\n";
917 rc = IPMI_CC_UNSPECIFIED_ERROR;
918 }
919 if (rc == IPMI_CC_OK)
920 {
921 controls |= controls_transfer_completed;
922 }
923 }
924 break;
925 case fw_update_control_request::CTRL_XFER_ABORT:
926 if (DEBUG)
927 std::cerr << "send abort request\n";
928 if (usb_get_status())
929 {
930 if (0 != usb_detach_device())
931 {
932 rc = IPMI_CC_USB_ATTACH_FAIL;
933 }
934 }
anil kumar appana31f88872019-08-02 15:16:27 +0000935 fw_update_status.firmwareUpdateAbortState();
Vernon Mauery52ce6622019-05-22 09:19:46 -0700936 controls |= controls_transfer_aborted;
937 break;
938 case fw_update_control_request::CTRL_SET_FILENAME:
939 fw_xfer_uri.clear();
940 fw_xfer_uri.insert(0, ctrl_req->filename, ctrl_req->nlen);
941 break;
942 case fw_update_control_request::CTRL_USB_ATTACH:
943 if (usb_get_status())
944 {
945 rc = IPMI_CC_INVALID_FIELD_REQUEST;
946 }
947 else if (0 != usb_attach_device())
948 {
949 rc = IPMI_CC_USB_ATTACH_FAIL;
950 }
951 else
952 {
953 rc = IPMI_CC_OK;
954 }
955 break;
956 case fw_update_control_request::CTRL_USB_DETACH:
957 if (!usb_get_status())
958 {
959 rc = IPMI_CC_INVALID_FIELD_REQUEST;
960 }
961 if (0 != usb_detach_device())
962 {
963 rc = IPMI_CC_USB_ATTACH_FAIL;
964 }
965 else
966 {
967 rc = IPMI_CC_OK;
968 }
969 break;
970 default:
971 if (DEBUG)
972 std::cerr << "control byte " << std::hex << ctrl_req->control
973 << " unknown\n";
974 rc = IPMI_CC_INVALID_FIELD_REQUEST;
975 break;
976 }
977
978 if (rc == IPMI_CC_OK)
979 {
980 *ctrl_resp = controls;
981 *data_len = sizeof(*ctrl_resp);
982 }
983
984 return rc;
985}
986
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530987#ifdef INTEL_PFR_ENABLED
988using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
989 uint8_t, // Major Version Number
990 uint8_t, // Minor Version Number
991 uint32_t, // Build Number
992 uint32_t, // Build Timestamp
993 uint32_t>; // Update Timestamp
994ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
Vernon Mauery52ce6622019-05-22 09:19:46 -0700995{
Vernon Mauery52ce6622019-05-22 09:19:46 -0700996 // Byte 1 - Count (N) Number of devices data is being returned for.
AppaRao Puli37fde6b2019-10-25 16:37:50 +0530997 // Bytes 2:16 - Device firmare information(fwVersionInfoType)
Vernon Mauery52ce6622019-05-22 09:19:46 -0700998 // Bytes - 17:(15xN) - Repeat of 2 through 16
999
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301000 std::vector<fwVersionInfoType> fwVerInfoList;
1001 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
1002 for (const auto &fwDev : fwVersionIdMap)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001003 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301004 std::string verStr;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001005 try
1006 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301007 auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001008
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301009 ipmi::Value result = ipmi::getDbusProperty(
1010 *busp, service, fwDev.second, versionIntf, "Version");
1011 verStr = std::get<std::string>(result);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001012 }
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301013 catch (const std::exception &e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001014 {
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301015 phosphor::logging::log<phosphor::logging::level::INFO>(
1016 "Failed to fetch Version property",
1017 phosphor::logging::entry("ERROR=%s", e.what()),
1018 phosphor::logging::entry("PATH=%s", fwDev.second),
1019 phosphor::logging::entry("INTERFACE=%s", versionIntf));
1020 continue;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001021 }
1022
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301023 if (verStr.empty())
1024 {
1025 phosphor::logging::log<phosphor::logging::level::INFO>(
1026 "Version is empty.",
1027 phosphor::logging::entry("PATH=%s", fwDev.second),
1028 phosphor::logging::entry("INTERFACE=%s", versionIntf));
1029 continue;
1030 }
1031
1032 // BMC Version format: <major>.<minor>-<build bum>-<build hash>
1033 std::vector<std::string> splitVer;
1034 boost::split(splitVer, verStr, boost::is_any_of(".-"));
1035 if (splitVer.size() < 3)
1036 {
1037 phosphor::logging::log<phosphor::logging::level::INFO>(
1038 "Invalid Version format.",
1039 phosphor::logging::entry("Version=%s", verStr.c_str()),
1040 phosphor::logging::entry("PATH=%s", fwDev.second));
1041 continue;
1042 }
1043
1044 uint8_t majorNum = 0;
1045 uint8_t minorNum = 0;
1046 uint32_t buildNum = 0;
1047 try
1048 {
1049 majorNum = std::stoul(splitVer[0], nullptr, 16);
1050 minorNum = std::stoul(splitVer[1], nullptr, 16);
1051 buildNum = std::stoul(splitVer[2], nullptr, 16);
1052 }
1053 catch (const std::exception &e)
1054 {
1055 phosphor::logging::log<phosphor::logging::level::INFO>(
1056 "Failed to convert stoul.",
1057 phosphor::logging::entry("ERROR=%s", e.what()));
1058 continue;
1059 }
1060
1061 // Build Timestamp - Not supported.
1062 // Update Timestamp - TODO: Need to check with CPLD team.
1063 fwVerInfoList.emplace_back(
1064 fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
1065 minorNum, buildNum, 0, 0));
Vernon Mauery52ce6622019-05-22 09:19:46 -07001066 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001067
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301068 return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001069}
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301070#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001071
1072struct fw_security_revision_info
1073{
1074 uint8_t id_tag;
1075 uint16_t sec_rev;
1076} __attribute__((packed));
1077
1078static ipmi_ret_t ipmi_firmware_get_fw_security_revision(
1079 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1080 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1081{
1082 if (DEBUG)
1083 std::cerr << "Get FW security revision info\n";
1084
1085 // Byte 1 - Count (N) Number of devices data is being returned for.
1086 // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image
1087 // 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR
1088 // Image
1089 // Byte 3 - Major Version Number
1090 // Byte 4 - Minor Version Number
1091 // Bytes 5:8 - Build Number
1092 // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL
1093 // timestamp
1094 // Bytes 13:16 - Update Timestamp
1095 // Bytes - 17:(15xN) - Repeat of 2 through 16
1096
1097 uint8_t count = 0;
1098 auto ret_count = reinterpret_cast<uint8_t *>(response);
1099 auto info =
1100 reinterpret_cast<struct fw_security_revision_info *>(ret_count + 1);
1101
Vernon Mauery15419dd2019-05-24 09:40:30 -07001102 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001103 for (uint8_t id_tag = 1; id_tag < 6; id_tag++)
1104 {
1105 const char *fw_path;
1106 switch (id_tag)
1107 {
1108 case 1:
1109 fw_path = FW_UPDATE_ACTIVE_INFO_PATH;
1110 break;
1111 case 2:
1112 fw_path = FW_UPDATE_BACKUP_INFO_PATH;
1113 break;
1114 case 3:
1115 case 4:
1116 case 5:
1117 continue; // skip for now
1118 break;
1119 }
1120 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001121 bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
Vernon Mauery52ce6622019-05-22 09:19:46 -07001122 "org.freedesktop.DBus.Properties", "GetAll");
1123 method.append(FW_UPDATE_INFO_INTERFACE, "security_version");
1124 ipmi::DbusVariant sec_rev;
1125 try
1126 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001127 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001128
1129 if (reply.is_method_error())
1130 continue;
1131
1132 reply.read(sec_rev);
1133 }
1134 catch (sdbusplus::exception::SdBusError &e)
1135 {
1136 std::cerr << "SDBus Error: " << e.what();
1137 return IPMI_CC_UNSPECIFIED_ERROR;
1138 }
1139
1140 info->id_tag = id_tag;
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001141 info->sec_rev = std::get<int>(sec_rev);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001142 count++;
1143 info++;
1144 }
1145 *ret_count = count;
1146
1147 // Status code.
1148 ipmi_ret_t rc = IPMI_CC_OK;
1149 *data_len = sizeof(count) + count * sizeof(*info);
1150
1151 return rc;
1152}
1153
1154struct fw_channel_size
1155{
1156 uint8_t channel_id;
1157 uint32_t channel_size;
1158} __attribute__((packed));
1159
1160enum
1161{
1162 CHANNEL_RESVD = 0,
1163 CHANNEL_KCS,
1164 CHANNEL_RMCP_PLUS,
1165 CHANNEL_USB_DATA,
1166 CHANNEL_USB_MASS_STORAGE,
1167} channel_transfer_type;
1168
anil kumar appana159547c2019-05-31 16:08:34 +00001169static constexpr uint8_t channelListSize = 2;
1170/** @brief implements Maximum Firmware Transfer size command
1171 * @parameter
1172 * - none
1173 * @returns IPMI completion code plus response data
1174 * - count - channel count
1175 * - channelList - channel list information
1176 */
1177ipmi::RspType<uint8_t, // channel count
1178 std::array<std::tuple<uint8_t, uint32_t>,
1179 channelListSize> // channel
1180 // list
1181 >
1182 ipmiFirmwareMaxTransferSize()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001183{
anil kumar appana159547c2019-05-31 16:08:34 +00001184 constexpr uint8_t KCSMaxBufSize = 128;
1185 constexpr uint32_t RMCPPLUSMaxBufSize = 50 * 1024;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001186 if (DEBUG)
1187 std::cerr << "Get FW max transfer size\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -07001188 // Byte 1 - Count (N) Number of devices data is being returned for.
1189 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+,
1190 // 03 – usb data, 04 – usb mass storage
1191 // Byte 3-6 - transfer size (little endian)
1192 // Bytes - 7:(5xN) - Repeat of 2 through 6
anil kumar appana159547c2019-05-31 16:08:34 +00001193 constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
1194 channelList = {{{CHANNEL_KCS, KCSMaxBufSize},
1195 {CHANNEL_RMCP_PLUS, RMCPPLUSMaxBufSize}}};
1196 return ipmi::responseSuccess(channelListSize, channelList);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001197}
1198
1199enum
1200{
1201 EXEC_CTX_RESVD = 0,
1202 EXEC_CTX_FULL_LINUX = 0x10,
1203 EXEC_CTX_SAFE_MODE_LINUX = 0x11,
1204} bmc_execution_context;
1205
1206struct fw_execution_context
1207{
1208 uint8_t context;
1209 uint8_t image_selection;
1210} __attribute__((packed));
1211
1212static ipmi_ret_t ipmi_firmware_get_fw_execution_context(
1213 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1214 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1215{
1216 if (DEBUG)
1217 std::cerr << "Get FW execution context\n";
1218
1219 // Byte 1 - execution context
1220 // 0x10 - full linux stack, 0x11 - safe-mode linux stack
1221 // Byte 2 - current image selection
1222 // 1 - primary, 2 - secondary
1223
1224 auto info = reinterpret_cast<struct fw_execution_context *>(response);
anil kumar appana31f88872019-08-02 15:16:27 +00001225 info->context = EXEC_CTX_FULL_LINUX;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001226
anil kumar appana31f88872019-08-02 15:16:27 +00001227 info->image_selection = getActiveBootImage();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001228
1229 // Status code.
1230 ipmi_ret_t rc = IPMI_CC_OK;
1231 *data_len = sizeof(*info);
1232
1233 return rc;
1234}
1235
anil kumar appana31f88872019-08-02 15:16:27 +00001236uint8_t getActiveBootImage(void)
1237{
1238 // 0x01 - primaryImage
1239 constexpr uint8_t primaryImage = 0x01;
1240 // 0x02 - secondaryImage
1241 constexpr uint8_t secondaryImage = 0x02;
1242 uint8_t bootImage = primaryImage;
1243
1244 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1245 auto method = bus->new_method_call(
1246 "xyz.openbmc_project.U_Boot.Environment.Manager",
1247 "/xyz/openbmc_project/u_boot/environment/mgr",
1248 "xyz.openbmc_project.U_Boot.Environment.Manager", "Read");
1249 method.append("bootcmd");
1250 std::string value;
1251 try
1252 {
1253 auto reply = bus->call(method);
1254 reply.read(value);
1255 }
1256 catch (sdbusplus::exception::SdBusError &e)
1257 {
1258 std::cerr << "SDBus Error: " << e.what();
1259 return IPMI_CC_UNSPECIFIED_ERROR;
1260 }
1261 /* cheking for secondary FitImage Address 22480000 */
1262 if (value.find(secondaryFitImageStartAddr) != std::string::npos)
1263 {
1264 bootImage = secondaryImage;
1265 }
1266 else
1267 {
1268 bootImage = primaryImage;
1269 }
1270
1271 return bootImage;
1272}
anil kumar appana6c7d9382019-05-31 14:33:13 +00001273/** @brief implements firmware get status command
1274 * @parameter
1275 * - none
1276 * @returns IPMI completion code plus response data
1277 * - status - processing status
1278 * - percentage - percentage completion
1279 * - check - channel integrity check status
1280 **/
1281ipmi::RspType<uint8_t, // status
1282 uint8_t, // percentage
1283 uint8_t // check
1284 >
1285 ipmiFrmwareGetStatus()
Vernon Mauery52ce6622019-05-22 09:19:46 -07001286
Vernon Mauery52ce6622019-05-22 09:19:46 -07001287{
1288 if (DEBUG)
1289 std::cerr << "Get FW update status\n";
Vernon Mauery52ce6622019-05-22 09:19:46 -07001290 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1291 // 5=ready, f=error, 83=ac cycle required)
1292 // Byte 2 - percent
1293 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
anil kumar appana6c7d9382019-05-31 14:33:13 +00001294 uint8_t status = fw_update_status.state();
1295 uint8_t percent = fw_update_status.percent();
1296 uint8_t check = xfer_hash_check ? xfer_hash_check->status() : 0;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001297
1298 // Status code.
anil kumar appana6c7d9382019-05-31 14:33:13 +00001299 return ipmi::responseSuccess(status, percent, check);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001300}
1301
1302static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0);
1303static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1);
1304static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2);
1305static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3);
1306struct fw_update_options_request
1307{
1308 uint8_t mask;
1309 uint8_t options;
1310} __attribute__((packed));
1311
Vernon Mauery52ce6622019-05-22 09:19:46 -07001312uint32_t fw_update_options = 0;
1313static ipmi_ret_t ipmi_firmware_update_options(
1314 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1315 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1316{
1317 if (DEBUG)
1318 std::cerr << "Get/set FW update options\n";
1319
1320 // request:
1321 // Byte 1 - mask
1322 // Byte 2 - options
1323 // Byte 3-34 - optional integrity check expected value
1324 // response:
1325 // Byte 1 - set options
1326
1327 auto fw_options =
1328 reinterpret_cast<struct fw_update_options_request *>(request);
1329
1330 const char *path = FW_UPDATE_SERVER_INFO_PATH;
1331 const char *iface = FW_UPDATE_SECURITY_INTERFACE;
1332 if ((fw_options->mask & FW_UPDATE_OPTIONS_NO_DOWNREV) &&
1333 (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) !=
1334 (fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV))
1335 {
1336 if (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV)
1337 {
1338 fw_update_options |= FW_UPDATE_OPTIONS_NO_DOWNREV;
anil kumar appana31f88872019-08-02 15:16:27 +00001339 /*setting flag to flase for deferring downgrade support*/
1340 fw_update_status.setInhibitDowngrade(true);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001341 }
1342 else
1343 {
1344 fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV;
anil kumar appana31f88872019-08-02 15:16:27 +00001345 /*setting flag to true for downgrade support*/
1346 fw_update_status.setInhibitDowngrade(false);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001347 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001348 }
1349 if ((fw_options->mask & FW_UPDATE_OPTIONS_DEFER_RESTART) &&
1350 (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) !=
1351 (fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART))
1352 {
1353 if (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART)
1354 {
1355 fw_update_options |= FW_UPDATE_OPTIONS_DEFER_RESTART;
anil kumar appana31f88872019-08-02 15:16:27 +00001356 /* setting flag to true to stop image activation */
1357 fw_update_status.setDeferRestart(true);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001358 }
1359 else
1360 {
anil kumar appana31f88872019-08-02 15:16:27 +00001361 /* setting flag to false for image activation */
Vernon Mauery52ce6622019-05-22 09:19:46 -07001362 fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART;
anil kumar appana31f88872019-08-02 15:16:27 +00001363 fw_update_status.setDeferRestart(false);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001364 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001365 }
1366 if (fw_options->mask & FW_UPDATE_OPTIONS_SHA2_CHECK)
1367 {
1368 auto hash_size = EVP_MD_size(EVP_sha256());
1369 if (fw_options->options & FW_UPDATE_OPTIONS_SHA2_CHECK)
1370 {
1371 if (*data_len != (sizeof(*fw_options) + hash_size))
1372 {
1373 *data_len = 0;
1374 return IPMI_CC_REQ_DATA_LEN_INVALID;
1375 }
1376 xfer_hash_check = std::make_shared<transfer_hash_check>();
1377 auto exp_hash = reinterpret_cast<uint8_t *>(fw_options + 1);
1378 xfer_hash_check->init({exp_hash, exp_hash + hash_size});
1379 fw_update_options |= FW_UPDATE_OPTIONS_SHA2_CHECK;
1380 }
1381 else
1382 {
1383 fw_update_options &= ~FW_UPDATE_OPTIONS_SHA2_CHECK;
1384 // delete the xfer_hash_check object
1385 xfer_hash_check.reset();
1386 }
1387 }
1388 auto options_rsp = reinterpret_cast<uint8_t *>(response);
1389 *options_rsp = fw_update_options;
1390
1391 if (DEBUG)
1392 std::cerr << "current fw_update_options = " << std::hex
1393 << fw_update_options << '\n';
1394 // Status code.
1395 *data_len = sizeof(*options_rsp);
1396 return IPMI_CC_OK;
1397}
1398
1399struct fw_cert_info
1400{
1401 uint16_t cert_len;
1402 uint64_t serial;
1403 uint8_t subject_len;
1404 char subject[255];
1405} __attribute__((packed));
1406
1407static ipmi_ret_t ipmi_firmware_get_root_cert_info(
1408 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1409 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1410{
1411 if (DEBUG)
1412 std::cerr << "Get FW root cert info\n";
1413
1414 // request:
1415 // Byte 1 - certificate ID: request which certificate (ignored)
1416
1417 // response:
1418 // Byte 1-2 - certificate length (little endian)
1419 // Byte 3-10 - serial number (little endian)
1420 // Byte 11 - subject length
1421 // Byte 12-N - subject data
1422
1423 auto cert_info = reinterpret_cast<struct fw_cert_info *>(response);
Vernon Mauery15419dd2019-05-24 09:40:30 -07001424 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1425 auto method = bus->new_method_call(
Vernon Mauery52ce6622019-05-22 09:19:46 -07001426 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH,
1427 "org.freedesktop.DBus.Properties", "GetAll");
1428 method.append(FW_UPDATE_SECURITY_INTERFACE);
1429 std::string subject;
1430 uint64_t serial;
1431 std::string cert;
1432 try
1433 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001434 auto reply = bus->call(method);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001435
1436 std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
1437 reply.read(properties);
1438
1439 for (const auto &t : properties)
1440 {
1441 auto key = t.first;
1442 auto value = t.second;
1443 if (key == "certificate_subject")
1444 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001445 subject = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001446 }
1447 else if (key == "cetificate_serial")
1448 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001449 serial = std::get<uint64_t>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001450 }
1451 else if (key == "certificate")
1452 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001453 cert = std::get<std::string>(value);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001454 }
1455 }
1456 }
1457 catch (sdbusplus::exception::SdBusError &e)
1458 {
1459 std::cerr << "SDBus Error: " << e.what();
1460 return IPMI_CC_UNSPECIFIED_ERROR;
1461 }
1462
1463 cert_info->cert_len = cert.size();
1464 cert_info->serial = serial;
1465 // truncate subject so it fits in the 255-byte array (if necessary)
1466 if (subject.size() > sizeof(cert_info->subject))
1467 subject.resize(sizeof(cert_info->subject));
1468 cert_info->subject_len = subject.size();
1469 std::copy(subject.begin(), subject.end(), cert_info->subject);
1470
1471 // Status code.
1472 ipmi_ret_t rc = IPMI_CC_OK;
1473 // make sure to account for the *actual* size of the subject
1474 *data_len = sizeof(*cert_info) - sizeof(cert_info->subject) +
1475 cert_info->subject_len;
1476
1477 return rc;
1478}
1479
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301480#ifdef INTEL_PFR_ENABLED
1481enum class FwGetRootCertDataTag : uint8_t
Vernon Mauery52ce6622019-05-22 09:19:46 -07001482{
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301483 activeRootKey = 1,
1484 recoveryRootKey,
1485 activeCSK,
1486 recoveryCSK,
1487};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001488
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301489static constexpr char *bmcActivePfmMTDDev = "/dev/mtd/pfm";
1490static constexpr char *bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
1491static constexpr size_t pfmBaseOffsetInImage = 0x400;
1492static constexpr size_t rootkeyOffsetInPfm = 0xA0;
1493static constexpr size_t cskKeyOffsetInPfm = 0x124;
1494static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
1495static constexpr size_t certKeyLen = 96;
1496static constexpr size_t cskSignatureLen = 96;
1497
1498ipmi::RspType<std::array<uint8_t, certKeyLen>,
1499 std::optional<std::array<uint8_t, cskSignatureLen>>>
1500 ipmiGetFwRootCertData(uint8_t certId)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001501{
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301502 size_t certKeyOffset = 0;
1503 size_t cskSigOffset = 0;
1504 std::string mtdDev;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001505
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301506 switch (static_cast<FwGetRootCertDataTag>(certId))
1507 {
1508 case FwGetRootCertDataTag::activeRootKey:
1509 {
1510 mtdDev = bmcActivePfmMTDDev;
1511 certKeyOffset = rootkeyOffsetInPfm;
1512 break;
1513 }
1514 case FwGetRootCertDataTag::recoveryRootKey:
1515 {
1516 mtdDev = bmcRecoveryImgMTDDev;
1517 certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
1518 break;
1519 }
1520 case FwGetRootCertDataTag::activeCSK:
1521 {
1522 mtdDev = bmcActivePfmMTDDev;
1523 certKeyOffset = cskKeyOffsetInPfm;
1524 cskSigOffset = cskSignatureOffsetInPfm;
1525 break;
1526 }
1527 case FwGetRootCertDataTag::recoveryCSK:
1528 {
1529 mtdDev = bmcRecoveryImgMTDDev;
1530 certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
1531 cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
1532 break;
1533 }
1534 default:
1535 {
1536 return ipmi::responseInvalidFieldRequest();
1537 }
1538 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001539
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301540 std::array<uint8_t, certKeyLen> certKey = {0};
Vernon Mauery52ce6622019-05-22 09:19:46 -07001541
Vernon Mauery52ce6622019-05-22 09:19:46 -07001542 try
1543 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301544 SPIDev spiDev(mtdDev);
1545 spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
1546
1547 if (cskSigOffset)
1548 {
1549 std::array<uint8_t, cskSignatureLen> cskSignature = {0};
1550 spiDev.spiReadData(cskSigOffset, cskSignatureLen,
1551 cskSignature.data());
1552 return ipmi::responseSuccess(certKey, cskSignature);
1553 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001554 }
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301555 catch (const std::exception &e)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001556 {
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301557 phosphor::logging::log<phosphor::logging::level::ERR>(
1558 "Exception caught in ipmiGetFwRootCertData",
1559 phosphor::logging::entry("MSG=%s", e.what()));
1560 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001561 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001562
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301563 return ipmi::responseSuccess(certKey, std::nullopt);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001564}
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301565#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001566
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301567ipmi::RspType<uint32_t>
1568 ipmiFwImageWriteData(const std::vector<uint8_t> &writeData)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001569{
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301570 const uint8_t ccCmdNotSupportedInPresentState = 0xD5;
1571 size_t writeDataLen = writeData.size();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001572
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301573 if (!writeDataLen)
1574 {
1575 return ipmi::responseReqDataLenInvalid();
1576 }
1577
Vernon Mauery52ce6622019-05-22 09:19:46 -07001578 if (fw_update_status.state() != fw_update_status_cache::FW_STATE_DOWNLOAD)
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301579 {
1580 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1581 "Invalid firmware update state.");
1582 return ipmi::response(ccCmdNotSupportedInPresentState);
1583 }
Vernon Mauery52ce6622019-05-22 09:19:46 -07001584
1585 std::ofstream out(FIRMWARE_BUFFER_FILE,
1586 std::ofstream::binary | std::ofstream::app);
1587 if (!out)
1588 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301589 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1590 "Error while opening file.");
1591 return ipmi::responseUnspecifiedError();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001592 }
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301593
1594 uint64_t fileDataLen = out.tellp();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301595
1596 if ((fileDataLen + writeDataLen) > FIRMWARE_BUFFER_MAX_SIZE)
Vernon Mauery52ce6622019-05-22 09:19:46 -07001597 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301598 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1599 "Firmware image size exceeds the limit");
1600 return ipmi::responseInvalidFieldRequest();
Vernon Mauery52ce6622019-05-22 09:19:46 -07001601 }
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301602
1603 const char *data = reinterpret_cast<const char *>(writeData.data());
1604 out.write(data, writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001605 out.close();
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301606
Vernon Mauery52ce6622019-05-22 09:19:46 -07001607 if (xfer_hash_check)
1608 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301609 xfer_hash_check->hash(writeData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001610 }
1611
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301612#ifdef INTEL_PFR_ENABLED
1613 /* PFR image block 0 - As defined in HAS */
1614 struct PFRImageBlock0
1615 {
1616 uint32_t tag;
1617 uint32_t pcLength;
1618 uint32_t pcType;
1619 uint32_t reserved1;
1620 uint8_t hash256[32];
1621 uint8_t hash384[48];
1622 uint8_t reserved2[32];
1623 } __attribute__((packed));
1624
1625 /* Get the PFR block 0 data and read the uploaded image
1626 * information( Image type, length etc) */
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301627 if (((fileDataLen + writeDataLen) >= sizeof(PFRImageBlock0)) &&
1628 (!block0Mapped))
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301629 {
1630 struct PFRImageBlock0 block0Data = {0};
1631
1632 std::ifstream inFile(FIRMWARE_BUFFER_FILE,
1633 std::ios::binary | std::ios::in);
1634 inFile.read(reinterpret_cast<char *>(&block0Data), sizeof(block0Data));
1635 inFile.close();
1636
1637 uint32_t magicNum = block0Data.tag;
1638
1639 /* Validate the magic number */
1640 if (magicNum != perBlock0MagicNum)
1641 {
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301642 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1643 "PFR image magic number not matched");
1644 return ipmi::responseInvalidFieldRequest();
Rajashekar Gade Reddydf5e3272019-09-05 18:10:53 +05301645 }
1646 // Note:imgLength, imgType and block0Mapped are in global scope, as
1647 // these are used in cascaded updates.
1648 imgLength = block0Data.pcLength;
1649 imgType = block0Data.pcType;
1650 block0Mapped = true;
1651 }
1652#endif // end of INTEL_PFR_ENABLED
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301653 return ipmi::responseSuccess(writeDataLen);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001654}
1655
Vernon Mauery52ce6622019-05-22 09:19:46 -07001656struct intc_app_get_buffer_size_resp
1657{
1658 uint8_t kcs_size;
1659 uint8_t ipmb_size;
1660} __attribute__((packed));
1661
1662static constexpr int KCS_MAX_BUFFER_SIZE = 63;
1663static constexpr int IPMB_MAX_BUFFER_SIZE = 128;
1664static ipmi_ret_t ipmi_intel_app_get_buffer_size(
1665 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1666 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1667{
1668 auto msg_reply =
1669 reinterpret_cast<intc_app_get_buffer_size_resp *>(response);
1670 // for now this is hard coded; really this number is dependent on
1671 // the BMC kcs driver as well as the host kcs driver....
1672 // we can't know the latter.
1673 msg_reply->kcs_size = KCS_MAX_BUFFER_SIZE / 4;
1674 msg_reply->ipmb_size = IPMB_MAX_BUFFER_SIZE / 4;
1675 *data_len = sizeof(*msg_reply);
1676
1677 return IPMI_CC_OK;
1678}
1679
1680static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20;
1681static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21;
1682static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22;
1683static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23;
1684static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001685static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26;
1686static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27;
anil kumar appanab57098a2019-05-28 16:22:33 +00001687static constexpr ipmi_cmd_t cmdFirmwareExitFirmwareUpdateMode = 0x28;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001688static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29;
1689static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a;
1690static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b;
Vernon Mauery52ce6622019-05-22 09:19:46 -07001691static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d;
1692static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0;
1693static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0;
1694
1695static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30;
1696static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66;
1697
1698static void register_netfn_firmware_functions()
1699{
1700 // guarantee that we start with an already timed out timestamp
1701 fw_random_number_timestamp =
1702 std::chrono::steady_clock::now() - FW_RANDOM_NUMBER_TTL;
1703
1704 unlink(FIRMWARE_BUFFER_FILE);
1705
1706 // <Get BT Interface Capabilities>
1707 if (DEBUG)
1708 std::cerr << "Registering firmware update commands\n";
1709
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301710#ifdef INTEL_PFR_ENABLED
1711 // Following commands are supported only for PFR enabled platforms
1712 // CMD:0x20 - Get Firmware Version Information
Vernon Mauery52ce6622019-05-22 09:19:46 -07001713
AppaRao Puli37fde6b2019-10-25 16:37:50 +05301714 // get firmware version information
1715 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1716 ipmi::firmware::cmdGetFwVersionInfo,
1717 ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
1718#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001719 // get firmware security version information
1720 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO,
1721 NULL, ipmi_firmware_get_fw_security_revision,
1722 PRIVILEGE_ADMIN);
1723
1724 // get channel information (max transfer sizes)
anil kumar appana159547c2019-05-31 16:08:34 +00001725 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1726 IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO,
1727 ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001728
1729 // get bmc execution context
1730 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_BMC_EXEC_CTX, NULL,
1731 ipmi_firmware_get_fw_execution_context,
1732 PRIVILEGE_ADMIN);
1733
1734 // get root certificate information
1735 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO,
1736 NULL, ipmi_firmware_get_root_cert_info,
1737 PRIVILEGE_ADMIN);
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301738#ifdef INTEL_PFR_ENABLED
Vernon Mauery52ce6622019-05-22 09:19:46 -07001739 // get root certificate data
Rajashekar Gade Reddyc8864062019-10-15 16:52:30 +05301740 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1741 ipmi::firmware::cmdFwGetRootCertData,
1742 ipmi::Privilege::Admin, ipmiGetFwRootCertData);
1743#endif
Vernon Mauery52ce6622019-05-22 09:19:46 -07001744
1745 // generate bmc fw update random number (for enter fw tranfer mode)
1746 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM,
1747 NULL, ipmi_firmware_get_fw_random_number,
1748 PRIVILEGE_ADMIN);
1749
AppaRao Puli4b3e1c72019-10-16 20:53:09 +05301750 // Set Firmware Update Mode(0x27)
1751 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1752 IPMI_CMD_FW_SET_FW_UPDATE_MODE,
1753 ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001754
1755 // exit firmware update mode
anil kumar appanab57098a2019-05-28 16:22:33 +00001756 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
1757 cmdFirmwareExitFirmwareUpdateMode,
1758 ipmi::Privilege::Admin, ipmiFirmwareExitFwUpdateMode);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001759
1760 // firmware control mechanism (set filename, usb, etc.)
1761 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_UPDATE_CONTROL, NULL,
1762 ipmi_firmware_control, PRIVILEGE_ADMIN);
1763
1764 // get firmware update status
anil kumar appana6c7d9382019-05-31 14:33:13 +00001765 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
1766 IPMI_CMD_FW_GET_STATUS, ipmi::Privilege::Admin,
1767 ipmiFrmwareGetStatus);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001768 // set firmware update options (no downgrade, etc.)
1769 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS,
1770 NULL, ipmi_firmware_update_options, PRIVILEGE_ADMIN);
1771
1772 // write image data
Rajashekar Gade Reddy3ec26202019-10-29 14:10:00 +05301773 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
1774 ipmi::firmware::cmdFwImageWriteData,
1775 ipmi::Privilege::Admin, ipmiFwImageWriteData);
Vernon Mauery52ce6622019-05-22 09:19:46 -07001776
Vernon Mauery52ce6622019-05-22 09:19:46 -07001777 // get buffer size is used by fw update (exclusively?)
1778 ipmi_register_callback(NETFUN_INTC_APP, IPMI_CMD_INTC_GET_BUFFER_SIZE, NULL,
1779 ipmi_intel_app_get_buffer_size, PRIVILEGE_USER);
1780 return;
1781}