/* * Copyright (c) 2012 Pigeon Point Systems. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Pigeon Point Systems nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. * PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL * PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #define _GNU_SOURCE 1 /* Serial Interface, Basic Mode plugin. */ #if defined HAVE_ALLOCA_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_CONFIG_H) # include #endif #define SERIAL_BM_MAX_MSG_SIZE 47 #define SERIAL_BM_MAX_RQ_SIZE 33 /* 40 - 7 */ #define SERIAL_BM_MAX_RS_SIZE 32 /* 40 - 8 */ #define SERIAL_BM_TIMEOUT 5 #define SERIAL_BM_RETRY_COUNT 5 #define SERIAL_BM_MAX_BUFFER_SIZE 250 #define BM_START 0xA0 #define BM_STOP 0xA5 #define BM_HANDSHAKE 0xA6 #define BM_ESCAPE 0xAA /* * IPMB message header */ struct ipmb_msg_hdr { unsigned char rsSA; unsigned char netFn; /* NET FN | RS LUN */ unsigned char csum1; unsigned char rqSA; unsigned char rqSeq; /* RQ SEQ | RQ LUN */ unsigned char cmd; unsigned char data[]; }; /* * Send Message command request for IPMB-format */ struct ipmi_send_message_rq { unsigned char channel; struct ipmb_msg_hdr msg; }; /* * Get Message command response for IPMB-format */ struct ipmi_get_message_rp { unsigned char completion; unsigned char channel; unsigned char netFn; unsigned char csum1; unsigned char rsSA; unsigned char rqSeq; unsigned char cmd; unsigned char data[]; }; /* * State for the received message */ enum { MSG_NONE, MSG_IN_PROGRESS, MSG_DONE }; /* * Message parsing context */ struct serial_bm_parse_ctx{ int state; uint8_t * msg; size_t msg_len; size_t max_len; int escape; }; /* * Receiving context */ struct serial_bm_recv_ctx { uint8_t buffer[SERIAL_BM_MAX_BUFFER_SIZE]; size_t buffer_size; size_t max_buffer_size; }; /* * Sending context */ struct serial_bm_request_ctx { uint8_t rsSA; uint8_t netFn; uint8_t rqSA; uint8_t rqSeq; uint8_t cmd; }; /* * Table of supported baud rates */ static const struct { int baudinit; int baudrate; } rates[] = { { B2400, 2400 }, { B9600, 9600 }, { B19200, 19200 }, { B38400, 38400 }, { B57600, 57600 }, { B115200, 115200 }, { B230400, 230400 }, #ifdef B460800 { B460800, 460800 }, #endif }; /* * Table of special characters */ static const struct { uint8_t character; uint8_t escape; } characters[] = { { BM_START, 0xB0 }, /* start */ { BM_STOP, 0xB5 }, /* stop */ { BM_HANDSHAKE, 0xB6 }, /* packet handshake */ { BM_ESCAPE, 0xBA }, /* data escape */ { 0x1B, 0x3B } /* escape */ }; static int is_system; /* * Setup serial interface */ static int serial_bm_setup(struct ipmi_intf * intf) { /* setup default LAN maximum request and response sizes */ intf->max_request_data_size = SERIAL_BM_MAX_RQ_SIZE; intf->max_response_data_size = SERIAL_BM_MAX_RS_SIZE; return 0; } /* * Open serial interface */ static int serial_bm_open(struct ipmi_intf * intf) { struct termios ti; unsigned int rate = 9600; char *p; int i; if (!intf->devfile) { lprintf(LOG_ERR, "Serial device is not specified"); return -1; } is_system = 0; /* check if baud rate is specified */ if ((p = strchr(intf->devfile, ':'))) { char * pp; /* separate device name from baud rate */ *p++ = '\0'; /* check for second colon */ if ((pp = strchr(p, ':'))) { /* this is needed to normally acquire baud rate */ *pp++ = '\0'; /* check if it is a system interface */ if (pp[0] == 'S' || pp[0] == 's') { is_system = 1; } } if (str2uint(p, &rate)) { lprintf(LOG_ERR, "Invalid baud rate specified\n"); return -1; } } intf->fd = open(intf->devfile, O_RDWR | O_NONBLOCK, 0); if (intf->fd < 0) { lperror(LOG_ERR, "Could not open device at %s", intf->devfile); return -1; } for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) { if (rates[i].baudrate == rate) { break; } } if (i >= sizeof(rates) / sizeof(rates[0])) { lprintf(LOG_ERR, "Unsupported baud rate %i specified", rate); return -1; } tcgetattr(intf->fd, &ti); cfsetispeed(&ti, rates[i].baudinit); cfsetospeed(&ti, rates[i].baudinit); /* 8N1 */ ti.c_cflag &= ~PARENB; ti.c_cflag &= ~CSTOPB; ti.c_cflag &= ~CSIZE; ti.c_cflag |= CS8; /* enable the receiver and set local mode */ ti.c_cflag |= (CLOCAL | CREAD); /* no flow control */ ti.c_cflag &= ~CRTSCTS; ti.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY); #ifdef IUCLC /* Only disable uppercase-to-lowercase mapping on input for platforms supporting the flag. */ ti.c_iflag &= ~(IUCLC); #endif ti.c_oflag &= ~(OPOST); ti.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | NOFLSH); /* set the new options for the port with flushing */ tcsetattr(intf->fd, TCSAFLUSH, &ti); if (intf->ssn_params.timeout == 0) intf->ssn_params.timeout = SERIAL_BM_TIMEOUT; if (intf->ssn_params.retry == 0) intf->ssn_params.retry = SERIAL_BM_RETRY_COUNT; intf->opened = 1; return 0; } /* * Close serial interface */ static void serial_bm_close(struct ipmi_intf * intf) { if (intf->opened) { close(intf->fd); intf->fd = -1; } ipmi_intf_session_cleanup(intf); intf->opened = 0; } /* * Allocate sequence number for tracking */ static uint8_t serial_bm_alloc_seq(void) { static uint8_t seq = 0; if (++seq == 64) { seq = 0; } return seq; } /* * Flush the buffers */ static int serial_bm_flush(struct ipmi_intf * intf) { #if defined(TCFLSH) return ioctl(intf->fd, TCFLSH, TCIOFLUSH); #elif defined(TIOCFLUSH) return ioctl(intf->fd, TIOCFLUSH); #else # error "unsupported platform, missing flush support (TCFLSH/TIOCFLUSH)" #endif } /* * Return escaped character for the given one */ static inline uint8_t serial_bm_get_escaped_char(uint8_t c) { int i; for (i = 0; i < 5; i++) { if (characters[i].character == c) { return characters[i].escape; } } return c; } /* * Return unescaped character for the given one */ static inline uint8_t serial_bm_get_unescaped_char(uint8_t c) { int i; for (i = 0; i < 5; i++) { if (characters[i].escape == c) { return characters[i].character; } } return c; } /* * Send message to serial port */ static int serial_bm_send_msg(struct ipmi_intf * intf, uint8_t * msg, int msg_len) { int i, size, tmp = 0; uint8_t * buf, * data; if (verbose > 3) { fprintf(stderr, "Sending request:\n"); fprintf(stderr, " rsSA = 0x%x\n", msg[0]); fprintf(stderr, " NetFN/rsLUN = 0x%x\n", msg[1]); fprintf(stderr, " rqSA = 0x%x\n", msg[3]); fprintf(stderr, " rqSeq/rqLUN = 0x%x\n", msg[4]); fprintf(stderr, " cmd = 0x%x\n", msg[5]); if (msg_len > 7) { fprintf(stderr, " data_len = %d\n", msg_len - 7); fprintf(stderr, " data = %s\n", buf2str(msg + 6, msg_len - 7)); } } if (verbose > 4) { fprintf(stderr, "Message data:\n"); fprintf(stderr, " %s\n", buf2str(msg, msg_len)); } /* calculate escaped characters number */ for (i = 0; i < msg_len; i++) { if (serial_bm_get_escaped_char(msg[i]) != msg[i]) { tmp++; } } /* calculate required buffer size */ size = msg_len + tmp + 2; /* allocate buffer for output data */ buf = data = (uint8_t *) alloca(size); if (!buf) { lperror(LOG_ERR, "ipmitool: alloca error"); return -1; } /* start character */ *buf++ = 0xA0; for (i = 0; i < msg_len; i++) { tmp = serial_bm_get_escaped_char(msg[i]); if (tmp != msg[i]) { *buf++ = 0xAA; } *buf++ = tmp; } /* stop character */ *buf++ = 0xA5; if (verbose > 5) { fprintf(stderr, "Sent serial data:\n %s\n", buf2str(data, size)); } /* write data to serial port */ tmp = write(intf->fd, data, size); if (tmp <= 0) { lperror(LOG_ERR, "ipmitool: write error"); return -1; } return 0; } /* * This function waits for incoming data */ static int serial_bm_wait_for_data(struct ipmi_intf * intf) { int n; struct pollfd pfd; pfd.fd = intf->fd; pfd.events = POLLIN; pfd.revents = 0; n = poll(&pfd, 1, intf->ssn_params.timeout * 1000); if (n < 0) { lperror(LOG_ERR, "Poll for serial data failed"); return -1; } else if (!n) { return -1; } return 0; } /* * This function parses incoming data in basic mode format to IPMB message */ static int serial_bm_parse_buffer(const uint8_t * data, int data_len, struct serial_bm_parse_ctx * ctx) { int i, tmp; for (i = 0; i < data_len; i++) { /* check for start of new message */ if (data[i] == BM_START) { ctx->state = MSG_IN_PROGRESS; ctx->escape = 0; ctx->msg_len = 0; /* check if message is not started */ } else if (ctx->state != MSG_IN_PROGRESS) { /* skip character */ continue; /* continue escape sequence */ } else if (ctx->escape) { /* get original character */ tmp = serial_bm_get_unescaped_char(data[i]); /* check if not special character */ if (tmp == data[i]) { lprintf(LOG_ERR, "ipmitool: bad response"); /* reset message state */ ctx->state = MSG_NONE; continue; } /* check message length */ if (ctx->msg_len >= ctx->max_len) { lprintf(LOG_ERR, "ipmitool: response is too long"); /* reset message state */ ctx->state = MSG_NONE; continue; } /* add parsed character */ ctx->msg[ctx->msg_len++] = tmp; /* clear escape flag */ ctx->escape = 0; /* check for escape character */ } else if (data[i] == BM_ESCAPE) { ctx->escape = 1; continue; /* check for stop character */ } else if (data[i] == BM_STOP) { ctx->state = MSG_DONE; return i + 1; /* check for packet handshake character */ } else if (data[i] == BM_HANDSHAKE) { /* just skip it */ continue; } else { /* check message length */ if (ctx->msg_len >= ctx->max_len) { lprintf(LOG_ERR, "ipmitool: response is too long"); return -1; } /* add parsed character */ ctx->msg[ctx->msg_len++] = data[i]; } } /* return number of parsed characters */ return i; } /* * Read and parse data from serial port */ static int serial_bm_recv_msg(struct ipmi_intf * intf, struct serial_bm_recv_ctx * recv_ctx, uint8_t * msg_data, size_t msg_len) { struct serial_bm_parse_ctx parse_ctx; int rv; parse_ctx.state = MSG_NONE; parse_ctx.msg = msg_data; parse_ctx.max_len = msg_len; do { /* wait for data in the port */ if (serial_bm_wait_for_data(intf)) { return 0; } /* read data into buffer */ rv = read(intf->fd, recv_ctx->buffer + recv_ctx->buffer_size, recv_ctx->max_buffer_size - recv_ctx->buffer_size); if (rv < 0) { lperror(LOG_ERR, "ipmitool: read error"); return -1; } if (verbose > 5) { fprintf(stderr, "Received serial data:\n %s\n", buf2str(recv_ctx->buffer + recv_ctx->buffer_size, rv)); } /* increment buffer size */ recv_ctx->buffer_size += rv; /* parse buffer */ rv = serial_bm_parse_buffer(recv_ctx->buffer, recv_ctx->buffer_size, &parse_ctx); if (rv < recv_ctx->buffer_size) { /* move non-parsed part of the buffer to the beginning */ memmove(recv_ctx->buffer, recv_ctx->buffer + rv, recv_ctx->buffer_size - rv); } /* decrement buffer size */ recv_ctx->buffer_size -= rv; } while (parse_ctx.state != MSG_DONE); if (verbose > 4) { printf("Received message:\n %s\n", buf2str(msg_data, parse_ctx.msg_len)); } /* received a message */ return parse_ctx.msg_len; } /* * Build IPMB message to be transmitted */ static int serial_bm_build_msg(const struct ipmi_intf * intf, const struct ipmi_rq * req, uint8_t * msg, size_t max_len, struct serial_bm_request_ctx * ctx, int * msg_len ) { uint8_t * data = msg, seq; struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) msg; struct ipmi_send_message_rq * inner_rq = NULL, * outer_rq = NULL; int bridging_level; /* acquire bridging level */ if (intf->target_addr && intf->target_addr != intf->my_addr) { if (intf->transit_addr != 0) { bridging_level = 2; } else { bridging_level = 1; } } else { bridging_level = 0; } /* check overall packet length */ if(req->msg.data_len + 7 + bridging_level * 8 > max_len) { lprintf(LOG_ERR, "ipmitool: Message data is too long"); return -1; } /* allocate new sequence number */ seq = serial_bm_alloc_seq() << 2; if (bridging_level) { /* compose send message request */ hdr->netFn = 0x18; hdr->cmd = 0x34; /* set pointer to send message request data */ outer_rq = (struct ipmi_send_message_rq *) (hdr + 1); /* compose the outer send message request */ if (bridging_level == 2) { outer_rq->channel = intf->transit_channel | 0x40; outer_rq->msg.rsSA = intf->transit_addr; outer_rq->msg.netFn = 0x18; outer_rq->msg.csum1 = -(outer_rq->msg.rsSA + outer_rq->msg.netFn); outer_rq->msg.rqSA = intf->my_addr; outer_rq->msg.rqSeq = seq; outer_rq->msg.cmd = 0x34; /* inner send message request is further */ inner_rq = (outer_rq + 1); } else { /* there is only outer send message reuest */ inner_rq = outer_rq; } /* compose the inner send message request */ inner_rq->channel = intf->target_channel | 0x40; inner_rq->msg.rsSA = intf->target_addr; inner_rq->msg.netFn = (req->msg.netfn << 2) | req->msg.lun; inner_rq->msg.csum1 = -(inner_rq->msg.rsSA + inner_rq->msg.netFn); inner_rq->msg.rqSA = intf->my_addr; inner_rq->msg.rqSeq = seq; inner_rq->msg.cmd = req->msg.cmd; /* check if interface is the system one */ if (is_system) { /* need response to LUN 2 */ outer_rq->msg.rqSeq |= 2; /* do not track response */ outer_rq->channel &= ~0x40; /* restore BMC SA if bridging not to primary IPMB channel */ if (outer_rq->channel) { outer_rq->msg.rqSA = IPMI_BMC_SLAVE_ADDR; } } /* fill-in the second context */ ctx[1].rsSA = outer_rq->msg.rsSA; ctx[1].netFn = outer_rq->msg.netFn; ctx[1].rqSA = outer_rq->msg.rqSA; ctx[1].rqSeq = outer_rq->msg.rqSeq; ctx[1].cmd = outer_rq->msg.cmd; /* move write pointer */ msg = (uint8_t *)(inner_rq + 1); } else { /* compose direct request */ hdr->netFn = (req->msg.netfn << 2) | req->msg.lun; hdr->cmd = req->msg.cmd; /* move write pointer */ msg = (uint8_t *)(hdr + 1); } /* fill-in the rest header fields */ hdr->rsSA = IPMI_BMC_SLAVE_ADDR; hdr->csum1 = -(hdr->rsSA + hdr->netFn); hdr->rqSA = IPMI_REMOTE_SWID; hdr->rqSeq = seq; /* fill-in the first context */ ctx[0].rsSA = hdr->rsSA; ctx[0].netFn = hdr->netFn; ctx[0].rqSA = hdr->rqSA; ctx[0].rqSeq = hdr->rqSeq; ctx[0].cmd = hdr->cmd; /* write request data */ memcpy(msg, req->msg.data, req->msg.data_len); /* move write pointer */ msg += req->msg.data_len; if (bridging_level) { /* write inner message checksum */ *msg++ = ipmi_csum(&inner_rq->msg.rqSA, req->msg.data_len + 3); /* check for double bridging */ if (bridging_level == 2) { /* write outer message checksum */ *msg++ = ipmi_csum(&outer_rq->msg.rqSA, 4); } /* write overall message checksum */ *msg++ = ipmi_csum(&hdr->rqSA, 4); } else { /* write overall message checksum */ *msg++ = ipmi_csum(&hdr->rqSA, req->msg.data_len + 3); } /* save message length */ *msg_len = msg - data; /* return bridging level */ return bridging_level; } /* * Wait for request response */ static int serial_bm_wait_response(struct ipmi_intf * intf, struct serial_bm_request_ctx * req_ctx, struct serial_bm_recv_ctx * read_ctx, uint8_t * msg, size_t max_len) { struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) msg; int msg_len, netFn, rqSeq; /* receive and match message */ while ((msg_len = serial_bm_recv_msg(intf, read_ctx, msg, max_len)) > 0) { /* validate message size */ if (msg_len < 8) { lprintf(LOG_ERR, "ipmitool: response is too short"); continue; } /* validate checksum 1 */ if (ipmi_csum(msg, 3)) { lprintf(LOG_ERR, "ipmitool: bad checksum 1"); continue; } /* validate checksum 2 */ if (ipmi_csum(msg + 3, msg_len - 3)) { lprintf(LOG_ERR, "ipmitool: bad checksum 2"); continue; } /* swap requester and responder LUNs */ netFn = ((req_ctx->netFn|4) & ~3) | (req_ctx->rqSeq & 3); rqSeq = (req_ctx->rqSeq & ~3) | (req_ctx->netFn & 3); /* check for the waited response */ if (hdr->rsSA == req_ctx->rqSA && hdr->netFn == netFn && hdr->rqSA == req_ctx->rsSA && hdr->rqSeq == rqSeq && hdr->cmd == req_ctx->cmd) { /* check if something new has been parsed */ if (verbose > 3) { fprintf(stderr, "Got response:\n"); fprintf(stderr, " rsSA = 0x%x\n", msg[0]); fprintf(stderr, " NetFN/rsLUN = 0x%x\n", msg[1]); fprintf(stderr, " rqSA = 0x%x\n", msg[3]); fprintf(stderr, " rqSeq/rqLUN = 0x%x\n", msg[4]); fprintf(stderr, " cmd = 0x%x\n", msg[5]); fprintf(stderr, " completion code = 0x%x\n", msg[6]); if (msg_len > 8) { fprintf(stderr, " data_len = %d\n", msg_len - 8); fprintf(stderr, " data = %s\n", buf2str(msg + 7, msg_len - 8)); } } /* copy only completion and response data */ memmove(msg, hdr + 1, msg_len - sizeof (*hdr) - 1); /* update message length */ msg_len -= sizeof (*hdr) + 1; /* the waited one */ break; } } return msg_len; } /* * Get message from receive message queue */ static int serial_bm_get_message(struct ipmi_intf * intf, struct serial_bm_request_ctx * req_ctx, struct serial_bm_recv_ctx * read_ctx, uint8_t * msg, size_t max_len) { uint8_t data[SERIAL_BM_MAX_MSG_SIZE]; struct serial_bm_request_ctx tmp_ctx; struct ipmi_get_message_rp * rp = (struct ipmi_get_message_rp *) data; struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) data; clock_t start, tm; int rv, netFn, rqSeq; start = clock(); do { /* fill-in request context */ tmp_ctx.rsSA = IPMI_BMC_SLAVE_ADDR; tmp_ctx.netFn = 0x18; tmp_ctx.rqSA = IPMI_REMOTE_SWID; tmp_ctx.rqSeq = serial_bm_alloc_seq() << 2; tmp_ctx.cmd = 0x33; /* fill-in request data */ hdr->rsSA = tmp_ctx.rsSA; hdr->netFn = tmp_ctx.netFn; hdr->csum1 = ipmi_csum(data, 2); hdr->rqSA = tmp_ctx.rqSA; hdr->rqSeq = tmp_ctx.rqSeq; hdr->cmd = tmp_ctx.cmd; hdr->data[0] = ipmi_csum(&hdr->rqSA, 3); /* send request */ serial_bm_flush(intf); serial_bm_send_msg(intf, data, 7); /* wait for response */ rv = serial_bm_wait_response(intf, &tmp_ctx, read_ctx, data, sizeof (data)); /* check for IO error or timeout */ if (rv <= 0) { return rv; } /* check completion code */ if (rp->completion == 0) { /* swap requester and responder LUNs */ netFn = ((req_ctx->netFn|4) & ~3) | (req_ctx->rqSeq & 3); rqSeq = (req_ctx->rqSeq & ~3) | (req_ctx->netFn & 3); /* check for the waited response */ if (rp->netFn == netFn && rp->rsSA == req_ctx->rsSA && rp->rqSeq == rqSeq && rp->cmd == req_ctx->cmd) { /* copy the rest of message */ memcpy(msg, rp->data, rv - sizeof (*rp) - 1); return rv - sizeof (*rp) - 1; } } else if (rp->completion != 0x80) { return 0; } tm = clock() - start; tm /= CLOCKS_PER_SEC; } while (tm < intf->ssn_params.timeout); return 0; } static struct ipmi_rs * serial_bm_send_request(struct ipmi_intf * intf, struct ipmi_rq * req) { static struct ipmi_rs rsp; uint8_t msg[SERIAL_BM_MAX_MSG_SIZE], * resp = msg; struct serial_bm_request_ctx req_ctx[3]; struct serial_bm_recv_ctx read_ctx; int retry, rv, msg_len, bridging_level; if (!intf->opened && intf->open && intf->open(intf) < 0) { return NULL; } /* reset receive context */ read_ctx.buffer_size = 0; read_ctx.max_buffer_size = SERIAL_BM_MAX_BUFFER_SIZE; /* Send the message and receive the answer */ for (retry = 0; retry < intf->ssn_params.retry; retry++) { /* build output message */ bridging_level = serial_bm_build_msg(intf, req, msg, sizeof (msg), req_ctx, &msg_len); if (msg_len < 0) { return NULL; } /* send request */ serial_bm_flush(intf); serial_bm_send_msg(intf, msg, msg_len); /* wait for response */ rv = serial_bm_wait_response(intf, &req_ctx[0], &read_ctx, msg, sizeof (msg)); /* check for IO error */ if (rv < 0) { return NULL; } /* check for timeout */ if (rv == 0) { continue; } /* check for bridging */ if (bridging_level && msg[0] == 0) { /* in the case of payload interface we check receive message queue */ if (is_system) { /* check message flags */ rv = serial_bm_get_message(intf, &req_ctx[1], &read_ctx, msg, sizeof (msg)); /* check for IO error */ if (rv < 0) { return NULL; } /* check for timeout */ if (rv == 0) { continue; } /* check if response for inner request is not encapsulated */ } else if (rv == 1) { /* wait for response for inner request */ rv = serial_bm_wait_response(intf, &req_ctx[1], &read_ctx, msg, sizeof (msg)); /* check for IO error */ if (rv < 0) { return NULL; } /* check for timeout */ if (rv == 0) { continue; } } else { /* skip outer level header */ resp = msg + 7; /* decrement response size */ rv -= 8; } /* check response size */ if (resp[0] == 0 && bridging_level == 2 && rv < 8) { lprintf(LOG_ERR, "ipmitool: Message response is too short"); /* invalid message length */ return NULL; } } /* check for double bridging */ if (bridging_level == 2 && resp[0] == 0) { /* get completion code */ rsp.ccode = resp[7]; rsp.data_len = rv - 9; memcpy(rsp.data, resp + 8, rsp.data_len); } else { rsp.ccode = resp[0]; rsp.data_len = rv - 1; memcpy(rsp.data, resp + 1, rsp.data_len); } /* return response */ return &rsp; } /* no valid response */ return NULL; } /* * Serial BM interface */ struct ipmi_intf ipmi_serial_bm_intf = { .name = "serial-basic", .desc = "Serial Interface, Basic Mode", .setup = serial_bm_setup, .open = serial_bm_open, .close = serial_bm_close, .sendrecv = serial_bm_send_request, };