Add true "isol" (Intel IPMI 1.5 SOL) functionality, which gives

"isol activate" command.

Patch submitted by Steffen Persvold (sp at scali dot com). Quoting his
email:

"The second patch (ipmitool_isol.patch) is a little more interesting. It
is basically an implementation of true SOL functionality for the "isol"
command on IPMI 1.5. This allows people with Intel based 1.5 controllers
that have the Intel specific SOL implementation (which is close to the
2.0 spec but not quite there) to use the "isol activate" command. This
applies to Dell 1850, 1855, 2850 and sc1425 servers at least (probably
more, but that's what I've tested). There is quite some code duplication
from the lanplus sol version, so at some point "someone" might do some
refactoring and merge the two (only separating what's different) but for
now I thought it was cleanest to do it this way."
This commit is contained in:
Dmitry Frolov
2007-08-28 04:35:22 +00:00
parent 86364dec74
commit a6e1840e4f
3 changed files with 1310 additions and 176 deletions

View File

@ -41,16 +41,17 @@
#define ISOL_ENABLE_PARAM 0x01
#define ISOL_AUTHENTICATION_PARAM 0x02
#define ISOL_ENABLE_FLAG 0x01
#define ISOL_PRIVILEGE_LEVEL_USER 0x02
#define ISOL_BAUD_RATE_PARAM 0x05
#define ISOL_BAUD_RATE_9600 0x06
#define ISOL_BAUD_RATE_19200 0x07
#define ISOL_BAUD_RATE_38400 0x08
#define ISOL_BAUD_RATE_57600 0x09
#define ISOL_BAUD_RATE_115200 0x0A
#define ISOL_PREFERRED_BAUD_RATE 0x07
struct isol_config_parameters {
uint8_t enabled;
uint8_t privilege_level;
uint8_t bit_rate;
};
int ipmi_isol_main(struct ipmi_intf *, int, char **);
#endif /* IPMI_SOL_H */

View File

@ -32,7 +32,17 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
#include <termios.h>
#include <ipmitool/helper.h>
#include <ipmitool/log.h>
@ -41,39 +51,40 @@
#include <ipmitool/ipmi_intf.h>
#include <ipmitool/ipmi_isol.h>
const struct valstr ipmi_isol_baud_vals[] = {
{ ISOL_BAUD_RATE_9600, "9600" },
{ ISOL_BAUD_RATE_19200, "19200" },
{ ISOL_BAUD_RATE_38400, "38400" },
{ ISOL_BAUD_RATE_57600, "57600" },
{ ISOL_BAUD_RATE_115200, "115200" },
{ 0x00, NULL }
};
static struct termios _saved_tio;
static int _in_raw_mode = 0;
extern int verbose;
static int ipmi_isol_setup(struct ipmi_intf * intf, char baudsetting)
#define ISOL_ESCAPE_CHARACTER '~'
/*
* ipmi_get_isol_info
*/
static int ipmi_get_isol_info(struct ipmi_intf * intf,
struct isol_config_parameters * params)
{
struct ipmi_rs * rsp;
struct ipmi_rq req;
unsigned char data[6];
/* TEST FOR AVAILABILITY */
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_ISOL;
req.msg.cmd = GET_ISOL_CONFIG;
req.msg.data = data;
req.msg.data_len = 4;
/* GET ISOL ENABLED CONFIG */
memset(data, 0, 6);
data[0] = 0x00;
data[1] = ISOL_ENABLE_PARAM;
data[2] = ISOL_ENABLE_FLAG;
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_ISOL;
req.msg.cmd = SET_ISOL_CONFIG;
req.msg.data = data;
req.msg.data_len = 3;
data[2] = 0x00; /* block */
data[3] = 0x00; /* selector */
rsp = intf->sendrecv(intf, &req);
if (rsp == NULL) {
lprintf(LOG_ERR, "Error in Set ISOL Config Command");
lprintf(LOG_ERR, "Error in Get ISOL Config Command");
return -1;
}
if (rsp->ccode == 0xc1) {
@ -81,20 +92,19 @@ static int ipmi_isol_setup(struct ipmi_intf * intf, char baudsetting)
return -1;
}
if (rsp->ccode > 0) {
lprintf(LOG_ERR, "Error in Set ISOL Config Command: %s",
lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s",
val2str(rsp->ccode, completion_code_vals));
return -1;
}
params->enabled = rsp->data[1];
/* GET ISOL CONFIG */
/* GET ISOL AUTHENTICATON CONFIG */
memset(data, 0, 6);
data[0] = 0x00;
data[1] = ISOL_AUTHENTICATION_PARAM;
data[2] = 0x00; /* block */
data[3] = 0x00; /* selector */
req.msg.cmd = GET_ISOL_CONFIG;
req.msg.data_len = 4;
rsp = intf->sendrecv(intf, &req);
if (rsp == NULL) {
@ -106,86 +116,713 @@ static int ipmi_isol_setup(struct ipmi_intf * intf, char baudsetting)
val2str(rsp->ccode, completion_code_vals));
return -1;
}
params->privilege_level = rsp->data[1];
if (verbose > 1)
printbuf(rsp->data, rsp->data_len, "ISOL Config");
/* SET ISOL CONFIG - AUTHENTICATION */
memset(data, 0, 6);
data[0] = 0x00;
data[1] = ISOL_AUTHENTICATION_PARAM;
data[2] = ISOL_PRIVILEGE_LEVEL_USER | (rsp->data[1] & 0x80);
req.msg.cmd = SET_ISOL_CONFIG;
req.msg.data_len = 3;
rsp = intf->sendrecv(intf, &req);
if (rsp == NULL) {
lprintf(LOG_ERR, "Error in Set ISOL Config (Authentication) Command");
return -1;
}
if (rsp->ccode > 0) {
lprintf(LOG_ERR, "Error in Set ISOL Config (Authentication) Command: %s",
val2str(rsp->ccode, completion_code_vals));
return -1;
}
/* SET ISOL CONFIG - BAUD RATE */
/* GET ISOL BAUD RATE CONFIG */
memset(data, 0, 6);
data[0] = 0x00;
data[1] = ISOL_BAUD_RATE_PARAM;
data[2] = baudsetting;
req.msg.cmd = SET_ISOL_CONFIG;
req.msg.data_len = 3;
data[2] = 0x00; /* block */
data[3] = 0x00; /* selector */
rsp = intf->sendrecv(intf, &req);
if (rsp == NULL) {
lprintf(LOG_ERR, "Error in Set ISOL Config (Baud Rate) Command");
lprintf(LOG_ERR, "Error in Get ISOL Config Command");
return -1;
}
if (rsp->ccode > 0) {
lprintf(LOG_ERR, "Error in Set ISOL Config (Baud Rate) Command: %s",
lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s",
val2str(rsp->ccode, completion_code_vals));
return -1;
}
printf("Set ISOL Baud Rate to %s\n",
val2str(baudsetting, ipmi_isol_baud_vals));
params->bit_rate = rsp->data[1];
return 0;
}
static int ipmi_print_isol_info(struct ipmi_intf * intf)
{
struct isol_config_parameters params = {0};
if (ipmi_get_isol_info(intf, &params))
return -1;
if (csv_output)
{
printf("%s,", (params.enabled & 0x1)?"true": "false");
printf("%s,",
val2str((params.privilege_level & 0xf), ipmi_privlvl_vals));
printf("%s,",
val2str((params.bit_rate & 0xf), ipmi_bit_rate_vals));
}
else
{
printf("Enabled : %s\n",
(params.enabled & 0x1)?"true": "false");
printf("Privilege Level : %s\n",
val2str((params.privilege_level & 0xf), ipmi_privlvl_vals));
printf("Bit Rate (kbps) : %s\n",
val2str((params.bit_rate & 0xf), ipmi_bit_rate_vals));
}
return 0;
}
static int ipmi_isol_set_param(struct ipmi_intf * intf,
const char *param,
const char *value)
{
struct ipmi_rs * rsp;
struct ipmi_rq req;
unsigned char data[6];
struct isol_config_parameters params = {0};
/* We need other values to complete the request */
if (ipmi_get_isol_info(intf, &params))
return -1;
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_ISOL;
req.msg.cmd = SET_ISOL_CONFIG;
req.msg.data = data;
req.msg.data_len = 3;
memset(data, 0, 6);
/*
* enabled
*/
if (strcmp(param, "enabled") == 0)
{
data[1] = ISOL_ENABLE_PARAM;
if (strcmp(value, "true") == 0)
data[2] = 0x01;
else if (strcmp(value, "false") == 0)
data[2] = 0x00;
else {
lprintf(LOG_ERR, "Invalid value %s for parameter %s",
value, param);
lprintf(LOG_ERR, "Valid values are true and false");
return -1;
}
}
/*
* privilege-level
*/
else if (strcmp(param, "privilege-level") == 0)
{
data[1] = ISOL_AUTHENTICATION_PARAM;
if (! strcmp(value, "user"))
data[2] = 0x02;
else if (! strcmp(value, "operator"))
data[2] = 0x03;
else if (! strcmp(value, "admin"))
data[2] = 0x04;
else if (! strcmp(value, "oem"))
data[2] = 0x05;
else
{
lprintf(LOG_ERR, "Invalid value %s for parameter %s",
value, param);
lprintf(LOG_ERR, "Valid values are user, operator, admin, and oem");
return -1;
}
/* We need to mask bit7 from the fetched value */
data[2] |= (params.privilege_level & 0x80) ? 0x80 : 0x00;
}
/*
* bit-rate
*/
else if (strcmp(param, "bit-rate") == 0)
{
data[1] = ISOL_BAUD_RATE_PARAM;
if (strncmp(value, "9.6", 3) == 0) {
data[2] = 0x06;
}
else if (strncmp(value, "19.2", 4) == 0) {
data[2] = 0x07;
}
else if (strncmp(value, "38.4", 4) == 0) {
data[2] = 0x08;
}
else if (strncmp(value, "57.6", 4) == 0) {
data[2] = 0x09;
}
else if (strncmp(value, "115.2", 5) == 0) {
data[2] = 0x0A;
}
else {
lprintf(LOG_ERR, "ISOL - Unsupported baud rate: %s", value);
lprintf(LOG_ERR, "Valid values are 9.6, 19.2, 38.4, 57.6 and 115.2");
return -1;
}
}
else
{
lprintf(LOG_ERR, "Error: invalid ISOL parameter %s", param);
return -1;
}
/*
* Execute the request
*/
rsp = intf->sendrecv(intf, &req);
if (rsp == NULL) {
lprintf(LOG_ERR, "Error setting ISOL parameter '%s'", param);
return -1;
}
if (rsp->ccode > 0) {
lprintf(LOG_ERR, "Error setting ISOL parameter '%s': %s",
param, val2str(rsp->ccode, completion_code_vals));
return -1;
}
return 0;
}
static void
leave_raw_mode(void)
{
if (!_in_raw_mode)
return;
if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1)
perror("tcsetattr");
else
_in_raw_mode = 0;
}
static void
enter_raw_mode(void)
{
struct termios tio;
if (tcgetattr(fileno(stdin), &tio) == -1) {
perror("tcgetattr");
return;
}
_saved_tio = tio;
tio.c_iflag |= IGNPAR;
tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF)\
;
tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
// #ifdef IEXTEN
tio.c_lflag &= ~IEXTEN;
// #endif
tio.c_oflag &= ~OPOST;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1)
perror("tcsetattr");
else
_in_raw_mode = 1;
}
static void
sendBreak(struct ipmi_intf * intf)
{
struct ipmi_v2_payload v2_payload;
memset(&v2_payload, 0, sizeof(v2_payload));
v2_payload.payload.sol_packet.character_count = 0;
v2_payload.payload.sol_packet.generate_break = 1;
intf->send_sol(intf, &v2_payload);
}
/*
* suspendSelf
*
* Put ourself in the background
*
* param bRestoreTty specifies whether we will put our self back
* in raw mode when we resume
*/
static void
suspendSelf(int bRestoreTty)
{
leave_raw_mode();
kill(getpid(), SIGTSTP);
if (bRestoreTty)
enter_raw_mode();
}
/*
* printiSolEscapeSequences
*
* Send some useful documentation to the user
*/
static void
printiSolEscapeSequences(void)
{
printf(
"%c?\r\n\
Supported escape sequences:\r\n\
%c. - terminate connection\r\n\
%c^Z - suspend ipmitool\r\n\
%c^X - suspend ipmitool, but don't restore tty on restart\r\n\
%cB - send break\r\n\
%c? - this message\r\n\
%c%c - send the escape character by typing it twice\r\n\
(Note that escapes are only recognized immediately after newline.)\r\n",
ISOL_ESCAPE_CHARACTER,
ISOL_ESCAPE_CHARACTER,
ISOL_ESCAPE_CHARACTER,
ISOL_ESCAPE_CHARACTER,
ISOL_ESCAPE_CHARACTER,
ISOL_ESCAPE_CHARACTER,
ISOL_ESCAPE_CHARACTER,
ISOL_ESCAPE_CHARACTER);
}
/*
* output
*
* Send the specified data to stdout
*/
static void
output(struct ipmi_rs * rsp)
{
if (rsp)
{
int i;
for (i = 0; i < rsp->data_len; ++i)
putc(rsp->data[i], stdout);
fflush(stdout);
}
}
/*
* ipmi_isol_deactivate
*/
static int
ipmi_isol_deactivate(struct ipmi_intf * intf)
{
struct ipmi_rs * rsp;
struct ipmi_rq req;
uint8_t data[6];
struct isol_config_parameters params;
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_ISOL;
req.msg.cmd = ACTIVATE_ISOL;
req.msg.data = data;
req.msg.data_len = 5;
memset(data, 0, 6);
data[0] = 0x00; /* Deactivate */
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x00;
data[5] = 0x00;
rsp = intf->sendrecv(intf, &req);
if (rsp == NULL) {
lprintf(LOG_ERR, "Error deactivating ISOL");
return -1;
}
if (rsp->ccode > 0) {
lprintf(LOG_ERR, "Error deactivating ISOL: %s",
val2str(rsp->ccode, completion_code_vals));
return -1;
}
/* response contain 4 additional bytes : 80 00 32 ff
Don't know what to use them for yet... */
return 0;
}
/*
* processiSolUserInput
*
* Act on user input into the ISOL session. The only reason this
* is complicated is that we have to process escape sequences.
*
* return 0 on success
* 1 if we should exit
* < 0 on error (BMC probably closed the session)
*/
static int
processiSolUserInput(struct ipmi_intf * intf,
uint8_t * input,
uint16_t buffer_length)
{
static int escape_pending = 0;
static int last_was_cr = 1;
struct ipmi_v2_payload v2_payload;
int length = 0;
int retval = 0;
char ch;
int i;
memset(&v2_payload, 0, sizeof(v2_payload));
/*
* Our first order of business is to check the input for escape
* sequences to act on.
*/
for (i = 0; i < buffer_length; ++i)
{
ch = input[i];
if (escape_pending){
escape_pending = 0;
/*
* Process a possible escape sequence.
*/
switch (ch) {
case '.':
printf("%c. [terminated ipmitool]\r\n", ISOL_ESCAPE_CHARACTER);
retval = 1;
break;
case 'Z' - 64:
printf("%c^Z [suspend ipmitool]\r\n", ISOL_ESCAPE_CHARACTER);
suspendSelf(1); /* Restore tty back to raw */
continue;
case 'X' - 64:
printf("%c^X [suspend ipmitool]\r\n", ISOL_ESCAPE_CHARACTER);
suspendSelf(0); /* Don't restore to raw mode */
continue;
case 'B':
printf("%cb [send break]\r\n", ISOL_ESCAPE_CHARACTER);
sendBreak(intf);
continue;
case '?':
printiSolEscapeSequences();
continue;
default:
if (ch != ISOL_ESCAPE_CHARACTER)
v2_payload.payload.sol_packet.data[length++] =
ISOL_ESCAPE_CHARACTER;
v2_payload.payload.sol_packet.data[length++] = ch;
}
}
else
{
if (last_was_cr && (ch == ISOL_ESCAPE_CHARACTER)) {
escape_pending = 1;
continue;
}
v2_payload.payload.sol_packet.data[length++] = ch;
}
/*
* Normal character. Record whether it was a newline.
*/
last_was_cr = (ch == '\r' || ch == '\n');
}
/*
* If there is anything left to process we dispatch it to the BMC,
* send intf->session->sol_data.max_outbound_payload_size bytes
* at a time.
*/
if (length)
{
struct ipmi_rs * rsp;
v2_payload.payload.sol_packet.flush_outbound = 1; /* Not sure if necessary ? */
v2_payload.payload.sol_packet.character_count = length;
rsp = intf->send_sol(intf, &v2_payload);
if (! rsp) {
lprintf(LOG_ERR, "Error sending SOL data");
retval = -1;
}
/* If the sequence number is set we know we have new data */
else if ((rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) &&
(rsp->payload.sol_packet.packet_sequence_number))
output(rsp);
}
return retval;
}
/*
* ipmi_isol_red_pill
*/
static int
ipmi_isol_red_pill(struct ipmi_intf * intf)
{
char * buffer;
int numRead;
int bShouldExit = 0;
int bBmcClosedSession = 0;
fd_set read_fds;
struct timeval tv;
int retval;
int buffer_size = 255;
int timedout = 0;
buffer = (char*)malloc(buffer_size);
if (buffer == NULL) {
lprintf(LOG_ERR, "ipmitool: malloc failure");
return -1;
}
enter_raw_mode();
while (! bShouldExit)
{
FD_ZERO(&read_fds);
FD_SET(0, &read_fds);
FD_SET(intf->fd, &read_fds);
/* Wait up to half a second */
tv.tv_sec = 0;
tv.tv_usec = 500000;
retval = select(intf->fd + 1, &read_fds, NULL, NULL, &tv);
if (retval)
{
if (retval == -1)
{
/* ERROR */
perror("select");
return -1;
}
timedout = 0;
/*
* Process input from the user
*/
if (FD_ISSET(0, &read_fds))
{
bzero(buffer, sizeof(buffer));
numRead = read(fileno(stdin),
buffer,
buffer_size);
if (numRead > 0)
{
int rc = processiSolUserInput(intf, buffer, numRead);
if (rc)
{
if (rc < 0)
bShouldExit = bBmcClosedSession = 1;
else
bShouldExit = 1;
}
}
else
{
bShouldExit = 1;
}
}
/*
* Process input from the BMC
*/
else if (FD_ISSET(intf->fd, &read_fds))
{
struct ipmi_rs * rs = intf->recv_sol(intf);
if (! rs)
{
bShouldExit = bBmcClosedSession = 1;
}
else
output(rs);
}
/*
* ERROR in select
*/
else
{
lprintf(LOG_ERR, "Error: Select returned with nothing to read");
bShouldExit = 1;
}
}
else
{
if ((++timedout) == 20) /* Every 10 seconds we send a keepalive */
{
intf->keepalive(intf);
timedout = 0;
}
}
}
leave_raw_mode();
if (bBmcClosedSession)
{
lprintf(LOG_ERR, "SOL session closed by BMC");
}
else
ipmi_isol_deactivate(intf);
return 0;
}
/*
* ipmi_isol_activate
*/
static int
ipmi_isol_activate(struct ipmi_intf * intf)
{
struct ipmi_rs * rsp;
struct ipmi_rq req;
uint8_t data[6];
struct isol_config_parameters params;
if (ipmi_get_isol_info(intf, &params))
return -1;
if (!(params.enabled & 0x1)) {
lprintf(LOG_ERR, "ISOL is not enabled!");
return -1;
}
/*
* Setup a callback so that the lanplus processing knows what
* to do with packets that come unexpectedly (while waiting for
* an ACK, perhaps.
*/
intf->session->sol_data.sol_input_handler = output;
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_ISOL;
req.msg.cmd = ACTIVATE_ISOL;
req.msg.data = data;
req.msg.data_len = 5;
memset(data, 0, 6);
data[0] = 0x01;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x00;
data[5] = 0x00;
rsp = intf->sendrecv(intf, &req);
if (NULL != rsp) {
switch (rsp->ccode) {
case 0x00:
if (rsp->data_len == 4) {
break;
} else {
lprintf(LOG_ERR, "Error: Unexpected data length (%d) received "
"in ISOL activation response",
rsp->data_len);
return -1;
}
break;
case 0x80:
lprintf(LOG_ERR, "Info: ISOL already active on another session");
return -1;
case 0x81:
lprintf(LOG_ERR, "Info: ISOL disabled");
return -1;
case 0x82:
lprintf(LOG_ERR, "Info: ISOL activation limit reached");
return -1;
default:
lprintf(LOG_ERR, "Error activating ISOL: %s",
val2str(rsp->ccode, completion_code_vals));
return -1;
}
} else {
lprintf(LOG_ERR, "Error: No response activating ISOL");
return -1;
}
/* response contain 4 additional bytes : 80 01 32 ff
Don't know what to use them for yet... */
printf("[SOL Session operational. Use %c? for help]\r\n",
ISOL_ESCAPE_CHARACTER);
/*
* At this point we are good to go with our SOL session. We
* need to listen to
* 1) STDIN for user input
* 2) The FD for incoming SOL packets
*/
if (ipmi_isol_red_pill(intf)) {
lprintf(LOG_ERR, "Error in SOL session");
return -1;
}
return 0;
}
static void print_isol_set_usage(void) {
lprintf(LOG_NOTICE, "\nISOL set parameters and values: \n");
lprintf(LOG_NOTICE, " enabled true | false");
lprintf(LOG_NOTICE, " privilege-level user | operator | admin | oem");
lprintf(LOG_NOTICE, " bit-rate "
"9.6 | 19.2 | 38.4 | 57.6 | 115.2");
lprintf(LOG_NOTICE, "");
}
static void print_isol_usage(void) {
lprintf(LOG_NOTICE, "ISOL Commands: info");
lprintf(LOG_NOTICE, " set <parameter> <setting>");
lprintf(LOG_NOTICE, " activate");
}
int ipmi_isol_main(struct ipmi_intf * intf, int argc, char ** argv)
{
int ret = 0;
if (argc < 2 || strncmp(argv[0], "help", 4) == 0) {
lprintf(LOG_NOTICE, "ISOL Commands: setup <baud>");
lprintf(LOG_NOTICE, "ISOL Baud Rates: 9600, 19200, 38400, 57600, 115200");
return 0;
/*
* Help
*/
if (!argc || !strncmp(argv[0], "help", 4))
print_isol_usage();
/*
* Info
*/
else if (!strncmp(argv[0], "info", 4)) {
ret = ipmi_print_isol_info(intf);
}
if (strncmp(argv[0], "setup", 5) == 0) {
if (strncmp(argv[1], "9600", 4) == 0) {
ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_9600);
/*
* Set a parameter value
*/
else if (!strncmp(argv[0], "set", 3)) {
if (argc < 3) {
print_isol_set_usage();
return -1;
}
else if (strncmp(argv[1], "19200", 5) == 0) {
ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_19200);
ret = ipmi_isol_set_param(intf, argv[1], argv[2]);
}
else if (strncmp(argv[1], "38400", 5) == 0) {
ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_38400);
}
else if (strncmp(argv[1], "57600", 5) == 0) {
ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_57600);
}
else if (strncmp(argv[1], "115200", 6) == 0) {
ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_115200);
/*
* Activate
*/
else if (!strncmp(argv[0], "activate", 8)) {
ret = ipmi_isol_activate(intf);
}
else {
lprintf(LOG_ERR, "ISOL - Unsupported baud rate: %s", argv[1]);
print_isol_usage();
ret = -1;
}
}
return ret;
}

View File

@ -79,6 +79,11 @@ static struct ipmi_rs * ipmi_lan_recv_packet(struct ipmi_intf * intf);
static struct ipmi_rs * ipmi_lan_poll_recv(struct ipmi_intf * intf);
static int ipmi_lan_setup(struct ipmi_intf * intf);
static int ipmi_lan_keepalive(struct ipmi_intf * intf);
static struct ipmi_rs * ipmi_lan_send_payload(struct ipmi_intf * intf,
struct ipmi_v2_payload * payload);
static struct ipmi_rs * ipmi_lan_recv_sol(struct ipmi_intf * intf);
static struct ipmi_rs * ipmi_lan_send_sol(struct ipmi_intf * intf,
struct ipmi_v2_payload * payload);
static struct ipmi_rs * ipmi_lan_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req);
static int ipmi_lan_send_rsp(struct ipmi_intf * intf, struct ipmi_rs * rsp);
static int ipmi_lan_open(struct ipmi_intf * intf);
@ -93,6 +98,8 @@ struct ipmi_intf ipmi_lan_intf = {
close: ipmi_lan_close,
sendrecv: ipmi_lan_send_cmd,
sendrsp: ipmi_lan_send_rsp,
recv_sol: ipmi_lan_recv_sol,
send_sol: ipmi_lan_send_sol,
keepalive: ipmi_lan_keepalive,
target_addr: IPMI_BMC_SLAVE_ADDR,
};
@ -456,6 +463,66 @@ ipmi_lan_poll_recv(struct ipmi_intf * intf)
memcpy(&rsp->session.id, rsp->data+x, 4);
x += 4;
if (rsp->session.id == (intf->session->session_id + 0x10000000)) {
/* With SOL, authtype is always NONE, so we have no authcode */
rsp->session.payloadtype = IPMI_PAYLOAD_TYPE_SOL;
rsp->session.msglen = rsp->data[x++];
rsp->payload.sol_packet.packet_sequence_number =
rsp->data[x++] & 0x0F;
rsp->payload.sol_packet.acked_packet_number =
rsp->data[x++] & 0x0F;
rsp->payload.sol_packet.accepted_character_count =
rsp->data[x++];
rsp->payload.sol_packet.is_nack =
rsp->data[x] & 0x40;
rsp->payload.sol_packet.transfer_unavailable =
rsp->data[x] & 0x20;
rsp->payload.sol_packet.sol_inactive =
rsp->data[x] & 0x10;
rsp->payload.sol_packet.transmit_overrun =
rsp->data[x] & 0x08;
rsp->payload.sol_packet.break_detected =
rsp->data[x++] & 0x04;
x++; /* On ISOL there's and additional fifth byte before the data starts */
lprintf(LOG_DEBUG, "SOL sequence number : 0x%02x",
rsp->payload.sol_packet.packet_sequence_number);
lprintf(LOG_DEBUG, "SOL acked packet : 0x%02x",
rsp->payload.sol_packet.acked_packet_number);
lprintf(LOG_DEBUG, "SOL accepted char count : 0x%02x",
rsp->payload.sol_packet.accepted_character_count);
lprintf(LOG_DEBUG, "SOL is nack : %s",
rsp->payload.sol_packet.is_nack? "true" : "false");
lprintf(LOG_DEBUG, "SOL xfer unavailable : %s",
rsp->payload.sol_packet.transfer_unavailable? "true" : "false");
lprintf(LOG_DEBUG, "SOL inactive : %s",
rsp->payload.sol_packet.sol_inactive? "true" : "false");
lprintf(LOG_DEBUG, "SOL transmit overrun : %s",
rsp->payload.sol_packet.transmit_overrun? "true" : "false");
lprintf(LOG_DEBUG, "SOL break detected : %s",
rsp->payload.sol_packet.break_detected? "true" : "false");
}
else
{
/* Standard IPMI 1.5 packet */
rsp->session.payloadtype = IPMI_PAYLOAD_TYPE_IPMI;
if (intf->session->active && (rsp->session.authtype || intf->session->authtype))
x += 16;
@ -545,13 +612,16 @@ ipmi_lan_poll_recv(struct ipmi_intf * intf)
rsp = ipmi_lan_recv_packet(intf);
continue;
}
}
break;
}
/* shift response data to start of array */
if (rsp && rsp->data_len > x) {
rsp->data_len -= x + 1;
rsp->data_len -= x;
if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_IPMI)
rsp->data_len -= 1; /* We don't want the checksum */
memmove(rsp->data, rsp->data + x, rsp->data_len);
memset(rsp->data + rsp->data_len, 0, IPMI_BUF_SIZE - rsp->data_len);
}
@ -947,6 +1017,430 @@ ipmi_lan_send_rsp(struct ipmi_intf * intf, struct ipmi_rs * rsp)
return 0;
}
/*
* IPMI SOL Payload Format
* +--------------------+
* | rmcp.ver | 4 bytes
* | rmcp.__reserved |
* | rmcp.seq |
* | rmcp.class |
* +--------------------+
* | session.authtype | 9 bytes
* | session.seq |
* | session.id |
* +--------------------+
* | message length | 1 byte
* +--------------------+
* | sol.seq | 5 bytes
* | sol.ack_seq |
* | sol.acc_count |
* | sol.control |
* | sol.__reserved |
* +--------------------+
* | [request data] | data_len bytes
* +--------------------+
*/
uint8_t * ipmi_lan_build_sol_msg(struct ipmi_intf * intf,
struct ipmi_v2_payload * payload,
int * llen)
{
struct rmcp_hdr rmcp = {
.ver = RMCP_VERSION_1,
.class = RMCP_CLASS_IPMI,
.seq = 0xff,
};
struct ipmi_session * session = intf->session;
/* msg will hold the entire message to be sent */
uint8_t * msg;
int len = 0;
len = sizeof(rmcp) + // RMCP Header (4)
10 + // IPMI Session Header
5 + // SOL header
payload->payload.sol_packet.character_count; // The actual payload
msg = malloc(len);
if (msg == NULL) {
lprintf(LOG_ERR, "ipmitool: malloc failure");
return;
}
memset(msg, 0, len);
/* rmcp header */
memcpy(msg, &rmcp, sizeof(rmcp));
len = sizeof(rmcp);
/* ipmi session header */
msg[len++] = 0; /* SOL is always authtype = NONE */
msg[len++] = session->in_seq & 0xff;
msg[len++] = (session->in_seq >> 8) & 0xff;
msg[len++] = (session->in_seq >> 16) & 0xff;
msg[len++] = (session->in_seq >> 24) & 0xff;
msg[len++] = session->session_id & 0xff;
msg[len++] = (session->session_id >> 8) & 0xff;
msg[len++] = (session->session_id >> 16) & 0xff;
msg[len++] = ((session->session_id >> 24) + 0x10) & 0xff; /* Add 0x10 to MSB for SOL */
msg[len++] = payload->payload.sol_packet.character_count + 5;
/* sol header */
msg[len++] = payload->payload.sol_packet.packet_sequence_number;
msg[len++] = payload->payload.sol_packet.acked_packet_number;
msg[len++] = payload->payload.sol_packet.accepted_character_count;
msg[len] = payload->payload.sol_packet.is_nack ? 0x40 : 0;
msg[len] |= payload->payload.sol_packet.assert_ring_wor ? 0x20 : 0;
msg[len] |= payload->payload.sol_packet.generate_break ? 0x10 : 0;
msg[len] |= payload->payload.sol_packet.deassert_cts ? 0x08 : 0;
msg[len] |= payload->payload.sol_packet.deassert_dcd_dsr ? 0x04 : 0;
msg[len] |= payload->payload.sol_packet.flush_inbound ? 0x02 : 0;
msg[len++] |= payload->payload.sol_packet.flush_outbound ? 0x01 : 0;
len++; /* On SOL there's and additional fifth byte before the data starts */
if (payload->payload.sol_packet.character_count) {
/* We may have data to add */
memcpy(msg + len,
payload->payload.sol_packet.data,
payload->payload.sol_packet.character_count);
len += payload->payload.sol_packet.character_count;
}
session->in_seq++;
if (session->in_seq == 0)
session->in_seq++;
*llen = len;
return msg;
}
/*
* is_sol_packet
*/
static int
is_sol_packet(struct ipmi_rs * rsp)
{
return (rsp &&
(rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL));
}
/*
* sol_response_acks_packet
*/
static int
sol_response_acks_packet(struct ipmi_rs * rsp,
struct ipmi_v2_payload * payload)
{
return (is_sol_packet(rsp) &&
payload &&
(payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) &&
(rsp->payload.sol_packet.acked_packet_number ==
payload->payload.sol_packet.packet_sequence_number));
}
/*
* ipmi_lan_send_sol_payload
*
*/
static struct ipmi_rs *
ipmi_lan_send_sol_payload(struct ipmi_intf * intf,
struct ipmi_v2_payload * payload)
{
struct ipmi_rs * rsp = NULL;
uint8_t * msg;
int len;
int try = 0;
if (intf->opened == 0 && intf->open != NULL) {
if (intf->open(intf) < 0)
return NULL;
}
msg = ipmi_lan_build_sol_msg(intf, payload, &len);
if (len <= 0 || msg == NULL) {
lprintf(LOG_ERR, "Invalid SOL payload packet");
if (msg != NULL)
free(msg);
return NULL;
}
lprintf(LOG_DEBUG, ">> SENDING A SOL MESSAGE\n");
for (;;) {
if (ipmi_lan_send_packet(intf, msg, len) < 0) {
try++;
usleep(5000);
continue;
}
/* if we are set to noanswer we do not expect response */
if (intf->noanswer)
break;
if (payload->payload.sol_packet.packet_sequence_number == 0) {
/* We're just sending an ACK. No need to retry. */
break;
}
usleep(100);
rsp = ipmi_lan_recv_sol(intf); /* Grab the next packet */
if (sol_response_acks_packet(rsp, payload))
break;
else if (is_sol_packet(rsp) && rsp->data_len)
{
/*
* We're still waiting for our ACK, but we more data from
* the BMC
*/
intf->session->sol_data.sol_input_handler(rsp);
}
usleep(5000);
if (++try >= intf->session->retry) {
lprintf(LOG_DEBUG, " No response from remote controller");
break;
}
}
return rsp;
}
/*
* is_sol_partial_ack
*
* Determine if the response is a partial ACK/NACK that indicates
* we need to resend part of our packet.
*
* returns the number of characters we need to resend, or
* 0 if this isn't an ACK or we don't need to resend anything
*/
static int is_sol_partial_ack(struct ipmi_v2_payload * v2_payload,
struct ipmi_rs * rsp)
{
int chars_to_resend = 0;
if (v2_payload &&
rsp &&
is_sol_packet(rsp) &&
sol_response_acks_packet(rsp, v2_payload) &&
(rsp->payload.sol_packet.accepted_character_count <
v2_payload->payload.sol_packet.character_count))
{
if (rsp->payload.sol_packet.accepted_character_count == 0) {
/* We should not resend data */
chars_to_resend = 0;
}
else
{
chars_to_resend =
v2_payload->payload.sol_packet.character_count -
rsp->payload.sol_packet.accepted_character_count;
}
}
return chars_to_resend;
}
/*
* set_sol_packet_sequence_number
*/
static void set_sol_packet_sequence_number(struct ipmi_intf * intf,
struct ipmi_v2_payload * v2_payload)
{
/* Keep our sequence number sane */
if (intf->session->sol_data.sequence_number > 0x0F)
intf->session->sol_data.sequence_number = 1;
v2_payload->payload.sol_packet.packet_sequence_number =
intf->session->sol_data.sequence_number++;
}
/*
* ipmi_lan_send_sol
*
* Sends a SOL packet.. We handle partial ACK/NACKs from the BMC here.
*
* Returns a pointer to the SOL ACK we received, or
* 0 on failure
*
*/
struct ipmi_rs *
ipmi_lan_send_sol(struct ipmi_intf * intf,
struct ipmi_v2_payload * v2_payload)
{
struct ipmi_rs * rsp;
int chars_to_resend = 0;
v2_payload->payload_type = IPMI_PAYLOAD_TYPE_SOL;
/*
* Payload length is just the length of the character
* data here.
*/
v2_payload->payload.sol_packet.acked_packet_number = 0; /* NA */
set_sol_packet_sequence_number(intf, v2_payload);
v2_payload->payload.sol_packet.accepted_character_count = 0; /* NA */
rsp = ipmi_lan_send_sol_payload(intf, v2_payload);
/* Determine if we need to resend some of our data */
chars_to_resend = is_sol_partial_ack(v2_payload, rsp);
while (chars_to_resend)
{
/*
* We first need to handle any new data we might have
* received in our NACK
*/
if (rsp->data_len)
intf->session->sol_data.sol_input_handler(rsp);
set_sol_packet_sequence_number(intf, v2_payload);
/* Just send the required data */
memmove(v2_payload->payload.sol_packet.data,
v2_payload->payload.sol_packet.data +
rsp->payload.sol_packet.accepted_character_count,
chars_to_resend);
v2_payload->payload.sol_packet.character_count = chars_to_resend;
rsp = ipmi_lan_send_sol_payload(intf, v2_payload);
chars_to_resend = is_sol_partial_ack(v2_payload, rsp);
}
return rsp;
}
/*
* check_sol_packet_for_new_data
*
* Determine whether the SOL packet has already been seen
* and whether the packet has new data for us.
*
* This function has the side effect of removing an previously
* seen data, and moving new data to the front.
*
* It also "Remembers" the data so we don't get repeats.
*
*/
static int
check_sol_packet_for_new_data(struct ipmi_intf * intf,
struct ipmi_rs *rsp)
{
static uint8_t last_received_sequence_number = 0;
static uint8_t last_received_byte_count = 0;
int new_data_size = 0;
if (rsp &&
(rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL))
{
uint8_t unaltered_data_len = rsp->data_len;
if (rsp->payload.sol_packet.packet_sequence_number ==
last_received_sequence_number)
{
/*
* This is the same as the last packet, but may include
* extra data
*/
new_data_size = rsp->data_len - last_received_byte_count;
if (new_data_size > 0)
{
/* We have more data to process */
memmove(rsp->data,
rsp->data +
rsp->data_len - new_data_size,
new_data_size);
}
rsp->data_len = new_data_size;
}
/*
*Rember the data for next round
*/
if (rsp && rsp->payload.sol_packet.packet_sequence_number)
{
last_received_sequence_number =
rsp->payload.sol_packet.packet_sequence_number;
last_received_byte_count = unaltered_data_len;
}
}
return new_data_size;
}
/*
* ack_sol_packet
*
* Provided the specified packet looks reasonable, ACK it.
*/
static void
ack_sol_packet(struct ipmi_intf * intf,
struct ipmi_rs * rsp)
{
if (rsp &&
(rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) &&
(rsp->payload.sol_packet.packet_sequence_number))
{
struct ipmi_v2_payload ack;
memset(&ack, 0, sizeof(struct ipmi_v2_payload));
ack.payload_type = IPMI_PAYLOAD_TYPE_SOL;
/*
* Payload length is just the length of the character
* data here.
*/
ack.payload_length = 0;
/* ACK packets have sequence numbers of 0 */
ack.payload.sol_packet.packet_sequence_number = 0;
ack.payload.sol_packet.acked_packet_number =
rsp->payload.sol_packet.packet_sequence_number;
ack.payload.sol_packet.accepted_character_count = rsp->data_len;
ipmi_lan_send_sol_payload(intf, &ack);
}
}
/*
* ipmi_recv_sol
*
* Receive a SOL packet and send an ACK in response.
*
*/
struct ipmi_rs *
ipmi_lan_recv_sol(struct ipmi_intf * intf)
{
struct ipmi_rs * rsp = ipmi_lan_poll_recv(intf);
ack_sol_packet(intf, rsp);
/*
* Remembers the data sent, and alters the data to just
* include the new stuff.
*/
check_sol_packet_for_new_data(intf, rsp);
return rsp;
}
/* send a get device id command to keep session active */
static int
ipmi_lan_keepalive(struct ipmi_intf * intf)
@ -1448,6 +1942,8 @@ ipmi_lan_open(struct ipmi_intf * intf)
intf->abort = 1;
intf->session->sol_data.sequence_number = 1;
/* open port to BMC */
memset(&s->addr, 0, sizeof(struct sockaddr_in));
s->addr.sin_family = AF_INET;