From patchwork Sun Jul 15 20:25:09 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 171093 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 51D062C00DA for ; Mon, 16 Jul 2012 06:26:17 +1000 (EST) Received: from localhost ([::1]:46399 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SqVOR-00026b-C2 for incoming@patchwork.ozlabs.org; Sun, 15 Jul 2012 16:26:15 -0400 Received: from eggs.gnu.org ([208.118.235.92]:42551) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SqVO4-0001pf-Kl for qemu-devel@nongnu.org; Sun, 15 Jul 2012 16:25:54 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SqVO1-00084q-OP for qemu-devel@nongnu.org; Sun, 15 Jul 2012 16:25:52 -0400 Received: from vms173001pub.verizon.net ([206.46.173.1]:51391) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SqVO1-00084L-FJ for qemu-devel@nongnu.org; Sun, 15 Jul 2012 16:25:49 -0400 Received: from wf-rch.minyard.home ([unknown] [173.57.151.210]) by vms173001.mailsrvcs.net (Sun Java(tm) System Messaging Server 7u2-7.02 32bit (built Apr 16 2009)) with ESMTPA id <0M7700JMTY2AJ8XQ@vms173001.mailsrvcs.net> for qemu-devel@nongnu.org; Sun, 15 Jul 2012 15:25:23 -0500 (CDT) Received: from i.minyard.home (i2.minyard.home [192.168.27.116]) by wf-rch.minyard.home (Postfix) with ESMTP id 4CBC01F95C; Sun, 15 Jul 2012 15:25:15 -0500 (CDT) Received: by i.minyard.home (Postfix, from userid 1000) id EF49C8186D; Sun, 15 Jul 2012 15:25:12 -0500 (CDT) From: minyard@acm.org To: qemu-devel@nongnu.org Date: Sun, 15 Jul 2012 15:25:09 -0500 Message-id: <1342383911-6094-14-git-send-email-minyard@acm.org> X-Mailer: git-send-email 1.7.4.1 In-reply-to: <1342383911-6094-1-git-send-email-minyard@acm.org> References: <1342383911-6094-1-git-send-email-minyard@acm.org> X-detected-operating-system: by eggs.gnu.org: Solaris 10 (1203?) X-Received-From: 206.46.173.1 Cc: Corey Minyard Subject: [Qemu-devel] [PATCH 14/16] IPMI: Add a BT 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 BT hardware interface for IPMI. Signed-off-by: Corey Minyard --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/Makefile.objs | 1 + hw/ipmi_bt.c | 354 ++++++++++++++++++++++++++++++++++++ 4 files changed, 357 insertions(+), 0 deletions(-) create mode 100644 hw/ipmi_bt.c diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index b549389..f8f8e6d 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -10,6 +10,7 @@ CONFIG_VMMOUSE=y CONFIG_IPMI=y CONFIG_ISA_IPMI=y CONFIG_IPMI_KCS=y +CONFIG_IPMI_BT=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 af7d2a9..8c1177d 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -10,6 +10,7 @@ CONFIG_VMMOUSE=y CONFIG_IPMI=y CONFIG_ISA_IPMI=y CONFIG_IPMI_KCS=y +CONFIG_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 12cc9a2..c6f54fb 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -23,6 +23,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_IPMI_BT) += ipmi_bt.o hw-obj-$(CONFIG_SERIAL) += serial.o hw-obj-$(CONFIG_PARALLEL) += parallel.o diff --git a/hw/ipmi_bt.c b/hw/ipmi_bt.c new file mode 100644 index 0000000..d8525ef --- /dev/null +++ b/hw/ipmi_bt.c @@ -0,0 +1,354 @@ +/* + * QEMU IPMI BT 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" + +/* Control register */ +#define IPMI_BT_CLR_WR_BIT 0 +#define IPMI_BT_CLR_RD_BIT 1 +#define IPMI_BT_H2B_ATN_BIT 2 +#define IPMI_BT_B2H_ATN_BIT 3 +#define IPMI_BT_SMS_ATN_BIT 4 +#define IPMI_BT_HBUSY_BIT 6 +#define IPMI_BT_BBUSY_BIT 7 + +#define IPMI_BT_CLR_WR_MASK (1 << IPMI_BT_CLR_WR_BIT) +#define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1) +#define IPMI_BT_SET_CLR_WR(d,v) (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \ + (((v & 1) << IPMI_BT_CLR_WR_BIT))) + +#define IPMI_BT_CLR_RD_MASK (1 << IPMI_BT_CLR_RD_BIT) +#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1) +#define IPMI_BT_SET_CLR_RD(d,v) (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \ + (((v & 1) << IPMI_BT_CLR_RD_BIT))) + +#define IPMI_BT_H2B_ATN_MASK (1 << IPMI_BT_H2B_ATN_BIT) +#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1) +#define IPMI_BT_SET_H2B_ATN(d,v) (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \ + (((v & 1) << IPMI_BT_H2B_ATN_BIT))) + +#define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT) +#define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1) +#define IPMI_BT_SET_B2H_ATN(d,v) (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ + (((v & 1) << IPMI_BT_B2H_ATN_BIT))) + +#define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT) +#define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1) +#define IPMI_BT_SET_SMS_ATN(d,v) (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ + (((v & 1) << IPMI_BT_SMS_ATN_BIT))) + +#define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT) +#define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1) +#define IPMI_BT_SET_HBUSY(d,v) (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ + (((v & 1) << IPMI_BT_HBUSY_BIT))) + +#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) +#define IPMI_BT_GET_BBUSY(d) (((d) >> IPMI_BT_BBUSY_BIT) & 0x1) +#define IPMI_BT_SET_BBUSY(d,v) (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ + (((v & 1) << IPMI_BT_BBUSY_BIT))) + + +/* Mask register */ +#define IPMI_BT_B2H_IRQ_EN_BIT 0 +#define IPMI_BT_B2H_IRQ_BIT 1 + +#define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT) +#define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1) +#define IPMI_BT_SET_B2H_IRQ_EN(d,v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \ + (((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT))) + +#define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT) +#define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1) +#define IPMI_BT_SET_B2H_IRQ(d,v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ + (((v & 1) << IPMI_BT_B2H_IRQ_BIT))) + +typedef struct IPMIBtDevice { + IPMIDevice idev; + uint8_t control_reg; + uint8_t mask_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; + uint8_t waiting_seq; +} IPMIBtDevice; + +#define IPMI_CMD_GET_BT_INTF_CAP 0x36 + +static void ipmi_bt_handle_event(IPMIDevice *s) +{ + IPMIBtDevice *bt = DO_UPCAST(IPMIBtDevice, idev, s); + + ipmi_lock(s); + if (s->inlen < 4) + goto out; + /* Note that overruns are handled by handle_command */ + if (s->inmsg[0] != (s->inlen - 1)) { + /* Length mismatch, just ignore. */ + IPMI_BT_SET_BBUSY(bt->control_reg, 1); + s->inlen = 0; + goto out; + } + if ((s->inmsg[1] == (IPMI_NETFN_APP << 2)) && + (s->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { + /* We handle this one ourselves. */ + s->outmsg[0] = 9; + s->outmsg[1] = s->inmsg[1] | 0x04; + s->outmsg[2] = s->inmsg[2]; + s->outmsg[3] = s->inmsg[3]; + s->outmsg[4] = 0; + s->outmsg[5] = 1; /* Only support 1 outstanding request. */ + if (sizeof(s->inmsg) > 0xff) /* Input buffer size */ + s->outmsg[6] = 0xff; + else + s->outmsg[6] = (unsigned char ) sizeof(s->inmsg); + if (sizeof(s->outmsg) > 0xff) /* Output buffer size */ + s->outmsg[7] = 0xff; + else + s->outmsg[7] = (unsigned char) sizeof(s->outmsg); + s->outmsg[8] = 10; /* Max request to response time */ + s->outmsg[9] = 0; /* Don't recommend retries */ + s->outlen = 10; + IPMI_BT_SET_BBUSY(bt->control_reg, 0); + IPMI_BT_SET_B2H_ATN(bt->control_reg, 1); + if (s->use_irq && s->irqs_enabled && + !IPMI_BT_GET_B2H_IRQ(bt->mask_reg) && + IPMI_BT_GET_B2H_IRQ_EN(bt->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(bt->mask_reg, 1); + qemu_irq_raise(s->irq); + } + goto out; + } + bt->waiting_seq = s->inmsg[2]; + s->inmsg[2] = s->inmsg[1]; + { + IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(s->bmc); + bk->handle_command(s->bmc, s->inmsg + 2, s->inlen - 2, sizeof(s->inmsg), + bt->waiting_rsp); + } + out: + ipmi_unlock(s); +} + +static void ipmi_bt_handle_rsp(IPMIDevice *s, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len) +{ + IPMIBtDevice *bt = DO_UPCAST(IPMIBtDevice, idev, s); + + /* ipmi_lock is already claimed. */ + if (bt->waiting_rsp == msg_id) { + bt->waiting_rsp++; + if (rsp_len > (sizeof(s->outmsg) - 2)) { + s->outmsg[0] = 4; + s->outmsg[1] = rsp[0]; + s->outmsg[2] = bt->waiting_seq; + s->outmsg[3] = rsp[1]; + s->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + s->outlen = 5; + } else { + s->outmsg[0] = rsp_len + 1; + s->outmsg[1] = rsp[0]; + s->outmsg[2] = bt->waiting_seq; + memcpy(s->outmsg + 3, rsp + 1, rsp_len - 1); + s->outlen = rsp_len + 2; + } + IPMI_BT_SET_BBUSY(bt->control_reg, 0); + IPMI_BT_SET_B2H_ATN(bt->control_reg, 1); + if (s->use_irq && s->irqs_enabled && + !IPMI_BT_GET_B2H_IRQ(bt->mask_reg) && + IPMI_BT_GET_B2H_IRQ_EN(bt->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(bt->mask_reg, 1); + qemu_irq_raise(s->irq); + } + } +} + + +static uint32_t ipmi_bt_ioport_read(void *opaque, uint32_t addr) +{ + IPMIBtDevice *bt = opaque; + IPMIDevice *s = &bt->idev; + uint32_t ret = 0xff; + + ipmi_lock(s); + switch(addr & 3) { + case 0: + ret = bt->control_reg; + break; + case 1: + if (s->outpos < s->outlen) { + ret = s->outmsg[s->outpos]; + s->outpos++; + if (s->outpos == s->outlen) { + s->outpos = 0; + s->outlen = 0; + } + } else { + ret = 0xff; + } + break; + case 2: + ret = bt->mask_reg; + break; + } + ipmi_unlock(s); + return ret; +} + +static void ipmi_bt_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + IPMIBtDevice *bt = opaque; + IPMIDevice *s = &bt->idev; + + ipmi_lock(s); + switch(addr & 3) { + case 0: + if (IPMI_BT_GET_CLR_WR(val)) { + s->inlen = 0; + } + if (IPMI_BT_GET_CLR_RD(val)) { + s->outpos = 0; + } + if (IPMI_BT_GET_B2H_ATN(val)) { + IPMI_BT_SET_B2H_ATN(bt->control_reg, 0); + } + if (IPMI_BT_GET_SMS_ATN(val)) { + IPMI_BT_SET_SMS_ATN(bt->control_reg, 0); + } + if (IPMI_BT_GET_HBUSY(val)) { + /* Toggle */ + IPMI_BT_SET_HBUSY(bt->control_reg, + !IPMI_BT_GET_HBUSY(bt->control_reg)); + } + if (IPMI_BT_GET_H2B_ATN(val)) { + IPMI_BT_SET_BBUSY(bt->control_reg, 1); + ipmi_signal(s); + } + break; + + case 1: + if (s->inlen < sizeof(s->inmsg)) + s->inmsg[s->inlen] = val; + s->inlen++; + break; + + case 2: + if (IPMI_BT_GET_B2H_IRQ_EN(val) != + IPMI_BT_GET_B2H_IRQ_EN(bt->mask_reg)) { + if (IPMI_BT_GET_B2H_IRQ_EN(val)) { + if (IPMI_BT_GET_B2H_ATN(bt->control_reg) || + IPMI_BT_GET_SMS_ATN(bt->control_reg)) { + IPMI_BT_SET_B2H_IRQ(bt->mask_reg, 1); + qemu_irq_raise(s->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(bt->mask_reg, 1); + } else { + if (IPMI_BT_GET_B2H_IRQ(bt->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(bt->mask_reg, 0); + qemu_irq_lower(s->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(bt->mask_reg, 0); + } + } + if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(bt->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(bt->mask_reg, 0); + qemu_irq_lower(s->irq); + } + break; + } + ipmi_unlock(s); +} + +static const MemoryRegionPortio ipmi_bt_portio[] = { + { 0, 3, 1, .read = ipmi_bt_ioport_read, .write = ipmi_bt_ioport_write }, + PORTIO_END_OF_LIST() +}; + +static const MemoryRegionOps ipmi_bt_io_ops = { + .old_portio = ipmi_bt_portio +}; + +static void ipmi_bt_set_atn(IPMIDevice *s, int val, int irq) +{ + IPMIBtDevice *bt = DO_UPCAST(IPMIBtDevice, idev, s); + + if (!!val == IPMI_BT_GET_SMS_ATN(bt->control_reg)) + return; + + IPMI_BT_SET_SMS_ATN(bt->control_reg, val); + if (val) { + if (irq && s->use_irq && s->irqs_enabled && + !IPMI_BT_GET_B2H_ATN(bt->control_reg) && + IPMI_BT_GET_B2H_IRQ_EN(bt->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(bt->mask_reg, 1); + qemu_irq_raise(s->irq); + } + } else { + if (!IPMI_BT_GET_B2H_ATN(bt->control_reg) && + IPMI_BT_GET_B2H_IRQ(bt->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(bt->mask_reg, 0); + qemu_irq_lower(s->irq); + } + } +} + +static int ipmi_bt_init(IPMIDevice *s) +{ + if (!s->io_base) + s->io_base = 0xe4; + s->io_length = 3; + + memory_region_init_io(&s->io, &ipmi_bt_io_ops, + DO_UPCAST(IPMIBtDevice, idev, s), "ipmi-bt", 3); + + return 0; +} + +static void ipmi_bt_class_init(ObjectClass *klass, void *data) +{ + IPMIDeviceClass *k = IPMI_DEVICE_CLASS(klass); + + k->init = ipmi_bt_init; + k->smbios_type = IPMI_SMBIOS_BT; + k->set_atn = ipmi_bt_set_atn; + k->handle_rsp = ipmi_bt_handle_rsp; + k->handle_if_event = ipmi_bt_handle_event; +} + +static TypeInfo ipmi_bt_type = { + .name = "ipmi-interface-bt", + .parent = TYPE_IPMI_DEVICE, + .instance_size = sizeof(IPMIBtDevice), + .class_init = ipmi_bt_class_init, +}; + +static void ipmi_bt_register_types(void) +{ + type_register_static(&ipmi_bt_type); +} + +type_init(ipmi_bt_register_types)