From: Corey Minyard <cminyard@xxxxxxxxxx> This provides the simulation of the BT hardware interface for IPMI. Signed-off-by: Corey Minyard <cminyard@xxxxxxxxxx> --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/Makefile.objs | 1 + hw/ipmi_bt.c | 265 ++++++++++++++++++++++++++++++++++++ hw/ipmi_bt.h | 99 +++++++++++++ 5 files changed, 367 insertions(+), 0 deletions(-) create mode 100644 hw/ipmi_bt.c create mode 100644 hw/ipmi_bt.h 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 99e5d1e..e1d30cc 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..39f099e --- /dev/null +++ b/hw/ipmi_bt.c @@ -0,0 +1,265 @@ +/* + * 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" +#include "ipmi_bt.h" + +#define IPMI_CMD_GET_BT_INTF_CAP 0x36 + +static void ipmi_bt_handle_event(IPMIState *s) +{ + IPMIBtState *bt = s->typeinfo; + + ipmi_lock(); + 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]; + s->handle_command(s, s->inmsg + 2, s->inlen - 2, sizeof(s->inmsg), + bt->waiting_rsp); + out: + ipmi_unlock(); +} + +static void ipmi_bt_handle_rsp(IPMIState *s, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len) +{ + IPMIBtState *bt = s->typeinfo; + + /* 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) +{ + IPMIState *s = opaque; + IPMIBtState *bt = s->typeinfo; + uint32_t ret = 0xff; + + ipmi_lock(); + 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(); + return ret; +} + +static void ipmi_bt_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + IPMIState *s = opaque; + IPMIBtState *bt = s->typeinfo; + + ipmi_lock(); + 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(); + } + 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(); +} + +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(IPMIState *s, int val, int irq) +{ + IPMIBtState *bt = s->typeinfo; + + 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 void ipmi_bt_init(IPMIState *s) +{ + IPMIBtState *bt; + + s->smbios_type = IPMI_SMBIOS_BT; + + bt = g_malloc0(sizeof(*bt)); + s->typeinfo = bt; + s->set_atn = ipmi_bt_set_atn; + s->handle_rsp = ipmi_bt_handle_rsp; + s->handle_if_event = ipmi_bt_handle_event; + + memory_region_init_io(&s->io, &ipmi_bt_io_ops, s, "ipmi-bt", 3); +} + +static void ipmi_bt_register_types(void) +{ + register_ipmi_type(IPMI_BT, ipmi_bt_init); +} + +type_init(ipmi_bt_register_types) diff --git a/hw/ipmi_bt.h b/hw/ipmi_bt.h new file mode 100644 index 0000000..6e38105 --- /dev/null +++ b/hw/ipmi_bt.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef HW_IPMI_BT_H +#define HW_IPMI_BT_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 IPMIBtState { + 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; +} IPMIBtState; + +#endif -- 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