[PATCH 4/9] Add a base IPMI interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Corey Minyard <cminyard@xxxxxxxxxx>

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@xxxxxxxxxx>
---
 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

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)
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux