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