From patchwork Thu Jul 19 18:53:26 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 172041 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 543F12C00B0 for ; Fri, 20 Jul 2012 05:44:35 +1000 (EST) Received: from localhost ([::1]:36674 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SrvtG-0002sm-BB for incoming@patchwork.ozlabs.org; Thu, 19 Jul 2012 14:55:58 -0400 Received: from eggs.gnu.org ([208.118.235.92]:43847) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Srvrl-0007oz-M7 for qemu-devel@nongnu.org; Thu, 19 Jul 2012 14:54:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Srvri-0008DZ-Md for qemu-devel@nongnu.org; Thu, 19 Jul 2012 14:54:25 -0400 Received: from vms173003pub.verizon.net ([206.46.173.3]:25847) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Srvri-0008Cm-Gd for qemu-devel@nongnu.org; Thu, 19 Jul 2012 14:54:22 -0400 Received: from wf-rch.minyard.home ([unknown] [173.57.151.210]) by vms173003.mailsrvcs.net (Sun Java(tm) System Messaging Server 7u2-7.02 32bit (built Apr 16 2009)) with ESMTPA id <0M7F00FE38HMYYOC@vms173003.mailsrvcs.net> for qemu-devel@nongnu.org; Thu, 19 Jul 2012 13:53:51 -0500 (CDT) Received: from i.minyard.home (i2.minyard.home [192.168.27.116]) by wf-rch.minyard.home (Postfix) with ESMTP id A723F1F95C; Thu, 19 Jul 2012 13:53:39 -0500 (CDT) Received: by i.minyard.home (Postfix, from userid 1000) id 1A5DB81DEB; Thu, 19 Jul 2012 13:53:36 -0500 (CDT) From: minyard@acm.org To: qemu-devel@nongnu.org Date: Thu, 19 Jul 2012 13:53:26 -0500 Message-id: <1342724013-1633-12-git-send-email-minyard@acm.org> X-Mailer: git-send-email 1.7.4.1 In-reply-to: <1342724013-1633-1-git-send-email-minyard@acm.org> References: <1342724013-1633-1-git-send-email-minyard@acm.org> X-detected-operating-system: by eggs.gnu.org: Solaris 10 (1203?) X-Received-From: 206.46.173.3 Cc: Corey Minyard Subject: [Qemu-devel] [PATCH 11/18] Add a base IPMI interface X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Corey Minyard 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 --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/Makefile.objs | 2 + hw/ipmi.c | 167 +++++++++++++++++++++++++ hw/ipmi.h | 236 ++++++++++++++++++++++++++++++++++++ qemu-doc.texi | 2 + 6 files changed, 409 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 27e362a..256cfae 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..252eab0 --- /dev/null +++ b/hw/ipmi.c @@ -0,0 +1,167 @@ +/* + * 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" + +#ifdef DO_IPMI_THREAD +static void *ipmi_thread(void *opaque) +{ + IPMIInterface *s = opaque; + int64_t wait_until; + + ipmi_lock(s); + 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(s); + return NULL; +} +#endif + +static int ipmi_do_hw_op(IPMIInterface *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(IPMIInterface *s, int val) +{ + s->irqs_enabled = val; +} + +void ipmi_interface_reset(IPMIInterface *s) +{ + IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(s->bmc); + + if (bk->handle_reset) { + bk->handle_reset(s->bmc); + } +} + +int ipmi_interface_init(IPMIInterface *s) +{ + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (k->init) { + int rc = k->init(s); + if (rc) { + return rc; + } + } + + if (!s->slave_addr) { + s->slave_addr = 0x20; + } + +#ifdef DO_IPMI_THREAD + qemu_mutex_init(&s->lock); + qemu_cond_init(&s->waker); + qemu_thread_create(&s->thread, ipmi_thread, s, 0); +#endif + + return 0; +} + +static void ipmi_interface_class_init(ObjectClass *class, void *data) +{ + IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class); + + ik->do_hw_op = ipmi_do_hw_op; + ik->set_irq_enable = ipmi_set_irq_enable; +} + +static TypeInfo ipmi_interface_type_info = { + .name = TYPE_IPMI_INTERFACE, + .parent = TYPE_OBJECT, + .instance_size = sizeof(IPMIInterface), + .abstract = true, + .class_size = sizeof(IPMIInterfaceClass), + .class_init = ipmi_interface_class_init, +}; + +int ipmi_bmc_init(IPMIBmc *s) +{ + IPMIBmcClass *k = IPMI_BMC_GET_CLASS(s); + + if (k->init) { + int rc = k->init(s); + if (rc) { + return rc; + } + } + return 0; +} + +static TypeInfo ipmi_bmc_type_info = { + .name = TYPE_IPMI_BMC, + .parent = TYPE_OBJECT, + .instance_size = sizeof(IPMIBmc), + .abstract = true, + .class_size = sizeof(IPMIBmcClass), +}; + +static void ipmi_register_types(void) +{ + type_register_static(&ipmi_interface_type_info); + type_register_static(&ipmi_bmc_type_info); +} + +type_init(ipmi_register_types) diff --git a/hw/ipmi.h b/hw/ipmi.h new file mode 100644 index 0000000..66077de --- /dev/null +++ b/hw/ipmi.h @@ -0,0 +1,236 @@ +/* + * IPMI base class + * + * 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 "qdev.h" + +/* + * Create a separate thread for the IPMI interface itself. This is a + * better simulation and lets the IPMI interface do things asynchronously + * if necessary. + */ +/* #define DO_IPMI_THREAD */ + +#ifdef DO_IPMI_THREAD +#include "qemu-thread.h" + +#define ipmi_lock(s) qemu_mutex_lock(&(s)->lock) +#define ipmi_unlock(s) qemu_mutex_unlock(&(s)->lock) +#define ipmi_signal(s) \ +do { \ + (s)->do_wake = 1; \ + qemu_cond_signal(&(s)->waker); \ +} while (0) + +#else +#define ipmi_lock(s) ((s)->lockcount++) +#define ipmi_unlock(s) ((s)->lockcount--) +#define ipmi_signal(s) \ +do { \ + (s)->do_wake = 1; \ + while ((s)->do_wake) { \ + (s)->do_wake = 0; \ + (IPMI_INTERFACE_GET_CLASS(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 + +/* Specified in the SMBIOS spec. */ +#define IPMI_SMBIOS_KCS 0x01 +#define IPMI_SMBIOS_SMIC 0x02 +#define IPMI_SMBIOS_BT 0x03 +#define IPMI_SMBIOS_SSIF 0x04 + +typedef struct IPMIBmc IPMIBmc; + +/* + * An IPMI Interface, the interface for talking between the target + * and the BMC. + */ +#define TYPE_IPMI_INTERFACE "ipmi-interface" +#define IPMI_INTERFACE(obj) \ + OBJECT_CHECK(IPMIInterface, (obj), TYPE_IPMI_INTERFACE) +#define IPMI_INTERFACE_CLASS(klass) \ + OBJECT_CLASS_CHECK(IPMIInterfaceClass, (klass), TYPE_IPMI_INTERFACE) +#define IPMI_INTERFACE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IPMIInterfaceClass, (obj), TYPE_IPMI_INTERFACE) + +typedef struct IPMIInterface { + Object parent_obj; + + void *bmc; /* Really IPMIBmc */ + +#ifdef DO_IPMI_THREAD + QemuThread thread; + QemuCond waker; + QemuMutex lock; +#else + int lockcount; +#endif + int do_wake; + + int obf_irq_set; + int atn_irq_set; + qemu_irq irq; + int use_irq; + int irqs_enabled; + + unsigned long io_base; + unsigned long io_length; + + 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; +} IPMIInterface; + +typedef struct IPMIInterfaceClass { + ObjectClass parent_class; + + unsigned int smbios_type; + + int (*init)(struct IPMIInterface *s); + + /* + * 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 IPMIInterface *s, enum ipmi_op op, int checkonly); + + /* + * Enable/disable irqs on the interface when the BMC requests this. + */ + void (*set_irq_enable)(struct IPMIInterface *s, int val); + + /* + * Handle an event that occurred on the interface, generally the. + * target writing to a register. + */ + void (*handle_if_event)(struct IPMIInterface *s); + + /* + * The interfaces use this to perform certain ops + */ + void (*set_atn)(struct IPMIInterface *s, int val, int irq); + + /* + * Handle a response from the bmc. Must be called with ipmi_lock + * held. + */ + void (*handle_rsp)(struct IPMIInterface *s, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len); + +} IPMIInterfaceClass; + +int ipmi_interface_init(IPMIInterface *s); +void ipmi_interface_reset(IPMIInterface *s); + +/* + * Define a BMC simulator (or perhaps a connection to a real BMC) + */ +#define TYPE_IPMI_BMC "ipmi-bmc" +#define IPMI_BMC(obj) \ + OBJECT_CHECK(IPMIBmc, (obj), TYPE_IPMI_BMC) +#define IPMI_BMC_CLASS(klass) \ + OBJECT_CLASS_CHECK(IPMIBmcClass, (klass), TYPE_IPMI_BMC) +#define IPMI_BMC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IPMIBmcClass, (obj), TYPE_IPMI_BMC) + +struct IPMIBmc { + Object parent_obj; + + IPMIInterface *intf; + CharDriverState *chr; +}; + +typedef struct IPMIBmcClass { + ObjectClass parent_class; + + int (*init)(IPMIBmc *s); + + /* Called when the system resets to report to the bmc. */ + void (*handle_reset)(struct IPMIBmc *s); + + /* + * Handle a command to the bmc. Must be called with ipmi_lock + * held. + */ + void (*handle_command)(struct IPMIBmc *s, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id); +} IPMIBmcClass; + +int ipmi_bmc_init(IPMIBmc *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