blob: 470c332c77859178c6bb5bf369e85227340a5b4a [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) {
James Feist980b6f12019-01-18 09:47:03 -0800300 // docs say to call this in a loop until no events are left
301 // to be processed
302 do {
303 r = sd_bus_process(context->bus, NULL);
304 } while (r > 0);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800305 }
306
307 return r;
308}
309
310static int dispatch_timer(struct kcsbridged_context *context)
311{
312 if (context->fds[TIMER_FD].revents & POLLIN) {
313 struct kcs_msg_req *req;
314 uint8_t rsp[3];
315
316 MSG_OUT("Timeout on msg with seq: 0x%02x\n", context->seqnum);
317
318 context->expired = 1;
319
320 req = &context->req;
321 rsp[0] = ((req->netfn | 1) << 2) | (req->lun & 0x3);
322 rsp[1] = req->cmd;
323 rsp[2] = 0xce; /* Command response could not be provided */
324 if (write(context->fds[KCS_FD].fd, rsp, 3) < 0)
325 MSG_ERR("Failed to send the timeout response!\n");
326 }
327
328 return 0;
329}
330
331static int dispatch_kcs(struct kcsbridged_context *context)
332{
333 struct kcs_msg_req *req = &context->req;
334 sd_bus_message *msg;
335 int r = 0, len;
336 uint8_t data[KCS_MESSAGE_SIZE];
337
338 if (!(context->fds[KCS_FD].revents & POLLIN))
339 goto out;
340
341 len = read(context->fds[KCS_FD].fd, data, sizeof(data));
342 if (len < 0 || handle_kcs_request(context, data, len))
343 goto out;
344
Jia, Chunhui9e382a12019-01-08 15:05:24 +0800345 r = sd_bus_message_new_signal(context->bus, &msg, objPath, DBUS_INTF,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800346 "ReceivedMessage");
347 if (r < 0) {
348 MSG_ERR("Failed to create signal: %s\n", strerror(-r));
349 goto out;
350 }
351
352 r = sd_bus_message_append(msg, "yyyy", context->seqnum, req->netfn,
353 req->lun, req->cmd);
354 if (r < 0) {
355 MSG_ERR("Couldn't append header to signal: %s\n", strerror(-r));
356 goto bail;
357 }
358
359 r = sd_bus_message_append_array(msg, 'y', req->data, req->data_len);
360 if (r < 0) {
361 MSG_ERR("Couldn't append array to signal: %s\n", strerror(-r));
362 goto bail;
363 }
364
365 r = sd_bus_send(context->bus, msg, NULL);
366 if (r < 0) {
367 MSG_ERR("Couldn't emit dbus signal: %s\n", strerror(-r));
368 goto bail;
369 }
370
371 kcs_set_timer(context, KCS_TIMEOUT_IN_SEC);
372
373 MSG_OUT("Recv req msg -> seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x\n",
374 context->seqnum, req->netfn, req->lun, req->cmd);
375
376 if (verbosity == KCS_LOG_DEBUG && req->data_len != 0)
377 kcs_dump_data(req->data, req->data_len);
378
379bail:
380 sd_bus_message_unref(msg);
381out:
382 return r;
383}
384
385static void usage(const char *name)
386{
387 fprintf(stderr,
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800388 "Usage %s [--v[v] | --syslog] --i <ID> --d <DEVICE>\n"
389 "--v Be verbose\n"
390 "--vv Be verbose and dump entire messages\n"
391 "--s, --syslog Log output to syslog (pointless without --verbose)\n"
Jia, Chunhui37db8192018-12-21 16:12:19 +0800392 "--i, --instanceid <ID> instance id (string type) optional\n"
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800393 "--d, --device <DEVICE> Use <DEVICE> file.\n\n",
Haiyue Wanga1c50752018-04-03 15:16:09 +0800394 name);
395}
396
397static const sd_bus_vtable ipmid_vtable[] = {
398 SD_BUS_VTABLE_START(0),
399 SD_BUS_METHOD("sendMessage", "yyyyyay", "x", &method_send_message,
400 SD_BUS_VTABLE_UNPRIVILEGED),
401 SD_BUS_METHOD("setAttention", "", "x", &method_set_sms_atn,
402 SD_BUS_VTABLE_UNPRIVILEGED),
403 SD_BUS_METHOD("clearAttention", "", "x", &method_clear_sms_atn,
404 SD_BUS_VTABLE_UNPRIVILEGED),
405 SD_BUS_METHOD("forceAbort", "", "x", &method_force_abort,
406 SD_BUS_VTABLE_UNPRIVILEGED),
407 SD_BUS_SIGNAL("ReceivedMessage", "yyyyay", 0),
408 SD_BUS_VTABLE_END};
409
410int main(int argc, char *argv[])
411{
412 struct kcsbridged_context *context;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800413 const char *name = argv[0];
Jia, Chunhui37db8192018-12-21 16:12:19 +0800414 bool deviceOptFlag = false;
Haiyue Wanga1c50752018-04-03 15:16:09 +0800415 int opt, polled, r;
416 static const struct option long_options[] = {
417 {"device", required_argument, 0, 'd'},
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800418 {"instanceid", required_argument, 0, 'i'},
Haiyue Wanga1c50752018-04-03 15:16:09 +0800419 {"v", no_argument, &verbosity, KCS_LOG_VERBOSE},
420 {"vv", no_argument, &verbosity, KCS_LOG_DEBUG},
421 {"syslog", no_argument, 0, 's'},
422 {0, 0, 0, 0}};
423
424 context = calloc(1, sizeof(*context));
425 if (!context) {
426 fprintf(stderr, "OOM!\n");
427 return -1;
428 }
429
Jia, Chunhui37db8192018-12-21 16:12:19 +0800430 snprintf(busName, NAMEBUFFERLEN, "%s", DEFAULT_DBUS);
431 snprintf(objPath, NAMEBUFFERLEN, "%s", DEFAULT_OBJ);
432
Haiyue Wanga1c50752018-04-03 15:16:09 +0800433 kcs_vlog = &kcs_log_console;
434 while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
435 switch (opt) {
436 case 0:
437 break;
438
439 case 'd':
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800440 snprintf(kcsDevice, NAMEBUFFERLEN, "%s", optarg);
441 deviceOptFlag = true;
442 break;
443
444 case 'i':
445 if (sizeof(*optarg) > OPTMAXLEN) {
446 fprintf(stderr, "ID is too long!\n");
447 exit(EXIT_FAILURE);
448 }
449 if ((NULL != strstr(optarg, "."))
450 || (NULL != strstr(optarg, "/"))) {
451 fprintf(stderr, "invalid ID!\n");
452 exit(EXIT_FAILURE);
453 }
454 snprintf(busName, NAMEBUFFERLEN, "%s%s", DBUS_NAME,
455 optarg);
456 snprintf(objPath, NAMEBUFFERLEN, "%s%s", OBJ_NAME,
457 optarg);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800458 break;
459
460 case 's':
461 if (kcs_vlog != &vsyslog) {
462 openlog(LOG_PREFIX, LOG_ODELAY, LOG_DAEMON);
463 kcs_vlog = &vsyslog;
464 }
465 break;
466
467 default:
468 usage(name);
469 exit(EXIT_FAILURE);
470 }
471 }
472
Jia, Chunhui37db8192018-12-21 16:12:19 +0800473 if (false == deviceOptFlag) {
Haiyue Wanga1c50752018-04-03 15:16:09 +0800474 usage(name);
Jia, Chunhui37db8192018-12-21 16:12:19 +0800475 MSG_OUT("Flag: device %d \n", deviceOptFlag);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800476 exit(EXIT_FAILURE);
477 }
478
479 if (verbosity == KCS_LOG_VERBOSE)
480 MSG_OUT("Verbose logging\n");
481 else if (verbosity == KCS_LOG_DEBUG)
482 MSG_OUT("Debug logging\n");
483
484 MSG_OUT("Starting\n");
485 r = sd_bus_default_system(&context->bus);
486 if (r < 0) {
487 MSG_ERR("Failed to connect to system bus: %s\n", strerror(-r));
488 goto finish;
489 }
490
491 MSG_OUT("Registering dbus methods/signals\n");
Jia, Chunhui37db8192018-12-21 16:12:19 +0800492 r = sd_bus_add_object_vtable(context->bus, NULL, objPath, DBUS_INTF,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800493 ipmid_vtable, context);
494 if (r < 0) {
495 MSG_ERR("Failed to issue method call: %s\n", strerror(-r));
496 goto finish;
497 }
498
Jia, Chunhui37db8192018-12-21 16:12:19 +0800499 MSG_OUT("Requesting dbus : %s objpath:%s \n", busName, objPath);
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800500 r = sd_bus_request_name(context->bus, busName,
Haiyue Wanga1c50752018-04-03 15:16:09 +0800501 SD_BUS_NAME_ALLOW_REPLACEMENT
502 | SD_BUS_NAME_REPLACE_EXISTING);
503 if (r < 0) {
504 MSG_ERR("Failed to acquire service name: %s\n", strerror(-r));
505 goto finish;
506 }
507
508 MSG_OUT("Getting dbus file descriptors\n");
509 context->fds[SD_BUS_FD].fd = sd_bus_get_fd(context->bus);
510 if (context->fds[SD_BUS_FD].fd < 0) {
511 r = -errno;
512 MSG_OUT("Couldn't get the bus file descriptor: %s\n",
513 strerror(errno));
514 goto finish;
515 }
516
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800517 MSG_OUT("Opening %s\n", kcsDevice);
518 context->fds[KCS_FD].fd = open(kcsDevice, O_RDWR | O_NONBLOCK);
Haiyue Wanga1c50752018-04-03 15:16:09 +0800519 if (context->fds[KCS_FD].fd < 0) {
520 r = -errno;
Jia, Chunhui33aea0d2018-12-12 15:20:26 +0800521 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", kcsDevice,
522 strerror(errno));
Haiyue Wanga1c50752018-04-03 15:16:09 +0800523 goto finish;
524 }
525
526 MSG_OUT("Creating timer fd\n");
527 context->fds[TIMER_FD].fd = timerfd_create(CLOCK_MONOTONIC, 0);
528 if (context->fds[TIMER_FD].fd < 0) {
529 r = -errno;
530 MSG_ERR("Couldn't create timer fd: %s\n", strerror(errno));
531 goto finish;
532 }
533 context->fds[SD_BUS_FD].events = POLLIN;
534 context->fds[KCS_FD].events = POLLIN;
535 context->fds[TIMER_FD].events = POLLIN;
536
537 MSG_OUT("Entering polling loop\n");
538
539 while (1) {
540 polled = poll(context->fds, TOTAL_FDS, 5000);
541 if (polled == 0)
542 continue;
543 if (polled < 0) {
544 r = -errno;
545 MSG_ERR("Error from poll(): %s\n", strerror(errno));
546 goto finish;
547 }
548
549 r = dispatch_sd_bus(context);
550 if (r < 0) {
551 MSG_ERR("Error handling dbus event: %s\n",
552 strerror(-r));
553 goto finish;
554 }
555 r = dispatch_kcs(context);
556 if (r < 0) {
557 MSG_ERR("Error handling KCS event: %s\n", strerror(-r));
558 goto finish;
559 }
560 r = dispatch_timer(context);
561 if (r < 0) {
562 MSG_ERR("Error handling timer event: %s\n",
563 strerror(-r));
564 goto finish;
565 }
566 }
567
568finish:
569 close(context->fds[KCS_FD].fd);
570 close(context->fds[TIMER_FD].fd);
571 sd_bus_unref(context->bus);
572 free(context);
573
574 return r;
575}