blob: dd9ea832e01e86590b42b21f201f1ddc2b4116bb [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>
20#include <map>
21#include <random>
22#include <sdbusplus/bus.hpp>
23#include <sdbusplus/bus/match.hpp>
24#include <sdbusplus/server/object.hpp>
25#include <sdbusplus/timer.hpp>
26#include <sstream>
27
28static void register_netfn_firmware_functions() __attribute__((constructor));
29
30// oem return code for firmware update control
31constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5;
32constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80;
33
34static sdbusplus::bus::bus _bus(ipmid_get_sd_bus_connection());
35
36static constexpr bool DEBUG = false;
37
38static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] =
39 "xyz.openbmc_project.fwupdate1.server";
40
41static constexpr char FW_UPDATE_SERVER_PATH[] =
42 "/xyz/openbmc_project/fwupdate1";
43static constexpr char FW_UPDATE_SERVER_INFO_PATH[] =
44 "/xyz/openbmc_project/fwupdate1/info";
45static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] =
46 "/xyz/openbmc_project/fwupdate1/info/bmc_active";
47static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] =
48 "/xyz/openbmc_project/fwupdate1/info/bmc_backup";
49
50static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1";
51static constexpr char FW_UPDATE_INFO_INTERFACE[] =
52 "xyz.openbmc_project.fwupdate1.fwinfo";
53static constexpr char FW_UPDATE_SECURITY_INTERFACE[] =
54 "xyz.openbmc_project.fwupdate1.security";
55
56constexpr std::size_t operator""_MB(unsigned long long v)
57{
58 return 1024u * 1024u * v;
59}
60static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB;
61
62static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin";
63static bool local_download_is_active(void)
64{
65 struct stat sb;
66 if (stat(FIRMWARE_BUFFER_FILE, &sb) < 0)
67 return false;
68 return true;
69}
70
71class fw_update_status_cache
72{
73 public:
74 enum
75 {
76 FW_STATE_INIT = 0,
77 FW_STATE_IDLE,
78 FW_STATE_DOWNLOAD,
79 FW_STATE_VERIFY,
80 FW_STATE_WRITE,
81 FW_STATE_READY,
82 FW_STATE_ERROR = 0x0f,
83 FW_STATE_AC_CYCLE_REQUIRED = 0x83,
84 };
85 fw_update_status_cache()
86 {
87 _match = std::make_shared<sdbusplus::bus::match::match>(
88 _bus,
89 sdbusplus::bus::match::rules::propertiesChanged(
90 FW_UPDATE_SERVER_PATH, FW_UPDATE_INTERFACE),
91 [&](sdbusplus::message::message &msg) {
92 if (DEBUG)
93 std::cerr << "propertiesChanged lambda\n";
94 std::map<std::string, ipmi::DbusVariant> props;
95 std::vector<std::string> inval;
96 std::string iface;
97 msg.read(iface, props, inval);
98 _parse_props(props);
99 });
100 _initial_fetch();
101 }
102
103 uint8_t state()
104 {
105 if (DEBUG)
106 std::cerr << "fw-state: 0x" << std::hex << (int)_state << '\n';
107 if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
108 local_download_is_active())
109 {
110 _state = FW_STATE_DOWNLOAD;
111 _percent = 0;
112 }
113 return _state;
114 }
115 uint8_t percent()
116 {
117 return _percent;
118 }
119 std::string msg()
120 {
121 return _msg;
122 }
123#ifdef UPDATER_ENABLED
124 std::string get_software_obj_path()
125 {
126 return _software_obj_path;
127 }
128 void set_software_obj_path(std::string &obj_path)
129 {
130 _software_obj_path = obj_path;
131 _state = FW_STATE_WRITE;
132 _percent = 0;
133 _match = std::make_shared<sdbusplus::bus::match::match>(
134 _bus,
135 sdbusplus::bus::match::rules::propertiesChanged(
136 _software_obj_path,
137 "xyz.openbmc_project.Software.ActivationProgress"),
138 [&](sdbusplus::message::message &msg) {
139 if (DEBUG)
140 std::cerr << "propertiesChanged lambda\n";
141 std::map<std::string, ipmi::DbusVariant> props;
142 std::vector<std::string> inval;
143 std::string iface;
144 msg.read(iface, props, inval);
145 _parse_props(props);
146 });
147 }
148 uint8_t activation_timer_timeout()
149 {
150 std::cerr << "activation_timer_timout(): increase percentage...\n";
151 _percent = _percent + 5;
152 std::cerr << "_percent = " << std::string((char *)&_percent) << "\n";
153 return _percent;
154 }
155#endif
156 protected:
157 void _parse_props(std::map<std::string, ipmi::DbusVariant> &properties)
158 {
159 if (DEBUG)
160 std::cerr << "propertiesChanged (" << properties.size()
161 << " elements)";
162 for (const auto &t : properties)
163 {
164 auto key = t.first;
165 auto value = t.second;
166 if (key == "state")
167 {
168 auto state =
169 sdbusplus::message::variant_ns::get<std::string>(value);
170 if (DEBUG)
171 std::cerr << ", state=" << state;
172 if (state == "INIT")
173 _state = FW_STATE_INIT;
174 else if (state == "IDLE")
175 _state = FW_STATE_IDLE;
176 else if (state == "DOWNLOAD")
177 _state = FW_STATE_DOWNLOAD;
178 else if (state == "VERIFY")
179 _state = FW_STATE_VERIFY;
180 else if (state == "WRITE")
181 _state = FW_STATE_WRITE;
182 else if (state == "READY")
183 _state = FW_STATE_READY;
184 else if (state == "ERROR")
185 _state = FW_STATE_ERROR;
186 else if (state == "AC_CYCLE_REQUIRED")
187 _state = FW_STATE_AC_CYCLE_REQUIRED;
188 else
189 {
190 _state = FW_STATE_ERROR;
191 _msg = "internal error";
192 }
193 }
194 else if (key == "percent")
195 {
196 _percent = sdbusplus::message::variant_ns::get<int32_t>(value);
197 if (DEBUG)
198 std::cerr << ", pct=" << (int)_percent;
199 }
200 else if (key == "msg")
201 {
202 _msg = sdbusplus::message::variant_ns::get<std::string>(value);
203 if (DEBUG)
204 std::cerr << ", msg='" << _msg << '\'';
205 }
206 else if (key == "Progress")
207 {
208 _percent = sdbusplus::message::variant_ns::get<uint8_t>(value);
209 ;
210 if (_percent == 100)
211 _state = FW_STATE_READY;
212 }
213 }
214 if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
215 local_download_is_active())
216 {
217 _state = FW_STATE_DOWNLOAD;
218 _percent = 0;
219 }
220 if (DEBUG)
221 std::cerr << '\n';
222 }
223 void _initial_fetch()
224 {
225#ifndef UPDATER_ENABLED
226 auto method = _bus.new_method_call(
227 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH,
228 "org.freedesktop.DBus.Properties", "GetAll");
229 method.append(FW_UPDATE_INTERFACE);
230 if (DEBUG)
231 std::cerr << "fetch fw status via dbus...\n";
232 try
233 {
234 auto reply = _bus.call(method);
235
236 std::map<std::string, ipmi::DbusVariant> properties;
237 reply.read(properties);
238 _parse_props(properties);
239 }
240 catch (sdbusplus::exception::SdBusError &e)
241 {
242 std::cerr << "Failed in _initial_fetch(): SDBus Error: " << e.what()
243 << "\n";
244 return;
245 }
246#endif
247 }
248
249 std::shared_ptr<sdbusplus::bus::match::match> _match;
250 uint8_t _state = 0;
251 uint8_t _percent = 0;
252 std::string _msg;
253
254 private:
255 std::string _software_obj_path;
256};
257
258static fw_update_status_cache fw_update_status;
259
260static std::chrono::steady_clock::time_point fw_random_number_timestamp;
261static constexpr int FW_RANDOM_NUMBER_LENGTH = 8;
262static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30);
263static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH];
264
265static ipmi_ret_t ipmi_firmware_get_fw_random_number(
266 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
267 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
268{
269 std::random_device rd;
270 std::default_random_engine gen(rd());
271 std::uniform_int_distribution<> dist{0, 255};
272
273 if (*data_len != 0)
274 {
275 *data_len = 0;
276 return IPMI_CC_REQ_DATA_LEN_INVALID;
277 }
278
279 fw_random_number_timestamp = std::chrono::steady_clock::now();
280
281 uint8_t *msg_reply = static_cast<uint8_t *>(response);
282 for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
283 fw_random_number[i] = msg_reply[i] = dist(gen);
284
285 if (DEBUG)
286 std::cerr << "FW Rand Num: 0x" << std::hex << (int)msg_reply[0] << " 0x"
287 << (int)msg_reply[1] << " 0x" << (int)msg_reply[2] << " 0x"
288 << (int)msg_reply[3] << " 0x" << (int)msg_reply[4] << " 0x"
289 << (int)msg_reply[5] << " 0x" << (int)msg_reply[6] << " 0x"
290 << (int)msg_reply[7] << '\n';
291
292 *data_len = FW_RANDOM_NUMBER_LENGTH;
293
294 return IPMI_CC_OK;
295}
296
297static ipmi_ret_t ipmi_firmware_enter_fw_transfer_mode(
298 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
299 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
300{
301 if (DEBUG)
302 std::cerr << "Enter FW transfer mode requested, data_len = "
303 << *data_len << '\n';
304
305 if (*data_len != FW_RANDOM_NUMBER_LENGTH)
306 {
307 *data_len = 0;
308 return IPMI_CC_REQ_DATA_LEN_INVALID;
309 }
310 *data_len = 0;
311
312 auto rq_time = std::chrono::steady_clock::now();
313 if (DEBUG)
314 std::cerr << "now - fwts = "
315 << std::chrono::duration_cast<std::chrono::microseconds>(
316 rq_time - fw_random_number_timestamp)
317 .count()
318 << " us\n";
319 if (std::chrono::duration_cast<std::chrono::microseconds>(
320 rq_time - fw_random_number_timestamp)
321 .count() > std::chrono::duration_cast<std::chrono::microseconds>(
322 FW_RANDOM_NUMBER_TTL)
323 .count())
324 {
325 if (DEBUG)
326 std::cerr << "key timeout\n";
327 return IPMI_CC_PARM_OUT_OF_RANGE;
328 }
329
330 uint8_t *msg_request = static_cast<uint8_t *>(request);
331 for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
332 {
333 if (fw_random_number[i] != msg_request[i])
334 {
335 if (DEBUG)
336 std::cerr << "key error" << (int)fw_random_number[i]
337 << "!=" << (int)msg_request[i] << "\n";
338 return IPMI_CC_INVALID_FIELD_REQUEST;
339 }
340 }
341
342 if (fw_update_status.state() != fw_update_status_cache::FW_STATE_IDLE
343 // TODO: Allowing FW_STATE_INIT here to let image activation available
344 // without being in FW_STATE_IDLE, need to fix/adjust the state machine
345 // to match xyz.openbmc_project.Software.BMC.Updater service activation
346 // mechanism at finer grain
347 && fw_update_status.state() != fw_update_status_cache::FW_STATE_INIT)
348 {
349 if (DEBUG)
350 std::cerr << "not in INIT or IDLE\n";
351 return IPMI_CC_INVALID_FIELD_REQUEST;
352 }
353 // FIXME? c++ doesn't off an option for exclusive file creation
354 FILE *fp = fopen(FIRMWARE_BUFFER_FILE, "wx");
355 if (!fp)
356 {
357 if (DEBUG)
358 std::cerr << "failed to create buffer file\n";
359 return IPMI_CC_INVALID_FIELD_REQUEST;
360 }
361 fclose(fp);
362
363 return IPMI_CC_OK;
364}
365
366static ipmi_ret_t ipmi_firmware_exit_fw_update_mode(
367 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
368 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
369{
370 if (DEBUG)
371 std::cerr << "Exit FW update mode\n";
372 *data_len = 0;
373
374 ipmi_ret_t rc = IPMI_CC_OK;
375 switch (fw_update_status.state())
376 {
377 case fw_update_status_cache::FW_STATE_INIT:
378 case fw_update_status_cache::FW_STATE_IDLE:
379 rc = IPMI_CC_INVALID_FIELD_REQUEST;
380 break;
381 case fw_update_status_cache::FW_STATE_DOWNLOAD:
382 unlink(FIRMWARE_BUFFER_FILE);
383 case fw_update_status_cache::FW_STATE_VERIFY:
384 break;
385 case fw_update_status_cache::FW_STATE_WRITE:
386 rc = IPMI_CC_INVALID_FIELD_REQUEST;
387 break;
388 case fw_update_status_cache::FW_STATE_READY:
389 case fw_update_status_cache::FW_STATE_ERROR:
390 unlink(FIRMWARE_BUFFER_FILE);
391 break;
392 case fw_update_status_cache::FW_STATE_AC_CYCLE_REQUIRED:
393 rc = IPMI_CC_INVALID_FIELD_REQUEST;
394 break;
395 }
396 if (rc == IPMI_CC_OK)
397 {
398 // attempt to reset the state machine -- this may fail
399 auto method = _bus.new_method_call(
400 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH,
401 "org.freedesktop.DBus.Properties", "Abort");
402 try
403 {
404 auto reply = _bus.call(method);
405
406 ipmi::DbusVariant retval;
407 reply.read(retval);
408 if (sdbusplus::message::variant_ns::get<int>(retval) != 0)
409 rc = IPMI_CC_INVALID_FIELD_REQUEST;
410 }
411 catch (sdbusplus::exception::SdBusError &e)
412 {
413 std::cerr << "SDBus Error: " << e.what() << "\n";
414 return IPMI_CC_UNSPECIFIED_ERROR;
415 }
416 }
417
418 return rc;
419}
420
421static void post_transfer_complete_handler(
422 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher);
423static bool request_start_firmware_update(const std::string &uri)
424{
425 if (DEBUG)
426 std::cerr << "request start firmware update()\n";
427
428#ifdef UPDATER_ENABLED
429 // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
430 // the code gets to this point, the file should be transferred start the
431 // request (creating a new file in /tmp/images causes the update manager to
432 // check if it is ready for activation)
433 static std::unique_ptr<sdbusplus::bus::match::match> fw_update_matcher;
434 post_transfer_complete_handler(fw_update_matcher);
435 std::filesystem::rename(
436 uri, "/tmp/images/" +
437 boost::uuids::to_string(boost::uuids::random_generator()()));
438#else
439 auto method =
440 _bus.new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH,
441 FW_UPDATE_INTERFACE, "start");
442 if (DEBUG)
443 std::cerr << "fwupdate1.start: " << uri << '\n';
444 method.append(uri);
445 try
446 {
447 auto reply = _bus.call(method);
448
449 uint32_t retval;
450 reply.read(retval);
451 if (retval != 0)
452 {
453 if (DEBUG)
454 std::cerr << "method returned non-zero: " << retval << "\n";
455 return false;
456 }
457 return true;
458 }
459 catch (sdbusplus::exception::SdBusError &e)
460 {
461 std::cerr << "SDBus Error: " << e.what();
462 return false;
463 }
464#endif
465 return true;
466}
467
468static bool request_abort_firmware_update()
469{
470 auto method =
471 _bus.new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH,
472 FW_UPDATE_INTERFACE, "abort");
473 try
474 {
475 auto reply = _bus.call(method);
476
477 unlink(FIRMWARE_BUFFER_FILE);
478 uint32_t retval;
479 reply.read(retval);
480 if (retval != 0)
481 return false;
482 }
483 catch (sdbusplus::exception::SdBusError &e)
484 {
485 std::cerr << "SDBus Error: " << e.what() << "\n";
486 unlink(FIRMWARE_BUFFER_FILE);
487 return false;
488 }
489 return true;
490}
491
492class transfer_hash_check
493{
494 public:
495 enum hash_check
496 {
497 CHECK_NOT_REQUESTED = 0,
498 CHECK_REQUESTED,
499 CHECK_PASSED_SHA2,
500 CHECK_RESVD1,
501 CHECK_FAILED_SHA2 = 0xe2,
502 CHECK_RESVD2 = 0xe3,
503 };
504
505 protected:
506 EVP_MD_CTX *_ctx;
507 std::vector<uint8_t> _expected;
508 enum hash_check _check;
509 bool _started;
510
511 public:
512 transfer_hash_check() : _check(CHECK_NOT_REQUESTED), _started(false)
513 {
514 }
515 ~transfer_hash_check()
516 {
517 if (_ctx)
518 {
519 EVP_MD_CTX_destroy(_ctx);
520 _ctx = NULL;
521 }
522 }
523 void init(const std::vector<uint8_t> &expected)
524 {
525 _expected = expected;
526 _check = CHECK_REQUESTED;
527 _ctx = EVP_MD_CTX_create();
528 EVP_DigestInit(_ctx, EVP_sha256());
529 }
530 void hash(const std::vector<uint8_t> &data)
531 {
532 if (!_started)
533 _started = true;
534 EVP_DigestUpdate(_ctx, data.data(), data.size());
535 }
536 void clear()
537 {
538 // if not started, nothing to clear
539 if (_started)
540 {
541 if (_ctx)
542 EVP_MD_CTX_destroy(_ctx);
543 if (_check != CHECK_NOT_REQUESTED)
544 _check = CHECK_REQUESTED;
545 _ctx = EVP_MD_CTX_create();
546 EVP_DigestInit(_ctx, EVP_sha256());
547 }
548 }
549 enum hash_check check()
550 {
551 if (_check == CHECK_REQUESTED)
552 {
553 unsigned int len;
554 std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
555 EVP_DigestFinal(_ctx, digest.data(), &len);
556 if (digest == _expected)
557 {
558 if (DEBUG)
559 std::cerr << "transfer sha2 check passed\n";
560 _check = CHECK_PASSED_SHA2;
561 }
562 else
563 {
564 if (DEBUG)
565 std::cerr << "transfer sha2 check failed\n";
566 _check = CHECK_FAILED_SHA2;
567 }
568 }
569 return _check;
570 }
571 uint8_t status() const
572 {
573 return static_cast<uint8_t>(_check);
574 }
575};
576
577std::shared_ptr<transfer_hash_check> xfer_hash_check;
578
579#ifdef UPDATER_ENABLED
580static void activate_image(const char *obj_path)
581{
582 if (DEBUG)
583 {
584 std::cerr << "activateImage()...\n";
585 std::cerr << "obj_path = " << obj_path << "\n";
586 }
587 auto method_call = _bus.new_method_call(
588 "xyz.openbmc_project.Software.BMC.Updater", obj_path,
589 "org.freedesktop.DBus.Properties", "Set");
590 method_call.append("xyz.openbmc_project.Software.Activation",
591 "RequestedActivation",
592 sdbusplus::message::variant<std::string>(
593 "xyz.openbmc_project.Software.Activation."
594 "RequestedActivations.Active"));
595
596 try
597 {
598 auto method_call_reply = _bus.call(method_call);
599 }
600 catch (sdbusplus::exception::SdBusError &e)
601 {
602 std::cerr << "Failed in activate_image(): SDBus Error: " << e.what()
603 << "\n";
604 return;
605 }
606}
607
608static void post_transfer_complete_handler(
609 std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher)
610{
611 // Setup timer for watching signal
612 static phosphor::Timer timer(
613 [&fw_update_matcher]() { fw_update_matcher = nullptr; });
614
615 static phosphor::Timer activation_status_timer([]() {
616 if (fw_update_status.activation_timer_timeout() >= 95)
617 {
618 activation_status_timer.stop();
619 }
620 });
621
622 timer.start(std::chrono::microseconds(5000000), false);
623
624 // callback function for capturing signal
625 auto callback = [&fw_update_matcher](sdbusplus::message::message &m) {
626 if (DEBUG)
627 std::cerr << "[complete] Match fired\n";
628 bool flag = false;
629
630 std::vector<std::pair<
631 std::string,
632 std::vector<std::pair<std::string,
633 sdbusplus::message::variant<std::string>>>>>
634 interfaces_properties;
635
636 sdbusplus::message::object_path obj_path;
637
638 try
639 {
640 m.read(obj_path, interfaces_properties); // Read in the object path
641 // that was just created
642 }
643 catch (std::exception &e)
644 {
645 std::cerr
646 << "[complete] Failed at post_transfer_complete-handler : "
647 << e.what() << "\n";
648 }
649 // constructing response message
650 if (DEBUG)
651 std::cerr << "[complete] obj path = " << obj_path.str << "\n";
652 for (auto &interface : interfaces_properties)
653 {
654 if (DEBUG)
655 std::cerr << "[complete] interface = " << interface.first
656 << "\n";
657
658 if (interface.first == "xyz.openbmc_project.Software.Activation")
659 {
660 // cancel timer only when
661 // xyz.openbmc_project.Software.Activation interface is
662 // added
663
664 if (DEBUG)
665 std::cerr << "[complete] Attempt to cancel timer...\n";
666 try
667 {
668 timer.stop();
669 activation_status_timer.start(
670 std::chrono::microseconds(3000000), true);
671 }
672 catch (std::exception &e)
673 {
674 std::cerr << "[complete] cancel timer error: " << e.what()
675 << "\n";
676 }
677
678 fw_update_status.set_software_obj_path(obj_path.str);
679 activate_image(obj_path.str.c_str());
680 if (DEBUG)
681 std::cerr << "[complete] returned from activeImage()\n";
682
683 fw_update_matcher = nullptr;
684 }
685 }
686 };
687
688 // Adding matcher
689 fw_update_matcher = std::make_unique<sdbusplus::bus::match::match>(
690 _bus,
691 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
692 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
693 callback);
694}
695#endif
696
697class MappedFile
698{
699 public:
700 MappedFile(const std::string &fname) : addr(nullptr), fsize(0)
701 {
702 std::error_code ec;
703 size_t sz = std::filesystem::file_size(fname, ec);
704 int fd = open(fname.c_str(), O_RDONLY);
705 if (!ec || fd < 0)
706 {
707 return;
708 }
709 void *tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
710 close(fd);
711 if (tmp == MAP_FAILED)
712 {
713 return;
714 }
715 addr = tmp;
716 fsize = sz;
717 }
718
719 ~MappedFile()
720 {
721 if (addr)
722 {
723 munmap(addr, fsize);
724 }
725 }
726 const uint8_t *data() const
727 {
728 return static_cast<const uint8_t *>(addr);
729 }
730 size_t size() const
731 {
732 return fsize;
733 }
734
735 private:
736 size_t fsize;
737 void *addr;
738};
739
740static int transfer_from_file(const std::string &uri, bool move = true)
741{
742 std::error_code ec;
743 if (DEBUG)
744 std::cerr << "transfer_from_file(" << uri << ")\n";
745 if (move)
746 {
747 std::filesystem::rename(uri, FIRMWARE_BUFFER_FILE, ec);
748 }
749 else
750 {
751 std::filesystem::copy(uri, FIRMWARE_BUFFER_FILE,
752 std::filesystem::copy_options::overwrite_existing,
753 ec);
754 }
755 if (xfer_hash_check)
756 {
757 MappedFile mappedfw(uri);
758 xfer_hash_check->hash(
759 {mappedfw.data(), mappedfw.data() + mappedfw.size()});
760 }
761 if (ec.value())
762 {
763 std::cerr << "cp/mv returns: " << ec.message() << "(" << ec.value()
764 << ")\n";
765 }
766 return ec.value();
767}
768
769template <typename... ArgTypes>
770static int executeCmd(const char *path, ArgTypes &&... tArgs)
771{
772 boost::process::child execProg(path, const_cast<char *>(tArgs)...);
773 execProg.wait();
774 return execProg.exit_code();
775}
776
777constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl";
778constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt";
779constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img";
780constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev";
781constexpr size_t fwPathMaxLength = 255;
782static int transfer_from_usb(const std::string &uri)
783{
784 int ret, sysret;
785 char fwpath[fwPathMaxLength];
786 if (DEBUG)
787 std::cerr << "transfer_from_usb(" << uri << ")\n";
788 ret = executeCmd(USB_CTRL_PATH, "mount", FWUPDATE_USB_VOL_IMG,
789 FWUPDATE_MOUNT_POINT);
790 if (ret)
791 {
792 return ret;
793 }
794
795 std::string usb_path = std::string(FWUPDATE_MOUNT_POINT) + "/" + uri;
796 ret = transfer_from_file(usb_path, false);
797
798 executeCmd(USB_CTRL_PATH, "cleanup", FWUPDATE_USB_VOL_IMG,
799 FWUPDATE_MOUNT_POINT);
800 return ret;
801}
802
803static bool transfer_firmware_from_uri(const std::string &uri)
804{
805 static constexpr char FW_URI_FILE[] = "file://";
806 static constexpr char FW_URI_USB[] = "usb://";
807 if (DEBUG)
808 std::cerr << "transfer_firmware_from_uri(" << uri << ")\n";
809 if (boost::algorithm::starts_with(uri, FW_URI_FILE))
810 {
811 std::string fname = uri.substr(sizeof(FW_URI_FILE) - 1);
812 if (fname != FIRMWARE_BUFFER_FILE)
813 {
814 return 0 == transfer_from_file(fname);
815 }
816 return true;
817 }
818 if (boost::algorithm::starts_with(uri, FW_URI_USB))
819 {
820 std::string fname = uri.substr(sizeof(FW_URI_USB) - 1);
821 return 0 == transfer_from_usb(fname);
822 }
823 return false;
824}
825
826/* Get USB-mass-storage device status: inserted => true, ejected => false */
827static int usb_get_status()
828{
829 static constexpr char usb_gadget_base[] = "/sys/kernel/config/usb_gadget/";
830 auto usb_device =
831 std::filesystem::path(usb_gadget_base) / FWUPDATE_USB_DEV_NAME;
832 std::error_code ec;
833 return std::filesystem::exists(usb_device, ec) && !ec;
834}
835
836/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
837static int usb_attach_device()
838{
839 if (usb_get_status())
840 {
841 return 1;
842 }
843 int ret =
844 executeCmd(USB_CTRL_PATH, "setup", FWUPDATE_USB_VOL_IMG,
845 std::to_string(FIRMWARE_BUFFER_MAX_SIZE / 1_MB).c_str());
846 if (!ret)
847 {
848 ret = executeCmd(USB_CTRL_PATH, "insert", FWUPDATE_USB_DEV_NAME,
849 FWUPDATE_USB_VOL_IMG);
850 }
851 return ret;
852}
853
854/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
855static int usb_detach_device()
856{
857 if (!usb_get_status())
858 {
859 return 1;
860 }
861 return executeCmd(USB_CTRL_PATH, "eject", FWUPDATE_USB_DEV_NAME);
862}
863
864constexpr uint8_t controls_init = 0x00;
865constexpr uint8_t controls_transfer_started = 0x01;
866constexpr uint8_t controls_transfer_completed = 0x02;
867constexpr uint8_t controls_transfer_aborted = 0x04;
868constexpr uint8_t controls_usb_attached = 0x08;
869
870struct fw_update_control_request
871{
872 enum knob
873 {
874 CTRL_GET = 0,
875 CTRL_XFER_START,
876 CTRL_XFER_COMPLETE,
877 CTRL_XFER_ABORT,
878 CTRL_SET_FILENAME,
879 CTRL_USB_ATTACH,
880 CTRL_USB_DETACH,
881 } __attribute__((packed));
882 enum knob control;
883 uint8_t nlen;
884 char filename[fwPathMaxLength];
885} __attribute__((packed));
886
887static ipmi_ret_t ipmi_firmware_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
888 ipmi_request_t request,
889 ipmi_response_t response,
890 ipmi_data_len_t data_len,
891 ipmi_context_t context)
892{
893 static std::string fw_xfer_uri;
894
895 if (DEBUG)
896 std::cerr << "FW update control\n";
897 *data_len = 0;
898
899 static uint8_t controls = controls_init;
900 ipmi_ret_t rc = IPMI_CC_OK;
901 auto ctrl_req = reinterpret_cast<fw_update_control_request *>(request);
902 auto ctrl_resp = reinterpret_cast<uint8_t *>(response);
903
904 if (usb_get_status())
905 {
906 controls |= controls_usb_attached;
907 }
908 else
909 {
910 controls &= ~controls_usb_attached;
911 }
912
913 switch (ctrl_req->control)
914 {
915 case fw_update_control_request::CTRL_GET:
916 break;
917 case fw_update_control_request::CTRL_XFER_START:
918 {
919 controls |= controls_transfer_started;
920 // reset buffer to empty (truncate file)
921 std::ofstream out(FIRMWARE_BUFFER_FILE,
922 std::ofstream::binary | std::ofstream::trunc);
923 fw_xfer_uri = std::string("file://") + FIRMWARE_BUFFER_FILE;
924 if (xfer_hash_check)
925 {
926 xfer_hash_check->clear();
927 }
928 if (DEBUG)
929 std::cerr << "transfer start\n";
930 }
931 break;
932 case fw_update_control_request::CTRL_XFER_COMPLETE:
933 {
934 if (usb_get_status())
935 {
936 rc = IPMI_CC_REQ_INVALID_PHASE;
937 }
938 // finish transfer based on URI
939 if (!transfer_firmware_from_uri(fw_xfer_uri))
940 {
941 rc = IPMI_CC_UNSPECIFIED_ERROR;
942 break;
943 }
944 // transfer complete
945 if (xfer_hash_check)
946 {
947 if (transfer_hash_check::CHECK_PASSED_SHA2 !=
948 xfer_hash_check->check())
949 {
950 if (DEBUG)
951 std::cerr << "xfer_hash_check returns not "
952 "CHECK_PASSED_SHA2\n";
953 rc = IPMI_CC_UNSPECIFIED_ERROR;
954 break;
955 }
956 }
957 // start the request
958 if (!request_start_firmware_update(FIRMWARE_BUFFER_FILE))
959 {
960 if (DEBUG)
961 std::cerr
962 << "request_start_firmware_update returns failure\n";
963 rc = IPMI_CC_UNSPECIFIED_ERROR;
964 }
965 if (rc == IPMI_CC_OK)
966 {
967 controls |= controls_transfer_completed;
968 }
969 }
970 break;
971 case fw_update_control_request::CTRL_XFER_ABORT:
972 if (DEBUG)
973 std::cerr << "send abort request\n";
974 if (usb_get_status())
975 {
976 if (0 != usb_detach_device())
977 {
978 rc = IPMI_CC_USB_ATTACH_FAIL;
979 }
980 }
981 if (!request_abort_firmware_update())
982 {
983 if (DEBUG)
984 std::cerr
985 << "abort_start_firmware_update returns failure\n";
986 rc = IPMI_CC_UNSPECIFIED_ERROR;
987 }
988 controls |= controls_transfer_aborted;
989 break;
990 case fw_update_control_request::CTRL_SET_FILENAME:
991 fw_xfer_uri.clear();
992 fw_xfer_uri.insert(0, ctrl_req->filename, ctrl_req->nlen);
993 break;
994 case fw_update_control_request::CTRL_USB_ATTACH:
995 if (usb_get_status())
996 {
997 rc = IPMI_CC_INVALID_FIELD_REQUEST;
998 }
999 else if (0 != usb_attach_device())
1000 {
1001 rc = IPMI_CC_USB_ATTACH_FAIL;
1002 }
1003 else
1004 {
1005 rc = IPMI_CC_OK;
1006 }
1007 break;
1008 case fw_update_control_request::CTRL_USB_DETACH:
1009 if (!usb_get_status())
1010 {
1011 rc = IPMI_CC_INVALID_FIELD_REQUEST;
1012 }
1013 if (0 != usb_detach_device())
1014 {
1015 rc = IPMI_CC_USB_ATTACH_FAIL;
1016 }
1017 else
1018 {
1019 rc = IPMI_CC_OK;
1020 }
1021 break;
1022 default:
1023 if (DEBUG)
1024 std::cerr << "control byte " << std::hex << ctrl_req->control
1025 << " unknown\n";
1026 rc = IPMI_CC_INVALID_FIELD_REQUEST;
1027 break;
1028 }
1029
1030 if (rc == IPMI_CC_OK)
1031 {
1032 *ctrl_resp = controls;
1033 *data_len = sizeof(*ctrl_resp);
1034 }
1035
1036 return rc;
1037}
1038
1039struct fw_version_info
1040{
1041 uint8_t id_tag;
1042 uint8_t major;
1043 uint8_t minor;
1044 uint32_t build;
1045 uint32_t build_time;
1046 uint32_t update_time;
1047} __attribute__((packed));
1048
1049static ipmi_ret_t ipmi_firmware_get_fw_version_info(
1050 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1051 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1052{
1053 if (DEBUG)
1054 std::cerr << "Get FW Version Info\n";
1055
1056 // Byte 1 - Count (N) Number of devices data is being returned for.
1057 // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image
1058 // 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR
1059 // Image
1060 // Byte 3 - Major Version Number
1061 // Byte 4 - Minor Version Number
1062 // Bytes 5:8 - Build Number
1063 // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL
1064 // timestamp
1065 // Bytes 13:16 - Update Timestamp
1066 // Bytes - 17:(15xN) - Repeat of 2 through 16
1067
1068 uint8_t count = 0;
1069 auto ret_count = reinterpret_cast<uint8_t *>(response);
1070 auto info = reinterpret_cast<struct fw_version_info *>(ret_count + 1);
1071
1072 for (uint8_t id_tag = 1; id_tag < 6; id_tag++)
1073 {
1074 const char *fw_path;
1075 switch (id_tag)
1076 {
1077 case 1:
1078 fw_path = FW_UPDATE_ACTIVE_INFO_PATH;
1079 break;
1080 case 2:
1081 fw_path = FW_UPDATE_BACKUP_INFO_PATH;
1082 break;
1083 case 3:
1084 case 4:
1085 case 5:
1086 continue; // skip for now
1087 break;
1088 }
1089 auto method =
1090 _bus.new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
1091 "org.freedesktop.DBus.Properties", "GetAll");
1092 method.append(FW_UPDATE_INFO_INTERFACE);
1093 std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
1094 try
1095 {
1096 auto reply = _bus.call(method);
1097
1098 if (reply.is_method_error())
1099 continue;
1100
1101 reply.read(properties);
1102 }
1103 catch (sdbusplus::exception::SdBusError &e)
1104 {
1105 std::cerr << "SDBus Error: " << e.what();
1106 return IPMI_CC_UNSPECIFIED_ERROR;
1107 }
1108 uint8_t major = 0;
1109 uint8_t minor = 0;
1110 uint32_t build = 0;
1111 int32_t build_time = 0;
1112 int32_t update_time = 0;
1113 for (const auto &t : properties)
1114 {
1115 auto key = t.first;
1116 auto value = t.second;
1117 if (key == "version")
1118 {
1119 auto strver =
1120 sdbusplus::message::variant_ns::get<std::string>(value);
1121 std::stringstream ss;
1122 ss << std::hex << strver;
1123 uint32_t t;
1124 ss >> t;
1125 major = t;
1126 ss.ignore();
1127 ss >> t;
1128 minor = t;
1129 ss.ignore();
1130 ss >> build;
1131 }
1132 else if (key == "build_time")
1133 {
1134 build_time =
1135 sdbusplus::message::variant_ns::get<int32_t>(value);
1136 }
1137 else if (key == "update_time")
1138 {
1139 update_time =
1140 sdbusplus::message::variant_ns::get<int32_t>(value);
1141 }
1142 }
1143
1144 info->id_tag = id_tag;
1145 info->major = major;
1146 info->minor = minor;
1147 info->build = build;
1148 info->build_time = build_time;
1149 info->update_time = update_time;
1150 count++;
1151 info++;
1152 }
1153 *ret_count = count;
1154
1155 // Status code.
1156 ipmi_ret_t rc = IPMI_CC_OK;
1157 *data_len = sizeof(count) + count * sizeof(*info);
1158
1159 return rc;
1160}
1161
1162struct fw_security_revision_info
1163{
1164 uint8_t id_tag;
1165 uint16_t sec_rev;
1166} __attribute__((packed));
1167
1168static ipmi_ret_t ipmi_firmware_get_fw_security_revision(
1169 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1170 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1171{
1172 if (DEBUG)
1173 std::cerr << "Get FW security revision info\n";
1174
1175 // Byte 1 - Count (N) Number of devices data is being returned for.
1176 // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image
1177 // 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR
1178 // Image
1179 // Byte 3 - Major Version Number
1180 // Byte 4 - Minor Version Number
1181 // Bytes 5:8 - Build Number
1182 // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL
1183 // timestamp
1184 // Bytes 13:16 - Update Timestamp
1185 // Bytes - 17:(15xN) - Repeat of 2 through 16
1186
1187 uint8_t count = 0;
1188 auto ret_count = reinterpret_cast<uint8_t *>(response);
1189 auto info =
1190 reinterpret_cast<struct fw_security_revision_info *>(ret_count + 1);
1191
1192 for (uint8_t id_tag = 1; id_tag < 6; id_tag++)
1193 {
1194 const char *fw_path;
1195 switch (id_tag)
1196 {
1197 case 1:
1198 fw_path = FW_UPDATE_ACTIVE_INFO_PATH;
1199 break;
1200 case 2:
1201 fw_path = FW_UPDATE_BACKUP_INFO_PATH;
1202 break;
1203 case 3:
1204 case 4:
1205 case 5:
1206 continue; // skip for now
1207 break;
1208 }
1209 auto method =
1210 _bus.new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
1211 "org.freedesktop.DBus.Properties", "GetAll");
1212 method.append(FW_UPDATE_INFO_INTERFACE, "security_version");
1213 ipmi::DbusVariant sec_rev;
1214 try
1215 {
1216 auto reply = _bus.call(method);
1217
1218 if (reply.is_method_error())
1219 continue;
1220
1221 reply.read(sec_rev);
1222 }
1223 catch (sdbusplus::exception::SdBusError &e)
1224 {
1225 std::cerr << "SDBus Error: " << e.what();
1226 return IPMI_CC_UNSPECIFIED_ERROR;
1227 }
1228
1229 info->id_tag = id_tag;
1230 info->sec_rev = sdbusplus::message::variant_ns::get<int>(sec_rev);
1231 count++;
1232 info++;
1233 }
1234 *ret_count = count;
1235
1236 // Status code.
1237 ipmi_ret_t rc = IPMI_CC_OK;
1238 *data_len = sizeof(count) + count * sizeof(*info);
1239
1240 return rc;
1241}
1242
1243struct fw_channel_size
1244{
1245 uint8_t channel_id;
1246 uint32_t channel_size;
1247} __attribute__((packed));
1248
1249enum
1250{
1251 CHANNEL_RESVD = 0,
1252 CHANNEL_KCS,
1253 CHANNEL_RMCP_PLUS,
1254 CHANNEL_USB_DATA,
1255 CHANNEL_USB_MASS_STORAGE,
1256} channel_transfer_type;
1257
1258static ipmi_ret_t ipmi_firmware_max_transfer_size(
1259 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1260 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1261{
1262 if (DEBUG)
1263 std::cerr << "Get FW max transfer size\n";
1264
1265 // Byte 1 - Count (N) Number of devices data is being returned for.
1266 // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+,
1267 // 03 – usb data, 04 – usb mass storage
1268 // Byte 3-6 - transfer size (little endian)
1269 // Bytes - 7:(5xN) - Repeat of 2 through 6
1270
1271 uint8_t count = 0;
1272 auto ret_count = reinterpret_cast<uint8_t *>(response);
1273 auto info = reinterpret_cast<struct fw_channel_size *>(ret_count + 1);
1274
1275 info->channel_id = CHANNEL_KCS;
1276 info->channel_size = 128;
1277 info++;
1278 count++;
1279
1280 info->channel_id = CHANNEL_RMCP_PLUS;
1281 info->channel_size = 50 * 1024;
1282 info++;
1283 count++;
1284
1285 /*
1286 info->channel_id = CHANNEL_USB_MASS_STORAGE;
1287 info->channel_size = 128;
1288 info++;
1289 count++;
1290 */
1291
1292 *ret_count = count;
1293
1294 // Status code.
1295 ipmi_ret_t rc = IPMI_CC_OK;
1296 *data_len = sizeof(count) + count * sizeof(*info);
1297
1298 return rc;
1299}
1300
1301enum
1302{
1303 EXEC_CTX_RESVD = 0,
1304 EXEC_CTX_FULL_LINUX = 0x10,
1305 EXEC_CTX_SAFE_MODE_LINUX = 0x11,
1306} bmc_execution_context;
1307
1308struct fw_execution_context
1309{
1310 uint8_t context;
1311 uint8_t image_selection;
1312} __attribute__((packed));
1313
1314static ipmi_ret_t ipmi_firmware_get_fw_execution_context(
1315 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1316 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1317{
1318 if (DEBUG)
1319 std::cerr << "Get FW execution context\n";
1320
1321 // Byte 1 - execution context
1322 // 0x10 - full linux stack, 0x11 - safe-mode linux stack
1323 // Byte 2 - current image selection
1324 // 1 - primary, 2 - secondary
1325
1326 auto info = reinterpret_cast<struct fw_execution_context *>(response);
1327 auto method =
1328 _bus.new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH,
1329 "org.freedesktop.DBus.Properties", "GetAll");
1330 method.append(FW_UPDATE_SECURITY_INTERFACE);
1331 int active_img;
1332 bool safe_mode;
1333 std::string cert;
1334 try
1335 {
1336 auto reply = _bus.call(method);
1337
1338 if (!reply.is_method_error())
1339 {
1340 std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
1341 reply.read(properties);
1342
1343 for (const auto &t : properties)
1344 {
1345 auto key = t.first;
1346 auto value = t.second;
1347 if (key == "active_partition")
1348 {
1349 active_img =
1350 sdbusplus::message::variant_ns::get<int>(value);
1351 }
1352 else if (key == "safe_mode")
1353 {
1354 safe_mode =
1355 sdbusplus::message::variant_ns::get<bool>(value);
1356 }
1357 }
1358 }
1359 }
1360 catch (sdbusplus::exception::SdBusError &e)
1361 {
1362 std::cerr << "SDBus Error: " << e.what();
1363 return IPMI_CC_UNSPECIFIED_ERROR;
1364 }
1365
1366 if (safe_mode)
1367 info->context = EXEC_CTX_SAFE_MODE_LINUX;
1368 else
1369 info->context = EXEC_CTX_FULL_LINUX;
1370
1371 info->image_selection = active_img;
1372
1373 // Status code.
1374 ipmi_ret_t rc = IPMI_CC_OK;
1375 *data_len = sizeof(*info);
1376
1377 return rc;
1378}
1379
1380struct fw_update_status_response
1381{
1382 uint8_t status;
1383 uint8_t percent;
1384 uint8_t check;
1385} __attribute__((packed));
1386
1387static ipmi_ret_t ipmi_firmware_get_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1388 ipmi_request_t request,
1389 ipmi_response_t response,
1390 ipmi_data_len_t data_len,
1391 ipmi_context_t context)
1392{
1393 if (DEBUG)
1394 std::cerr << "Get FW update status\n";
1395
1396 // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
1397 // 5=ready, f=error, 83=ac cycle required)
1398 // Byte 2 - percent
1399 // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
1400
1401 auto fw_status =
1402 reinterpret_cast<struct fw_update_status_response *>(response);
1403
1404 fw_status->status = fw_update_status.state();
1405 fw_status->percent = fw_update_status.percent();
1406 fw_status->check = xfer_hash_check ? xfer_hash_check->status() : 0;
1407
1408 // Status code.
1409 *data_len = sizeof(*fw_status);
1410 return IPMI_CC_OK;
1411}
1412
1413static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0);
1414static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1);
1415static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2);
1416static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3);
1417struct fw_update_options_request
1418{
1419 uint8_t mask;
1420 uint8_t options;
1421} __attribute__((packed));
1422
1423bool fw_update_set_dbus_property(const std::string &path,
1424 const std::string &iface,
1425 const std::string &name,
1426 ipmi::DbusVariant value)
1427{
1428 auto method =
1429 _bus.new_method_call(FW_UPDATE_SERVER_DBUS_NAME, path.c_str(),
1430 "org.freedesktop.DBus.Properties", "Set");
1431 method.append(iface, name, value);
1432 try
1433 {
1434 auto reply = _bus.call(method);
1435 auto err = reply.is_method_error();
1436 if (err)
1437 if (DEBUG)
1438 std::cerr << "failed to set prop " << path << '/' << iface
1439 << '.' << name << '\n';
1440 return err;
1441 }
1442 catch (sdbusplus::exception::SdBusError &e)
1443 {
1444 std::cerr << "SDBus Error: " << e.what();
1445 return false;
1446 }
1447}
1448
1449uint32_t fw_update_options = 0;
1450static ipmi_ret_t ipmi_firmware_update_options(
1451 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1452 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1453{
1454 if (DEBUG)
1455 std::cerr << "Get/set FW update options\n";
1456
1457 // request:
1458 // Byte 1 - mask
1459 // Byte 2 - options
1460 // Byte 3-34 - optional integrity check expected value
1461 // response:
1462 // Byte 1 - set options
1463
1464 auto fw_options =
1465 reinterpret_cast<struct fw_update_options_request *>(request);
1466
1467 const char *path = FW_UPDATE_SERVER_INFO_PATH;
1468 const char *iface = FW_UPDATE_SECURITY_INTERFACE;
1469 if ((fw_options->mask & FW_UPDATE_OPTIONS_NO_DOWNREV) &&
1470 (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) !=
1471 (fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV))
1472 {
1473 if (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV)
1474 {
1475 fw_update_options |= FW_UPDATE_OPTIONS_NO_DOWNREV;
1476 }
1477 else
1478 {
1479 fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV;
1480 // send dbus
1481 }
1482 const char *name = "inhibit_downgrade";
1483 fw_update_set_dbus_property(
1484 path, iface, name,
1485 (bool)(!!(fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV)));
1486 }
1487 if ((fw_options->mask & FW_UPDATE_OPTIONS_DEFER_RESTART) &&
1488 (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) !=
1489 (fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART))
1490 {
1491 if (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART)
1492 {
1493 fw_update_options |= FW_UPDATE_OPTIONS_DEFER_RESTART;
1494 }
1495 else
1496 {
1497 fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART;
1498 }
1499 const char *name = "defer_restart";
1500 fw_update_set_dbus_property(
1501 path, iface, name,
1502 (bool)(!!(fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART)));
1503 }
1504 if (fw_options->mask & FW_UPDATE_OPTIONS_SHA2_CHECK)
1505 {
1506 auto hash_size = EVP_MD_size(EVP_sha256());
1507 if (fw_options->options & FW_UPDATE_OPTIONS_SHA2_CHECK)
1508 {
1509 if (*data_len != (sizeof(*fw_options) + hash_size))
1510 {
1511 *data_len = 0;
1512 return IPMI_CC_REQ_DATA_LEN_INVALID;
1513 }
1514 xfer_hash_check = std::make_shared<transfer_hash_check>();
1515 auto exp_hash = reinterpret_cast<uint8_t *>(fw_options + 1);
1516 xfer_hash_check->init({exp_hash, exp_hash + hash_size});
1517 fw_update_options |= FW_UPDATE_OPTIONS_SHA2_CHECK;
1518 }
1519 else
1520 {
1521 fw_update_options &= ~FW_UPDATE_OPTIONS_SHA2_CHECK;
1522 // delete the xfer_hash_check object
1523 xfer_hash_check.reset();
1524 }
1525 }
1526 auto options_rsp = reinterpret_cast<uint8_t *>(response);
1527 *options_rsp = fw_update_options;
1528
1529 if (DEBUG)
1530 std::cerr << "current fw_update_options = " << std::hex
1531 << fw_update_options << '\n';
1532 // Status code.
1533 *data_len = sizeof(*options_rsp);
1534 return IPMI_CC_OK;
1535}
1536
1537struct fw_cert_info
1538{
1539 uint16_t cert_len;
1540 uint64_t serial;
1541 uint8_t subject_len;
1542 char subject[255];
1543} __attribute__((packed));
1544
1545static ipmi_ret_t ipmi_firmware_get_root_cert_info(
1546 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1547 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1548{
1549 if (DEBUG)
1550 std::cerr << "Get FW root cert info\n";
1551
1552 // request:
1553 // Byte 1 - certificate ID: request which certificate (ignored)
1554
1555 // response:
1556 // Byte 1-2 - certificate length (little endian)
1557 // Byte 3-10 - serial number (little endian)
1558 // Byte 11 - subject length
1559 // Byte 12-N - subject data
1560
1561 auto cert_info = reinterpret_cast<struct fw_cert_info *>(response);
1562 auto method = _bus.new_method_call(
1563 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH,
1564 "org.freedesktop.DBus.Properties", "GetAll");
1565 method.append(FW_UPDATE_SECURITY_INTERFACE);
1566 std::string subject;
1567 uint64_t serial;
1568 std::string cert;
1569 try
1570 {
1571 auto reply = _bus.call(method);
1572
1573 std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
1574 reply.read(properties);
1575
1576 for (const auto &t : properties)
1577 {
1578 auto key = t.first;
1579 auto value = t.second;
1580 if (key == "certificate_subject")
1581 {
1582 subject =
1583 sdbusplus::message::variant_ns::get<std::string>(value);
1584 }
1585 else if (key == "cetificate_serial")
1586 {
1587 serial = sdbusplus::message::variant_ns::get<uint64_t>(value);
1588 }
1589 else if (key == "certificate")
1590 {
1591 cert = sdbusplus::message::variant_ns::get<std::string>(value);
1592 }
1593 }
1594 }
1595 catch (sdbusplus::exception::SdBusError &e)
1596 {
1597 std::cerr << "SDBus Error: " << e.what();
1598 return IPMI_CC_UNSPECIFIED_ERROR;
1599 }
1600
1601 cert_info->cert_len = cert.size();
1602 cert_info->serial = serial;
1603 // truncate subject so it fits in the 255-byte array (if necessary)
1604 if (subject.size() > sizeof(cert_info->subject))
1605 subject.resize(sizeof(cert_info->subject));
1606 cert_info->subject_len = subject.size();
1607 std::copy(subject.begin(), subject.end(), cert_info->subject);
1608
1609 // Status code.
1610 ipmi_ret_t rc = IPMI_CC_OK;
1611 // make sure to account for the *actual* size of the subject
1612 *data_len = sizeof(*cert_info) - sizeof(cert_info->subject) +
1613 cert_info->subject_len;
1614
1615 return rc;
1616}
1617
1618struct fw_cert_data_req
1619{
1620 uint8_t cert_id;
1621 uint16_t offset;
1622 uint16_t count;
1623} __attribute__((packed));
1624
1625static ipmi_ret_t ipmi_firmware_get_root_cert_data(
1626 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1627 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1628{
1629 if (DEBUG)
1630 std::cerr << "Get FW root cert data\n";
1631
1632 // request:
1633 // Byte 1 - certificate ID: request which certificate (ignored)
1634 // Byte 2-3 - offset within cert to start at
1635 // Byte 4-5 - number of bytes to return
1636
1637 // response:
1638 // Byte 1-N - certificate data
1639
1640 if (*data_len != sizeof(fw_cert_data_req))
1641 return IPMI_CC_REQ_DATA_LEN_INVALID;
1642
1643 auto cert_data_req = reinterpret_cast<struct fw_cert_data_req *>(request);
1644 auto method = _bus.new_method_call(
1645 FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH,
1646 "org.freedesktop.DBus.Properties", "Get");
1647 method.append(FW_UPDATE_SECURITY_INTERFACE, "certificate");
1648 ipmi::DbusVariant cert;
1649 try
1650 {
1651 auto reply = _bus.call(method);
1652 reply.read(cert);
1653 }
1654 catch (sdbusplus::exception::SdBusError &e)
1655 {
1656 std::cerr << "SDBus Error: " << e.what();
1657 return IPMI_CC_UNSPECIFIED_ERROR;
1658 }
1659 auto cert_data = sdbusplus::message::variant_ns::get<std::string>(cert);
1660
1661 if (cert_data_req->offset >= cert_data.size())
1662 {
1663 *data_len = 0;
1664 return IPMI_CC_INVALID_FIELD_REQUEST;
1665 }
1666 auto first = cert_data.begin() + cert_data_req->offset;
1667 auto last = first + cert_data_req->count;
1668 if (last > cert_data.end())
1669 last = cert_data.end();
1670
1671 auto data_out = reinterpret_cast<char *>(response);
1672 std::copy(first, last, data_out);
1673
1674 // Status code.
1675 ipmi_ret_t rc = IPMI_CC_OK;
1676 *data_len = (last - first);
1677
1678 return rc;
1679}
1680
1681static ipmi_ret_t ipmi_firmware_write_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1682 ipmi_request_t request,
1683 ipmi_response_t response,
1684 ipmi_data_len_t data_len,
1685 ipmi_context_t context)
1686{
1687 if (DEBUG)
1688 std::cerr << "write fw data (" << *data_len << " bytes)\n";
1689
1690 auto bytes_in = *data_len;
1691 *data_len = 0;
1692 if (fw_update_status.state() != fw_update_status_cache::FW_STATE_DOWNLOAD)
1693 return IPMI_CC_INVALID;
1694
1695 std::ofstream out(FIRMWARE_BUFFER_FILE,
1696 std::ofstream::binary | std::ofstream::app);
1697 if (!out)
1698 {
1699 return IPMI_CC_UNSPECIFIED_ERROR;
1700 }
1701 if (out.tellp() > FIRMWARE_BUFFER_MAX_SIZE)
1702 {
1703 return IPMI_CC_INVALID_FIELD_REQUEST;
1704 }
1705 auto data = reinterpret_cast<uint8_t *>(request);
1706 out.write(reinterpret_cast<char *>(data), bytes_in);
1707 out.close();
1708 if (xfer_hash_check)
1709 {
1710 xfer_hash_check->hash({data, data + bytes_in});
1711 }
1712
1713 // Status code.
1714 return IPMI_CC_OK;
1715}
1716
1717static constexpr char NOT_IMPLEMENTED[] = "NOT IMPLEMENTED";
1718
1719static ipmi_ret_t ipmi_firmware_wildcard_handler(
1720 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1721 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1722{
1723 if (DEBUG)
1724 std::cerr << "Handling stubbed Netfn:[0x" << std::hex << +netfn
1725 << "], Cmd:[0x" << std::hex << +cmd << "]\n";
1726
1727 // Status code.
1728 ipmi_ret_t rc = IPMI_CC_OK;
1729
1730 *data_len = sizeof(NOT_IMPLEMENTED);
1731
1732 // Now pack actual response
1733 char *msg_reply = static_cast<char *>(response);
1734 std::copy(std::begin(NOT_IMPLEMENTED), std::end(NOT_IMPLEMENTED),
1735 msg_reply);
1736
1737 return rc;
1738}
1739
1740struct intc_app_get_buffer_size_resp
1741{
1742 uint8_t kcs_size;
1743 uint8_t ipmb_size;
1744} __attribute__((packed));
1745
1746static constexpr int KCS_MAX_BUFFER_SIZE = 63;
1747static constexpr int IPMB_MAX_BUFFER_SIZE = 128;
1748static ipmi_ret_t ipmi_intel_app_get_buffer_size(
1749 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1750 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1751{
1752 auto msg_reply =
1753 reinterpret_cast<intc_app_get_buffer_size_resp *>(response);
1754 // for now this is hard coded; really this number is dependent on
1755 // the BMC kcs driver as well as the host kcs driver....
1756 // we can't know the latter.
1757 msg_reply->kcs_size = KCS_MAX_BUFFER_SIZE / 4;
1758 msg_reply->ipmb_size = IPMB_MAX_BUFFER_SIZE / 4;
1759 *data_len = sizeof(*msg_reply);
1760
1761 return IPMI_CC_OK;
1762}
1763
1764static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20;
1765static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21;
1766static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22;
1767static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23;
1768static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24;
1769static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_DATA = 0x25;
1770static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26;
1771static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27;
1772static constexpr ipmi_cmd_t IPMI_CMD_FW_EXIT_FW_UPDATE_MODE = 0x28;
1773static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29;
1774static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a;
1775static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b;
1776static constexpr ipmi_cmd_t IPMI_CMD_FW_IMAGE_WRITE = 0x2c;
1777static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d;
1778static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0;
1779static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0;
1780
1781static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30;
1782static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66;
1783
1784static void register_netfn_firmware_functions()
1785{
1786 // guarantee that we start with an already timed out timestamp
1787 fw_random_number_timestamp =
1788 std::chrono::steady_clock::now() - FW_RANDOM_NUMBER_TTL;
1789
1790 unlink(FIRMWARE_BUFFER_FILE);
1791
1792 // <Get BT Interface Capabilities>
1793 if (DEBUG)
1794 std::cerr << "Registering firmware update commands\n";
1795
1796 // get firmware version information
1797 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_VERSION_INFO,
1798 NULL, ipmi_firmware_get_fw_version_info,
1799 PRIVILEGE_ADMIN);
1800
1801 // get firmware security version information
1802 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO,
1803 NULL, ipmi_firmware_get_fw_security_revision,
1804 PRIVILEGE_ADMIN);
1805
1806 // get channel information (max transfer sizes)
1807 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO,
1808 NULL, ipmi_firmware_max_transfer_size,
1809 PRIVILEGE_ADMIN);
1810
1811 // get bmc execution context
1812 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_BMC_EXEC_CTX, NULL,
1813 ipmi_firmware_get_fw_execution_context,
1814 PRIVILEGE_ADMIN);
1815
1816 // get root certificate information
1817 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO,
1818 NULL, ipmi_firmware_get_root_cert_info,
1819 PRIVILEGE_ADMIN);
1820
1821 // get root certificate data
1822 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_DATA,
1823 NULL, ipmi_firmware_get_root_cert_data,
1824 PRIVILEGE_ADMIN);
1825
1826 // generate bmc fw update random number (for enter fw tranfer mode)
1827 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM,
1828 NULL, ipmi_firmware_get_fw_random_number,
1829 PRIVILEGE_ADMIN);
1830
1831 // enter firmware update mode
1832 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_MODE,
1833 NULL, ipmi_firmware_enter_fw_transfer_mode,
1834 PRIVILEGE_ADMIN);
1835
1836 // exit firmware update mode
1837 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_EXIT_FW_UPDATE_MODE,
1838 NULL, ipmi_firmware_exit_fw_update_mode,
1839 PRIVILEGE_ADMIN);
1840
1841 // firmware control mechanism (set filename, usb, etc.)
1842 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_UPDATE_CONTROL, NULL,
1843 ipmi_firmware_control, PRIVILEGE_ADMIN);
1844
1845 // get firmware update status
1846 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_STATUS, NULL,
1847 ipmi_firmware_get_status, PRIVILEGE_ADMIN);
1848
1849 // set firmware update options (no downgrade, etc.)
1850 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS,
1851 NULL, ipmi_firmware_update_options, PRIVILEGE_ADMIN);
1852
1853 // write image data
1854 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_IMAGE_WRITE, NULL,
1855 ipmi_firmware_write_data, PRIVILEGE_ADMIN);
1856
1857 // get update timestamps
1858 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_TIMESTAMP, NULL,
1859 ipmi_firmware_wildcard_handler, PRIVILEGE_ADMIN);
1860
1861 // get error message (when in error state)
1862 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_UPDATE_ERR_MSG,
1863 NULL, ipmi_firmware_wildcard_handler,
1864 PRIVILEGE_ADMIN);
1865
1866 // get remote firmware information (PSU, HSBP, etc.)
1867 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_REMOTE_FW_INFO,
1868 NULL, ipmi_firmware_wildcard_handler,
1869 PRIVILEGE_ADMIN);
1870
1871 // <Wildcard Command>
1872 ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_WILDCARD, NULL,
1873 ipmi_firmware_wildcard_handler, PRIVILEGE_ADMIN);
1874
1875 // get buffer size is used by fw update (exclusively?)
1876 ipmi_register_callback(NETFUN_INTC_APP, IPMI_CMD_INTC_GET_BUFFER_SIZE, NULL,
1877 ipmi_intel_app_get_buffer_size, PRIVILEGE_USER);
1878 return;
1879}