1 /*
2 * serial_ipmi.c
3 *
4 * MontaVista IPMI LAN server serial port interface.
5 *
6 * Author: MontaVista Software, Inc.
7 * Corey Minyard <minyard@mvista.com>
8 * source@mvista.com
9 *
10 * Copyright 2012 MontaVista Software Inc.
11 *
12 * This software is available to you under a choice of one of two
13 * licenses. You may choose to be licensed under the terms of the GNU
14 * Lesser General Public License (GPL) Version 2 or the modified BSD
15 * license below. The following disclamer applies to both licenses:
16 *
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * GNU Lesser General Public Licence
29 *
30 * This program is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public License
32 * as published by the Free Software Foundation; either version 2 of
33 * the License, or (at your option) any later version.
34 *
35 * You should have received a copy of the GNU Lesser General Public
36 * License along with this program; if not, write to the Free
37 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
38 *
39 * Modified BSD Licence
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 *
45 * 1. Redistributions of source code must retain the above copyright
46 * notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above
48 * copyright notice, this list of conditions and the following
49 * disclaimer in the documentation and/or other materials provided
50 * with the distribution.
51 * 3. The name of the author may not be used to endorse or promote
52 * products derived from this software without specific prior
53 * written permission.
54 */
55
56 #include <ctype.h>
57 #include <string.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <errno.h>
61 #include <sys/types.h>
62 #include <sys/wait.h>
63 #include <OpenIPMI/ipmi_mc.h>
64 #include <OpenIPMI/ipmi_msgbits.h>
65 #include <OpenIPMI/serserv.h>
66
67 #define EVENT_BUFFER_GLOBAL_ENABLE (1 << 2)
68 #define EVENT_LOG_GLOBAL_ENABLE (1 << 3)
69 #define SUPPORTED_GLOBAL_ENABLES (EVENT_BUFFER_GLOBAL_ENABLE | \
70 EVENT_LOG_GLOBAL_ENABLE)
71
72 static void
raw_send(serserv_data_t * si,unsigned char * data,unsigned int len)73 raw_send(serserv_data_t *si, unsigned char *data, unsigned int len)
74 {
75 if (si->sysinfo->debug & DEBUG_RAW_MSG)
76 debug_log_raw_msg(si->sysinfo, data, len, "Raw serial send:");
77 si->send_out(si, data, len);
78 }
79
80 static unsigned char hex2char[16] = {
81 '0', '1', '2', '3', '4', '5', '6', '7',
82 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
83 };
84
fromhex(unsigned char c)85 static int fromhex(unsigned char c)
86 {
87 if (isdigit(c))
88 return c - '0';
89 else if (isxdigit(c))
90 return tolower(c) - 'a' + 10;
91 else
92 return -1;
93 }
94
95 static int
unformat_ipmb_msg(msg_t * msg,unsigned char * msgd,unsigned int len,serserv_data_t * si)96 unformat_ipmb_msg(msg_t *msg, unsigned char *msgd, unsigned int len,
97 serserv_data_t *si)
98 {
99 if (len < 7) {
100 fprintf(stderr, "Message too short\n");
101 return -1;
102 }
103
104 if (ipmb_checksum(msgd, len, 0) != 0) {
105 fprintf(stderr, "Message checksum failure\n");
106 return -1;
107 }
108 len--;
109
110 msg->rs_addr = msgd[0];
111 msg->netfn = msgd[1] >> 2;
112 msg->rs_lun = msgd[1] & 3;
113 msg->rq_addr = msgd[3];
114 msg->rq_seq = msgd[4] >> 2;
115 msg->rq_lun = msgd[4] & 3;
116 msg->cmd = msgd[5];
117
118 msg->len = len - 6;
119 msg->data = msgd + 6;
120
121 msg->src_addr = NULL;
122 msg->src_len = 0;
123
124 return 0;
125 }
126
127 static void
format_ipmb_rsp(msg_t * msg,unsigned char * msgd,unsigned int * msgd_len,serserv_data_t * mi)128 format_ipmb_rsp(msg_t *msg, unsigned char *msgd,
129 unsigned int *msgd_len, serserv_data_t *mi)
130 {
131 msgd[0] = msg->rs_addr;
132 msgd[1] = (msg->netfn << 2) | msg->rs_lun;
133 msgd[2] = -ipmb_checksum(msgd, 2, 0);
134 msgd[3] = msg->rq_addr;
135 msgd[4] = (msg->rq_seq << 2) | msg->rq_lun;
136 msgd[5] = msg->cmd;
137 memcpy(msgd + 6, msg->data, msg->len);
138 *msgd_len = msg->len + 6;
139 msgd[*msgd_len] = -ipmb_checksum(msgd + 3, (*msgd_len) - 3, 0);
140 (*msgd_len)++;
141 }
142
handle_attn(channel_t * chan,int val,int irq)143 static void handle_attn(channel_t *chan, int val, int irq)
144 {
145 serserv_data_t *si = chan->chan_info;
146
147 if (val && si->do_attn)
148 raw_send(si, si->attn_chars, si->attn_chars_len);
149 }
150
151 /***********************************************************************
152 *
153 * Radisys ASCII codec.
154 *
155 ***********************************************************************/
156
157 #define RA_MAX_CHARS_SIZE (((IPMI_SIM_MAX_MSG_LENGTH + 1) * 3) + 4)
158
159 struct ra_data {
160 unsigned char recv_chars[RA_MAX_CHARS_SIZE];
161 unsigned int recv_chars_len;
162 int recv_chars_too_many;
163 };
164
ra_format_msg(const unsigned char * msg,unsigned int msg_len,serserv_data_t * si)165 static void ra_format_msg(const unsigned char *msg, unsigned int msg_len,
166 serserv_data_t *si)
167 {
168 unsigned int i;
169 unsigned int len;
170 unsigned char c[RA_MAX_CHARS_SIZE];
171
172 len = 0;
173 for (i = 0; i < msg_len; i++) {
174 c[len] = hex2char[msg[i] >> 4];
175 len++;
176 c[len] = hex2char[msg[i] & 0xf];
177 len++;
178 }
179 c[len] = 0x0d;
180 len++;
181
182 raw_send(si, c, len);
183 }
184
185 static int
ra_ipmb_handler(channel_t * chan,msg_t * msg)186 ra_ipmb_handler(channel_t *chan, msg_t *msg)
187 {
188 serserv_data_t *si = chan->chan_info;
189
190 ra_format_msg(msg->data, msg->len, si);
191 free(msg);
192 return 1;
193 }
194
195 /*
196 * Called when the '0x0d' is seen.
197 */
ra_unformat_msg(unsigned char * r,unsigned int len,serserv_data_t * si)198 static int ra_unformat_msg(unsigned char *r, unsigned int len,
199 serserv_data_t *si)
200 {
201 unsigned char real_o[IPMI_SIM_MAX_MSG_LENGTH + 1];
202 unsigned char *o = real_o + 1;
203 msg_t msg;
204 unsigned int p = 0;
205 unsigned int i = 0;
206 int rv;
207
208 if (si->sysinfo->debug & DEBUG_RAW_MSG)
209 debug_log_raw_msg(si->sysinfo, r, len, "Raw serial receive:");
210
211 while (p < len) {
212 rv = fromhex(r[p]);
213 if (rv < 0)
214 return rv;
215 o[i] = rv << 4;
216 p++;
217 if (p >= len)
218 return -1;
219 rv = fromhex(r[p]);
220 if (rv < 0)
221 return rv;
222 o[i] |= rv;
223 p++;
224 i++;
225 }
226
227 if (i < 1)
228 return -1;
229
230 memset(&msg, 0, sizeof(msg));
231 if ((o[0] == si->sysinfo->bmc_ipmb) || (o[0] == 1)) {
232 rv = unformat_ipmb_msg(&msg, o, i, si);
233 if (rv)
234 return rv;
235 } else {
236 msg.rs_addr = 1;
237 msg.netfn = IPMI_APP_NETFN;
238 msg.rs_lun = 0;
239 msg.rq_addr = 1;
240 msg.rq_seq = 0;
241 msg.rq_lun = 0;
242 msg.cmd = IPMI_SEND_MSG_CMD;
243
244 msg.len = i + 1;
245 msg.data = real_o;
246 real_o[0] = 0;
247 }
248 channel_smi_send(&si->channel, &msg);
249 return 0;
250 }
251
252 static void
ra_handle_char(unsigned char ch,serserv_data_t * si)253 ra_handle_char(unsigned char ch, serserv_data_t *si)
254 {
255 struct ra_data *info = si->codec_info;
256 unsigned int len = info->recv_chars_len;
257 unsigned char *r;
258 int rv;
259
260 if (ch == 0x0d) {
261 /* End of command, handle it. */
262 if (info->recv_chars_too_many) {
263 /* Input data overrun. */
264 fprintf(stderr, "Data overrun\n");
265 info->recv_chars_too_many = 0;
266 info->recv_chars_len = 0;
267 return;
268 }
269 rv = ra_unformat_msg(info->recv_chars, info->recv_chars_len, si);
270 info->recv_chars_too_many = 0;
271 info->recv_chars_len = 0;
272 if (rv) {
273 /* Bad input data. */
274 fprintf(stderr, "Bad input data\n");
275 return;
276 }
277 return;
278 }
279
280 if (info->recv_chars_too_many)
281 return;
282
283 r = info->recv_chars;
284
285 if (len >= sizeof(info->recv_chars)) {
286 info->recv_chars_too_many = 1;
287 } else if ((len > 0) && isspace(r[len-1]) && isspace(ch)) {
288 /* Ignore multiple spaces together. */
289 } else {
290 r[len] = ch;
291 info->recv_chars_len++;
292 }
293 }
294
295 static void
ra_send(msg_t * omsg,serserv_data_t * si)296 ra_send(msg_t *omsg, serserv_data_t *si)
297 {
298 unsigned char msg[IPMI_SIM_MAX_MSG_LENGTH + 7];
299 unsigned int msg_len;
300
301 if (omsg->netfn == IPMI_APP_NETFN && omsg->cmd == IPMI_SEND_MSG_CMD)
302 return; /* These are dummies, ignore them. */
303
304 format_ipmb_rsp(omsg, msg, &msg_len, si);
305
306 ra_format_msg(msg, msg_len, si);
307 }
308
309 int
ra_setup(serserv_data_t * si)310 ra_setup(serserv_data_t *si)
311 {
312 struct ra_data *info;
313 info = malloc(sizeof(*info));
314 if (!info)
315 return -1;
316
317 info->recv_chars_len = 0;
318 info->recv_chars_too_many = 0;
319 si->codec_info = info;
320 si->channel.recv_in_q = ra_ipmb_handler;
321 return 0;
322 }
323
324 /***********************************************************************
325 *
326 * Direct Mode codec.
327 *
328 ***********************************************************************/
329
330 #define DM_START_CHAR 0xA0
331 #define DM_STOP_CHAR 0xA5
332 #define DM_PACKET_HANDSHAKE 0xA6
333 #define DM_DATA_ESCAPE_CHAR 0xAA
334
335 struct dm_data {
336 unsigned char recv_msg[IPMI_SIM_MAX_MSG_LENGTH + 4];
337 unsigned int recv_msg_len;
338 int recv_msg_too_many;
339 int in_recv_msg;
340 int in_escape;
341 };
342
343 static void
dm_handle_msg(unsigned char * imsg,unsigned int len,serserv_data_t * si)344 dm_handle_msg(unsigned char *imsg, unsigned int len, serserv_data_t *si)
345 {
346 int rv;
347 msg_t msg;
348
349 if (si->sysinfo->debug & DEBUG_RAW_MSG)
350 debug_log_raw_msg(si->sysinfo, imsg, len, "Raw serial receive:");
351
352 memset(&msg, 0, sizeof(msg));
353 rv = unformat_ipmb_msg(&msg, imsg, len, si);
354 if (rv)
355 return;
356 channel_smi_send(&si->channel, &msg);
357 }
358
359 static void
dm_handle_char(unsigned char ch,serserv_data_t * si)360 dm_handle_char(unsigned char ch, serserv_data_t *si)
361 {
362 struct dm_data *info = si->codec_info;
363 unsigned int len = info->recv_msg_len;
364 unsigned char c;
365
366 switch (ch) {
367 case DM_START_CHAR:
368 if (info->in_recv_msg)
369 fprintf(stderr, "Msg started in the middle of another\n");
370 info->in_recv_msg = 1;
371 info->recv_msg_len = 0;
372 info->recv_msg_too_many = 0;
373 info->in_escape = 0;
374 break;
375
376 case DM_STOP_CHAR:
377 if (!info->in_recv_msg)
378 fprintf(stderr, "Empty message\n");
379 else if (info->in_escape) {
380 info->in_recv_msg = 0;
381 fprintf(stderr, "Message ended in escape\n");
382 } else if (info->recv_msg_too_many) {
383 fprintf(stderr, "Message too long\n");
384 info->in_recv_msg = 0;
385 } else {
386 dm_handle_msg(info->recv_msg, info->recv_msg_len, si);
387 info->in_recv_msg = 0;
388 }
389 info->in_escape = 0;
390
391 c = DM_PACKET_HANDSHAKE;
392 raw_send(si, &c, 1);
393 break;
394
395 case DM_PACKET_HANDSHAKE:
396 info->in_escape = 0;
397 break;
398
399 case DM_DATA_ESCAPE_CHAR:
400 if (!info->recv_msg_too_many)
401 info->in_escape = 1;
402 break;
403
404 default:
405 if (!info->in_recv_msg)
406 /* Ignore characters outside of messages. */
407 break;
408
409 if (info->in_escape) {
410 info->in_escape = 0;
411 switch (ch) {
412 case 0xB0: ch = DM_START_CHAR; break;
413 case 0xB5: ch = DM_STOP_CHAR; break;
414 case 0xB6: ch = DM_PACKET_HANDSHAKE; break;
415 case 0xBA: ch = DM_DATA_ESCAPE_CHAR; break;
416 case 0x3B: ch = 0x1b; break;
417 default:
418 fprintf(stderr, "Invalid escape char: 0x%x\n", ch);
419 info->recv_msg_too_many = 1;
420 return;
421 }
422 }
423
424 if (!info->recv_msg_too_many) {
425 if (len >= sizeof(info->recv_msg)) {
426 info->recv_msg_too_many = 1;
427 break;
428 }
429
430 info->recv_msg[len] = ch;
431 info->recv_msg_len++;
432 }
433 break;
434 }
435 }
436
437 static void
dm_send(msg_t * imsg,serserv_data_t * si)438 dm_send(msg_t *imsg, serserv_data_t *si)
439 {
440 unsigned int i;
441 unsigned int len = 0;
442 unsigned char c[(IPMI_SIM_MAX_MSG_LENGTH + 7) * 2];
443 unsigned char msg[IPMI_SIM_MAX_MSG_LENGTH + 7];
444 unsigned int msg_len;
445
446 format_ipmb_rsp(imsg, msg, &msg_len, si);
447
448 c[len++] = 0xA0;
449 for (i = 0; i < msg_len; i++) {
450 switch (msg[i]) {
451 case 0xA0:
452 c[len++] = 0xAA;
453 c[len++] = 0xB0;
454 break;
455
456 case 0xA5:
457 c[len++] = 0xAA;
458 c[len++] = 0xB5;
459 break;
460
461 case 0xA6:
462 c[len++] = 0xAA;
463 c[len++] = 0xB6;
464 break;
465
466 case 0xAA:
467 c[len++] = 0xAA;
468 c[len++] = 0xBA;
469 break;
470
471 case 0x1B:
472 c[len++] = 0xAA;
473 c[len++] = 0x3B;
474 break;
475
476 default:
477 c[len++] = msg[i];
478 }
479
480 }
481 c[len++] = 0xA5;
482
483 raw_send(si, c, len);
484 }
485
486 static int
dm_setup(serserv_data_t * si)487 dm_setup(serserv_data_t *si)
488 {
489 struct dm_data *info;
490
491 info = malloc(sizeof(*info));
492 if (!info)
493 return -1;
494 memset(info, 0, sizeof(*info));
495 si->channel.set_atn = handle_attn;
496 si->codec_info = info;
497 return 0;
498 }
499
500
501 /***********************************************************************
502 *
503 * Terminal Mode codec.
504 *
505 ***********************************************************************/
506
507 #define TM_MAX_CHARS_SIZE (((IPMI_SIM_MAX_MSG_LENGTH + 1) * 3) + 4)
508
509 struct tm_data {
510 unsigned char recv_chars[TM_MAX_CHARS_SIZE];
511 unsigned int recv_chars_len;
512 int recv_chars_too_many;
513 };
514
515 static void
tm_send(msg_t * msg,serserv_data_t * si)516 tm_send(msg_t *msg, serserv_data_t *si)
517 {
518 unsigned int i;
519 unsigned int len;
520 unsigned char c[TM_MAX_CHARS_SIZE];
521 unsigned char t;
522
523 len = 0;
524 c[len] = '[';
525 len++;
526
527 t = msg->netfn << 2 | msg->rs_lun;
528 c[len] = hex2char[t >> 4];
529 len++;
530 c[len] = hex2char[t & 0xf];
531 len++;
532
533 /*
534 * Insert the sequence number and bridge bits. Bridge bits
535 * are always zero.
536 */
537 t = msg->rq_seq << 2;
538 c[len] = hex2char[t >> 4];
539 len++;
540 c[len] = hex2char[t & 0xf];
541 len++;
542
543 c[len] = hex2char[msg->cmd >> 4];
544 len++;
545 c[len] = hex2char[msg->cmd & 0xf];
546 len++;
547
548 /* Now the rest of the message. */
549 for (i = 0; ; ) {
550 c[len] = hex2char[msg->data[i] >> 4];
551 len++;
552 c[len] = hex2char[msg->data[i] & 0xf];
553 len++;
554 i++;
555 if (i == msg->len)
556 break;
557 c[len] = ' ';
558 len++;
559 }
560 c[len] = ']';
561 len++;
562 c[len] = 0x0a;
563 len++;
564
565 raw_send(si, c, len);
566 }
567
568 /*
569 * Called when the ']' is seen, the leading '[' is removed, too. We
570 * get this with a leading space and no more than one space between
571 * items.
572 */
tm_unformat_msg(unsigned char * r,unsigned int len,serserv_data_t * si)573 static int tm_unformat_msg(unsigned char *r, unsigned int len,
574 serserv_data_t *si)
575 {
576 unsigned char o[IPMI_SIM_MAX_MSG_LENGTH];
577 msg_t msg;
578 unsigned int p = 0;
579 unsigned int i = 0;
580 int rv;
581
582 if (si->sysinfo->debug & DEBUG_RAW_MSG)
583 debug_log_raw_msg(si->sysinfo, r, len, "Raw serial receive:");
584
585 #define SKIP_SPACE if (isspace(r[p])) p++
586 #define ENSURE_MORE if (p >= len) return -1
587
588 SKIP_SPACE;
589 while (p < len) {
590 if (i >= sizeof(o))
591 return -1;
592 ENSURE_MORE;
593 rv = fromhex(r[p]);
594 if (rv < 0)
595 return rv;
596 o[i] = rv << 4;
597 p++;
598 ENSURE_MORE;
599 rv = fromhex(r[p]);
600 if (rv < 0)
601 return rv;
602 o[i] |= rv;
603 p++;
604 i++;
605 SKIP_SPACE;
606 }
607
608 if (i < 3)
609 return -1;
610
611 memset(&msg, 0, sizeof(msg));
612 msg.netfn = o[0] >> 2;
613 msg.rq_lun = o[0] & 3;
614 msg.rq_seq = o[1] >> 2;
615 msg.cmd = o[2];
616 msg.data = o + 3;
617 msg.len = i - 3;
618 msg.src_addr = NULL;
619 msg.src_len = 0;
620
621 channel_smi_send(&si->channel, &msg);
622 return 0;
623 #undef SKIP_SPACE
624 #undef ENSURE_MORE
625 }
626
627 static void
tm_handle_char(unsigned char ch,serserv_data_t * si)628 tm_handle_char(unsigned char ch, serserv_data_t *si)
629 {
630 struct tm_data *info = si->codec_info;
631 unsigned int len = info->recv_chars_len;
632 unsigned char *r;
633 int rv;
634
635 if (ch == '[') {
636 /*
637 * Start of a command. Note that if a command is
638 * already in progress (len != 0) we abort it.
639 */
640 if (len != 0)
641 fprintf(stderr, "Msg started in the middle of another\n");
642
643 /* Convert the leading '[' to a space, that's innocuous. */
644 info->recv_chars[0] = ' ';
645 info->recv_chars_len = 1;
646 info->recv_chars_too_many = 0;
647 return;
648 }
649
650 if (len == 0)
651 /* Ignore everything outside [ ]. */
652 return;
653
654 if (ch == ']') {
655 /* End of command, handle it. */
656 if (info->recv_chars_too_many) {
657 /* Input data overrun. */
658 fprintf(stderr, "Data overrun\n");
659 info->recv_chars_too_many = 0;
660 info->recv_chars_len = 0;
661 return;
662 }
663 rv = tm_unformat_msg(info->recv_chars, info->recv_chars_len, si);
664 info->recv_chars_too_many = 0;
665 info->recv_chars_len = 0;
666 if (rv) {
667 /* Bad input data. */
668 fprintf(stderr, "Bad input data\n");
669 return;
670 }
671 return;
672 }
673
674 if (info->recv_chars_too_many)
675 return;
676
677 r = info->recv_chars;
678
679 if (len >= sizeof(info->recv_chars)) {
680 info->recv_chars_too_many = 1;
681 } else if ((len > 0) && isspace(r[len-1]) && isspace(ch)) {
682 /* Ignore multiple spaces together. */
683 } else {
684 r[len] = ch;
685 info->recv_chars_len++;
686 }
687 }
688
689 static int
tm_setup(serserv_data_t * si)690 tm_setup(serserv_data_t *si)
691 {
692 struct tm_data *info;
693
694 info = malloc(sizeof(*info));
695 if (!info)
696 return -1;
697
698 info->recv_chars_len = 0;
699 info->recv_chars_too_many = 0;
700 si->channel.set_atn = handle_attn;
701 si->codec_info = info;
702 return 0;
703 }
704
705
706 /***********************************************************************
707 *
708 * VM Mode codec.
709 *
710 ***********************************************************************/
711
712 /*
713 * This protocol has an end-of-message marker, everything from the
714 * beginning or the last end of message is a new message (or command).
715 * Messages are normal IPMI messages with the following header:
716 *
717 * seq
718 * netfn << 2 | lun
719 * cmd
720 * data....
721 * checksum
722 *
723 * The sequence is return in the response as-is.
724 * Commands are special things to alert the other end of things like
725 * attn, requests for reset/nmi/etc.
726 */
727
728 #define VM_MSG_CHAR 0xA0 /* Marks end of message */
729 #define VM_CMD_CHAR 0xA1 /* Marks end of a command */
730 #define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */
731
732 #define VM_PROTOCOL_VERSION 1
733 #define VM_CMD_VERSION 0xff /* A version number byte follows */
734 #define VM_CMD_NOATTN 0x00
735 #define VM_CMD_ATTN 0x01
736 #define VM_CMD_ATTN_IRQ 0x02
737 #define VM_CMD_POWEROFF 0x03
738 #define VM_CMD_RESET 0x04
739 #define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */
740 #define VM_CMD_DISABLE_IRQ 0x06
741 #define VM_CMD_SEND_NMI 0x07
742 #define VM_CMD_CAPABILITIES 0x08
743 #define VM_CAPABILITIES_POWER 0x01
744 #define VM_CAPABILITIES_RESET 0x02
745 #define VM_CAPABILITIES_IRQ 0x04
746 #define VM_CAPABILITIES_NMI 0x08
747 #define VM_CAPABILITIES_ATTN 0x10
748 #define VM_CAPABILITIES_GRACEFUL_SHUTDOWN 0x20
749 #define VM_CMD_GRACEFUL_SHUTDOWN 0x09
750
751 struct vm_data {
752 unsigned char recv_msg[IPMI_SIM_MAX_MSG_LENGTH + 4];
753 unsigned int recv_msg_len;
754 int recv_msg_too_many;
755 int in_escape;
756 int attn_works;
757 };
758
759 static void
vm_handle_msg(unsigned char * imsg,unsigned int len,serserv_data_t * si)760 vm_handle_msg(unsigned char *imsg, unsigned int len, serserv_data_t *si)
761 {
762 msg_t msg;
763
764 if (si->sysinfo->debug & DEBUG_RAW_MSG)
765 debug_log_raw_msg(si->sysinfo, imsg, len, "Raw serial receive:");
766
767 if (len < 4) {
768 fprintf(stderr, "Message too short\n");
769 return;
770 }
771
772 if (ipmb_checksum(imsg, len, 0) != 0) {
773 fprintf(stderr, "Message checksum failure\n");
774 return;
775 }
776 len--;
777
778 memset(&msg, 0, sizeof(msg));
779 msg.rq_seq = imsg[0];
780 msg.netfn = imsg[1] >> 2;
781 msg.rs_lun = imsg[1] & 0x3;
782 msg.cmd = imsg[2];
783 msg.len = len - 3;
784 msg.data = imsg + 3;
785
786 channel_smi_send(&si->channel, &msg);
787 }
788
789 static void
vm_handle_cmd(unsigned char * imsg,unsigned int len,serserv_data_t * si)790 vm_handle_cmd(unsigned char *imsg, unsigned int len, serserv_data_t *si)
791 {
792 struct vm_data *info = si->codec_info;
793
794 if (si->sysinfo->debug & DEBUG_RAW_MSG)
795 debug_log_raw_msg(si->sysinfo, imsg, len, "Raw serial cmd:");
796
797 if (len < 1)
798 return;
799
800 switch (imsg[0]) {
801 case VM_CMD_VERSION:
802 /* We only support one version for now. */
803 break;
804
805 case VM_CMD_CAPABILITIES:
806 if (len < 2)
807 return;
808 if (imsg[1] & VM_CAPABILITIES_POWER)
809 si->channel.hw_capabilities |= (1 << HW_OP_POWERON);
810 if (imsg[1] & VM_CAPABILITIES_GRACEFUL_SHUTDOWN)
811 si->channel.hw_capabilities |= (1 << HW_OP_GRACEFUL_SHUTDOWN);
812 if (imsg[1] & VM_CAPABILITIES_RESET)
813 si->channel.hw_capabilities |= (1 << HW_OP_RESET);
814 if (imsg[1] & VM_CAPABILITIES_IRQ)
815 si->channel.hw_capabilities |= (1 << HW_OP_IRQ_ENABLE);
816 if (imsg[1] & VM_CAPABILITIES_NMI)
817 si->channel.hw_capabilities |= (1 << HW_OP_SEND_NMI);
818 if (imsg[1] & VM_CAPABILITIES_ATTN)
819 info->attn_works = 1;
820 break;
821
822 case VM_CMD_RESET:
823 /* The remote end reset, report it. */
824 if (si->sysinfo->target_reset)
825 si->sysinfo->target_reset(si->sysinfo);
826 break;
827 }
828 }
829
830 static void
vm_handle_char(unsigned char ch,serserv_data_t * si)831 vm_handle_char(unsigned char ch, serserv_data_t *si)
832 {
833 struct vm_data *info = si->codec_info;
834 unsigned int len = info->recv_msg_len;
835
836 switch (ch) {
837 case VM_MSG_CHAR:
838 case VM_CMD_CHAR:
839 if (info->in_escape) {
840 fprintf(stderr, "Message ended in escape\n");
841 } else if (info->recv_msg_too_many) {
842 fprintf(stderr, "Message too long\n");
843 } else if (info->recv_msg_len == 0) {
844 /* Nothing to do */
845 } else if (ch == VM_MSG_CHAR) {
846 vm_handle_msg(info->recv_msg, info->recv_msg_len, si);
847 } else if (ch == VM_CMD_CHAR) {
848 vm_handle_cmd(info->recv_msg, info->recv_msg_len, si);
849 }
850 info->in_escape = 0;
851 info->recv_msg_len = 0;
852 info->recv_msg_too_many = 0;
853 break;
854
855 case VM_ESCAPE_CHAR:
856 if (!info->recv_msg_too_many)
857 info->in_escape = 1;
858 break;
859
860 default:
861 if (info->in_escape) {
862 info->in_escape = 0;
863 ch &= ~0x10;
864 }
865
866 if (!info->recv_msg_too_many) {
867 if (len >= sizeof(info->recv_msg)) {
868 info->recv_msg_too_many = 1;
869 break;
870 }
871
872 info->recv_msg[len] = ch;
873 info->recv_msg_len++;
874 }
875 break;
876 }
877 }
878
879 static void
vm_add_char(unsigned char ch,unsigned char * c,unsigned int * pos)880 vm_add_char(unsigned char ch, unsigned char *c, unsigned int *pos)
881 {
882 switch (ch) {
883 case VM_MSG_CHAR:
884 case VM_CMD_CHAR:
885 case VM_ESCAPE_CHAR:
886 c[(*pos)++] = VM_ESCAPE_CHAR;
887 c[(*pos)++] = ch | 0x10;
888 break;
889
890 default:
891 c[(*pos)++] = ch;
892 }
893 }
894
895 static void
vm_send(msg_t * imsg,serserv_data_t * si)896 vm_send(msg_t *imsg, serserv_data_t *si)
897 {
898 unsigned int i;
899 unsigned int len = 0;
900 unsigned char c[(IPMI_SIM_MAX_MSG_LENGTH + 7) * 2];
901 unsigned char csum;
902 unsigned char ch;
903
904 ch = imsg->rq_seq;
905 vm_add_char(ch, c, &len);
906 csum = ipmb_checksum(&ch, 1, 0);
907
908 ch = (imsg->netfn << 2) | imsg->rs_lun;
909 vm_add_char(ch, c, &len);
910 csum = ipmb_checksum(&ch, 1, csum);
911
912 vm_add_char(imsg->cmd, c, &len);
913 csum = ipmb_checksum(&imsg->cmd, 1, csum);
914
915 for (i = 0; i < imsg->len; i++)
916 vm_add_char(imsg->data[i], c, &len);
917
918 vm_add_char(-ipmb_checksum(imsg->data, imsg->len, csum), c, &len);
919 c[len++] = VM_MSG_CHAR;
920
921 raw_send(si, c, len);
922 }
923
924 static void
vm_set_attn(channel_t * chan,int val,int irq)925 vm_set_attn(channel_t *chan, int val, int irq)
926 {
927 serserv_data_t *si = chan->chan_info;
928 unsigned int len = 0;
929 unsigned char c[3];
930
931 if (!val)
932 vm_add_char(VM_CMD_NOATTN, c, &len);
933 else if (irq)
934 vm_add_char(VM_CMD_ATTN_IRQ, c, &len);
935 else
936 vm_add_char(VM_CMD_ATTN, c, &len);
937
938 c[len++] = VM_CMD_CHAR;
939
940 raw_send(si, c, len);
941 }
942
943 static int
vm_hw_op(channel_t * chan,unsigned int op)944 vm_hw_op(channel_t *chan, unsigned int op)
945 {
946 serserv_data_t *si = chan->chan_info;
947 unsigned int len = 0;
948 unsigned char c[3];
949
950 switch(op) {
951 case HW_OP_RESET:
952 vm_add_char(VM_CMD_RESET, c, &len);
953 break;
954
955 case HW_OP_POWERON:
956 if (chan->start_cmd)
957 chan->start_cmd(chan);
958 return 0;
959
960 case HW_OP_POWEROFF:
961 if (si->connected)
962 vm_add_char(VM_CMD_POWEROFF, c, &len);
963 if (chan->stop_cmd)
964 chan->stop_cmd(chan, !si->connected);
965 break;
966
967 case HW_OP_GRACEFUL_SHUTDOWN:
968 if (si->connected)
969 vm_add_char(VM_CMD_GRACEFUL_SHUTDOWN, c, &len);
970 break;
971
972 case HW_OP_SEND_NMI:
973 vm_add_char(VM_CMD_SEND_NMI, c, &len);
974 break;
975
976 case HW_OP_IRQ_ENABLE:
977 vm_add_char(VM_CMD_ENABLE_IRQ, c, &len);
978 break;
979
980 case HW_OP_IRQ_DISABLE:
981 vm_add_char(VM_CMD_DISABLE_IRQ, c, &len);
982 break;
983
984 case HW_OP_CHECK_POWER:
985 return si->connected;
986 break;
987
988 default:
989 return 0;
990 }
991
992 c[len++] = VM_CMD_CHAR;
993
994 raw_send(si, c, len);
995 return 0;
996 }
997
998 static void
vm_connected(serserv_data_t * si)999 vm_connected(serserv_data_t *si)
1000 {
1001 unsigned int len = 0;
1002 unsigned char c[5];
1003
1004 vm_add_char(VM_CMD_VERSION, c, &len);
1005 vm_add_char(VM_PROTOCOL_VERSION, c, &len);
1006 c[len++] = VM_CMD_CHAR;
1007 raw_send(si, c, len);
1008 si->connected = 1;
1009 ipmi_resend_atn(&si->channel);
1010 }
1011
1012 static void
vm_disconnected(serserv_data_t * si)1013 vm_disconnected(serserv_data_t *si)
1014 {
1015 si->connected = 0;
1016 }
1017
1018 static int
vm_setup(serserv_data_t * si)1019 vm_setup(serserv_data_t *si)
1020 {
1021 struct vm_data *info;
1022
1023 info = malloc(sizeof(*info));
1024 if (!info)
1025 return -1;
1026 memset(info, 0, sizeof(*info));
1027 si->codec_info = info;
1028 si->channel.hw_op = vm_hw_op;
1029 si->channel.set_atn = vm_set_attn;
1030 si->channel.hw_capabilities = (1 << HW_OP_POWERON);
1031 return 0;
1032 }
1033
1034
1035 /***********************************************************************
1036 *
1037 * codec structure
1038 *
1039 ***********************************************************************/
1040 static ser_codec_t codecs[] = {
1041 { "TerminalMode",
1042 tm_handle_char, tm_send, tm_setup },
1043 { "Direct",
1044 dm_handle_char, dm_send, dm_setup },
1045 { "RadisysAscii",
1046 ra_handle_char, ra_send, ra_setup },
1047 { "VM",
1048 vm_handle_char, vm_send, vm_setup, vm_connected, vm_disconnected },
1049 { NULL }
1050 };
1051
1052 static ser_codec_t *
ser_lookup_codec(const char * name)1053 ser_lookup_codec(const char *name)
1054 {
1055 unsigned int i;
1056
1057 for (i = 0; codecs[i].name; i++) {
1058 if (strcmp(codecs[i].name, name) == 0)
1059 return &codecs[i];
1060 }
1061 return NULL;
1062 }
1063
1064 #define PP_GET_SERIAL_INTF_CMD 0x01
1065 #define PP_SET_SERIAL_INTF_CMD 0x02
1066 static unsigned char pp_oem_chars[] = { 0x00, 0x40, 0x0a };
1067 static int
pp_oem_handler(channel_t * chan,msg_t * msg,unsigned char * rdata,unsigned int * rdata_len)1068 pp_oem_handler(channel_t *chan, msg_t *msg, unsigned char *rdata,
1069 unsigned int *rdata_len)
1070 {
1071 serserv_data_t *ser = chan->chan_info;
1072
1073 if (msg->netfn != IPMI_OEM_GROUP_NETFN)
1074 return 0;
1075
1076 if ((msg->len < 3) || (memcmp(msg->data, pp_oem_chars, 3) != 0))
1077 return 0;
1078
1079 switch (msg->cmd) {
1080 case PP_GET_SERIAL_INTF_CMD:
1081 rdata[0] = 0;
1082 memcpy(rdata + 1, pp_oem_chars, 3);
1083 rdata[4] = 0;
1084 if (msg->data[3] == 1)
1085 rdata[4] |= ser->echo;
1086 *rdata_len = 5;
1087 return 1;
1088
1089 case PP_SET_SERIAL_INTF_CMD:
1090 if (msg->len < 5)
1091 rdata[0] = 0xcc;
1092 else if (msg->data[3] == 1) {
1093 ser->echo = msg->data[4] & 1;
1094 rdata[0] = 0;
1095 }
1096 memcpy(rdata + 1, pp_oem_chars, 3);
1097 *rdata_len = 4;
1098 return 1;
1099 }
1100
1101 return 0;
1102 }
1103
1104 static void
pp_oem_init(serserv_data_t * ser)1105 pp_oem_init(serserv_data_t *ser)
1106 {
1107 ser->echo = 1;
1108 ser->channel.oem_intf_recv_handler = pp_oem_handler;
1109 }
1110
1111 #define RA_CONTROLLER_OEM_NETFN 0x3e
1112 #define RA_GET_IPMB_ADDR_CMD 0x12
1113 static int
ra_oem_handler(channel_t * chan,msg_t * msg,unsigned char * rdata,unsigned int * rdata_len)1114 ra_oem_handler(channel_t *chan, msg_t *msg, unsigned char *rdata,
1115 unsigned int *rdata_len)
1116 {
1117 serserv_data_t *ser = chan->chan_info;
1118
1119 if (msg->netfn == RA_CONTROLLER_OEM_NETFN) {
1120 switch (msg->cmd) {
1121 case RA_GET_IPMB_ADDR_CMD:
1122 rdata[0] = 0;
1123 rdata[1] = ser->my_ipmb;
1124 *rdata_len = 2;
1125 return 1;
1126 }
1127 } else if (msg->netfn == IPMI_APP_NETFN) {
1128 switch (msg->cmd) {
1129 case IPMI_GET_MSG_FLAGS_CMD:
1130 /* No message flag support. */
1131 rdata[0] = 0xc1;
1132 *rdata_len = 1;
1133 return 1;
1134 }
1135 }
1136
1137 return 0;
1138 }
1139
1140 static void
ra_oem_init(serserv_data_t * ser)1141 ra_oem_init(serserv_data_t *ser)
1142 {
1143 ser->channel.oem_intf_recv_handler = ra_oem_handler;
1144 }
1145
1146 static ser_oem_handler_t oem_handlers[] = {
1147 { "PigeonPoint", pp_oem_handler, pp_oem_init },
1148 { "Radisys", ra_oem_handler, ra_oem_init },
1149 { NULL }
1150 };
1151
1152 static ser_oem_handler_t *
ser_lookup_oem(const char * name)1153 ser_lookup_oem(const char *name)
1154 {
1155 unsigned int i;
1156
1157 for (i = 0; oem_handlers[i].name; i++) {
1158 if (strcmp(oem_handlers[i].name, name) == 0)
1159 return &oem_handlers[i];
1160 }
1161 return NULL;
1162 }
1163
1164 static void
ser_return_rsp(channel_t * chan,msg_t * imsg,rsp_msg_t * rsp)1165 ser_return_rsp(channel_t *chan, msg_t *imsg, rsp_msg_t *rsp)
1166 {
1167 serserv_data_t *ser = chan->chan_info;
1168 msg_t msg;
1169
1170 if (!ser->connected)
1171 return;
1172
1173 msg.netfn = rsp->netfn;
1174 msg.cmd = rsp->cmd;
1175 msg.data = rsp->data;
1176 msg.len = rsp->data_len;
1177 msg.rq_lun = imsg->rs_lun;
1178 msg.rq_addr = imsg->rs_addr;
1179 msg.rs_lun = imsg->rq_lun;
1180 msg.rs_addr = imsg->rq_addr;
1181 msg.rq_seq = imsg->rq_seq;
1182 ser->codec->send(&msg, ser);
1183 }
1184
1185 void
serserv_handle_data(serserv_data_t * ser,uint8_t * data,unsigned int len)1186 serserv_handle_data(serserv_data_t *ser, uint8_t *data, unsigned int len)
1187 {
1188 unsigned int i;
1189
1190 for (i = 0; i < len; i++)
1191 ser->codec->handle_char(data[i], ser);
1192 }
1193
1194 int
serserv_init(serserv_data_t * ser)1195 serserv_init(serserv_data_t *ser)
1196 {
1197 ser->channel.return_rsp = ser_return_rsp;
1198
1199 ser->codec->setup(ser);
1200 if (ser->oem)
1201 ser->oem->init(ser);
1202
1203 chan_init(&ser->channel);
1204 return 0;
1205 }
1206
1207 int
serserv_read_config(char ** tokptr,sys_data_t * sys,const char ** errstr)1208 serserv_read_config(char **tokptr, sys_data_t *sys, const char **errstr)
1209 {
1210 serserv_data_t *ser;
1211 const char *tok, *tok2;
1212 char *endp;
1213 int err;
1214 unsigned int chan_num;
1215
1216 ser = malloc(sizeof(*ser));
1217 if (!ser) {
1218 *errstr = "Out of memory";
1219 return -1;
1220 }
1221 memset(ser, 0, sizeof(*ser));
1222
1223 tok = mystrtok(NULL, " \t\n", tokptr);
1224 if (!tok) {
1225 *errstr = "No channel given";
1226 goto out_err;
1227 }
1228 ser->channel.session_support = IPMI_CHANNEL_SESSION_LESS;
1229 ser->channel.medium_type = IPMI_CHANNEL_MEDIUM_RS232;
1230 if (strcmp(tok, "kcs") == 0) {
1231 chan_num = 15;
1232 ser->channel.protocol_type = IPMI_CHANNEL_PROTOCOL_KCS;
1233 } else if (strcmp(tok, "bt") == 0) {
1234 chan_num = 15;
1235 ser->channel.protocol_type = IPMI_CHANNEL_PROTOCOL_BT_v15;
1236 } else if (strcmp(tok, "smic") == 0) {
1237 chan_num = 15;
1238 ser->channel.protocol_type = IPMI_CHANNEL_PROTOCOL_SMIC;
1239 } else {
1240 chan_num = strtoul(tok, &endp, 0);
1241 if (*endp != '\0') {
1242 *errstr = "Channel not a valid number";
1243 goto out_err;
1244 }
1245 ser->channel.protocol_type = IPMI_CHANNEL_PROTOCOL_TMODE;
1246 }
1247 if (chan_num != 15) {
1248 *errstr = "Only BMC channel (channel 15, or kcs/bt/smic) is"
1249 " supported for serial";
1250 goto out_err;
1251 }
1252
1253 if (sys->chan_set[chan_num]) {
1254 *errstr = "System channel already defined";
1255 goto out_err;
1256 }
1257 ser->channel.channel_num = chan_num;
1258
1259 err = get_sock_addr(tokptr, &ser->addr.addr, &ser->addr.addr_len,
1260 NULL, SOCK_STREAM, errstr);
1261 if (err)
1262 goto out_err;
1263
1264 for (tok = mystrtok(NULL, " \t\n", tokptr); tok;
1265 tok = mystrtok(NULL, " \t\n", tokptr)) {
1266 if (strcmp(tok, "connect") == 0) {
1267 ser->do_connect = 1;
1268 continue;
1269 }
1270
1271 tok2 = mystrtok(NULL, " \t\n", tokptr);
1272 if (strcmp(tok, "codec") == 0) {
1273 if (!tok2) {
1274 *errstr = "Missing parameter for codec";
1275 return -1;
1276 }
1277 ser->codec = ser_lookup_codec(tok2);
1278 if (!ser->codec) {
1279 *errstr = "Invalid codec";
1280 return -1;
1281 }
1282 } else if (strcmp(tok, "oem") == 0) {
1283 if (!tok2) {
1284 *errstr = "Missing parameter for oem";
1285 return -1;
1286 }
1287 ser->oem = ser_lookup_oem(tok2);
1288 if (!ser->oem) {
1289 *errstr = "Invalid oem setting";
1290 return -1;
1291 }
1292 } else if (strcmp(tok, "attn") == 0) {
1293 unsigned int pos = 0;
1294 char *tokptr2 = NULL;
1295
1296 if (!tok2) {
1297 *errstr = "Missing parameter for attn";
1298 return -1;
1299 }
1300
1301 ser->do_attn = 1;
1302 tok2 = mystrtok((char *) tok2, ",", &tokptr2);
1303 while (tok2) {
1304 if (pos >= sizeof(ser->attn_chars)) {
1305 *errstr = "Too many attn characters";
1306 return -1;
1307 }
1308 ser->attn_chars[pos] = strtoul(tok2, &endp, 0);
1309 if (*endp != '\0') {
1310 *errstr = "Invalid attn value";
1311 return -1;
1312 }
1313 pos++;
1314 tok2 = mystrtok(NULL, ",", &tokptr2);
1315 }
1316 ser->attn_chars_len = pos;
1317 } else if (strcmp(tok, "ipmb") == 0) {
1318 char *endp;
1319 ser->my_ipmb = strtoul(tok2, &endp, 0);
1320 if (*endp != '\0') {
1321 *errstr = "Invalid IPMB address";
1322 return -1;
1323 }
1324 } else {
1325 *errstr = "Invalid setting, not connect, codec, oem, attn, or ipmb";
1326 return -1;
1327 }
1328 }
1329
1330 if (!ser->codec) {
1331 *errstr = "codec not specified";
1332 goto out_err;
1333 }
1334
1335 ser->sysinfo = sys;
1336 ser->channel.chan_info = ser;
1337
1338 sys->chan_set[chan_num] = &ser->channel;
1339 return 0;
1340
1341 out_err:
1342 free(ser);
1343 return -1;
1344 }
1345