ssifbridge: Replace ssifbridge with C++ version
Rewrite the C-style ssifbridge in modern C++.
Test:
On linux host, run 'ipmitool mc info' or 'ipmitool fru print'
Change-Id: I5970853abbf4b2cfe893c76e819a22047baaa4a4
Signed-off-by: Dung Cao <dung@os.amperecomputing.com>
diff --git a/meson.build b/meson.build
index 08e5b72..bf8f01d 100644
--- a/meson.build
+++ b/meson.build
@@ -1,22 +1,23 @@
-project('ssifbridge', 'c',
- default_options: [
- 'buildtype=debugoptimized',
- 'warning_level=3',
- 'werror=true',
- ],
- version: '1.0',
+project('ssifbridge', 'cpp',
+ default_options: [
+ 'buildtype=debugoptimized',
+ 'warning_level=3',
+ 'werror=true',
+ 'cpp_std=c++17'
+ ],
+ version: '1.0',
)
-# Wno-psabi reduces the number of "Note:" messages when cross-compiling some STL
-# stuff for ARM. See https://stackoverflow.com/questions/48149323/strange-gcc-warning-when-compiling-qt-project
-# Basically, gcc 6 and gcc 7 are not ABI compatible, but since the whole OpenBMC
-# project uses the same compiler, we can safely ignmore these info notes.
-add_project_arguments('-Wno-psabi', language: 'c')
+add_project_arguments('-Wno-psabi', language: 'cpp')
deps = [dependency('libsystemd', version : '>=221'),
dependency('systemd'),
- ]
+ dependency('phosphor-logging'),
+ dependency('sdbusplus'),
+ dependency('threads')
-executable('ssifbridged','ssifbridged.c',
+]
+
+executable('ssifbridged','ssifbridged.cpp',
dependencies: deps,
install: true)
diff --git a/ssifbridged.c b/ssifbridged.c
deleted file mode 100644
index 305eec7..0000000
--- a/ssifbridged.c
+++ /dev/null
@@ -1,713 +0,0 @@
-/*
- * Copyright (c) 2018-2020 Ampere Computing LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * This is a daemon that forwards requests and receive responses from SSIF over
- * the D-Bus IPMI Interface.
- *
- * This daemon is derived from btbridged from https://github.com/openbmc/btbridge.
- * There is no need to queue messages for SSIF since they should not arrive out
- * of sequence.
- * The timer is used as we want to abort 'stuck' commands after an expiry time.
- * Messages that are received out of order are discarded.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <limits.h>
-#include <poll.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <sys/mman.h>
-#include <linux/ioctl.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/timerfd.h>
-#include <time.h>
-#include <unistd.h>
-#include <systemd/sd-bus.h>
-
-static const char *ssif_bmc_device = "/dev/ipmi-ssif-host";
-
-#define PREFIX "SSIF_BRIDGED"
-
-#define SSIF_BMC_PATH ssif_bmc_device
-#define SSIF_BMC_TIMEOUT_SEC 2
-#define SSIF_MAX_REQ_LEN 254
-#define SSIF_MAX_RESP_LEN 254
-
-/* Completion code specifies a command times out */
-#define IPMI_CC_CANNOT_PROVIDE_RESP 0xce
-
-
-#define DBUS_NAME "org.openbmc.HostIpmi"
-#define OBJ_NAME "/org/openbmc/HostIpmi/1"
-
-#define SD_BUS_FD 0
-#define SSIF_FD 1
-#define TIMER_FD 2
-#define TOTAL_FDS 3
-
-#define MSG_OUT(f_, ...) do { \
- if (verbosity != SSIF_LOG_NONE) { \
- ssif_log(LOG_INFO, f_, ##__VA_ARGS__); \
- } \
-} while(0)
-#define MSG_ERR(f_, ...) do { \
- if (verbosity != SSIF_LOG_NONE) { \
- ssif_log(LOG_ERR, f_, ##__VA_ARGS__); \
- } \
-} while(0)
-
-struct ipmi_msg {
- uint8_t netfn;
- uint8_t lun;
- uint8_t seq;
- uint8_t cmd;
- uint8_t cc; /* Only used on responses */
- uint8_t *data;
- size_t data_len;
-};
-
-struct ssifbridged_context {
- /* The file descriptors to poll */
- struct pollfd fds[TOTAL_FDS];
-
- /* Pointer to sdbus */
- struct sd_bus *bus;
-
- /* Tracking variable for a pending message so that if it times out,
- * we can send a response using the correct lun, netfn and command to
- * indicate to the host that we timed out waiting for a response
- */
- struct ipmi_msg ssif_pending_msg;
-
- /* Flag to indicate whether we are awaiting a response */
- int awaiting_response;
-};
-
-static void (*ssif_vlog)(int p, const char *fmt, va_list args);
-static int running = 1;
-static enum {
- SSIF_LOG_NONE = 0,
- SSIF_LOG_VERBOSE,
- SSIF_LOG_DEBUG
-} verbosity;
-
-static void ssif_log_console(int p, const char *fmt, va_list args)
-{
- struct timespec time;
- FILE *s = (p < LOG_WARNING) ? stdout : stderr;
-
- clock_gettime(CLOCK_REALTIME, &time);
-
- fprintf(s, "[%s %ld.%.9ld] ", PREFIX, time.tv_sec, time.tv_nsec);
-
- vfprintf(s, fmt, args);
-}
-
- __attribute__((format(printf, 2, 3)))
-static void ssif_log(int p, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- ssif_vlog(p, fmt, args);
- va_end(args);
-}
-
-static struct ipmi_msg *ssif_msg_create(struct ssifbridged_context *context,
- uint8_t *ssif_data)
-{
- int len;
-
- assert(context && ssif_data);
-
- /*
- * len here is the length of the array.
- * Helpfully SSIF doesn't count the length byte
- */
- len = ssif_data[0] + 1;
-
- if (len < 3) {
- MSG_ERR("Trying to get SSIF message with a short length (%d)\n",
- len);
- return NULL;
- }
-
- MSG_OUT("Trying to get SSIF message with len (%d)\n", len);
-
- /* Don't count the lenfn/ln, seq and command */
- context->ssif_pending_msg.data_len = len - 3;
- context->ssif_pending_msg.netfn = ssif_data[1] >> 2;
- context->ssif_pending_msg.lun = ssif_data[1] & 0x3;
- /* Force sequence field = 0 for SSIF */
- context->ssif_pending_msg.seq = 0;
- context->ssif_pending_msg.cmd = ssif_data[2];
-
- return &context->ssif_pending_msg;
-}
-
-/*
- * Send request from the SSIF driver
- */
-static int send_received_message_signal(struct ssifbridged_context *context,
- struct ipmi_msg *req,
- uint8_t *data, uint8_t data_len)
-{
- sd_bus_message *msg = NULL;
- int r = 0;
-
- /* Notify sdbus for incoming message */
- r = sd_bus_message_new_signal(context->bus,
- &msg,
- OBJ_NAME,
- DBUS_NAME,
- "ReceivedMessage");
- if (r < 0) {
- MSG_ERR("Failed to create signal: %s\n", strerror(-r));
- return r;
- }
-
- r = sd_bus_message_append(msg, "yyyy",
- req->seq,
- req->netfn,
- req->lun,
- req->cmd);
- if (r < 0) {
- MSG_ERR("Couldn't append to signal: %s\n", strerror(-r));
- return r;
- }
-
- r = sd_bus_message_append_array(msg, 'y', data, data_len);
- if (r < 0) {
- MSG_ERR("Couldn't append array to signal: %s\n", strerror(-r));
- sd_bus_message_unref(msg);
- return r;
- }
-
- MSG_OUT("Sending dbus signal with seq 0x%02x, netfn 0x%02x, "
- "lun 0x%02x, cmd 0x%02x\n",
- req->seq,
- req->netfn,
- req->lun,
- req->cmd);
-
- if (verbosity == SSIF_LOG_DEBUG) {
- int i;
- for (i = 0; i < req->data_len; i++) {
- if (i % 8 == 0) {
- if (i)
- printf("\n");
- MSG_OUT("\t");
- }
- printf("0x%02x ", data[i + 3]);
- }
- if (req->data_len)
- printf("\n");
- }
-
- r = sd_bus_send(context->bus, msg, NULL);
- if (r < 0) {
- MSG_ERR("Couldn't emit dbus signal: %s\n", strerror(-r));
- return r;
- }
-
- sd_bus_message_unref(msg);
-
- return r;
-}
-
-/*
- * Send a response on the SSIF driver
- */
-static int ssif_send_response(struct ssifbridged_context *context,
- struct ipmi_msg *ssif_resp_msg)
-{
- uint8_t data[SSIF_MAX_RESP_LEN] = { 0 };
- int r = 0;
- int len = 0;
-
- assert(context);
-
- if (!ssif_resp_msg)
- return -EINVAL;
-
- /* netfn/lun + cmd + cc = 3 */
- data[0] = ssif_resp_msg->data_len + 3;
-
- /* Copy response message to data buffer */
- if (ssif_resp_msg->data_len)
- memcpy(data + 4, ssif_resp_msg->data, ssif_resp_msg->data_len);
-
- /* Prepare Header */
- data[1] = (ssif_resp_msg->netfn << 2) |
- (ssif_resp_msg->lun & 0x3);
- data[2] = ssif_resp_msg->cmd;
- data[3] = ssif_resp_msg->cc;
- if (ssif_resp_msg->data_len > sizeof(data) - 4) {
- MSG_ERR("Response message size (%zu) too big, truncating\n",
- ssif_resp_msg->data_len);
- ssif_resp_msg->data_len = sizeof(data) - 4;
- }
-
- /* Write data kernel space via system calls */
- len = write(context->fds[SSIF_FD].fd, data, data[0] + 1);
-
- if (len < 0) {
- MSG_ERR("Failed to write to driver (ret: %d, errno: %d)\n",
- len,
- errno);
- r = -errno;
- } else if (len != data[0] + 1) {
- MSG_ERR("Possible short write to %s, desired len: %d, "
- "written len: %d\n",
- SSIF_BMC_PATH,
- data[0] + 1,
- len);
- r = -EINVAL;
- } else {
- MSG_OUT("Successfully wrote %d of %d bytes to %s\n",
- len,
- data[0] + 1,
- SSIF_BMC_PATH);
- }
-
- return r;
-}
-
-static int method_send_message(sd_bus_message *msg,
- void *userdata,
- sd_bus_error *ret_error)
-{
- struct ssifbridged_context *context;
- sd_bus_message* resp_msg = NULL;
- struct ipmi_msg ssif_resp_msg;
- int r = 1;
-
- context = (struct ssifbridged_context *)userdata;
- if (!context) {
- sd_bus_error_set_const(ret_error,
- "org.openbmc.error",
- "Internal error");
- return -EINVAL;
- }
-
- r = sd_bus_message_new_method_return(msg, &resp_msg);
- if (r < 0) {
- MSG_ERR("Failed to create method response (ret: %d)\n", r);
- return r;
- }
-
- if (!context->awaiting_response) {
- /* We are not expecting a response at this time */
- MSG_ERR("Response message received when in wrong state. "
- "Discarding\n");
- r = -EBUSY;
- } else {
- uint8_t *data;
- size_t data_sz;
- uint8_t netfn, lun, seq, cmd, cc;
- struct itimerspec ts;
-
- context->awaiting_response = 0;
-
- r = sd_bus_message_read(msg, "yyyyy",
- &seq,
- &netfn,
- &lun,
- &cmd,
- &cc);
- if (r < 0) {
- MSG_ERR("Couldn't parse leading bytes of message: %s\n",
- strerror(-r));
- sd_bus_error_set_const(ret_error,
- "org.openbmc.error",
- "Bad message");
- r = -EINVAL;
- goto done;
- }
- r = sd_bus_message_read_array(msg, 'y',
- (const void **)&data,
- &data_sz);
- if (r < 0) {
- MSG_ERR("Couldn't parse data bytes of message: %s\n",
- strerror(-r));
- sd_bus_error_set_const(ret_error,
- "org.openbmc.error",
- "Bad message data");
- r = -EINVAL;
- goto done;
- }
-
- MSG_OUT("Received a dbus response for msg with seq 0x%02x\n",
- seq);
-
- ssif_resp_msg.netfn = netfn;
- ssif_resp_msg.lun = lun;
- ssif_resp_msg.seq = seq;
- ssif_resp_msg.cmd = cmd;
- ssif_resp_msg.cc = cc;
- ssif_resp_msg.data_len = data_sz;
- /* Because we've ref'ed the msg, don't need to memcpy data */
- ssif_resp_msg.data = data;
-
- /* Clear the timer */
- ts.it_interval.tv_sec = 0;
- ts.it_interval.tv_nsec = 0;
- ts.it_value.tv_sec = 0;
- ts.it_value.tv_nsec = 0;
- r = timerfd_settime(context->fds[TIMER_FD].fd,
- TFD_TIMER_ABSTIME,
- &ts,
- NULL);
-
- if (r < 0) {
- MSG_ERR("Failed to clear timer\n");
- }
-
- r = ssif_send_response(context, &ssif_resp_msg);
- }
-
-done:
- r = sd_bus_message_append(resp_msg, "x", r);
- if (r < 0) {
- MSG_ERR("Failed to add result to method (ret: %d)\n", r);
- }
-
- r = sd_bus_send(context->bus, resp_msg, NULL);
- if (r < 0) {
- MSG_ERR("Failed to send response (ret: %d)\n", r);
- }
-
- return r;
-}
-
-static int dispatch_timer(struct ssifbridged_context *context)
-{
- int r = 0;
-
- if (context->fds[TIMER_FD].revents & POLLIN) {
- if(!context->awaiting_response) {
- /* Got a timeout but not expecting a response */
- MSG_ERR("Timeout but no pending message\n");
- } else {
- struct itimerspec ts;
-
- /* Clear the timer */
- ts.it_interval.tv_sec = 0;
- ts.it_interval.tv_nsec = 0;
- ts.it_value.tv_sec = 0;
- ts.it_value.tv_nsec = 0;
- r = timerfd_settime(context->fds[TIMER_FD].fd,
- TFD_TIMER_ABSTIME,
- &ts,
- NULL);
- if (r < 0) {
- MSG_ERR("Failed to clear timer\n");
- }
-
- MSG_ERR("Timing out message\n");
-
- /* Add one to the netfn - response netfn is always
- * request netfn + 1
- */
- context->ssif_pending_msg.netfn += 1;
- context->ssif_pending_msg.cc =
- IPMI_CC_CANNOT_PROVIDE_RESP;
- context->ssif_pending_msg.data = NULL;
- context->ssif_pending_msg.data_len = 0;
-
- r = ssif_send_response(context,
- &context->ssif_pending_msg);
- if (r < 0) {
- MSG_ERR("Failed to send timeout message "
- "(ret: %d, errno: %d)\n",
- r,
- errno);
- }
-
- context->awaiting_response = 0;
- }
- }
-
- return r;
-}
-
-static int dispatch_sd_bus(struct ssifbridged_context *context)
-{
- int r = 0;
- if (context->fds[SD_BUS_FD].revents) {
- r = sd_bus_process(context->bus, NULL);
- if (r > 0)
- MSG_OUT("Processed %d dbus events\n", r);
- }
-
- return r;
-}
-
-static int dispatch_ssif(struct ssifbridged_context *context)
-{
- int r = 0;
-
- assert(context);
-
- if (context->fds[SSIF_FD].revents & POLLIN) {
- /* We have received data on the driver */
- struct itimerspec ts;
- struct ipmi_msg *req;
- uint8_t data[SSIF_MAX_REQ_LEN] = { 0 };
-
- r = read(context->fds[SSIF_FD].fd, data, sizeof(data));
- if (r < 0) {
- MSG_ERR("Couldn't read from ssif: %s\n", strerror(-r));
- return r;
- }
- if (r < data[0] + 1) {
- MSG_ERR("Short read from ssif (%d vs %d)\n",
- r,
- data[1] + 2);
- r = 0;
- return r;
- }
-
- /* Check if response is still awaiting */
- if (context->awaiting_response) {
- MSG_ERR("Received SSIF message while awaiting response."
- " Discarding\n");
- } else {
- /* Get SSIF request message that sent from
- * kernel space
- */
- req = ssif_msg_create(context, data);
- context->awaiting_response = 1;
-
- if (!req) {
- MSG_ERR("Can not create request\n");
- r = -ENOMEM;
- return r;
- }
-
- /* Set up the timer. We do this before sending
- * the signal to avoid a race condition with
- * the response
- */
- ts.it_interval.tv_sec = 0;
- ts.it_interval.tv_nsec = 0;
- ts.it_value.tv_nsec = 0;
- ts.it_value.tv_sec = SSIF_BMC_TIMEOUT_SEC;
- r = timerfd_settime(context->fds[TIMER_FD].fd,
- 0,
- &ts,
- NULL);
- if (r < 0)
- MSG_ERR("Failed to set timer (ret: %d, "
- "errno: %d)\n",
- r,
- errno);
-
- r = send_received_message_signal(context,
- req,
- data + 3,
- req->data_len);
- if (r < 0) {
- MSG_ERR("Failed to send Received Message "
- "signal (ret: %d)\n",
- r);
- }
- }
- }
-
- return r;
-}
-
-static void usage(const char *name)
-{
- fprintf(stderr, "\
- Usage %s [--v[v] | --syslog] [-d <DEVICE>]\n\
- --v Be verbose\n\
- --vv Be verbose and dump entire messages\n\
- -s, --syslog Log output to syslog (pointless without --verbose)\n\
- -d, --device <DEVICE> use <DEVICE> file. Default is '%s'\n\n",
- name, ssif_bmc_device);
-}
-
-static const sd_bus_vtable ipmid_vtable[] = {
- SD_BUS_VTABLE_START(0),
- SD_BUS_METHOD("sendMessage",
- "yyyyyay",
- "x",
- &method_send_message,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_SIGNAL("ReceivedMessage",
- "yyyyay",
- 0),
- SD_BUS_VTABLE_END
-};
-
-int main(int argc, char *argv[]) {
- struct ssifbridged_context *context;
- const char *name = argv[0];
- int opt, polled, r;
-
- static const struct option long_options[] = {
- { "device", required_argument, NULL, 'd' },
- { "v", no_argument, (int *)&verbosity, SSIF_LOG_VERBOSE },
- { "vv", no_argument, (int *)&verbosity, SSIF_LOG_DEBUG },
- { "syslog", no_argument, 0, 's' },
- { 0, 0, 0, 0 }
- };
-
- context = calloc(1, sizeof(*context));
- if (context == NULL) {
- MSG_ERR("Failed to allocate memory\n");
- exit(EXIT_FAILURE);
- }
-
- ssif_vlog = &ssif_log_console;
- while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
- switch (opt) {
- case 0:
- break;
- case 'd':
- ssif_bmc_device = optarg;
- break;
- case 's':
- /* Avoid a double openlog() */
- if (ssif_vlog != &vsyslog) {
- openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
- ssif_vlog = &vsyslog;
- }
- break;
- default:
- usage(name);
- exit(EXIT_FAILURE);
- }
- }
-
- if (verbosity == SSIF_LOG_VERBOSE)
- MSG_OUT("Verbose logging\n");
-
- if (verbosity == SSIF_LOG_DEBUG)
- MSG_OUT("Debug logging\n");
-
- MSG_OUT("Starting\n");
- r = sd_bus_default_system(&context->bus);
- if (r < 0) {
- MSG_ERR("Failed to connect to system bus: %s\n", strerror(-r));
- goto error;
- }
-
- MSG_OUT("Registering dbus methods/signals\n");
- r = sd_bus_add_object_vtable(context->bus,
- NULL,
- OBJ_NAME,
- DBUS_NAME,
- ipmid_vtable,
- context);
- if (r < 0) {
- MSG_ERR("Failed to issue method call: %s\n", strerror(-r));
- goto deregister_sdbus;
- }
-
- MSG_OUT("Requesting dbus name: %s\n", DBUS_NAME);
- r = sd_bus_request_name(context->bus, DBUS_NAME,
- SD_BUS_NAME_ALLOW_REPLACEMENT | SD_BUS_NAME_REPLACE_EXISTING);
- if (r < 0) {
- MSG_ERR("Failed to acquire service name: %s\n", strerror(-r));
- goto deregister_sdbus;
- }
-
- MSG_OUT("Getting dbus file descriptors\n");
- context->fds[SD_BUS_FD].fd = sd_bus_get_fd(context->bus);
- if (context->fds[SD_BUS_FD].fd < 0) {
- r = -errno;
- MSG_OUT("Couldn't get the bus file descriptor: %s\n",
- strerror(errno));
- goto deregister_sdbus;
- }
-
- MSG_OUT("Opening %s\n", SSIF_BMC_PATH);
- context->fds[SSIF_FD].fd = open(SSIF_BMC_PATH, O_RDWR | O_NONBLOCK);
- if (context->fds[SSIF_FD].fd < 0) {
- r = -errno;
- MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
- SSIF_BMC_PATH,
- strerror(errno));
- goto free_sdbus_fd;
- }
-
- MSG_OUT("Creating timer fd\n");
- context->fds[TIMER_FD].fd = timerfd_create(CLOCK_MONOTONIC, 0);
- if (context->fds[TIMER_FD].fd < 0) {
- r = -errno;
- MSG_ERR("Couldn't create timer fd: %s\n", strerror(errno));
- goto free_ssif_fd;
- }
- context->fds[SD_BUS_FD].events = POLLIN;
- context->fds[SSIF_FD].events = POLLIN;
- context->fds[TIMER_FD].events = POLLIN;
-
- MSG_OUT("Entering polling loop\n");
-
- while (running) {
- polled = poll(context->fds, TOTAL_FDS, -1);
- if (polled == 0)
- continue;
- if (polled < 0) {
- r = -errno;
- MSG_ERR("Error from poll(): %s\n",
- strerror(errno));
- goto finish;
- }
- r = dispatch_sd_bus(context);
- if (r < 0) {
- MSG_ERR("Error handling dbus event: %s\n",
- strerror(-r));
- goto finish;
- }
- r = dispatch_ssif(context);
- if (r < 0) {
- MSG_ERR("Error handling SSIF event: %s\n",
- strerror(-r));
- goto finish;
- }
- r = dispatch_timer(context);
- if (r < 0) {
- MSG_ERR("Error handling timer event: %s\n",
- strerror(-r));
- goto finish;
- }
- }
-
-finish:
- close(context->fds[TIMER_FD].fd);
-free_ssif_fd:
- close(context->fds[SSIF_FD].fd);
-free_sdbus_fd:
- close(context->fds[SD_BUS_FD].fd);
-deregister_sdbus:
- sd_bus_unref(context->bus);
-error:
- free(context);
-
- return r;
-}
diff --git a/ssifbridged.cpp b/ssifbridged.cpp
new file mode 100644
index 0000000..f49d7c5
--- /dev/null
+++ b/ssifbridged.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2021 Ampere Computing LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This is a daemon that forwards requests and receive responses from SSIF over
+ * the D-Bus IPMI Interface.
+ */
+
+#include <getopt.h>
+#include <linux/ipmi_bmc.h>
+
+#include <CLI/CLI.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/asio.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <iostream>
+
+using namespace phosphor::logging;
+
+static constexpr const char devBase[] = "/dev/ipmi-ssif-host";
+/* SSIF use IPMI SSIF channel */
+static constexpr const char* ssifBus =
+ "xyz.openbmc_project.Ipmi.Channel.ipmi_ssif";
+static constexpr const char* ssifObj =
+ "/xyz/openbmc_project/Ipmi/Channel/ipmi_ssif";
+
+class SsifChannel
+{
+ public:
+ static constexpr size_t ssifMessageSize = 4096;
+ static constexpr uint8_t netFnShift = 2;
+ static constexpr uint8_t lunMask = (1 << netFnShift) - 1;
+
+ SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
+ std::shared_ptr<sdbusplus::asio::connection>& bus,
+ const std::string& channel, bool verbose);
+ bool initOK() const
+ {
+ return !!dev;
+ }
+ void channelAbort(const char* msg, const boost::system::error_code& ec);
+ void async_read();
+ void processMessage(const boost::system::error_code& ecRd, size_t rlen);
+
+ protected:
+ std::array<uint8_t, ssifMessageSize> xferBuffer;
+ std::shared_ptr<boost::asio::io_context> io;
+ std::shared_ptr<sdbusplus::asio::connection> bus;
+ std::shared_ptr<sdbusplus::asio::object_server> server;
+ std::unique_ptr<boost::asio::posix::stream_descriptor> dev = nullptr;
+ bool verbose;
+};
+
+SsifChannel::SsifChannel(std::shared_ptr<boost::asio::io_context>& io,
+ std::shared_ptr<sdbusplus::asio::connection>& bus,
+ const std::string& device, bool verbose) :
+ io(io),
+ bus(bus), verbose(verbose)
+{
+ std::string devName = devBase;
+ if (!device.empty())
+ {
+ devName = device;
+ }
+
+ // open device
+ int fd = open(devName.c_str(), O_RDWR | O_NONBLOCK);
+ if (fd < 0)
+ {
+ log<level::ERR>("Couldn't open SSIF driver with flags O_RDWR",
+ entry("FILENAME=%s", devName.c_str()),
+ entry("ERROR=%s", strerror(errno)));
+ return;
+ }
+ else
+ {
+ dev = std::make_unique<boost::asio::posix::stream_descriptor>(*io,
+ fd);
+ }
+
+ async_read();
+ // register interfaces...
+ server = std::make_shared<sdbusplus::asio::object_server>(bus);
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
+ server->add_interface(ssifObj, ssifBus);
+ iface->initialize();
+}
+
+void SsifChannel::channelAbort(const char* msg,
+ const boost::system::error_code& ec)
+{
+ log<level::ERR>(msg, entry("ERROR=%s", ec.message().c_str()));
+ // bail; maybe a restart from systemd can clear the error
+ io->stop();
+}
+
+void SsifChannel::async_read()
+{
+ boost::asio::async_read(*dev,
+ boost::asio::buffer(xferBuffer, xferBuffer.size()),
+ boost::asio::transfer_at_least(2),
+ [this](const boost::system::error_code& ec,
+ size_t rlen) {
+ processMessage(ec, rlen);
+ });
+}
+
+void SsifChannel::processMessage(const boost::system::error_code& ecRd,
+ size_t rlen)
+{
+ if (ecRd || rlen < 2)
+ {
+ channelAbort("Failed to read req msg", ecRd);
+ return;
+ }
+ async_read();
+
+ auto rawIter = xferBuffer.cbegin();
+ auto rawEnd = rawIter + rlen;
+ uint8_t len = rawIter[0] + 1;
+ uint8_t netfn = rawIter[1] >> netFnShift;
+ uint8_t lun = rawIter[1] & lunMask;
+ uint8_t cmd = rawIter[2];
+ if (verbose)
+ {
+ log<level::INFO>("Read req msg", entry("NETFN=0x%02x", netfn),
+ entry("LUN=0x%02x", lun),
+ entry("CMD=0x%02x", cmd));
+ }
+ // copy out payload
+ std::vector<uint8_t> data(&rawIter[3], rawEnd);
+ // non-session bridges still need to pass an empty options map
+ std::map<std::string, std::variant<int>> options;
+ // the response is a tuple because dbus can only return a single value
+ using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
+ std::vector<uint8_t>>;
+ static constexpr const char ipmiQueueService[] =
+ "xyz.openbmc_project.Ipmi.Host";
+ static constexpr const char ipmiQueuePath[] =
+ "/xyz/openbmc_project/Ipmi";
+ static constexpr const char ipmiQueueIntf[] =
+ "xyz.openbmc_project.Ipmi.Server";
+ static constexpr const char ipmiQueueMethod[] = "execute";
+ bus->async_method_call(
+ [this, netfnCap{netfn}, lunCap{lun},
+ cmdCap{cmd}](const boost::system::error_code& ec,
+ const IpmiDbusRspType& response) {
+ std::vector<uint8_t> rsp;
+ const auto& [netfn, lun, cmd, cc, payload] = response;
+ if (ec)
+ {
+ log<level::ERR>(
+ "ssif<->ipmid bus error:", entry("NETFN=0x%02x", netfn),
+ entry("LUN=0x%02x", lun), entry("CMD=0x%02x", cmd),
+ entry("ERROR=%s", ec.message().c_str()));
+ // send unspecified error for a D-Bus error
+ constexpr uint8_t ccResponseNotAvailable = 0xce;
+ rsp.resize(sizeof(uint8_t) + sizeof(netfn) + sizeof(cmd) +
+ sizeof(cc));
+ rsp[0] = 3;
+ rsp[1] =
+ ((netfnCap + 1) << netFnShift) | (lunCap & lunMask);
+ rsp[2] = cmdCap;
+ rsp[3] = ccResponseNotAvailable;
+ }
+ else
+ {
+ rsp.resize(sizeof(uint8_t) + sizeof(netfn) + sizeof(cmd) +
+ sizeof(cc) + payload.size());
+
+ // write the response
+ auto rspIter = rsp.begin();
+ rspIter[0] = payload.size() + 3;
+ rspIter[1] = (netfn << netFnShift) | (lun & lunMask);
+ rspIter[2] = cmd;
+ rspIter[3] = cc;
+ if (payload.size())
+ {
+ std::copy(payload.cbegin(), payload.cend(),
+ &rspIter[4]);
+ }
+ }
+ if (verbose)
+ {
+ log<level::INFO>(
+ "Send rsp msg", entry("NETFN=0x%02x", netfn),
+ entry("LUN=0x%02x", lun), entry("CMD=0x%02x", cmd),
+ entry("CC=0x%02x", cc));
+ }
+ boost::system::error_code ecWr;
+ size_t wlen =
+ boost::asio::write(*dev, boost::asio::buffer(rsp), ecWr);
+ if (ecWr || wlen != rsp.size())
+ {
+ log<level::ERR>(
+ "Failed to send rsp msg", entry("SIZE=%d", wlen),
+ entry("EXPECT=%d", rsp.size()),
+ entry("ERROR=%s", ecWr.message().c_str()),
+ entry("NETFN=0x%02x", netfn), entry("LUN=0x%02x", lun),
+ entry("CMD=0x%02x", cmd), entry("CC=0x%02x", cc));
+ }
+ },
+ ipmiQueueService, ipmiQueuePath, ipmiQueueIntf, ipmiQueueMethod,
+ netfn, lun, cmd, data, options);
+}
+
+
+int main(int argc, char* argv[])
+{
+ CLI::App app("SSIF IPMI bridge");
+ std::string device;
+ app.add_option("-d,--device", device,
+ "use <DEVICE> file. Default is /dev/ipmi-ssif-host");
+ bool verbose = false;
+ app.add_option("-v,--verbose", verbose, "print more verbose output");
+ CLI11_PARSE(app, argc, argv);
+
+ // Connect to system bus
+ auto io = std::make_shared<boost::asio::io_context>();
+ sd_bus* dbus;
+ sd_bus_default_system(&dbus);
+ auto bus = std::make_shared<sdbusplus::asio::connection>(*io, dbus);
+ bus->request_name(ssifBus);
+ // Create the SSIF channel, listening on D-Bus and on the SSIF device
+ SsifChannel ssifchannel(io, bus, device, verbose);
+ if (!ssifchannel.initOK())
+ {
+ return EXIT_FAILURE;
+ }
+ io->run();
+
+ return 0;
+}