2 * IPMI BMC external connection
4 * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 * This is designed to connect with OpenIPMI's lanserv serial interface
27 * using the "VM" connection type. See that for details.
30 #include "qemu/osdep.h"
31 #include "qemu/error-report.h"
32 #include "qemu/module.h"
33 #include "qapi/error.h"
34 #include "qemu/timer.h"
35 #include "chardev/char-fe.h"
36 #include "hw/ipmi/ipmi.h"
37 #include "hw/qdev-properties.h"
38 #include "migration/vmstate.h"
39 #include "qom/object.h"
41 #define VM_MSG_CHAR 0xA0 /* Marks end of message */
42 #define VM_CMD_CHAR 0xA1 /* Marks end of a command */
43 #define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */
45 #define VM_PROTOCOL_VERSION 1
46 #define VM_CMD_VERSION 0xff /* A version number byte follows */
47 #define VM_CMD_NOATTN 0x00
48 #define VM_CMD_ATTN 0x01
49 #define VM_CMD_ATTN_IRQ 0x02
50 #define VM_CMD_POWEROFF 0x03
51 #define VM_CMD_RESET 0x04
52 #define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */
53 #define VM_CMD_DISABLE_IRQ 0x06
54 #define VM_CMD_SEND_NMI 0x07
55 #define VM_CMD_CAPABILITIES 0x08
56 #define VM_CAPABILITIES_POWER 0x01
57 #define VM_CAPABILITIES_RESET 0x02
58 #define VM_CAPABILITIES_IRQ 0x04
59 #define VM_CAPABILITIES_NMI 0x08
60 #define VM_CAPABILITIES_ATTN 0x10
61 #define VM_CAPABILITIES_GRACEFUL_SHUTDOWN 0x20
62 #define VM_CMD_GRACEFUL_SHUTDOWN 0x09
64 #define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern"
65 typedef struct IPMIBmcExtern IPMIBmcExtern
;
66 DECLARE_INSTANCE_CHECKER(IPMIBmcExtern
, IPMI_BMC_EXTERN
,
68 struct IPMIBmcExtern
{
75 unsigned char inbuf
[MAX_IPMI_MSG_SIZE
+ 2];
82 unsigned char outbuf
[(MAX_IPMI_MSG_SIZE
+ 2) * 2 + 1];
86 struct QEMUTimer
*extern_timer
;
88 /* A reset event is pending to be sent upstream. */
93 ipmb_checksum(const unsigned char *data
, int size
, unsigned char start
)
95 unsigned char csum
= start
;
97 for (; size
> 0; size
--, data
++) {
103 static void continue_send(IPMIBmcExtern
*ibe
)
106 if (ibe
->outlen
== 0) {
110 ret
= qemu_chr_fe_write(&ibe
->chr
, ibe
->outbuf
+ ibe
->outpos
,
111 ibe
->outlen
- ibe
->outpos
);
115 if (ibe
->outpos
< ibe
->outlen
) {
116 /* Not fully transmitted, try again in a 10ms */
117 timer_mod_ns(ibe
->extern_timer
,
118 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) + 10000000);
123 if (!ibe
->sending_cmd
) {
124 ibe
->waiting_rsp
= true;
126 ibe
->sending_cmd
= false;
129 if (ibe
->connected
&& ibe
->send_reset
) {
131 ibe
->outbuf
[0] = VM_CMD_RESET
;
132 ibe
->outbuf
[1] = VM_CMD_CHAR
;
135 ibe
->send_reset
= false;
136 ibe
->sending_cmd
= true;
140 if (ibe
->waiting_rsp
) {
141 /* Make sure we get a response within 4 seconds. */
142 timer_mod_ns(ibe
->extern_timer
,
143 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) + 4000000000ULL);
149 static void extern_timeout(void *opaque
)
151 IPMIBmcExtern
*ibe
= opaque
;
152 IPMIInterface
*s
= ibe
->parent
.intf
;
154 if (ibe
->connected
) {
155 if (ibe
->waiting_rsp
&& (ibe
->outlen
== 0)) {
156 IPMIInterfaceClass
*k
= IPMI_INTERFACE_GET_CLASS(s
);
157 /* The message response timed out, return an error. */
158 ibe
->waiting_rsp
= false;
159 ibe
->inbuf
[1] = ibe
->outbuf
[1] | 0x04;
160 ibe
->inbuf
[2] = ibe
->outbuf
[2];
161 ibe
->inbuf
[3] = IPMI_CC_TIMEOUT
;
162 k
->handle_rsp(s
, ibe
->outbuf
[0], ibe
->inbuf
+ 1, 3);
169 static void addchar(IPMIBmcExtern
*ibe
, unsigned char ch
)
175 ibe
->outbuf
[ibe
->outlen
] = VM_ESCAPE_CHAR
;
180 ibe
->outbuf
[ibe
->outlen
] = ch
;
185 static void ipmi_bmc_extern_handle_command(IPMIBmc
*b
,
186 uint8_t *cmd
, unsigned int cmd_len
,
187 unsigned int max_cmd_len
,
190 IPMIBmcExtern
*ibe
= IPMI_BMC_EXTERN(b
);
191 IPMIInterface
*s
= ibe
->parent
.intf
;
192 uint8_t err
= 0, csum
;
196 /* We already have a command queued. Shouldn't ever happen. */
197 error_report("IPMI KCS: Got command when not finished with the"
198 " previous command");
202 /* If it's too short or it was truncated, return an error. */
204 err
= IPMI_CC_REQUEST_DATA_LENGTH_INVALID
;
205 } else if ((cmd_len
> max_cmd_len
) || (cmd_len
> MAX_IPMI_MSG_SIZE
)) {
206 err
= IPMI_CC_REQUEST_DATA_TRUNCATED
;
207 } else if (!ibe
->connected
) {
208 err
= IPMI_CC_BMC_INIT_IN_PROGRESS
;
211 IPMIInterfaceClass
*k
= IPMI_INTERFACE_GET_CLASS(s
);
212 unsigned char rsp
[3];
213 rsp
[0] = cmd
[0] | 0x04;
216 ibe
->waiting_rsp
= false;
217 k
->handle_rsp(s
, msg_id
, rsp
, 3);
221 addchar(ibe
, msg_id
);
222 for (i
= 0; i
< cmd_len
; i
++) {
223 addchar(ibe
, cmd
[i
]);
225 csum
= ipmb_checksum(&msg_id
, 1, 0);
226 addchar(ibe
, -ipmb_checksum(cmd
, cmd_len
, csum
));
228 ibe
->outbuf
[ibe
->outlen
] = VM_MSG_CHAR
;
231 /* Start the transmit */
238 static void handle_hw_op(IPMIBmcExtern
*ibe
, unsigned char hw_op
)
240 IPMIInterface
*s
= ibe
->parent
.intf
;
241 IPMIInterfaceClass
*k
= IPMI_INTERFACE_GET_CLASS(s
);
245 /* We only support one version at this time. */
256 case VM_CMD_ATTN_IRQ
:
260 case VM_CMD_POWEROFF
:
261 k
->do_hw_op(s
, IPMI_POWEROFF_CHASSIS
, 0);
265 k
->do_hw_op(s
, IPMI_RESET_CHASSIS
, 0);
268 case VM_CMD_ENABLE_IRQ
:
269 k
->set_irq_enable(s
, 1);
272 case VM_CMD_DISABLE_IRQ
:
273 k
->set_irq_enable(s
, 0);
276 case VM_CMD_SEND_NMI
:
277 k
->do_hw_op(s
, IPMI_SEND_NMI
, 0);
280 case VM_CMD_GRACEFUL_SHUTDOWN
:
281 k
->do_hw_op(s
, IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP
, 0);
286 static void handle_msg(IPMIBmcExtern
*ibe
)
288 IPMIInterfaceClass
*k
= IPMI_INTERFACE_GET_CLASS(ibe
->parent
.intf
);
290 if (ibe
->in_escape
) {
291 ipmi_debug("msg escape not ended\n");
294 if (ibe
->inpos
< 5) {
295 ipmi_debug("msg too short\n");
298 if (ibe
->in_too_many
) {
299 ibe
->inbuf
[3] = IPMI_CC_REQUEST_DATA_TRUNCATED
;
301 } else if (ipmb_checksum(ibe
->inbuf
, ibe
->inpos
, 0) != 0) {
302 ipmi_debug("msg checksum failure\n");
305 ibe
->inpos
--; /* Remove checkum */
308 timer_del(ibe
->extern_timer
);
309 ibe
->waiting_rsp
= false;
310 k
->handle_rsp(ibe
->parent
.intf
, ibe
->inbuf
[0], ibe
->inbuf
+ 1, ibe
->inpos
- 1);
313 static int can_receive(void *opaque
)
318 static void receive(void *opaque
, const uint8_t *buf
, int size
)
320 IPMIBmcExtern
*ibe
= opaque
;
324 for (i
= 0; i
< size
; i
++) {
325 unsigned char ch
= buf
[i
];
330 ibe
->in_too_many
= false;
335 if (ibe
->in_too_many
) {
336 ipmi_debug("cmd in too many\n");
337 ibe
->in_too_many
= false;
341 if (ibe
->in_escape
) {
342 ipmi_debug("cmd in escape\n");
343 ibe
->in_too_many
= false;
345 ibe
->in_escape
= false;
348 ibe
->in_too_many
= false;
349 if (ibe
->inpos
< 1) {
352 hw_op
= ibe
->inbuf
[0];
358 ibe
->in_escape
= true;
362 if (ibe
->in_escape
) {
364 ibe
->in_escape
= false;
366 if (ibe
->in_too_many
) {
369 if (ibe
->inpos
>= sizeof(ibe
->inbuf
)) {
370 ibe
->in_too_many
= true;
373 ibe
->inbuf
[ibe
->inpos
] = ch
;
381 handle_hw_op(ibe
, hw_op
);
384 static void chr_event(void *opaque
, QEMUChrEvent event
)
386 IPMIBmcExtern
*ibe
= opaque
;
387 IPMIInterface
*s
= ibe
->parent
.intf
;
388 IPMIInterfaceClass
*k
= IPMI_INTERFACE_GET_CLASS(s
);
392 case CHR_EVENT_OPENED
:
393 ibe
->connected
= true;
396 addchar(ibe
, VM_CMD_VERSION
);
397 addchar(ibe
, VM_PROTOCOL_VERSION
);
398 ibe
->outbuf
[ibe
->outlen
] = VM_CMD_CHAR
;
400 addchar(ibe
, VM_CMD_CAPABILITIES
);
401 v
= VM_CAPABILITIES_IRQ
| VM_CAPABILITIES_ATTN
;
402 if (k
->do_hw_op(ibe
->parent
.intf
, IPMI_POWEROFF_CHASSIS
, 1) == 0) {
403 v
|= VM_CAPABILITIES_POWER
;
405 if (k
->do_hw_op(ibe
->parent
.intf
, IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP
, 1)
407 v
|= VM_CAPABILITIES_GRACEFUL_SHUTDOWN
;
409 if (k
->do_hw_op(ibe
->parent
.intf
, IPMI_RESET_CHASSIS
, 1) == 0) {
410 v
|= VM_CAPABILITIES_RESET
;
412 if (k
->do_hw_op(ibe
->parent
.intf
, IPMI_SEND_NMI
, 1) == 0) {
413 v
|= VM_CAPABILITIES_NMI
;
416 ibe
->outbuf
[ibe
->outlen
] = VM_CMD_CHAR
;
418 ibe
->sending_cmd
= false;
422 case CHR_EVENT_CLOSED
:
423 if (!ibe
->connected
) {
426 ibe
->connected
= false;
428 * Don't hang the OS trying to handle the ATN bit, other end will
429 * resend on a reconnect.
432 if (ibe
->waiting_rsp
) {
433 ibe
->waiting_rsp
= false;
434 ibe
->inbuf
[1] = ibe
->outbuf
[1] | 0x04;
435 ibe
->inbuf
[2] = ibe
->outbuf
[2];
436 ibe
->inbuf
[3] = IPMI_CC_BMC_INIT_IN_PROGRESS
;
437 k
->handle_rsp(s
, ibe
->outbuf
[0], ibe
->inbuf
+ 1, 3);
441 case CHR_EVENT_BREAK
:
442 case CHR_EVENT_MUX_IN
:
443 case CHR_EVENT_MUX_OUT
:
449 static void ipmi_bmc_extern_handle_reset(IPMIBmc
*b
)
451 IPMIBmcExtern
*ibe
= IPMI_BMC_EXTERN(b
);
453 ibe
->send_reset
= true;
457 static void ipmi_bmc_extern_realize(DeviceState
*dev
, Error
**errp
)
459 IPMIBmcExtern
*ibe
= IPMI_BMC_EXTERN(dev
);
461 if (!qemu_chr_fe_backend_connected(&ibe
->chr
)) {
462 error_setg(errp
, "IPMI external bmc requires chardev attribute");
466 qemu_chr_fe_set_handlers(&ibe
->chr
, can_receive
, receive
,
467 chr_event
, NULL
, ibe
, NULL
, true);
470 static int ipmi_bmc_extern_post_migrate(void *opaque
, int version_id
)
472 IPMIBmcExtern
*ibe
= opaque
;
475 * We don't directly restore waiting_rsp, Instead, we return an
476 * error on the interface if a response was being waited for.
478 if (ibe
->waiting_rsp
) {
479 IPMIInterface
*ii
= ibe
->parent
.intf
;
480 IPMIInterfaceClass
*iic
= IPMI_INTERFACE_GET_CLASS(ii
);
482 ibe
->waiting_rsp
= false;
483 ibe
->inbuf
[1] = ibe
->outbuf
[1] | 0x04;
484 ibe
->inbuf
[2] = ibe
->outbuf
[2];
485 ibe
->inbuf
[3] = IPMI_CC_BMC_INIT_IN_PROGRESS
;
486 iic
->handle_rsp(ii
, ibe
->outbuf
[0], ibe
->inbuf
+ 1, 3);
491 static const VMStateDescription vmstate_ipmi_bmc_extern
= {
492 .name
= TYPE_IPMI_BMC_EXTERN
,
494 .minimum_version_id
= 1,
495 .post_load
= ipmi_bmc_extern_post_migrate
,
496 .fields
= (VMStateField
[]) {
497 VMSTATE_BOOL(send_reset
, IPMIBmcExtern
),
498 VMSTATE_BOOL(waiting_rsp
, IPMIBmcExtern
),
499 VMSTATE_END_OF_LIST()
503 static void ipmi_bmc_extern_init(Object
*obj
)
505 IPMIBmcExtern
*ibe
= IPMI_BMC_EXTERN(obj
);
507 ibe
->extern_timer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
, extern_timeout
, ibe
);
508 vmstate_register(NULL
, 0, &vmstate_ipmi_bmc_extern
, ibe
);
511 static void ipmi_bmc_extern_finalize(Object
*obj
)
513 IPMIBmcExtern
*ibe
= IPMI_BMC_EXTERN(obj
);
515 timer_del(ibe
->extern_timer
);
516 timer_free(ibe
->extern_timer
);
519 static Property ipmi_bmc_extern_properties
[] = {
520 DEFINE_PROP_CHR("chardev", IPMIBmcExtern
, chr
),
521 DEFINE_PROP_END_OF_LIST(),
524 static void ipmi_bmc_extern_class_init(ObjectClass
*oc
, void *data
)
526 DeviceClass
*dc
= DEVICE_CLASS(oc
);
527 IPMIBmcClass
*bk
= IPMI_BMC_CLASS(oc
);
529 bk
->handle_command
= ipmi_bmc_extern_handle_command
;
530 bk
->handle_reset
= ipmi_bmc_extern_handle_reset
;
531 dc
->hotpluggable
= false;
532 dc
->realize
= ipmi_bmc_extern_realize
;
533 device_class_set_props(dc
, ipmi_bmc_extern_properties
);
536 static const TypeInfo ipmi_bmc_extern_type
= {
537 .name
= TYPE_IPMI_BMC_EXTERN
,
538 .parent
= TYPE_IPMI_BMC
,
539 .instance_size
= sizeof(IPMIBmcExtern
),
540 .instance_init
= ipmi_bmc_extern_init
,
541 .instance_finalize
= ipmi_bmc_extern_finalize
,
542 .class_init
= ipmi_bmc_extern_class_init
,
545 static void ipmi_bmc_extern_register_types(void)
547 type_register_static(&ipmi_bmc_extern_type
);
550 type_init(ipmi_bmc_extern_register_types
)