diff mbox

[4/9] Add a base IPMI interface

Message ID 1341861429-6297-5-git-send-email-minyard@acm.org
State New
Headers show

Commit Message

Corey Minyard July 9, 2012, 7:17 p.m. UTC
From: Corey Minyard <cminyard@mvista.com>

Add the basic IPMI types and infrastructure to QEMU.  Low-level
interfaces and simulation interfaces will register with this; it's
kind of the go-between to tie them together.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
 default-configs/i386-softmmu.mak   |    1 +
 default-configs/x86_64-softmmu.mak |    1 +
 hw/Makefile.objs                   |    2 +
 hw/ipmi.c                          |  147 +++++++++++++++++++++++++++
 hw/ipmi.h                          |  192 ++++++++++++++++++++++++++++++++++++
 qemu-doc.texi                      |    2 +
 qemu-options.hx                    |   35 +++++++
 sysemu.h                           |    8 ++
 vl.c                               |   46 +++++++++
 9 files changed, 434 insertions(+), 0 deletions(-)
 create mode 100644 hw/ipmi.c
 create mode 100644 hw/ipmi.h

Comments

Daniel P. Berrangé July 10, 2012, 9:17 a.m. UTC | #1
On Mon, Jul 09, 2012 at 02:17:04PM -0500, minyard@acm.org wrote:
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 125a4da..823f6bc 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2204,6 +2204,41 @@ Three button serial mouse. Configure the guest to use Microsoft protocol.
>  @end table
>  ETEXI
>  
> +DEF("ipmi", HAS_ARG, QEMU_OPTION_ipmi, \
> +    "-ipmi [kcs|bt,]dev|local|none  IPMI interface to the dev, or internal BMC\n",
> +    QEMU_ARCH_ALL)
> +STEXI
> +@item -ipmi [bt|kcs,]@var{dev}|local|none
> +@findex -ipmi
> +Set up an IPMI interface.  The physical interface may either be
> +KCS or BT, the default is KCS.  Two options are available for
> +simulation of the IPMI BMC.  If @code{local} is specified, then a
> +minimal internal BMC is used.  This BMC is basically useful as a
> +watchdog timer and for fooling a system into thinking IPMI is there.
> +
> +If @var{dev} is specified (see the serial section above for details on
> +what can be specified for @var{dev}) then a connection to an external IPMI
> +simulator is made.  This interface has the ability to do power control
> +and reset, so it can do the normal IPMI types of things required.
>
> +The OpenIPMI project's lanserv simulator is capable of providing
> +this interface.  It is also capable of an IPMI LAN interface, and
> +you can do power control (the lanserv simulator is capable of starting
> +a VM, too) and reset of a virtual machine over a standard remote LAN
> +interface.  For details on this, see OpenIPMI.
> +
> +The remote connection to a LAN interface will reconnect if disconnected,
> +so if a remote BMC fails and restarts, it will still be usable.
> +
> +For instance, to connect to an external interface on the local machine
> +port 9002 with a BT physical interface, do the following:
> +@table @code
> +@item -ipmi bt,tcp:localhost:9002
> +@end table
> +
> +Use @code{-ipmi none} to disable IPMI.
> +ETEXI

I tend to question the wisdom of exposing a remote accessible TCP socket
with no encryption or authentication, which can be used to shutdown/reset
QEMU instances, and who knows what other functions in the future.

While it might be claimed that one would only enable this if QEMU were
on a "trusted" management LAN, IMHO, current network threats/attacks
mean there is really no such thing as a trusted LAN anymore. So I can't
see this being practical to actually use in a production deployment.


BTW, the syntax you show here is the legacy approach where both front
and backend device config is mixed.  Does you patch work with the
modern QEMU syntax which is something like

  -chardev name=impi0,tcp:localhost:9002 -device bt,chardev=ipmi0

if it doesn't work, then you'll need to update your patches to support
this approach.

Regards,
Daniel
Paolo Bonzini July 10, 2012, 9:29 a.m. UTC | #2
Il 09/07/2012 21:17, minyard@acm.org ha scritto:
> +
> +/* Phyical interface types. */
> +#define IPMI_KCS	1
> +#define IPMI_BT		2
> +#define IPMI_SMIC	3

The code is not 100% consistent for values that are defined in hardware
specs (enums are preferred for new code, but that's not really
enforced).  However, for everything else please use enums rather than
arbitrary #defines.

Paolo
Markus Armbruster July 10, 2012, 11:12 a.m. UTC | #3
"Daniel P. Berrange" <berrange@redhat.com> writes:

> On Mon, Jul 09, 2012 at 02:17:04PM -0500, minyard@acm.org wrote:
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index 125a4da..823f6bc 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -2204,6 +2204,41 @@ Three button serial mouse. Configure the guest to use Microsoft protocol.
>>  @end table
>>  ETEXI
>>  
>> +DEF("ipmi", HAS_ARG, QEMU_OPTION_ipmi, \
>> +    "-ipmi [kcs|bt,]dev|local|none  IPMI interface to the dev, or internal BMC\n",
>> +    QEMU_ARCH_ALL)
>> +STEXI
>> +@item -ipmi [bt|kcs,]@var{dev}|local|none
>> +@findex -ipmi
>> +Set up an IPMI interface.  The physical interface may either be
>> +KCS or BT, the default is KCS.  Two options are available for
>> +simulation of the IPMI BMC.  If @code{local} is specified, then a
>> +minimal internal BMC is used.  This BMC is basically useful as a
>> +watchdog timer and for fooling a system into thinking IPMI is there.
>> +
>> +If @var{dev} is specified (see the serial section above for details on
>> +what can be specified for @var{dev}) then a connection to an external IPMI
>> +simulator is made.  This interface has the ability to do power control
>> +and reset, so it can do the normal IPMI types of things required.
>>
>> +The OpenIPMI project's lanserv simulator is capable of providing
>> +this interface.  It is also capable of an IPMI LAN interface, and
>> +you can do power control (the lanserv simulator is capable of starting
>> +a VM, too) and reset of a virtual machine over a standard remote LAN
>> +interface.  For details on this, see OpenIPMI.
>> +
>> +The remote connection to a LAN interface will reconnect if disconnected,
>> +so if a remote BMC fails and restarts, it will still be usable.
>> +
>> +For instance, to connect to an external interface on the local machine
>> +port 9002 with a BT physical interface, do the following:
>> +@table @code
>> +@item -ipmi bt,tcp:localhost:9002
>> +@end table
>> +
>> +Use @code{-ipmi none} to disable IPMI.
>> +ETEXI
[...]
> BTW, the syntax you show here is the legacy approach where both front
> and backend device config is mixed.  Does you patch work with the
> modern QEMU syntax which is something like
>
>   -chardev name=impi0,tcp:localhost:9002 -device bt,chardev=ipmi0
>
> if it doesn't work, then you'll need to update your patches to support
> this approach.

Yup.  Working -device is mandatory for new devices.  Convenience options
are optional, and whether they're worth the trouble depends.

Please introduce the convenience option -ipmi in a separate patch.
Corey Minyard July 10, 2012, 4:19 p.m. UTC | #4
On 07/10/2012 04:17 AM, Daniel P. Berrange wrote:
> On Mon, Jul 09, 2012 at 02:17:04PM -0500, minyard@acm.org wrote:
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index 125a4da..823f6bc 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -2204,6 +2204,41 @@ Three button serial mouse. Configure the guest to use Microsoft protocol.
>>   @end table
>>   ETEXI
>>   
>> +DEF("ipmi", HAS_ARG, QEMU_OPTION_ipmi, \
>> +    "-ipmi [kcs|bt,]dev|local|none  IPMI interface to the dev, or internal BMC\n",
>> +    QEMU_ARCH_ALL)
>> +STEXI
>> +@item -ipmi [bt|kcs,]@var{dev}|local|none
>> +@findex -ipmi
>> +Set up an IPMI interface.  The physical interface may either be
>> +KCS or BT, the default is KCS.  Two options are available for
>> +simulation of the IPMI BMC.  If @code{local} is specified, then a
>> +minimal internal BMC is used.  This BMC is basically useful as a
>> +watchdog timer and for fooling a system into thinking IPMI is there.
>> +
>> +If @var{dev} is specified (see the serial section above for details on
>> +what can be specified for @var{dev}) then a connection to an external IPMI
>> +simulator is made.  This interface has the ability to do power control
>> +and reset, so it can do the normal IPMI types of things required.
>>
>> +The OpenIPMI project's lanserv simulator is capable of providing
>> +this interface.  It is also capable of an IPMI LAN interface, and
>> +you can do power control (the lanserv simulator is capable of starting
>> +a VM, too) and reset of a virtual machine over a standard remote LAN
>> +interface.  For details on this, see OpenIPMI.
>> +
>> +The remote connection to a LAN interface will reconnect if disconnected,
>> +so if a remote BMC fails and restarts, it will still be usable.
>> +
>> +For instance, to connect to an external interface on the local machine
>> +port 9002 with a BT physical interface, do the following:
>> +@table @code
>> +@item -ipmi bt,tcp:localhost:9002
>> +@end table
>> +
>> +Use @code{-ipmi none} to disable IPMI.
>> +ETEXI
> I tend to question the wisdom of exposing a remote accessible TCP socket
> with no encryption or authentication, which can be used to shutdown/reset
> QEMU instances, and who knows what other functions in the future.

Actually, by default it's the other way around.  You create a server 
that takes a connection, and you connect from QEMU to the server. Still 
not perfect security, of course.

>
> While it might be claimed that one would only enable this if QEMU were
> on a "trusted" management LAN, IMHO, current network threats/attacks
> mean there is really no such thing as a trusted LAN anymore. So I can't
> see this being practical to actually use in a production deployment.

I would recommend not putting this on a LAN at all.  Just use localhost 
and use a root-only socket number.  That way, only root can run the 
server, and there's no external network access.

We debated this a bit over on the kvm list and there was no clear 
answer.  If you want a fully extensible IPMI management controller, I 
don't see putting that into QEMU.  It's big, the configuration is 
complicated, and it's limiting.  It seems more appropriate to me to make 
that external.  I could look at using ssl, but the key management will 
become a pain.

And you do have the "internal" version of a management controller. It 
doesn't do much, but if you need a secure one and you don't need a 
capable one, it would do fine.

-corey
diff mbox

Patch

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 2c78175..eb17afc 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -7,6 +7,7 @@  CONFIG_VGA_ISA=y
 CONFIG_VGA_CIRRUS=y
 CONFIG_VMWARE_VGA=y
 CONFIG_VMMOUSE=y
+CONFIG_IPMI=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 233a856..e4e3e4f 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -7,6 +7,7 @@  CONFIG_VGA_ISA=y
 CONFIG_VGA_CIRRUS=y
 CONFIG_VMWARE_VGA=y
 CONFIG_VMMOUSE=y
+CONFIG_IPMI=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 30c0b78..0d55997 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -20,6 +20,8 @@  hw-obj-$(CONFIG_M48T59) += m48t59.o
 hw-obj-$(CONFIG_ESCC) += escc.o
 hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
 
+hw-obj-$(CONFIG_IPMI) += ipmi.o
+
 hw-obj-$(CONFIG_SERIAL) += serial.o
 hw-obj-$(CONFIG_PARALLEL) += parallel.o
 hw-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
diff --git a/hw/ipmi.c b/hw/ipmi.c
new file mode 100644
index 0000000..86a097b
--- /dev/null
+++ b/hw/ipmi.c
@@ -0,0 +1,147 @@ 
+/*
+ * QEMU IPMI emulation
+ *
+ * Copyright (c) 2012 Corey Minyard, MontaVista Software, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "ipmi.h"
+#include "sysemu.h"
+#include "qmp-commands.h"
+
+static void (*ipmi_init_handlers[4])(IPMIState *s);
+
+void register_ipmi_type(unsigned int type, void (*init)(IPMIState *s))
+{
+    ipmi_init_handlers[type] = init;
+}
+
+static void (*ipmi_sim_init_handlers[2])(IPMIState *s);
+
+void register_ipmi_sim(unsigned int iftype, void (*init)(IPMIState *s))
+{
+    ipmi_sim_init_handlers[iftype] = init;
+}
+
+
+#ifdef DO_IPMI_THREAD
+static void *ipmi_thread(void *opaque)
+{
+    IPMIState *s = opaque;
+    int64_t wait_until;
+
+    ipmi_lock();
+    for (;;) {
+	qemu_cond_wait(&s->waker, &s->lock);
+	wait_until = 0;
+	while (s->do_wake) {
+	    s->do_wake = 0;
+	    s->handle_if_event(s);
+	}
+    }
+    ipmi_unlock();
+    return NULL;
+}
+#endif
+
+static int ipmi_do_hw_op(IPMIState *s, enum ipmi_op op, int checkonly)
+{
+    switch(op) {
+    case IPMI_RESET_CHASSIS:
+	if (checkonly)
+	    return 0;
+	qemu_system_reset_request();
+	return 0;
+
+    case IPMI_POWEROFF_CHASSIS:
+	if (checkonly)
+	    return 0;
+	qemu_system_powerdown_request();
+	return 0;
+
+    case IPMI_SEND_NMI:
+	if (checkonly)
+	    return 0;
+	qemu_mutex_lock_iothread();
+	qmp_inject_nmi(NULL);
+	qemu_mutex_unlock_iothread();
+	return 0;
+
+    case IPMI_POWERCYCLE_CHASSIS:
+    case IPMI_PULSE_DIAG_IRQ:
+    case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP:
+    case IPMI_POWERON_CHASSIS:
+    default:
+	return IPMI_CC_COMMAND_NOT_SUPPORTED;
+    }
+}
+
+static void ipmi_set_irq_enable(IPMIState *s, int val)
+{
+    s->irqs_enabled = val;
+}
+
+static void ipmi_reset_handler(void *opaque)
+{
+    IPMIState *s = opaque;
+
+    if (s->handle_reset)
+	s->handle_reset(s);
+}
+
+void ipmi_init(unsigned int type, IPMIState *s)
+{
+    s->do_hw_op = ipmi_do_hw_op;
+    s->set_irq_enable = ipmi_set_irq_enable;
+
+    if (!ipmi_init_handlers[type]) {
+	fprintf(stderr, "IPMI type is not supported\n");
+	abort();
+    }
+    ipmi_init_handlers[type](s);
+
+    if (!s->slave_addr)
+	s->slave_addr = 0x20;
+
+    if (s->chropts) {
+	if (!ipmi_sim_init_handlers[IPMI_SIM_EXTERNAL]) {
+	    fprintf(stderr, "IPMI: external interface requested, but"
+		    " no external interface is compiled in.\n");
+	    abort();
+	}
+	ipmi_sim_init_handlers[IPMI_SIM_EXTERNAL](s);
+    } else {
+	if (!ipmi_sim_init_handlers[IPMI_SIM_INTERNAL]) {
+	    fprintf(stderr, "IPMI: local interface requested, but"
+		    " no local interface is compiled in.\n");
+	    abort();
+	}
+	ipmi_sim_init_handlers[IPMI_SIM_INTERNAL](s);
+    }
+
+    qemu_register_reset(ipmi_reset_handler, s);
+
+#ifdef DO_IPMI_THREAD
+    qemu_mutex_init(&s->lock);
+    qemu_cond_init(&s->waker);
+    qemu_thread_create(&s->thread, ipmi_thread, s, 0);
+#endif
+}
diff --git a/hw/ipmi.h b/hw/ipmi.h
new file mode 100644
index 0000000..5517198
--- /dev/null
+++ b/hw/ipmi.h
@@ -0,0 +1,192 @@ 
+/*
+ * IPMI general stuff
+ *
+ * Copyright (c) 2012 Corey Minyard, MontaVista Software, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_IPMI_H
+#define HW_IPMI_H
+
+#include "memory.h"
+#include "qemu-common.h"
+#include "qemu-char.h"
+
+/* #define DO_IPMI_THREAD */
+
+#ifdef DO_IPMI_THREAD
+#include "qemu-thread.h"
+
+#define ipmi_lock() qemu_mutex_lock(&s->lock)
+#define ipmi_unlock() qemu_mutex_unlock(&s->lock)
+#define ipmi_signal() do { s->do_wake=1; qemu_cond_signal(&s->waker); } while(0)
+
+#else
+#define ipmi_lock() do {} while(0)
+#define ipmi_unlock() do {} while(0)
+#define ipmi_signal() \
+do {									\
+    s->do_wake = 1;							\
+    while (s->do_wake) {						\
+	s->do_wake = 0;							\
+	s->handle_if_event(s);						\
+    }									\
+} while(0)
+#endif
+
+#define MAX_IPMI_MSG_SIZE 300
+
+enum ipmi_op {
+    IPMI_RESET_CHASSIS,
+    IPMI_POWEROFF_CHASSIS,
+    IPMI_POWERON_CHASSIS,
+    IPMI_POWERCYCLE_CHASSIS,
+    IPMI_PULSE_DIAG_IRQ,
+    IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP,
+    IPMI_SEND_NMI
+};
+
+#define IPMI_CC_INVALID_CMD				0xc1
+#define IPMI_CC_COMMAND_INVALID_FOR_LUN			0xc2
+#define IPMI_CC_TIMEOUT					0xc3
+#define IPMI_CC_OUT_OF_SPACE				0xc4
+#define IPMI_CC_INVALID_RESERVATION			0xc5
+#define IPMI_CC_REQUEST_DATA_TRUNCATED			0xc6
+#define IPMI_CC_REQUEST_DATA_LENGTH_INVALID		0xc7
+#define IPMI_CC_PARM_OUT_OF_RANGE			0xc9
+#define IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES		0xca
+#define IPMI_CC_REQ_ENTRY_NOT_PRESENT			0xcb
+#define IPMI_CC_INVALID_DATA_FIELD			0xcc
+#define IPMI_CC_BMC_INIT_IN_PROGRESS			0xd2
+#define IPMI_CC_COMMAND_NOT_SUPPORTED			0xd5
+
+#define IPMI_NETFN_APP		0x06
+
+#define IPMI_DEBUG 1
+
+/* Phyical interface types. */
+#define IPMI_KCS	1
+#define IPMI_BT		2
+#define IPMI_SMIC	3
+
+#define IPMI_SMBIOS_KCS		0x01
+#define IPMI_SMBIOS_SMIC	0x02
+#define IPMI_SMBIOS_BT		0x03
+#define IPMI_SMBIOS_SSIF	0x04
+
+/* Simulation types */
+#define IPMI_SIM_INTERNAL	0
+#define IPMI_SIM_EXTERNAL	1
+
+/*
+ * Create a separate thread for the IPMI device itself.  This is a
+ * better simulation and lets the IPMI device do things asynchronously
+ * if necessary.
+ */
+
+typedef struct IPMIState {
+#ifdef DO_IPMI_THREAD
+    QemuThread thread;
+    QemuCond waker;
+    QemuMutex lock;
+#endif
+    int do_wake;
+
+    int obf_irq_set;
+    int atn_irq_set;
+    qemu_irq irq;
+    int use_irq;
+    int irqs_enabled;
+
+    unsigned int smbios_type;
+
+    MemoryRegion io;
+
+    uint8_t outmsg[MAX_IPMI_MSG_SIZE];
+    unsigned int outpos;
+    unsigned int outlen;
+
+    uint8_t inmsg[MAX_IPMI_MSG_SIZE];
+    unsigned int inlen;
+    int write_end;
+
+    unsigned char slave_addr;
+
+    /*
+     * If we have an external interface, it's chardev and options go here.
+     * Othersize chropts is NULL.
+     */
+    void *chropts; /* Really QemuOpts */
+
+    /*
+     * The interface (local simulator or external interface) puts
+     * its data here.
+     */
+    void *ifdata;
+
+    /* Called when the system resets to report to the interface. */
+    void (*handle_reset)(struct IPMIState *s);
+
+    /* The individual types (kcs, bt) store their data here. */
+    void *typeinfo;
+
+    void (*handle_if_event)(struct IPMIState *s);
+
+    /*
+     * Handle a command to the other end.  Must be called
+     * with ipmi_lock held.
+     */
+    void (*handle_command)(struct IPMIState *s,
+			   uint8_t *cmd, unsigned int cmd_len,
+			   unsigned int max_cmd_len,
+			   uint8_t msg_id);
+
+    /*
+     * The interfaces use this to perform certain ops
+     */
+    void (*set_atn)(struct IPMIState *s, int val, int irq);
+
+    /* Perform various operations on the hardware.  If checkonly is true,
+       it will return if the operation can be performed, but it will not
+       do the operation. */
+    int (*do_hw_op)(struct IPMIState *s, enum ipmi_op op, int checkonly);
+
+    /*
+     * Handle a response from the other end.  Must be called
+     * with ipmi_lock held.
+     */
+    void (*handle_rsp)(struct IPMIState *s, uint8_t msg_id,
+		       unsigned char *rsp, unsigned int rsp_len);
+
+    void (*set_irq_enable)(struct IPMIState *s, int val);
+} IPMIState;
+
+void register_ipmi_type(unsigned int type, void (*init)(IPMIState *s));
+void register_ipmi_sim(unsigned int iftype, void (*init)(IPMIState *s));
+void ipmi_init(unsigned int type, IPMIState *s);
+
+#ifdef IPMI_DEBUG
+#define ipmi_debug(fs, ...) \
+    fprintf(stderr, "IPMI (%s): " fs, __func__, ##__VA_ARGS__)
+#else
+#define ipmi_debug(fs,...)
+#endif
+
+#endif
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 0af0ff4..effa2da 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -195,6 +195,8 @@  PCI and ISA network adapters
 @item
 Serial ports
 @item
+IPMI BMC, either and internal or external one
+@item
 Creative SoundBlaster 16 sound card
 @item
 ENSONIQ AudioPCI ES1370 sound card
diff --git a/qemu-options.hx b/qemu-options.hx
index 125a4da..823f6bc 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2204,6 +2204,41 @@  Three button serial mouse. Configure the guest to use Microsoft protocol.
 @end table
 ETEXI
 
+DEF("ipmi", HAS_ARG, QEMU_OPTION_ipmi, \
+    "-ipmi [kcs|bt,]dev|local|none  IPMI interface to the dev, or internal BMC\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -ipmi [bt|kcs,]@var{dev}|local|none
+@findex -ipmi
+Set up an IPMI interface.  The physical interface may either be
+KCS or BT, the default is KCS.  Two options are available for
+simulation of the IPMI BMC.  If @code{local} is specified, then a
+minimal internal BMC is used.  This BMC is basically useful as a
+watchdog timer and for fooling a system into thinking IPMI is there.
+
+If @var{dev} is specified (see the serial section above for details on
+what can be specified for @var{dev}) then a connection to an external IPMI
+simulator is made.  This interface has the ability to do power control
+and reset, so it can do the normal IPMI types of things required.
+
+The OpenIPMI project's lanserv simulator is capable of providing
+this interface.  It is also capable of an IPMI LAN interface, and
+you can do power control (the lanserv simulator is capable of starting
+a VM, too) and reset of a virtual machine over a standard remote LAN
+interface.  For details on this, see OpenIPMI.
+
+The remote connection to a LAN interface will reconnect if disconnected,
+so if a remote BMC fails and restarts, it will still be usable.
+
+For instance, to connect to an external interface on the local machine
+port 9002 with a BT physical interface, do the following:
+@table @code
+@item -ipmi bt,tcp:localhost:9002
+@end table
+
+Use @code{-ipmi none} to disable IPMI.
+ETEXI
+
 DEF("parallel", HAS_ARG, QEMU_OPTION_parallel, \
     "-parallel dev   redirect the parallel port to char device 'dev'\n",
     QEMU_ARCH_ALL)
diff --git a/sysemu.h b/sysemu.h
index eb3da5a..b71cd7a 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -172,6 +172,14 @@  int do_pcie_aer_inject_error(Monitor *mon,
 
 extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];
 
+/* IPMI devices */
+
+#define MAX_IPMI_DEVICES 1
+
+extern int do_local_ipmi;
+extern int ipmi_types[MAX_IPMI_DEVICES];
+extern QemuOpts *ipmi_hds[MAX_IPMI_DEVICES];
+
 /* parallel ports */
 
 #define MAX_PARALLEL_PORTS 3
diff --git a/vl.c b/vl.c
index 2e59f21..ea3decd 100644
--- a/vl.c
+++ b/vl.c
@@ -120,6 +120,7 @@  int main(int argc, char **argv)
 #include "hw/xen.h"
 #include "hw/qdev.h"
 #include "hw/loader.h"
+#include "hw/ipmi.h"
 #include "bt-host.h"
 #include "net.h"
 #include "net/slirp.h"
@@ -193,6 +194,9 @@  static int no_frame = 0;
 #endif
 int no_quit = 0;
 CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+int do_local_ipmi;
+int ipmi_types[MAX_IPMI_DEVICES];
+QemuOpts *ipmi_hds[MAX_IPMI_DEVICES];
 CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
 int win2k_install_hack = 0;
@@ -1936,6 +1940,7 @@  struct device_config {
         DEV_VIRTCON,   /* -virtioconsole */
         DEV_DEBUGCON,  /* -debugcon */
         DEV_GDB,       /* -gdb, -s */
+        DEV_IPMI,      /* -ipmi        */
     } type;
     const char *cmdline;
     Location loc;
@@ -1993,6 +1998,42 @@  static int serial_parse(const char *devname)
     return 0;
 }
 
+static int ipmi_parse(const char *devname)
+{
+    static int index = 0;
+
+    if (strcmp(devname, "none") == 0)
+        return 0;
+    if (index >= MAX_IPMI_DEVICES) {
+        fprintf(stderr, "qemu: too many IPMI devices\n");
+        exit(1);
+    }
+    if (strncmp(devname, "kcs,", 4) == 0) {
+	ipmi_types[index] = IPMI_KCS;
+	devname += 4;
+    } else if (strncmp(devname, "bt,", 3) == 0) {
+	ipmi_types[index] = IPMI_BT;
+	devname += 3;
+    } else if (strncmp(devname, "smic,", 5) == 0) {
+	ipmi_types[index] = IPMI_SMIC;
+	devname += 5;
+    } else
+	ipmi_types[index] = IPMI_KCS;
+    if (strcmp(devname, "local") == 0) {
+	do_local_ipmi = 1;
+	index++;
+        return 0;
+    }
+    ipmi_hds[index] = qemu_chr_parse_compat("ipmi", devname);
+    if (!ipmi_hds[index]) {
+        fprintf(stderr, "qemu: could parse ipmi device '%s': %s\n",
+                devname, strerror(errno));
+        return -1;
+    }
+    index++;
+    return 0;
+}
+
 static int parallel_parse(const char *devname)
 {
     static int index = 0;
@@ -2875,6 +2916,9 @@  int main(int argc, char **argv, char **envp)
                     default_monitor = 0;
                 }
                 break;
+            case QEMU_OPTION_ipmi:
+		add_device_config(DEV_IPMI, optarg);
+                break;
             case QEMU_OPTION_watchdog:
                 if (watchdog) {
                     fprintf(stderr,
@@ -3520,6 +3564,8 @@  int main(int argc, char **argv, char **envp)
 
     if (foreach_device_config(DEV_SERIAL, serial_parse) < 0)
         exit(1);
+    if (foreach_device_config(DEV_IPMI, ipmi_parse) < 0)
+        exit(1);
     if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0)
         exit(1);
     if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0)