blob: cd7b583661791333128857a7e004a2e074452025 [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
Jia, Chunhui37db8192018-12-21 16:12:19 +080043#define DEFAULT_DBUS "org.openbmc.HostIpmi"
44#define DEFAULT_OBJ "/org/openbmc/HostIpmi/1"
45#define DBUS_INTF "org.openbmc.HostIpmi"
46
Haiyue Wanga1c50752018-04-03 15:16:09 +080047#define KCS_TIMEOUT_IN_SEC 5
48#define KCS_MESSAGE_SIZE 256
49
50#define SD_BUS_FD 0
51#define KCS_FD 1
52#define TIMER_FD 2
53#define TOTAL_FDS 3
54
Jia, Chunhui33aea0d2018-12-12 15:20:26 +080055#define NAMEBUFFERLEN 50
56#define OPTMAXLEN (NAMEBUFFERLEN - sizeof(OBJ_NAME) - 1)
57
58char kcsDevice[NAMEBUFFERLEN];
59char busName[NAMEBUFFERLEN];
60char objPath[NAMEBUFFERLEN];
61
Haiyue Wanga1c50752018-04-03 15:16:09 +080062struct kcs_msg_req {
63 uint8_t netfn;
64 uint8_t lun;
65 uint8_t cmd;
66 uint8_t *data;
67 size_t data_len;
68};
69
70struct kcsbridged_context {
71 struct pollfd fds[TOTAL_FDS];
72 struct sd_bus *bus;
73
74 /*
75 * Request and Response Messages are paired together as a Write Transfer
76 * to the BMC to send the request followed by a Read Transfer from the
77 * BMC to get the response.
78 */
79 int expired;
80 uint8_t seqnum;
81 struct kcs_msg_req req;
82};
83
84enum { KCS_LOG_NONE = 0, KCS_LOG_VERBOSE, KCS_LOG_DEBUG };
85
86static void (*kcs_vlog)(int p, const char *fmt, va_list args);
87static int verbosity = KCS_LOG_NONE;
88
89#define MSG_OUT(f_, ...) \
90 do { \
91 if (verbosity != KCS_LOG_NONE) \
92 kcs_log(LOG_INFO, f_, ##__VA_ARGS__); \
93 } while (0)
94
95#define MSG_ERR(f_, ...) \
96 do { \
97 if (verbosity != KCS_LOG_NONE) \
98 kcs_log(LOG_ERR, f_, ##__VA_ARGS__); \
99 } while (0)
100
101static void kcs_log_console(int p, const char *fmt, va_list args)
102{
103 vfprintf(stderr, fmt, args);
104}
105
106__attribute__((format(printf, 2, 3))) static void kcs_log(int p,
107 const char *fmt, ...)
108{
109 va_list args;
110
111 va_start(args, fmt);
112 kcs_vlog(p, fmt, args);
113 va_end(args);
114}
115
116static void kcs_dump_data(uint8_t *data, size_t data_len)
117{
118 size_t i;
119 int str_len;
120 char str[64];
121
122 str_len = 0;
123 for (i = 0; i < data_len; i++) {
124 if (i % 8 == 0) {
125 if (i != 0) {
126 kcs_log(LOG_INFO, "%s\n", str);
127 str_len = 0;
128 }
129 str_len += sprintf(&str[str_len], "\t");
130 }
131
132 str_len += sprintf(&str[str_len], "0x%02x ", data[i]);
133 }
134
135 if (str_len != 0)
136 kcs_log(LOG_INFO, "%s\n", str);
137}
138
139static void kcs_set_timer(struct kcsbridged_context *context, int seconds)
140{
141 struct itimerspec ts;
142 int r;
143
144 ts.it_interval.tv_sec = 0;
145 ts.it_interval.tv_nsec = 0;
146 ts.it_value.tv_nsec = 0;
147 ts.it_value.tv_sec = seconds;
148
149 r = timerfd_settime(context->fds[TIMER_FD].fd, 0, &ts, NULL);
150 if (r == -1)
151 MSG_ERR("Couldn't set timerfd: %s\n", strerror(errno));
152}
153
154static int handle_kcs_request(struct kcsbridged_context *context, uint8_t *msg,
155 size_t msglen)
156{
157 struct kcs_msg_req *req;
158
159 if (msglen < 2) {
160 MSG_ERR("KCS message with a short length (%zd)\n", msglen);
161 return -1;
162 }
163
164 context->expired = 0;
165 context->seqnum++;
166
167 req = &context->req;
168 req->netfn = msg[0] >> 2;
169 req->lun = msg[0] & 0x3;
170 req->cmd = msg[1];
171 req->data = msg + 2;
172 req->data_len = msglen - 2;
173
174 return 0;
175}
176
177static int method_send_message(sd_bus_message *msg, void *userdata,
178 sd_bus_error *err)
179{
180 struct kcsbridged_context *context = userdata;
181 uint8_t netfn, lun, seqnum, cmd, cc;
182 struct kcs_msg_req *req;
183 uint8_t *data;
184 size_t data_sz;
185 int r;
186 uint8_t rsp[KCS_MESSAGE_SIZE];
187
188 if (!context || context->expired) {
189 sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
190 r = 0;
191 goto out;
192 }
193
194 r = sd_bus_message_read(msg, "yyyyy", &seqnum, &netfn, &lun, &cmd, &cc);
195 if (r < 0) {
196 sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
197 r = -EINVAL;
198 goto out;
199 }
200
201 req = &context->req;
202 if (context->seqnum != seqnum || (req->netfn | 1) != netfn
203 || req->lun != lun || req->cmd != cmd) {
204 sd_bus_error_set_const(err, DBUS_ERR, "No matching request");
205 r = -EINVAL;
206 goto out;
207 }
208
209 kcs_set_timer(context, 0); /* Stop the timer. */
210
211 r = sd_bus_message_read_array(msg, 'y', (const void **)&data, &data_sz);
212 if (r < 0 || data_sz > sizeof(rsp) - 3) {
213 sd_bus_error_set_const(err, DBUS_ERR, "Bad message data");
214 r = -EINVAL;
215 goto out;
216 }
217
218 rsp[0] = (netfn << 2) | (lun & 0x3);
219 rsp[1] = cmd;
220 rsp[2] = cc;
221 if (data_sz)
222 memcpy(rsp + 3, data, data_sz);
223
224 r = write(context->fds[KCS_FD].fd, rsp, 3 + data_sz);
225 if (r > 0)
226 r = 0;
227
228 MSG_OUT("Send rsp msg <- seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x cc=0x%02x\n",
229 seqnum, netfn, lun, cmd, cc);
230
231 if (verbosity == KCS_LOG_DEBUG && data_sz != 0)
232 kcs_dump_data(data, data_sz);
233
234out:
235 return sd_bus_reply_method_return(msg, "x", r);
236}
237
238static int method_set_sms_atn(sd_bus_message *msg, void *userdata,
239 sd_bus_error *err)
240{
241 struct kcsbridged_context *context = userdata;
242 int r;
243
244 MSG_OUT("Sending SET_SMS_ATN\n");
245
246 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_SET_SMS_ATN);
247 if (r == -1) {
248 r = errno;
249 MSG_ERR("Couldn't SET_SMS_ATN: %s\n", strerror(r));
250 return sd_bus_reply_method_errno(msg, errno, err);
251 }
252
253 r = 0;
254 return sd_bus_reply_method_return(msg, "x", r);
255}
256
257static int method_clear_sms_atn(sd_bus_message *msg, void *userdata,
258 sd_bus_error *err)
259{
260 struct kcsbridged_context *context = userdata;
261 int r;
262
263 MSG_OUT("Sending CLEAR_SMS_ATN\n");
264
265 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_CLEAR_SMS_ATN);
266 if (r == -1) {
267 r = errno;
268 MSG_ERR("Couldn't CLEAR_SMS_ATN: %s\n", strerror(r));
269 return sd_bus_reply_method_errno(msg, errno, err);
270 }
271
272 r = 0;
273 return sd_bus_reply_method_return(msg, "x", r);
274}
275
276static int method_force_abort(sd_bus_message *msg, void *userdata,
277 sd_bus_error *err)
278{
279 struct kcsbridged_context *context = userdata;
280 int r;
281
282 MSG_OUT("Sending FORCE_ABORT\n");
283
284 r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_FORCE_ABORT);
285 if (r == -1) {
286 r = errno;
287 MSG_ERR("Couldn't FORCE_ABORT: %s\n", strerror(r));
288 return sd_bus_reply_method_errno(msg, errno, err);
289 }
290
291 r = 0;
292 return sd_bus_reply_method_return(msg, "x", r);
293}
294
295static int dispatch_sd_bus(struct kcsbridged_context *context)
296{
297 int r = 0;
298
299 if (context->fds[SD_BUS_FD].revents) {
300 r = sd_bus_process(context->bus, NULL);
301 if (r > 0)
302 MSG_OUT("Processed %d dbus events\n", r);
303 }
304
305 return r;
306}
307
308static int dispatch_timer(struct kcsbridged_context *context)
309{
310 if (context->fds[TIMER_FD].revents & POLLIN) {
311 struct kcs_msg_req *req;
312 uint8_t rsp[3];
313
314 MSG_OUT("Timeout on msg with seq: 0x%02x\n", context->seqnum);
315
316 context->expired = 1;
317
318 req = &context->req;
319 rsp[0] = ((req->netfn | 1) << 2) | (req->lun & 0x3);
320 rsp[1] = req->cmd;
321 rsp[2] = 0xce; /* Command response could not be provided */
322 if (write(context->fds[KCS_FD].fd, rsp, 3) < 0)
323 MSG_ERR("Failed to send the timeout response!\n");
324 }
325
326 return 0;
327}
328
329static int dispatch_kcs(struct kcsbridged_context *context)
330{
331 struct kcs_msg_req *req = &context->req;
332 sd_bus_message *msg;
333 int r = 0, len;
334 uint8_t data[KCS_MESSAGE_SIZE];
335
336 if (!(context->fds[KCS_FD].revents & POLLIN))
337 goto out;
338
339 len = read(context->fds[KCS_FD].fd, data, sizeof(data));
340 if (len < 0 || handle_kcs_request(context, data, len))
341 goto out;
342
Jia, Chunhui9e382a12019-01-08 15:05:24 +0800343 r = sd_bus_message_new_signal(context->bus, &msg, objPath, DBUS_INTF,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800344 "ReceivedMessage");
345 if (r < 0) {
346 MSG_ERR("Failed to create signal: %s\n", strerror(-r));
347 goto out;
348 }
349
350 r = sd_bus_message_append(msg, "yyyy", context->seqnum, req->netfn,
351 req->lun, req->cmd);
352 if (r < 0) {
353 MSG_ERR("Couldn't append header to signal: %s\n", strerror(-r));
354 goto bail;
355 }
356
357 r = sd_bus_message_append_array(msg, 'y', req->data, req->data_len);
358 if (r < 0) {
359 MSG_ERR("Couldn't append array to signal: %s\n", strerror(-r));
360 goto bail;
361 }
362
363 r = sd_bus_send(context->bus, msg, NULL);
364 if (r < 0) {
365 MSG_ERR("Couldn't emit dbus signal: %s\n", strerror(-r));
366 goto bail;
367 }
368
369 kcs_set_timer(context, KCS_TIMEOUT_IN_SEC);
370
371 MSG_OUT("Recv req msg -> seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x\n",
372 context->seqnum, req->netfn, req->lun, req->cmd);
373
374 if (verbosity == KCS_LOG_DEBUG && req->data_len != 0)
375 kcs_dump_data(req->data, req->data_len);
376
377bail:
378 sd_bus_message_unref(msg);
379out:
380 return r;
381}
382
383static void usage(const char *name)
384{
385 fprintf(stderr,
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800386 "Usage %s [--v[v] | --syslog] --i <ID> --d <DEVICE>\n"
387 "--v Be verbose\n"
388 "--vv Be verbose and dump entire messages\n"
389 "--s, --syslog Log output to syslog (pointless without --verbose)\n"
Jia, Chunhui37db8192018-12-21 16:12:19 +0800390 "--i, --instanceid <ID> instance id (string type) optional\n"
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800391 "--d, --device <DEVICE> Use <DEVICE> file.\n\n",
Haiyue Wanga1c50752018-04-03 15:16:09 +0800392 name);
393}
394
395static const sd_bus_vtable ipmid_vtable[] = {
396 SD_BUS_VTABLE_START(0),
397 SD_BUS_METHOD("sendMessage", "yyyyyay", "x", &method_send_message,
398 SD_BUS_VTABLE_UNPRIVILEGED),
399 SD_BUS_METHOD("setAttention", "", "x", &method_set_sms_atn,
400 SD_BUS_VTABLE_UNPRIVILEGED),
401 SD_BUS_METHOD("clearAttention", "", "x", &method_clear_sms_atn,
402 SD_BUS_VTABLE_UNPRIVILEGED),
403 SD_BUS_METHOD("forceAbort", "", "x", &method_force_abort,
404 SD_BUS_VTABLE_UNPRIVILEGED),
405 SD_BUS_SIGNAL("ReceivedMessage", "yyyyay", 0),
406 SD_BUS_VTABLE_END};
407
408int main(int argc, char *argv[])
409{
410 struct kcsbridged_context *context;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800411 const char *name = argv[0];
Jia, Chunhui37db8192018-12-21 16:12:19 +0800412 bool deviceOptFlag = false;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800413 int opt, polled, r;
414 static const struct option long_options[] = {
415 {"device", required_argument, 0, 'd'},
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800416 {"instanceid", required_argument, 0, 'i'},
Haiyue Wanga1c50752018-04-03 15:16:09 +0800417 {"v", no_argument, &verbosity, KCS_LOG_VERBOSE},
418 {"vv", no_argument, &verbosity, KCS_LOG_DEBUG},
419 {"syslog", no_argument, 0, 's'},
420 {0, 0, 0, 0}};
421
422 context = calloc(1, sizeof(*context));
423 if (!context) {
424 fprintf(stderr, "OOM!\n");
425 return -1;
426 }
427
Jia, Chunhui37db8192018-12-21 16:12:19 +0800428 snprintf(busName, NAMEBUFFERLEN, "%s", DEFAULT_DBUS);
429 snprintf(objPath, NAMEBUFFERLEN, "%s", DEFAULT_OBJ);
430
Haiyue Wanga1c50752018-04-03 15:16:09 +0800431 kcs_vlog = &kcs_log_console;
432 while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
433 switch (opt) {
434 case 0:
435 break;
436
437 case 'd':
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800438 snprintf(kcsDevice, NAMEBUFFERLEN, "%s", optarg);
439 deviceOptFlag = true;
440 break;
441
442 case 'i':
443 if (sizeof(*optarg) > OPTMAXLEN) {
444 fprintf(stderr, "ID is too long!\n");
445 exit(EXIT_FAILURE);
446 }
447 if ((NULL != strstr(optarg, "."))
448 || (NULL != strstr(optarg, "/"))) {
449 fprintf(stderr, "invalid ID!\n");
450 exit(EXIT_FAILURE);
451 }
452 snprintf(busName, NAMEBUFFERLEN, "%s%s", DBUS_NAME,
453 optarg);
454 snprintf(objPath, NAMEBUFFERLEN, "%s%s", OBJ_NAME,
455 optarg);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800456 break;
457
458 case 's':
459 if (kcs_vlog != &vsyslog) {
460 openlog(LOG_PREFIX, LOG_ODELAY, LOG_DAEMON);
461 kcs_vlog = &vsyslog;
462 }
463 break;
464
465 default:
466 usage(name);
467 exit(EXIT_FAILURE);
468 }
469 }
470
Jia, Chunhui37db8192018-12-21 16:12:19 +0800471 if (false == deviceOptFlag) {
Haiyue Wanga1c50752018-04-03 15:16:09 +0800472 usage(name);
Jia, Chunhui37db8192018-12-21 16:12:19 +0800473 MSG_OUT("Flag: device %d \n", deviceOptFlag);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800474 exit(EXIT_FAILURE);
475 }
476
477 if (verbosity == KCS_LOG_VERBOSE)
478 MSG_OUT("Verbose logging\n");
479 else if (verbosity == KCS_LOG_DEBUG)
480 MSG_OUT("Debug logging\n");
481
482 MSG_OUT("Starting\n");
483 r = sd_bus_default_system(&context->bus);
484 if (r < 0) {
485 MSG_ERR("Failed to connect to system bus: %s\n", strerror(-r));
486 goto finish;
487 }
488
489 MSG_OUT("Registering dbus methods/signals\n");
Jia, Chunhui37db8192018-12-21 16:12:19 +0800490 r = sd_bus_add_object_vtable(context->bus, NULL, objPath, DBUS_INTF,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800491 ipmid_vtable, context);
492 if (r < 0) {
493 MSG_ERR("Failed to issue method call: %s\n", strerror(-r));
494 goto finish;
495 }
496
Jia, Chunhui37db8192018-12-21 16:12:19 +0800497 MSG_OUT("Requesting dbus : %s objpath:%s \n", busName, objPath);
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800498 r = sd_bus_request_name(context->bus, busName,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800499 SD_BUS_NAME_ALLOW_REPLACEMENT
500 | SD_BUS_NAME_REPLACE_EXISTING);
501 if (r < 0) {
502 MSG_ERR("Failed to acquire service name: %s\n", strerror(-r));
503 goto finish;
504 }
505
506 MSG_OUT("Getting dbus file descriptors\n");
507 context->fds[SD_BUS_FD].fd = sd_bus_get_fd(context->bus);
508 if (context->fds[SD_BUS_FD].fd < 0) {
509 r = -errno;
510 MSG_OUT("Couldn't get the bus file descriptor: %s\n",
511 strerror(errno));
512 goto finish;
513 }
514
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800515 MSG_OUT("Opening %s\n", kcsDevice);
516 context->fds[KCS_FD].fd = open(kcsDevice, O_RDWR | O_NONBLOCK);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800517 if (context->fds[KCS_FD].fd < 0) {
518 r = -errno;
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800519 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", kcsDevice,
520 strerror(errno));
Haiyue Wanga1c50752018-04-03 15:16:09 +0800521 goto finish;
522 }
523
524 MSG_OUT("Creating timer fd\n");
525 context->fds[TIMER_FD].fd = timerfd_create(CLOCK_MONOTONIC, 0);
526 if (context->fds[TIMER_FD].fd < 0) {
527 r = -errno;
528 MSG_ERR("Couldn't create timer fd: %s\n", strerror(errno));
529 goto finish;
530 }
531 context->fds[SD_BUS_FD].events = POLLIN;
532 context->fds[KCS_FD].events = POLLIN;
533 context->fds[TIMER_FD].events = POLLIN;
534
535 MSG_OUT("Entering polling loop\n");
536
537 while (1) {
538 polled = poll(context->fds, TOTAL_FDS, 5000);
539 if (polled == 0)
540 continue;
541 if (polled < 0) {
542 r = -errno;
543 MSG_ERR("Error from poll(): %s\n", strerror(errno));
544 goto finish;
545 }
546
547 r = dispatch_sd_bus(context);
548 if (r < 0) {
549 MSG_ERR("Error handling dbus event: %s\n",
550 strerror(-r));
551 goto finish;
552 }
553 r = dispatch_kcs(context);
554 if (r < 0) {
555 MSG_ERR("Error handling KCS event: %s\n", strerror(-r));
556 goto finish;
557 }
558 r = dispatch_timer(context);
559 if (r < 0) {
560 MSG_ERR("Error handling timer event: %s\n",
561 strerror(-r));
562 goto finish;
563 }
564 }
565
566finish:
567 close(context->fds[KCS_FD].fd);
568 close(context->fds[TIMER_FD].fd);
569 sd_bus_unref(context->bus);
570 free(context);
571
572 return r;
573}