From patchwork Thu Nov 12 19:02:22 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 543619 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 9DE46140271 for ; Fri, 13 Nov 2015 06:11:37 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=HdRtf4SR; dkim-atps=neutral Received: from localhost ([::1]:49296 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZwxHT-0001Wy-Hj for incoming@patchwork.ozlabs.org; Thu, 12 Nov 2015 14:11:35 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:58780) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zwx91-0001Wu-GJ for qemu-devel@nongnu.org; Thu, 12 Nov 2015 14:02:53 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Zwx8y-00052n-Tt for qemu-devel@nongnu.org; Thu, 12 Nov 2015 14:02:51 -0500 Received: from mail-pa0-x22b.google.com ([2607:f8b0:400e:c03::22b]:34081) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zwx8y-00052D-Jz for qemu-devel@nongnu.org; Thu, 12 Nov 2015 14:02:48 -0500 Received: by padhx2 with SMTP id hx2so73148454pad.1 for ; Thu, 12 Nov 2015 11:02:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=Tgq69ytTLF75nPtk+WaRpQJCl4lo9Piz8TrNiTK2vK0=; b=HdRtf4SRjYGXApVU7iU+EAS5NRGvu/JS/8WuScaBoCBZwQyK9BUu4DKPWVVTFVA4kW 7aHIlGyUf2Goo/kKh2AYxNUKj/kQO1eRx0dnkq/9jGXG85E0nZ7XJNsU5Zjh2JhmLdy0 nbzZ9MQPY9JOmGliJXnQImiOV4dV4+h3xhbxDAMWDw35lxD8mYWblzzJ3hWuC3FPC9mp kuH84sFSOqlDBD8gX3dvQnbbb2Z9tcyr59lQpo/ikbRRfOP6QI7WOWYUstsorKjTjYyT YujmfmjrJwUUCzC8OmvT6wJ6+vIgbIq/op+wU2zZIKHI3KJ8x0qc8wiyDTXxZZdaoyjM iMEg== X-Received: by 10.66.102.101 with SMTP id fn5mr25865500pab.66.1447354967852; Thu, 12 Nov 2015 11:02:47 -0800 (PST) Received: from t430.minyard.home (pool-173-57-176-17.dllstx.fios.verizon.net. [173.57.176.17]) by smtp.gmail.com with ESMTPSA id oi3sm16085510pbb.53.2015.11.12.11.02.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 12 Nov 2015 11:02:45 -0800 (PST) Received: from t430.minyard.home (t430.minyard.home [127.0.0.1]) by t430.minyard.home (8.14.7/8.14.7) with ESMTP id tACJ2gLR019722; Thu, 12 Nov 2015 13:02:42 -0600 Received: (from cminyard@localhost) by t430.minyard.home (8.14.7/8.14.7/Submit) id tACJ2glw019721; Thu, 12 Nov 2015 13:02:42 -0600 From: minyard@acm.org To: qemu-devel@nongnu.org, Paolo Bonzini , "Michael S. Tsirkin" , Igor Mammedov Date: Thu, 12 Nov 2015 13:02:22 -0600 Message-Id: <1447354953-18893-7-git-send-email-minyard@acm.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1447354953-18893-1-git-send-email-minyard@acm.org> References: <1447354953-18893-1-git-send-email-minyard@acm.org> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:400e:c03::22b Cc: Corey Minyard Subject: [Qemu-devel] [PATCH v4 06/17] 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/ipmi/Makefile.objs | 1 + hw/ipmi/isa_ipmi_bt.c | 489 +++++++++++++++++++++++++++++++++++++ 4 files changed, 492 insertions(+) create mode 100644 hw/ipmi/isa_ipmi_bt.c diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 1c24aab..1730fbb 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -13,6 +13,7 @@ CONFIG_IPMI=y CONFIG_IPMI_LOCAL=y CONFIG_IPMI_EXTERN=y CONFIG_ISA_IPMI_KCS=y +CONFIG_ISA_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 9d1f934..22a99a2 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -13,6 +13,7 @@ CONFIG_IPMI=y CONFIG_IPMI_LOCAL=y CONFIG_IPMI_EXTERN=y CONFIG_ISA_IPMI_KCS=y +CONFIG_ISA_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs index 28618c5..a90318d 100644 --- a/hw/ipmi/Makefile.objs +++ b/hw/ipmi/Makefile.objs @@ -2,3 +2,4 @@ common-obj-$(CONFIG_IPMI) += ipmi.o common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o +common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c new file mode 100644 index 0000000..70e2c0d --- /dev/null +++ b/hw/ipmi/isa_ipmi_bt.c @@ -0,0 +1,489 @@ +/* + * QEMU ISA IPMI BT emulation + * + * Copyright (c) 2015 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/hw.h" +#include "hw/ipmi/ipmi.h" +#include "hw/isa/isa.h" +#include "hw/i386/pc.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 IPMIBT { + IPMIBmc *bmc; + + bool do_wake; + + qemu_irq irq; + + uint32_t io_base; + unsigned long io_length; + MemoryRegion io; + + bool obf_irq_set; + bool atn_irq_set; + bool use_irq; + bool irqs_enabled; + + uint8_t outmsg[MAX_IPMI_MSG_SIZE]; + uint32_t outpos; + uint32_t outlen; + + uint8_t inmsg[MAX_IPMI_MSG_SIZE]; + uint32_t inlen; + + 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; +} IPMIBT; + +#define IPMI_CMD_GET_BT_INTF_CAP 0x36 + +static void ipmi_bt_handle_event(IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (ib->inlen < 4) { + goto out; + } + /* Note that overruns are handled by handle_command */ + if (ib->inmsg[0] != (ib->inlen - 1)) { + /* Length mismatch, just ignore. */ + IPMI_BT_SET_BBUSY(ib->control_reg, 1); + ib->inlen = 0; + goto out; + } + if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) && + (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { + /* We handle this one ourselves. */ + ib->outmsg[0] = 9; + ib->outmsg[1] = ib->inmsg[1] | 0x04; + ib->outmsg[2] = ib->inmsg[2]; + ib->outmsg[3] = ib->inmsg[3]; + ib->outmsg[4] = 0; + ib->outmsg[5] = 1; /* Only support 1 outstanding request. */ + if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */ + ib->outmsg[6] = 0xff; + } else { + ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg); + } + if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */ + ib->outmsg[7] = 0xff; + } else { + ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg); + } + ib->outmsg[8] = 10; /* Max request to response time */ + ib->outmsg[9] = 0; /* Don't recommend retries */ + ib->outlen = 10; + IPMI_BT_SET_BBUSY(ib->control_reg, 0); + IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); + if (ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + goto out; + } + ib->waiting_seq = ib->inmsg[2]; + ib->inmsg[2] = ib->inmsg[1]; + { + IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc); + bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2, + sizeof(ib->inmsg), ib->waiting_rsp); + } + out: + return; +} + +static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (ib->waiting_rsp == msg_id) { + ib->waiting_rsp++; + if (rsp_len > (sizeof(ib->outmsg) - 2)) { + ib->outmsg[0] = 4; + ib->outmsg[1] = rsp[0]; + ib->outmsg[2] = ib->waiting_seq; + ib->outmsg[3] = rsp[1]; + ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + ib->outlen = 5; + } else { + ib->outmsg[0] = rsp_len + 1; + ib->outmsg[1] = rsp[0]; + ib->outmsg[2] = ib->waiting_seq; + memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1); + ib->outlen = rsp_len + 2; + } + IPMI_BT_SET_BBUSY(ib->control_reg, 0); + IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); + if (ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + } +} + + +static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + uint32_t ret = 0xff; + + switch (addr & 3) { + case 0: + ret = ib->control_reg; + break; + case 1: + if (ib->outpos < ib->outlen) { + ret = ib->outmsg[ib->outpos]; + ib->outpos++; + if (ib->outpos == ib->outlen) { + ib->outpos = 0; + ib->outlen = 0; + } + } else { + ret = 0xff; + } + break; + case 2: + ret = ib->mask_reg; + break; + } + return ret; +} + +static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + ib->do_wake = 1; + while (ib->do_wake) { + ib->do_wake = 0; + iic->handle_if_event(ii); + } +} + +static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + switch (addr & 3) { + case 0: + if (IPMI_BT_GET_CLR_WR(val)) { + ib->inlen = 0; + } + if (IPMI_BT_GET_CLR_RD(val)) { + ib->outpos = 0; + } + if (IPMI_BT_GET_B2H_ATN(val)) { + IPMI_BT_SET_B2H_ATN(ib->control_reg, 0); + } + if (IPMI_BT_GET_SMS_ATN(val)) { + IPMI_BT_SET_SMS_ATN(ib->control_reg, 0); + } + if (IPMI_BT_GET_HBUSY(val)) { + /* Toggle */ + IPMI_BT_SET_HBUSY(ib->control_reg, + !IPMI_BT_GET_HBUSY(ib->control_reg)); + } + if (IPMI_BT_GET_H2B_ATN(val)) { + IPMI_BT_SET_BBUSY(ib->control_reg, 1); + ipmi_bt_signal(ib, ii); + } + break; + + case 1: + if (ib->inlen < sizeof(ib->inmsg)) { + ib->inmsg[ib->inlen] = val; + } + ib->inlen++; + break; + + case 2: + if (IPMI_BT_GET_B2H_IRQ_EN(val) != + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + if (IPMI_BT_GET_B2H_IRQ_EN(val)) { + if (IPMI_BT_GET_B2H_ATN(ib->control_reg) || + IPMI_BT_GET_SMS_ATN(ib->control_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1); + } else { + if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); + } + } + if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + break; + } +} + +static const MemoryRegionOps ipmi_bt_io_ops = { + .read = ipmi_bt_ioport_read, + .write = ipmi_bt_ioport_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) { + return; + } + + IPMI_BT_SET_SMS_ATN(ib->control_reg, val); + if (val) { + if (irq && ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_ATN(ib->control_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + } else { + if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) && + IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + } +} + +static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (is_cold) { + /* Disable the BT interrupt on reset */ + if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); + } +} + +static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + ib->irqs_enabled = val; +} + +static void ipmi_bt_init(IPMIInterface *ii, Error **errp) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + ib->io_length = 3; + + memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3); +} + +static void ipmi_bt_class_init(IPMIInterfaceClass *iic) +{ + iic->init = ipmi_bt_init; + iic->set_atn = ipmi_bt_set_atn; + iic->handle_rsp = ipmi_bt_handle_rsp; + iic->handle_if_event = ipmi_bt_handle_event; + iic->set_irq_enable = ipmi_bt_set_irq_enable; + iic->reset = ipmi_bt_handle_reset; +} + + +#define TYPE_ISA_IPMI_BT "isa-ipmi-bt" +#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \ + TYPE_ISA_IPMI_BT) + +typedef struct ISAIPMIBTDevice { + ISADevice dev; + int32 isairq; + IPMIBT bt; +} ISAIPMIBTDevice; + +static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isadev = ISA_DEVICE(dev); + ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev); + IPMIInterface *ii = IPMI_INTERFACE(dev); + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + if (!iib->bt.bmc) { + error_setg(errp, "IPMI device requires a bmc attribute to be set"); + return; + } + + iib->bt.bmc->intf = ii; + + iic->init(ii, errp); + if (*errp) + return; + + if (iib->isairq > 0) { + isa_init_irq(isadev, &iib->bt.irq, iib->isairq); + iib->bt.use_irq = 1; + } + + qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); + + isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); +} + +static void isa_ipmi_bt_init(Object *obj) +{ + ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); + + ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); +} + +static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) +{ + ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); + + return &iib->bt; +} + +static Property ipmi_isa_properties[] = { + DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4), + DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), + DEFINE_PROP_END_OF_LIST(), +}; + +static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); + + dc->realize = isa_ipmi_bt_realize; + dc->props = ipmi_isa_properties; + + iic->get_backend_data = isa_ipmi_bt_get_backend_data; + ipmi_bt_class_init(iic); +} + +static const TypeInfo isa_ipmi_bt_info = { + .name = TYPE_ISA_IPMI_BT, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISAIPMIBTDevice), + .instance_init = isa_ipmi_bt_init, + .class_init = isa_ipmi_bt_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IPMI_INTERFACE }, + { } + } +}; + +static void ipmi_register_types(void) +{ + type_register_static(&isa_ipmi_bt_info); +} + +type_init(ipmi_register_types)