~ubuntu-branches/ubuntu/karmic/openipmi/karmic

« back to all changes in this revision

Viewing changes to lib/ipmi_payload.c

  • Committer: Bazaar Package Importer
  • Author(s): Noèl Köthe
  • Date: 2005-07-04 21:29:17 UTC
  • Revision ID: james.westby@ubuntu.com-20050704212917-igddk5jawjmhrlay
Tags: upstream-2.0.1
Import upstream version 2.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ipmi_payload.c
 
3
 *
 
4
 * MontaVista IPMI code for handling IPMI-specific data formatting
 
5
 *
 
6
 * Author: MontaVista Software, Inc.
 
7
 *         Corey Minyard <minyard@mvista.com>
 
8
 *         source@mvista.com
 
9
 *
 
10
 * Copyright 2002,2003,2004 MontaVista Software Inc.
 
11
 *
 
12
 *  This program is free software; you can redistribute it and/or
 
13
 *  modify it under the terms of the GNU Lesser General Public License
 
14
 *  as published by the Free Software Foundation; either version 2 of
 
15
 *  the License, or (at your option) any later version.
 
16
 *
 
17
 *
 
18
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 
19
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 
20
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
21
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
22
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 
23
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 
24
 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
25
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 
26
 *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 
27
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
28
 *
 
29
 *  You should have received a copy of the GNU Lesser General Public
 
30
 *  License along with this program; if not, write to the Free
 
31
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
32
 */
 
33
 
 
34
#include <string.h>
 
35
 
 
36
#include <OpenIPMI/ipmi_lan.h>
 
37
#include <OpenIPMI/ipmi_err.h>
 
38
#include <OpenIPMI/ipmi_addr.h>
 
39
#include <OpenIPMI/ipmi_conn.h>
 
40
#include <OpenIPMI/ipmi_msgbits.h>
 
41
#include <OpenIPMI/ipmi_debug.h>
 
42
#include <OpenIPMI/internal/ipmi_int.h>
 
43
 
 
44
#if defined(DEBUG_MSG) || defined(DEBUG_RAWMSG)
 
45
static void
 
46
dump_hex(void *vdata, int len)
 
47
{
 
48
    unsigned char *data = vdata;
 
49
    int i;
 
50
    for (i=0; i<len; i++) {
 
51
        if ((i != 0) && ((i % 16) == 0)) {
 
52
            ipmi_log(IPMI_LOG_DEBUG_CONT, "\n  ");
 
53
        }
 
54
        ipmi_log(IPMI_LOG_DEBUG_CONT, " %2.2x", data[i]);
 
55
    }
 
56
}
 
57
#endif
 
58
 
 
59
static unsigned char
 
60
ipmb_checksum(unsigned char *data, int size)
 
61
{
 
62
        unsigned char csum = 0;
 
63
        
 
64
        for (; size > 0; size--, data++)
 
65
                csum += *data;
 
66
 
 
67
        return -csum;
 
68
}
 
69
 
 
70
static int
 
71
ipmi_format_msg(ipmi_con_t    *ipmi,
 
72
                ipmi_addr_t   *addr,
 
73
                unsigned int  addr_len,
 
74
                ipmi_msg_t    *msg,
 
75
                unsigned char *out_data,
 
76
                unsigned int  *out_data_len,
 
77
                int           *out_of_session,
 
78
                unsigned char seq)
 
79
{
 
80
    unsigned char *tmsg = out_data;
 
81
    int           pos;
 
82
    int           msgstart;
 
83
 
 
84
    if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
 
85
        /* It's a message straight to the BMC. */
 
86
        ipmi_system_interface_addr_t *si_addr
 
87
            = (ipmi_system_interface_addr_t *) addr;
 
88
 
 
89
        if ((msg->data_len + 7) > *out_data_len)
 
90
            return E2BIG;
 
91
        if (ipmi->hacks & IPMI_CONN_HACK_20_AS_MAIN_ADDR)
 
92
            tmsg[0] = 0x20;
 
93
        else
 
94
            tmsg[0] = ipmi->ipmb_addr; /* To the BMC. */
 
95
        tmsg[1] = (msg->netfn << 2) | si_addr->lun;
 
96
        tmsg[2] = ipmb_checksum(tmsg, 2);
 
97
        tmsg[3] = 0x81; /* Remote console IPMI Software ID */
 
98
        tmsg[4] = seq << 2;
 
99
        tmsg[5] = msg->cmd;
 
100
        memcpy(tmsg+6, msg->data, msg->data_len);
 
101
        pos = msg->data_len + 6;
 
102
        tmsg[pos] = ipmb_checksum(tmsg+3, pos-3);
 
103
        pos++;
 
104
    } else {
 
105
        /* It's an IPMB address, route it using a send message
 
106
           command. */
 
107
        ipmi_ipmb_addr_t *ipmb_addr = (ipmi_ipmb_addr_t *) addr;
 
108
        int              do_broadcast = 0;
 
109
 
 
110
        if ((addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)
 
111
            && (!ipmi->broadcast_broken))
 
112
        {
 
113
            do_broadcast = 1;
 
114
        }
 
115
 
 
116
        if ((msg->data_len + 15 + do_broadcast) > *out_data_len)
 
117
            return E2BIG;
 
118
 
 
119
        pos = 0;
 
120
        if (ipmi->hacks & IPMI_CONN_HACK_20_AS_MAIN_ADDR)
 
121
            tmsg[pos++] = 0x20;
 
122
        else
 
123
            tmsg[pos++] = ipmi->ipmb_addr; /* BMC is the bridge. */
 
124
        tmsg[pos++] = (IPMI_APP_NETFN << 2) | 0;
 
125
        tmsg[pos++] = ipmb_checksum(tmsg, 2);
 
126
        tmsg[pos++] = 0x81; /* Remote console IPMI Software ID */
 
127
        tmsg[pos++] = (seq << 2) | 0; /* LUN is zero */
 
128
        tmsg[pos++] = IPMI_SEND_MSG_CMD;
 
129
        tmsg[pos++] = ((ipmb_addr->channel & 0xf)
 
130
                       | (1 << 6)); /* Turn on tracking. */
 
131
        if (do_broadcast)
 
132
            tmsg[pos++] = 0; /* Do a broadcast. */
 
133
        msgstart = pos;
 
134
        tmsg[pos++] = ipmb_addr->slave_addr;
 
135
        tmsg[pos++] = (msg->netfn << 2) | ipmb_addr->lun;
 
136
        tmsg[pos++] = ipmb_checksum(tmsg+msgstart, 2);
 
137
        msgstart = pos;
 
138
        tmsg[pos++] = ipmi->ipmb_addr;
 
139
        tmsg[pos++] = (seq << 2) | 2; /* add 2 as the SMS LUN */
 
140
        tmsg[pos++] = msg->cmd;
 
141
        memcpy(tmsg+pos, msg->data, msg->data_len);
 
142
        pos += msg->data_len;
 
143
        tmsg[pos] = ipmb_checksum(tmsg+msgstart, pos-msgstart);
 
144
        pos++;
 
145
        tmsg[pos] = ipmb_checksum(tmsg+3, pos-3);
 
146
        pos++;
 
147
    }
 
148
 
 
149
    *out_data_len = pos;
 
150
    return 0;
 
151
}
 
152
 
 
153
static int
 
154
ipmi_get_recv_seq(ipmi_con_t    *ipmi,
 
155
                  unsigned char *data,
 
156
                  unsigned int  data_len,
 
157
                  unsigned char *seq)
 
158
{
 
159
    if (data_len < 8) { /* Minimum size of an IPMI msg. */
 
160
        if (DEBUG_RAWMSG || DEBUG_MSG_ERR)
 
161
            ipmi_log(IPMI_LOG_DEBUG,
 
162
                     "Dropped message because too small(6)");
 
163
        return EINVAL;
 
164
    }
 
165
 
 
166
    if ((data[5] == IPMI_READ_EVENT_MSG_BUFFER_CMD)
 
167
        && ((data[1] >> 2) == (IPMI_APP_NETFN | 1)))
 
168
    {
 
169
        /* An async event has no seq #, handle async. */
 
170
        return ENOSYS;
 
171
    }
 
172
 
 
173
    *seq = data[4] >> 2;
 
174
    return 0;
 
175
}
 
176
 
 
177
static int
 
178
ipmi_handle_recv(ipmi_con_t    *ipmi,
 
179
                 ipmi_msgi_t   *rspi,
 
180
                 ipmi_addr_t   *orig_addr,
 
181
                 unsigned int  orig_addr_len,
 
182
                 ipmi_msg_t    *orig_msg,
 
183
                 unsigned char *data,
 
184
                 unsigned int  data_len)
 
185
{
 
186
    ipmi_msg_t    *msg = &(rspi->msg);
 
187
    ipmi_addr_t   *addr = &(rspi->addr);
 
188
    ipmi_addr_t   addr2;
 
189
    unsigned int  addr_len;
 
190
    unsigned int  seq;
 
191
    unsigned char *tmsg = data;
 
192
 
 
193
    if (data_len < 8) { /* Minimum size of an IPMI msg. */
 
194
        if (DEBUG_RAWMSG || DEBUG_MSG_ERR)
 
195
            ipmi_log(IPMI_LOG_DEBUG,
 
196
                     "Dropped message because too small(6)");
 
197
        return EINVAL;
 
198
    }
 
199
 
 
200
    /* We don't check the checksums, because the network layer should
 
201
       validate all this for us. */
 
202
 
 
203
    seq = data[4] >> 2;
 
204
 
 
205
    if ((tmsg[5] == IPMI_SEND_MSG_CMD)
 
206
        && ((tmsg[1] >> 2) == (IPMI_APP_NETFN | 1)))
 
207
    {
 
208
        /* It's a response to a sent message. */
 
209
        ipmi_ipmb_addr_t *ipmb_addr = (ipmi_ipmb_addr_t *) addr;
 
210
        ipmi_ipmb_addr_t *ipmb2 = (ipmi_ipmb_addr_t *) orig_addr;
 
211
 
 
212
        /* FIXME - this entire thing is a cheap hack. */
 
213
        if (tmsg[6] != 0) {
 
214
            /* Got an error from the send message.  We don't have any
 
215
               IPMB information to work with, so just extract it from
 
216
               the original message. */
 
217
            memcpy(ipmb_addr, ipmb2, sizeof(*ipmb_addr));
 
218
            /* Just in case it's a broadcast. */
 
219
            ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
 
220
            addr_len = sizeof(ipmi_ipmb_addr_t);
 
221
            msg->netfn = orig_msg->netfn | 1;
 
222
            msg->cmd = orig_msg->cmd;
 
223
            msg->data = tmsg + 6;
 
224
            msg->data_len = 1;
 
225
            if (ipmi->handle_send_rsp_err) {
 
226
                ipmi->handle_send_rsp_err(ipmi, msg);
 
227
            }
 
228
        } else {
 
229
            if (data_len < 15)
 
230
                /* The response to a send message was not carrying the
 
231
                   payload. */
 
232
                return EINVAL;
 
233
 
 
234
            if (tmsg[10] == ipmi->ipmb_addr) {
 
235
                ipmi_system_interface_addr_t *si_addr
 
236
                    = (ipmi_system_interface_addr_t *) addr;
 
237
 
 
238
                /* It's directly from the BMC, so it's a system interface
 
239
                   message. */
 
240
                si_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
 
241
                si_addr->channel = 0xf;
 
242
                si_addr->lun = tmsg[11] & 3;
 
243
            } else {
 
244
                /* This is a hack, but the channel does not come back in the
 
245
                   message.  So we use the channel from the original
 
246
                   instead. */
 
247
                ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
 
248
                ipmb_addr->channel = ipmb2->channel;
 
249
                ipmb_addr->slave_addr = tmsg[10];
 
250
                ipmb_addr->lun = tmsg[11] & 0x3;
 
251
            }
 
252
            msg->netfn = tmsg[8] >> 2;
 
253
            msg->cmd = tmsg[12];
 
254
            addr_len = sizeof(ipmi_ipmb_addr_t);
 
255
            msg->data = tmsg+13;
 
256
            msg->data_len = data_len - 15;
 
257
        }
 
258
    } else if ((orig_addr->addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
 
259
               && (((ipmi->hacks & IPMI_CONN_HACK_20_AS_MAIN_ADDR)
 
260
                    && (tmsg[3] == 0x20))
 
261
                   || ((! (ipmi->hacks & IPMI_CONN_HACK_20_AS_MAIN_ADDR))
 
262
                       && (tmsg[3] == ipmi->ipmb_addr))))
 
263
    {
 
264
        /* In some cases, a message from the IPMB looks like it came
 
265
           from the BMC itself, IMHO a misinterpretation of the
 
266
           errata.  IPMIv1_5_rev1_1_0926 markup, section 6.12.4,
 
267
           didn't clear things up at all.  Some manufacturers have
 
268
           interpreted it this way, but IMHO it is incorrect. */
 
269
        memcpy(addr, orig_addr, orig_addr_len);
 
270
        addr_len = orig_addr_len;
 
271
        if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)
 
272
            addr->addr_type = IPMI_IPMB_ADDR_TYPE;
 
273
        msg->netfn = tmsg[1] >> 2;
 
274
        msg->cmd = tmsg[5];
 
275
        msg->data = tmsg+6;
 
276
        msg->data_len = data_len - 7;
 
277
    } else {
 
278
        /* It's not encapsulated in a send message response. */
 
279
 
 
280
        if (((ipmi->hacks & IPMI_CONN_HACK_20_AS_MAIN_ADDR)
 
281
             && (tmsg[3] == 0x20))
 
282
            || ((!(ipmi->hacks & IPMI_CONN_HACK_20_AS_MAIN_ADDR))
 
283
                && (tmsg[3] == ipmi->ipmb_addr)))
 
284
        {
 
285
            ipmi_system_interface_addr_t *si_addr
 
286
                = (ipmi_system_interface_addr_t *) addr;
 
287
 
 
288
            /* It's directly from the BMC, so it's a system interface
 
289
               message. */
 
290
            si_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
 
291
            si_addr->channel = 0xf;
 
292
            si_addr->lun = tmsg[4] & 3;
 
293
        } else {
 
294
            ipmi_ipmb_addr_t *ipmb_addr = (ipmi_ipmb_addr_t *) addr;
 
295
            ipmi_ipmb_addr_t *ipmb2 = (ipmi_ipmb_addr_t *) orig_addr;
 
296
 
 
297
            /* A message from the IPMB. */
 
298
            ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
 
299
            /* This is a hack, but the channel does not come back in the
 
300
               message.  So we use the channel from the original
 
301
               instead. */
 
302
            ipmb_addr->channel = ipmb2->channel;
 
303
            ipmb_addr->slave_addr = tmsg[3];
 
304
            ipmb_addr->lun = tmsg[4] & 0x3;
 
305
        }
 
306
 
 
307
        msg->netfn = tmsg[1] >> 2;
 
308
        msg->cmd = tmsg[5];
 
309
        addr_len = sizeof(ipmi_system_interface_addr_t);
 
310
        msg->data = tmsg+6;
 
311
        msg->data_len = data_len - 6;
 
312
        msg->data_len--; /* Remove the checksum */
 
313
    }
 
314
    
 
315
    /* Convert broadcast addresses to regular IPMB addresses, since
 
316
       they come back that way. */
 
317
    memcpy(&addr2, orig_addr, orig_addr_len);
 
318
    if (addr2.addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)
 
319
        addr2.addr_type = IPMI_IPMB_ADDR_TYPE;
 
320
 
 
321
    /* Validate that this response if for this command. */
 
322
    if (((orig_msg->netfn | 1) != msg->netfn)
 
323
        || (orig_msg->cmd != msg->cmd)
 
324
        || (! ipmi_addr_equal(&addr2, orig_addr_len, addr, addr_len)))
 
325
    {
 
326
        if (DEBUG_RAWMSG || DEBUG_MSG_ERR) {
 
327
            ipmi_log(IPMI_LOG_DEBUG_START,
 
328
                     "Dropped message seq %d - netfn/cmd/addr mismatch\n"
 
329
                     " netfn     = %2.2x, exp netfn = %2.2x\n"
 
330
                     " cmd       = %2.2x, exp cmd   = %2.2x\n"
 
331
                     " addr      =",
 
332
                     seq,
 
333
                     msg->netfn, orig_msg->netfn | 1,
 
334
                     msg->cmd, orig_msg->cmd);
 
335
            dump_hex(addr, addr_len);
 
336
            ipmi_log(IPMI_LOG_DEBUG_CONT,
 
337
                     "\n exp addr=");
 
338
            dump_hex(&addr2, orig_addr_len);
 
339
            if (data_len) {
 
340
                ipmi_log(IPMI_LOG_DEBUG_CONT, "\n data     =\n  ");
 
341
                dump_hex(tmsg, data_len);
 
342
            }
 
343
            dump_hex(addr, addr_len);
 
344
            ipmi_log(IPMI_LOG_DEBUG_END, " ");
 
345
        }
 
346
        return EINVAL;
 
347
    }
 
348
 
 
349
    rspi->addr_len = addr_len;
 
350
    memcpy(rspi->data, msg->data, msg->data_len);
 
351
    msg->data = rspi->data;
 
352
 
 
353
    if (DEBUG_MSG) {
 
354
        char buf1[32], buf2[32], buf3[32];
 
355
        ipmi_log(IPMI_LOG_DEBUG_START, "incoming msg from IPMB addr =");
 
356
        dump_hex((unsigned char *) addr, addr_len);
 
357
        ipmi_log(IPMI_LOG_DEBUG_CONT,
 
358
                "\n msg  = netfn=%s cmd=%s data_len=%d. cc=%s",
 
359
                ipmi_get_netfn_string(msg->netfn, buf1, 32),
 
360
                ipmi_get_command_string(msg->netfn, msg->cmd, buf2, 32),
 
361
                 msg->data_len,
 
362
                ipmi_get_cc_string(msg->data[0], buf3, 32));
 
363
        if (msg->data_len) {
 
364
            ipmi_log(IPMI_LOG_DEBUG_CONT, "\n data =\n  ");
 
365
            dump_hex(msg->data, msg->data_len);
 
366
        }
 
367
        ipmi_log(IPMI_LOG_DEBUG_END, " ");
 
368
    }
 
369
 
 
370
    return 0;
 
371
}
 
372
 
 
373
static void
 
374
ipmi_handle_recv_async(ipmi_con_t    *ipmi,
 
375
                       unsigned char *tmsg,
 
376
                       unsigned int  data_len)
 
377
{
 
378
    ipmi_addr_t  addr;
 
379
    unsigned int addr_len;
 
380
    ipmi_msg_t   msg;
 
381
 
 
382
    if ((tmsg[5] == IPMI_READ_EVENT_MSG_BUFFER_CMD)
 
383
        && ((tmsg[1] >> 2) == (IPMI_APP_NETFN | 1)))
 
384
    {
 
385
        /* It is an event from the event buffer. */
 
386
        ipmi_system_interface_addr_t *si_addr
 
387
            = (ipmi_system_interface_addr_t *) &addr;
 
388
 
 
389
        if (tmsg[6] != 0) {
 
390
            /* An error getting the events, just ignore it. */
 
391
            if (DEBUG_RAWMSG || DEBUG_MSG_ERR)
 
392
                ipmi_log(IPMI_LOG_DEBUG, "Dropped message err getting event");
 
393
            return;
 
394
        }
 
395
 
 
396
        si_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
 
397
        si_addr->channel = 0xf;
 
398
        si_addr->lun = tmsg[4] & 3;
 
399
 
 
400
        msg.netfn = tmsg[1] >> 2;
 
401
        msg.cmd = tmsg[5];
 
402
        addr_len = sizeof(ipmi_system_interface_addr_t);
 
403
        msg.data = tmsg+6;
 
404
        msg.data_len = data_len - 6;
 
405
        if (DEBUG_MSG) {
 
406
            char buf1[32], buf2[32], buf3[32];
 
407
            ipmi_log(IPMI_LOG_DEBUG_START, "incoming async event\n addr =");
 
408
            dump_hex((unsigned char *) &addr, addr_len);
 
409
            ipmi_log(IPMI_LOG_DEBUG_CONT,
 
410
                     "\n msg  = netfn=%s cmd=%s data_len=%d. cc=%s",
 
411
                     ipmi_get_netfn_string(msg.netfn, buf1, 32),
 
412
                     ipmi_get_command_string(msg.netfn, msg.cmd, buf2, 32),
 
413
                     msg.data_len,
 
414
                     ipmi_get_cc_string(msg.data[0], buf3, 32));
 
415
            if (msg.data_len) {
 
416
                ipmi_log(IPMI_LOG_DEBUG_CONT, "\n data(len=%d.) =\n  ",
 
417
                         msg.data_len);
 
418
                dump_hex(msg.data, msg.data_len);
 
419
            }
 
420
            ipmi_log(IPMI_LOG_DEBUG_END, " ");
 
421
        }
 
422
        if (ipmi->handle_async_event)
 
423
            ipmi->handle_async_event(ipmi, &addr, addr_len, &msg);
 
424
    } else {
 
425
        ipmi_log(IPMI_LOG_SEVERE, "ipmi_lan.c(ipmi_handle_recv_async): "
 
426
                 "Got an invalid async event, shouldn't happen");
 
427
    }
 
428
}
 
429
 
 
430
ipmi_payload_t _ipmi_payload =
 
431
{ ipmi_format_msg, ipmi_get_recv_seq, ipmi_handle_recv,
 
432
  ipmi_handle_recv_async };