blob: d943c3b30903f1a173bb592e0bf8d96487c96c8c [file] [log] [blame]
Haiyue Wanga1c50752018-04-03 15:16:09 +08001/* Copyright 2017 - 2018 Intel
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <assert.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <getopt.h>
20#include <limits.h>
21#include <linux/ipmi_bmc.h>
22#include <poll.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <syslog.h>
28#include <sys/mman.h>
29#include <sys/ioctl.h>
30#include <sys/stat.h>
31#include <sys/timerfd.h>
32#include <systemd/sd-bus.h>
33#include <time.h>
34#include <unistd.h>
Jia, Chunhui33aea0d2018-12-12 15:20:26 +080035#include <stdbool.h>
Haiyue Wanga1c50752018-04-03 15:16:09 +080036
37#define DBUS_ERR "org.openbmc.error"
Haiyue Wanga1c50752018-04-03 15:16:09 +080038
39#define LOG_PREFIX "KCSBRIDGED"
Jia, Chunhui33aea0d2018-12-12 15:20:26 +080040#define DBUS_NAME "org.openbmc.HostIpmi."
41#define OBJ_NAME "/org/openbmc/HostIpmi/"
Haiyue Wanga1c50752018-04-03 15:16:09 +080042
43#define KCS_TIMEOUT_IN_SEC 5
44#define KCS_MESSAGE_SIZE 256
45
46#define SD_BUS_FD 0
47#define KCS_FD 1
48#define TIMER_FD 2
49#define TOTAL_FDS 3
50
Jia, Chunhui33aea0d2018-12-12 15:20:26 +080051#define NAMEBUFFERLEN 50
52#define OPTMAXLEN (NAMEBUFFERLEN - sizeof(OBJ_NAME) - 1)
53
54char kcsDevice[NAMEBUFFERLEN];
55char busName[NAMEBUFFERLEN];
56char objPath[NAMEBUFFERLEN];
57
Haiyue Wanga1c50752018-04-03 15:16:09 +080058struct kcs_msg_req {
59 uint8_t netfn;
60 uint8_t lun;
61 uint8_t cmd;
62 uint8_t *data;
63 size_t data_len;
64};
65
66struct kcsbridged_context {
67 struct pollfd fds[TOTAL_FDS];
68 struct sd_bus *bus;
69
70 /*
71 * Request and Response Messages are paired together as a Write Transfer
72 * to the BMC to send the request followed by a Read Transfer from the
73 * BMC to get the response.
74 */
75 int expired;
76 uint8_t seqnum;
77 struct kcs_msg_req req;
78};
79
80enum { KCS_LOG_NONE = 0, KCS_LOG_VERBOSE, KCS_LOG_DEBUG };
81
82static void (*kcs_vlog)(int p, const char *fmt, va_list args);
83static int verbosity = KCS_LOG_NONE;
84
85#define MSG_OUT(f_, ...) \
86 do { \
87 if (verbosity != KCS_LOG_NONE) \
88 kcs_log(LOG_INFO, f_, ##__VA_ARGS__); \
89 } while (0)
90
91#define MSG_ERR(f_, ...) \
92 do { \
93 if (verbosity != KCS_LOG_NONE) \
94 kcs_log(LOG_ERR, f_, ##__VA_ARGS__); \
95 } while (0)
96
97static void kcs_log_console(int p, const char *fmt, va_list args)
98{
99 vfprintf(stderr, fmt, args);
100}
101
102__attribute__((format(printf, 2, 3))) static void kcs_log(int p,
103 const char *fmt, ...)
104{
105 va_list args;
106
107 va_start(args, fmt);
108 kcs_vlog(p, fmt, args);
109 va_end(args);
110}
111
112static void kcs_dump_data(uint8_t *data, size_t data_len)
113{
114 size_t i;
115 int str_len;
116 char str[64];
117
118 str_len = 0;
119 for (i = 0; i < data_len; i++) {
120 if (i % 8 == 0) {
121 if (i != 0) {
122 kcs_log(LOG_INFO, "%s\n", str);
123 str_len = 0;
124 }
125 str_len += sprintf(&str[str_len], "\t");
126 }
127
128 str_len += sprintf(&str[str_len], "0x%02x ", data[i]);
129 }
130
131 if (str_len != 0)
132 kcs_log(LOG_INFO, "%s\n", str);
133}
134
135static void kcs_set_timer(struct kcsbridged_context *context, int seconds)
136{
137 struct itimerspec ts;
138 int r;
139
140 ts.it_interval.tv_sec = 0;
141 ts.it_interval.tv_nsec = 0;
142 ts.it_value.tv_nsec = 0;
143 ts.it_value.tv_sec = seconds;
144
145 r = timerfd_settime(context->fds[TIMER_FD].fd, 0, &ts, NULL);
146 if (r == -1)
147 MSG_ERR("Couldn't set timerfd: %s\n", strerror(errno));
148}
149
150static int handle_kcs_request(struct kcsbridged_context *context, uint8_t *msg,
151 size_t msglen)
152{
153 struct kcs_msg_req *req;
154
155 if (msglen < 2) {
156 MSG_ERR("KCS message with a short length (%zd)\n", msglen);
157 return -1;
158 }
159
160 context->expired = 0;
161 context->seqnum++;
162
163 req = &context->req;
164 req->netfn = msg[0] >> 2;
165 req->lun = msg[0] & 0x3;
166 req->cmd = msg[1];
167 req->data = msg + 2;
168 req->data_len = msglen - 2;
169
170 return 0;
171}
172
173static int method_send_message(sd_bus_message *msg, void *userdata,
174 sd_bus_error *err)
175{
176 struct kcsbridged_context *context = userdata;
177 uint8_t netfn, lun, seqnum, cmd, cc;
178 struct kcs_msg_req *req;
179 uint8_t *data;
180 size_t data_sz;
181 int r;
182 uint8_t rsp[KCS_MESSAGE_SIZE];
183
184 if (!context || context->expired) {
185 sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
186 r = 0;
187 goto out;
188 }
189
190 r = sd_bus_message_read(msg, "yyyyy", &seqnum, &netfn, &lun, &cmd, &cc);
191 if (r < 0) {
192 sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
193 r = -EINVAL;
194 goto out;
195 }
196
197 req = &context->req;
198 if (context->seqnum != seqnum || (req->netfn | 1) != netfn
199 || req->lun != lun || req->cmd != cmd) {
200 sd_bus_error_set_const(err, DBUS_ERR, "No matching request");
201 r = -EINVAL;
202 goto out;
203 }
204
205 kcs_set_timer(context, 0); /* Stop the timer. */
206
207 r = sd_bus_message_read_array(msg, 'y', (const void **)&data, &data_sz);
208 if (r < 0 || data_sz > sizeof(rsp) - 3) {
209 sd_bus_error_set_const(err, DBUS_ERR, "Bad message data");
210 r = -EINVAL;
211 goto out;
212 }
213
214 rsp[0] = (netfn << 2) | (lun & 0x3);
215 rsp[1] = cmd;
216 rsp[2] = cc;
217 if (data_sz)
218 memcpy(rsp + 3, data, data_sz);
219
220 r = write(context->fds[KCS_FD].fd, rsp, 3 + data_sz);
221 if (r > 0)
222 r = 0;
223
224 MSG_OUT("Send rsp msg <- seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x cc=0x%02x\n",
225 seqnum, netfn, lun, cmd, cc);
226
227 if (verbosity == KCS_LOG_DEBUG && data_sz != 0)
228 kcs_dump_data(data, data_sz);
229
230out:
231 return sd_bus_reply_method_return(msg, "x", r);
232}
233
234static int method_set_sms_atn(sd_bus_message *msg, void *userdata,
235 sd_bus_error *err)
236{
237 struct kcsbridged_context *context = userdata;
238 int r;
239
240 MSG_OUT("Sending SET_SMS_ATN\n");
241
242 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_SET_SMS_ATN);
243 if (r == -1) {
244 r = errno;
245 MSG_ERR("Couldn't SET_SMS_ATN: %s\n", strerror(r));
246 return sd_bus_reply_method_errno(msg, errno, err);
247 }
248
249 r = 0;
250 return sd_bus_reply_method_return(msg, "x", r);
251}
252
253static int method_clear_sms_atn(sd_bus_message *msg, void *userdata,
254 sd_bus_error *err)
255{
256 struct kcsbridged_context *context = userdata;
257 int r;
258
259 MSG_OUT("Sending CLEAR_SMS_ATN\n");
260
261 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_CLEAR_SMS_ATN);
262 if (r == -1) {
263 r = errno;
264 MSG_ERR("Couldn't CLEAR_SMS_ATN: %s\n", strerror(r));
265 return sd_bus_reply_method_errno(msg, errno, err);
266 }
267
268 r = 0;
269 return sd_bus_reply_method_return(msg, "x", r);
270}
271
272static int method_force_abort(sd_bus_message *msg, void *userdata,
273 sd_bus_error *err)
274{
275 struct kcsbridged_context *context = userdata;
276 int r;
277
278 MSG_OUT("Sending FORCE_ABORT\n");
279
280 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_FORCE_ABORT);
281 if (r == -1) {
282 r = errno;
283 MSG_ERR("Couldn't FORCE_ABORT: %s\n", strerror(r));
284 return sd_bus_reply_method_errno(msg, errno, err);
285 }
286
287 r = 0;
288 return sd_bus_reply_method_return(msg, "x", r);
289}
290
291static int dispatch_sd_bus(struct kcsbridged_context *context)
292{
293 int r = 0;
294
295 if (context->fds[SD_BUS_FD].revents) {
296 r = sd_bus_process(context->bus, NULL);
297 if (r > 0)
298 MSG_OUT("Processed %d dbus events\n", r);
299 }
300
301 return r;
302}
303
304static int dispatch_timer(struct kcsbridged_context *context)
305{
306 if (context->fds[TIMER_FD].revents & POLLIN) {
307 struct kcs_msg_req *req;
308 uint8_t rsp[3];
309
310 MSG_OUT("Timeout on msg with seq: 0x%02x\n", context->seqnum);
311
312 context->expired = 1;
313
314 req = &context->req;
315 rsp[0] = ((req->netfn | 1) << 2) | (req->lun & 0x3);
316 rsp[1] = req->cmd;
317 rsp[2] = 0xce; /* Command response could not be provided */
318 if (write(context->fds[KCS_FD].fd, rsp, 3) < 0)
319 MSG_ERR("Failed to send the timeout response!\n");
320 }
321
322 return 0;
323}
324
325static int dispatch_kcs(struct kcsbridged_context *context)
326{
327 struct kcs_msg_req *req = &context->req;
328 sd_bus_message *msg;
329 int r = 0, len;
330 uint8_t data[KCS_MESSAGE_SIZE];
331
332 if (!(context->fds[KCS_FD].revents & POLLIN))
333 goto out;
334
335 len = read(context->fds[KCS_FD].fd, data, sizeof(data));
336 if (len < 0 || handle_kcs_request(context, data, len))
337 goto out;
338
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800339 r = sd_bus_message_new_signal(context->bus, &msg, objPath, busName,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800340 "ReceivedMessage");
341 if (r < 0) {
342 MSG_ERR("Failed to create signal: %s\n", strerror(-r));
343 goto out;
344 }
345
346 r = sd_bus_message_append(msg, "yyyy", context->seqnum, req->netfn,
347 req->lun, req->cmd);
348 if (r < 0) {
349 MSG_ERR("Couldn't append header to signal: %s\n", strerror(-r));
350 goto bail;
351 }
352
353 r = sd_bus_message_append_array(msg, 'y', req->data, req->data_len);
354 if (r < 0) {
355 MSG_ERR("Couldn't append array to signal: %s\n", strerror(-r));
356 goto bail;
357 }
358
359 r = sd_bus_send(context->bus, msg, NULL);
360 if (r < 0) {
361 MSG_ERR("Couldn't emit dbus signal: %s\n", strerror(-r));
362 goto bail;
363 }
364
365 kcs_set_timer(context, KCS_TIMEOUT_IN_SEC);
366
367 MSG_OUT("Recv req msg -> seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x\n",
368 context->seqnum, req->netfn, req->lun, req->cmd);
369
370 if (verbosity == KCS_LOG_DEBUG && req->data_len != 0)
371 kcs_dump_data(req->data, req->data_len);
372
373bail:
374 sd_bus_message_unref(msg);
375out:
376 return r;
377}
378
379static void usage(const char *name)
380{
381 fprintf(stderr,
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800382 "Usage %s [--v[v] | --syslog] --i <ID> --d <DEVICE>\n"
383 "--v Be verbose\n"
384 "--vv Be verbose and dump entire messages\n"
385 "--s, --syslog Log output to syslog (pointless without --verbose)\n"
386 "--i, --instanceid <ID> instance id (string type)\n"
387 "--d, --device <DEVICE> Use <DEVICE> file.\n\n",
Haiyue Wanga1c50752018-04-03 15:16:09 +0800388 name);
389}
390
391static const sd_bus_vtable ipmid_vtable[] = {
392 SD_BUS_VTABLE_START(0),
393 SD_BUS_METHOD("sendMessage", "yyyyyay", "x", &method_send_message,
394 SD_BUS_VTABLE_UNPRIVILEGED),
395 SD_BUS_METHOD("setAttention", "", "x", &method_set_sms_atn,
396 SD_BUS_VTABLE_UNPRIVILEGED),
397 SD_BUS_METHOD("clearAttention", "", "x", &method_clear_sms_atn,
398 SD_BUS_VTABLE_UNPRIVILEGED),
399 SD_BUS_METHOD("forceAbort", "", "x", &method_force_abort,
400 SD_BUS_VTABLE_UNPRIVILEGED),
401 SD_BUS_SIGNAL("ReceivedMessage", "yyyyay", 0),
402 SD_BUS_VTABLE_END};
403
404int main(int argc, char *argv[])
405{
406 struct kcsbridged_context *context;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800407 const char *name = argv[0];
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800408 bool deviceOptFlag = false, dbusOptFlag = false;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800409 int opt, polled, r;
410 static const struct option long_options[] = {
411 {"device", required_argument, 0, 'd'},
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800412 {"instanceid", required_argument, 0, 'i'},
Haiyue Wanga1c50752018-04-03 15:16:09 +0800413 {"v", no_argument, &verbosity, KCS_LOG_VERBOSE},
414 {"vv", no_argument, &verbosity, KCS_LOG_DEBUG},
415 {"syslog", no_argument, 0, 's'},
416 {0, 0, 0, 0}};
417
418 context = calloc(1, sizeof(*context));
419 if (!context) {
420 fprintf(stderr, "OOM!\n");
421 return -1;
422 }
423
424 kcs_vlog = &kcs_log_console;
425 while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
426 switch (opt) {
427 case 0:
428 break;
429
430 case 'd':
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800431 snprintf(kcsDevice, NAMEBUFFERLEN, "%s", optarg);
432 deviceOptFlag = true;
433 break;
434
435 case 'i':
436 if (sizeof(*optarg) > OPTMAXLEN) {
437 fprintf(stderr, "ID is too long!\n");
438 exit(EXIT_FAILURE);
439 }
440 if ((NULL != strstr(optarg, "."))
441 || (NULL != strstr(optarg, "/"))) {
442 fprintf(stderr, "invalid ID!\n");
443 exit(EXIT_FAILURE);
444 }
445 snprintf(busName, NAMEBUFFERLEN, "%s%s", DBUS_NAME,
446 optarg);
447 snprintf(objPath, NAMEBUFFERLEN, "%s%s", OBJ_NAME,
448 optarg);
449 dbusOptFlag = true;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800450 break;
451
452 case 's':
453 if (kcs_vlog != &vsyslog) {
454 openlog(LOG_PREFIX, LOG_ODELAY, LOG_DAEMON);
455 kcs_vlog = &vsyslog;
456 }
457 break;
458
459 default:
460 usage(name);
461 exit(EXIT_FAILURE);
462 }
463 }
464
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800465 if ((false == deviceOptFlag) || (false == dbusOptFlag)) {
Haiyue Wanga1c50752018-04-03 15:16:09 +0800466 usage(name);
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800467 MSG_OUT("Flag: device %d dbus %d \n", deviceOptFlag,
468 dbusOptFlag);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800469 exit(EXIT_FAILURE);
470 }
471
472 if (verbosity == KCS_LOG_VERBOSE)
473 MSG_OUT("Verbose logging\n");
474 else if (verbosity == KCS_LOG_DEBUG)
475 MSG_OUT("Debug logging\n");
476
477 MSG_OUT("Starting\n");
478 r = sd_bus_default_system(&context->bus);
479 if (r < 0) {
480 MSG_ERR("Failed to connect to system bus: %s\n", strerror(-r));
481 goto finish;
482 }
483
484 MSG_OUT("Registering dbus methods/signals\n");
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800485 r = sd_bus_add_object_vtable(context->bus, NULL, objPath, busName,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800486 ipmid_vtable, context);
487 if (r < 0) {
488 MSG_ERR("Failed to issue method call: %s\n", strerror(-r));
489 goto finish;
490 }
491
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800492 MSG_OUT("Requesting dbus name: %s objpath:%s \n", busName, objPath);
493 r = sd_bus_request_name(context->bus, busName,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800494 SD_BUS_NAME_ALLOW_REPLACEMENT
495 | SD_BUS_NAME_REPLACE_EXISTING);
496 if (r < 0) {
497 MSG_ERR("Failed to acquire service name: %s\n", strerror(-r));
498 goto finish;
499 }
500
501 MSG_OUT("Getting dbus file descriptors\n");
502 context->fds[SD_BUS_FD].fd = sd_bus_get_fd(context->bus);
503 if (context->fds[SD_BUS_FD].fd < 0) {
504 r = -errno;
505 MSG_OUT("Couldn't get the bus file descriptor: %s\n",
506 strerror(errno));
507 goto finish;
508 }
509
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800510 MSG_OUT("Opening %s\n", kcsDevice);
511 context->fds[KCS_FD].fd = open(kcsDevice, O_RDWR | O_NONBLOCK);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800512 if (context->fds[KCS_FD].fd < 0) {
513 r = -errno;
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800514 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", kcsDevice,
515 strerror(errno));
Haiyue Wanga1c50752018-04-03 15:16:09 +0800516 goto finish;
517 }
518
519 MSG_OUT("Creating timer fd\n");
520 context->fds[TIMER_FD].fd = timerfd_create(CLOCK_MONOTONIC, 0);
521 if (context->fds[TIMER_FD].fd < 0) {
522 r = -errno;
523 MSG_ERR("Couldn't create timer fd: %s\n", strerror(errno));
524 goto finish;
525 }
526 context->fds[SD_BUS_FD].events = POLLIN;
527 context->fds[KCS_FD].events = POLLIN;
528 context->fds[TIMER_FD].events = POLLIN;
529
530 MSG_OUT("Entering polling loop\n");
531
532 while (1) {
533 polled = poll(context->fds, TOTAL_FDS, 5000);
534 if (polled == 0)
535 continue;
536 if (polled < 0) {
537 r = -errno;
538 MSG_ERR("Error from poll(): %s\n", strerror(errno));
539 goto finish;
540 }
541
542 r = dispatch_sd_bus(context);
543 if (r < 0) {
544 MSG_ERR("Error handling dbus event: %s\n",
545 strerror(-r));
546 goto finish;
547 }
548 r = dispatch_kcs(context);
549 if (r < 0) {
550 MSG_ERR("Error handling KCS event: %s\n", strerror(-r));
551 goto finish;
552 }
553 r = dispatch_timer(context);
554 if (r < 0) {
555 MSG_ERR("Error handling timer event: %s\n",
556 strerror(-r));
557 goto finish;
558 }
559 }
560
561finish:
562 close(context->fds[KCS_FD].fd);
563 close(context->fds[TIMER_FD].fd);
564 sd_bus_unref(context->bus);
565 free(context);
566
567 return r;
568}