From patchwork Mon Jul 9 19:17:06 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 169929 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 884412C007A for ; Tue, 10 Jul 2012 05:20:36 +1000 (EST) Received: from localhost ([::1]:39453 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoJTj-0002ea-En for incoming@patchwork.ozlabs.org; Mon, 09 Jul 2012 15:18:39 -0400 Received: from eggs.gnu.org ([208.118.235.92]:40644) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoJT3-0000hV-I1 for qemu-devel@nongnu.org; Mon, 09 Jul 2012 15:17:59 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SoJT0-0003Nt-CM for qemu-devel@nongnu.org; Mon, 09 Jul 2012 15:17:57 -0400 Received: from vms173005pub.verizon.net ([206.46.173.5]:54577) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoJT0-0003Lh-0r for qemu-devel@nongnu.org; Mon, 09 Jul 2012 15:17:54 -0400 Received: from wf-rch.minyard.home ([unknown] [173.57.151.210]) by vms173005.mailsrvcs.net (Sun Java(tm) System Messaging Server 7u2-7.02 32bit (built Apr 16 2009)) with ESMTPA id <0M6W008GCQWWSFP8@vms173005.mailsrvcs.net> for qemu-devel@nongnu.org; Mon, 09 Jul 2012 14:17:21 -0500 (CDT) Received: from i.minyard.home (i2.minyard.home [192.168.27.116]) by wf-rch.minyard.home (Postfix) with ESMTP id 2AB881F953; Mon, 09 Jul 2012 14:17:12 -0500 (CDT) Received: by i.minyard.home (Postfix, from userid 1000) id 3E1448178B; Mon, 09 Jul 2012 14:17:12 -0500 (CDT) From: minyard@acm.org To: qemu-devel@nongnu.org Date: Mon, 09 Jul 2012 14:17:06 -0500 Message-id: <1341861429-6297-7-git-send-email-minyard@acm.org> X-Mailer: git-send-email 1.7.4.1 In-reply-to: <1341861429-6297-1-git-send-email-minyard@acm.org> References: <1341861429-6297-1-git-send-email-minyard@acm.org> X-detected-operating-system: by eggs.gnu.org: Solaris 10 (1203?) X-Received-From: 206.46.173.5 Cc: Corey Minyard Subject: [Qemu-devel] [PATCH 6/9] IPMI: Add a KCS low-level 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 This provides the simulation of the KCS hardware interface. Signed-off-by: Corey Minyard --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/Makefile.objs | 1 + hw/ipmi_kcs.c | 259 ++++++++++++++++++++++++++++++++++++ hw/ipmi_kcs.h | 82 +++++++++++ 5 files changed, 344 insertions(+), 0 deletions(-) create mode 100644 hw/ipmi_kcs.c create mode 100644 hw/ipmi_kcs.h diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index c0aff0d..b549389 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -9,6 +9,7 @@ CONFIG_VMWARE_VGA=y CONFIG_VMMOUSE=y CONFIG_IPMI=y CONFIG_ISA_IPMI=y +CONFIG_IPMI_KCS=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 615e4f2..af7d2a9 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -9,6 +9,7 @@ CONFIG_VMWARE_VGA=y CONFIG_VMMOUSE=y CONFIG_IPMI=y CONFIG_ISA_IPMI=y +CONFIG_IPMI_KCS=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 8f27ffe..99e5d1e 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -22,6 +22,7 @@ hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o hw-obj-$(CONFIG_IPMI) += ipmi.o hw-obj-$(CONFIG_ISA_IPMI) += isa_ipmi.o +hw-obj-$(CONFIG_IPMI_KCS) += ipmi_kcs.o hw-obj-$(CONFIG_SERIAL) += serial.o hw-obj-$(CONFIG_PARALLEL) += parallel.o diff --git a/hw/ipmi_kcs.c b/hw/ipmi_kcs.c new file mode 100644 index 0000000..61188c9 --- /dev/null +++ b/hw/ipmi_kcs.c @@ -0,0 +1,259 @@ +/* + * QEMU IPMI KCS 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 "ipmi_kcs.h" + +#define SET_OBF() \ + do { \ + IPMI_KCS_SET_OBF(kcs->status_reg, 1); \ + if (s->use_irq && s->irqs_enabled && !s->obf_irq_set) { \ + s->obf_irq_set = 1; \ + if (!s->atn_irq_set) \ + qemu_irq_raise(s->irq); \ + } \ + } while (0) + +static void ipmi_kcs_handle_event(IPMIState *s) +{ + IPMIKcsState *kcs = s->typeinfo; + if (kcs->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) { + if (IPMI_KCS_GET_STATE(kcs->status_reg) != IPMI_KCS_ERROR_STATE) { + kcs->waiting_rsp++; /* Invalidate the message */ + s->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR; + s->outlen = 1; + s->outpos = 0; + IPMI_KCS_SET_STATE(kcs->status_reg, IPMI_KCS_ERROR_STATE); + SET_OBF(); + } + goto out; + } + + switch (IPMI_KCS_GET_STATE(kcs->status_reg)) { + case IPMI_KCS_IDLE_STATE: + if (kcs->cmd_reg == IPMI_KCS_WRITE_START_CMD) { + IPMI_KCS_SET_STATE(kcs->status_reg, IPMI_KCS_WRITE_STATE); + kcs->cmd_reg = -1; + s->write_end = 0; + s->inlen = 0; + SET_OBF(); + } + break; + + case IPMI_KCS_READ_STATE: + handle_read: + if (s->outpos >= s->outlen) { + IPMI_KCS_SET_STATE(kcs->status_reg, IPMI_KCS_IDLE_STATE); + SET_OBF(); + } else if (kcs->data_in_reg == IPMI_KCS_READ_CMD) { + kcs->data_out_reg = s->outmsg[s->outpos]; + s->outpos++; + SET_OBF(); + } else { + s->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; + s->outlen = 1; + s->outpos = 0; + IPMI_KCS_SET_STATE(kcs->status_reg, IPMI_KCS_ERROR_STATE); + SET_OBF(); + goto out; + } + break; + + case IPMI_KCS_WRITE_STATE: + if (kcs->data_in_reg != -1) { + /* + * Don't worry about input overrun here, that will be + * handled in the BMC. + */ + if (s->inlen < sizeof(s->inmsg)) + s->inmsg[s->inlen] = kcs->data_in_reg; + s->inlen++; + } + if (s->write_end) { + s->outlen = 0; + s->write_end = 0; + s->outpos = 0; + s->handle_command(s, s->inmsg, s->inlen, sizeof(s->inmsg), + kcs->waiting_rsp); + goto out_noibf; + } else if (kcs->cmd_reg == IPMI_KCS_WRITE_END_CMD) { + kcs->cmd_reg = -1; + s->write_end = 1; + } + SET_OBF(); + break; + + case IPMI_KCS_ERROR_STATE: + if (kcs->data_in_reg != -1) { + IPMI_KCS_SET_STATE(kcs->status_reg, IPMI_KCS_READ_STATE); + kcs->data_in_reg = IPMI_KCS_READ_CMD; + goto handle_read; + } + break; + } + + if (kcs->cmd_reg != -1) { + /* Got an invalid command */ + s->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; + s->outlen = 1; + s->outpos = 0; + IPMI_KCS_SET_STATE(kcs->status_reg, IPMI_KCS_ERROR_STATE); + } + + out: + kcs->cmd_reg = -1; + kcs->data_in_reg = -1; + IPMI_KCS_SET_IBF(kcs->status_reg, 0); + out_noibf: + return; +} + +static void ipmi_kcs_handle_rsp(IPMIState *s, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len) +{ + IPMIKcsState *kcs = s->typeinfo; + + /* ipmi_lock is already claimed. */ + if (kcs->waiting_rsp == msg_id) { + kcs->waiting_rsp++; + if (rsp_len > sizeof(s->outmsg)) { + s->outmsg[0] = rsp[0]; + s->outmsg[1] = rsp[1]; + s->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + s->outlen = 3; + } else { + memcpy(s->outmsg, rsp, rsp_len); + s->outlen = rsp_len; + } + IPMI_KCS_SET_STATE(kcs->status_reg, IPMI_KCS_READ_STATE); + kcs->data_in_reg = IPMI_KCS_READ_CMD; + ipmi_signal(); + } +} + + +static uint32_t ipmi_kcs_ioport_read(void *opaque, uint32_t addr) +{ + IPMIState *s = opaque; + IPMIKcsState *kcs = s->typeinfo; + uint32_t ret; + + ipmi_lock(); + switch(addr & 1) { + case 0: + ret = kcs->data_out_reg; + IPMI_KCS_SET_OBF(kcs->status_reg, 0); + if (s->obf_irq_set) { + s->obf_irq_set = 0; + if (!s->atn_irq_set) + qemu_irq_lower(s->irq); + } + break; + case 1: + ret = kcs->status_reg; + if (s->atn_irq_set) { + s->atn_irq_set = 0; + if (!s->obf_irq_set) + qemu_irq_lower(s->irq); + } + break; + } + ipmi_unlock(); + return ret; +} + +static void ipmi_kcs_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + IPMIState *s = opaque; + IPMIKcsState *kcs = s->typeinfo; + + if (IPMI_KCS_GET_IBF(kcs->status_reg)) + return; + + ipmi_lock(); + switch(addr & 1) { + case 0: + kcs->data_in_reg = val; + break; + + case 1: + kcs->cmd_reg = val; + break; + } + IPMI_KCS_SET_IBF(kcs->status_reg, 1); + ipmi_signal(); + ipmi_unlock(); +} + +static const MemoryRegionPortio ipmi_kcs_portio[] = { + { 0, 2, 1, .read = ipmi_kcs_ioport_read, .write = ipmi_kcs_ioport_write }, + PORTIO_END_OF_LIST() +}; + +static const MemoryRegionOps ipmi_kcs_io_ops = { + .old_portio = ipmi_kcs_portio +}; + +static void ipmi_kcs_set_atn(IPMIState *s, int val, int irq) +{ + IPMIKcsState *kcs = s->typeinfo; + + IPMI_KCS_SET_SMS_ATN(kcs->status_reg, val); + if (val) { + if (irq && !s->atn_irq_set && s->use_irq && s->irqs_enabled) { + s->atn_irq_set = 1; + if (!s->obf_irq_set) + qemu_irq_raise(s->irq); + } + } else { + if (s->atn_irq_set) { + s->atn_irq_set = 0; + if (!s->obf_irq_set) + qemu_irq_lower(s->irq); + } + } +} + +static void ipmi_kcs_init(IPMIState *s) +{ + IPMIKcsState *kcs; + + s->smbios_type = IPMI_SMBIOS_KCS; + + kcs = g_malloc0(sizeof(*kcs)); + s->typeinfo = kcs; + s->set_atn = ipmi_kcs_set_atn; + s->handle_rsp = ipmi_kcs_handle_rsp; + s->handle_if_event = ipmi_kcs_handle_event; + + memory_region_init_io(&s->io, &ipmi_kcs_io_ops, s, "ipmi-kcs", 2); +} + +static void ipmi_kcs_register_types(void) +{ + register_ipmi_type(IPMI_KCS, ipmi_kcs_init); +} + +type_init(ipmi_kcs_register_types) diff --git a/hw/ipmi_kcs.h b/hw/ipmi_kcs.h new file mode 100644 index 0000000..4552158 --- /dev/null +++ b/hw/ipmi_kcs.h @@ -0,0 +1,82 @@ +/* + * QEMU IPMI KCS 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. + */ + +#ifndef HW_IPMI_KCS_H +#define HW_IPMI_KCS_H + +#define IPMI_KCS_OBF_BIT 0 +#define IPMI_KCS_IBF_BIT 1 +#define IPMI_KCS_SMS_ATN_BIT 2 +#define IPMI_KCS_CD_BIT 3 + +#define IPMI_KCS_OBF_MASK (1 << IPMI_KCS_OBF_BIT) +#define IPMI_KCS_GET_OBF(d) (((d) >> IPMI_KCS_OBF_BIT) & 0x1) +#define IPMI_KCS_SET_OBF(d,v) (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \ + (((v) & 1) << IPMI_KCS_OBF_BIT)) +#define IPMI_KCS_IBF_MASK (1 << IPMI_KCS_IBF_BIT) +#define IPMI_KCS_GET_IBF(d) (((d) >> IPMI_KCS_IBF_BIT) & 0x1) +#define IPMI_KCS_SET_IBF(d,v) (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \ + (((v) & 1) << IPMI_KCS_IBF_BIT)) +#define IPMI_KCS_SMS_ATN_MASK (1 << IPMI_KCS_SMS_ATN_BIT) +#define IPMI_KCS_GET_SMS_ATN(d) (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1) +#define IPMI_KCS_SET_SMS_ATN(d,v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \ + (((v) & 1) << IPMI_KCS_SMS_ATN_BIT)) +#define IPMI_KCS_CD_MASK (1 << IPMI_KCS_CD_BIT) +#define IPMI_KCS_GET_CD(d) (((d) >> IPMI_KCS_CD_BIT) & 0x1) +#define IPMI_KCS_SET_CD(d,v) (d) = (((d) & ~IPMI_KCS_CD_MASK) | \ + (((v) & 1) << IPMI_KCS_CD_BIT)) + +#define IPMI_KCS_IDLE_STATE 0 +#define IPMI_KCS_READ_STATE 1 +#define IPMI_KCS_WRITE_STATE 2 +#define IPMI_KCS_ERROR_STATE 3 + +#define IPMI_KCS_GET_STATE(d) (((d) >> 6) & 0x3) +#define IPMI_KCS_SET_STATE(d,v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6)) + +#define IPMI_KCS_ABORT_STATUS_CMD 0x60 +#define IPMI_KCS_WRITE_START_CMD 0x61 +#define IPMI_KCS_WRITE_END_CMD 0x62 +#define IPMI_KCS_READ_CMD 0x68 + +#define IPMI_KCS_STATUS_NO_ERR 0x00 +#define IPMI_KCS_STATUS_ABORTED_ERR 0x01 +#define IPMI_KCS_STATUS_BAD_CC_ERR 0x02 +#define IPMI_KCS_STATUS_LENGTH_ERR 0x06 + +typedef struct IPMIKcsState { + uint8_t status_reg; + uint8_t data_out_reg; + + int data_in_reg; /* -1 means not written */ + int cmd_reg; + + /* + * This is a response number that we send with the command to make + * sure that the response matches the command. + */ + uint8_t waiting_rsp; +} IPMIKcsState; + +#endif