* [PATCH 1/9] smbios: Add a function to directly add an entry
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
@ 2012-07-02 19:44 ` minyard
2012-07-02 19:44 ` [PATCH 2/9] pc: move SMBIOS setup to after device init minyard
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
There was no way to directly add a table entry to the SMBIOS table,
even though the BIOS supports this. So add a function to do this.
This is in preparation for the IPMI handler adding it's SMBIOS table
entry.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
hw/smbios.c | 27 +++++++++++++++++++++++++++
hw/smbios.h | 15 ++++++++-------
2 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/hw/smbios.c b/hw/smbios.c
index c57237d..98c7f99 100644
--- a/hw/smbios.c
+++ b/hw/smbios.c
@@ -178,6 +178,33 @@ static void smbios_build_type_1_fields(const char *t)
strlen(buf) + 1, buf);
}
+int smbios_table_entry_add(struct smbios_structure_header *entry)
+{
+ struct smbios_table *table;
+ struct smbios_structure_header *header;
+ unsigned int size = entry->length;
+
+ if (!smbios_entries) {
+ smbios_entries_len = sizeof(uint16_t);
+ smbios_entries = g_malloc0(smbios_entries_len);
+ }
+ smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
+ sizeof(*table) + size);
+ table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
+ table->header.type = SMBIOS_TABLE_ENTRY;
+ table->header.length = cpu_to_le16(sizeof(*table) + size);
+
+ header = (struct smbios_structure_header *)(table->data);
+ memcpy(header, entry, size);
+
+ smbios_check_collision(header->type, SMBIOS_TABLE_ENTRY);
+
+ smbios_entries_len += sizeof(*table) + size;
+ (*(uint16_t *)smbios_entries) =
+ cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+ return 0;
+}
+
int smbios_entry_add(const char *t)
{
char buf[1024];
diff --git a/hw/smbios.h b/hw/smbios.h
index 94e3641..6431a15 100644
--- a/hw/smbios.h
+++ b/hw/smbios.h
@@ -13,21 +13,22 @@
*
*/
+/* This goes at the beginning of every SMBIOS structure. */
+struct smbios_structure_header {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+} QEMU_PACKED;
+
int smbios_entry_add(const char *t);
void smbios_add_field(int type, int offset, int len, void *data);
uint8_t *smbios_get_table(size_t *length);
+int smbios_table_entry_add(struct smbios_structure_header *entry);
/*
* SMBIOS spec defined tables
*/
-/* This goes at the beginning of every SMBIOS structure. */
-struct smbios_structure_header {
- uint8_t type;
- uint8_t length;
- uint16_t handle;
-} QEMU_PACKED;
-
/* SMBIOS type 0 - BIOS Information */
struct smbios_type_0 {
struct smbios_structure_header header;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 2/9] pc: move SMBIOS setup to after device init
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
2012-07-02 19:44 ` [PATCH 1/9] smbios: Add a function to directly add an entry minyard
@ 2012-07-02 19:44 ` minyard
2012-07-02 19:44 ` [PATCH 3/9] isa: Add a way to query for a free interrupt minyard
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
Setting up the firmware interface for the SMBIOS table needs to
be done later in the process, after device initialization, so that
devices can add entries to the table.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
hw/pc.c | 22 +++++++++++++---------
hw/pc.h | 9 +++++----
hw/pc_piix.c | 12 ++++++++----
3 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/hw/pc.c b/hw/pc.c
index fb04c8b..c0acb6a 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -971,20 +971,12 @@ void pc_cpus_init(const char *cpu_model)
}
void pc_memory_init(MemoryRegion *system_memory,
- const char *kernel_filename,
- const char *kernel_cmdline,
- const char *initrd_filename,
ram_addr_t below_4g_mem_size,
ram_addr_t above_4g_mem_size,
- MemoryRegion *rom_memory,
MemoryRegion **ram_memory)
{
- int linux_boot, i;
- MemoryRegion *ram, *option_rom_mr;
+ MemoryRegion *ram;
MemoryRegion *ram_below_4g, *ram_above_4g;
- void *fw_cfg;
-
- linux_boot = (kernel_filename != NULL);
/* Allocate RAM. We allocate it as a single memory region and use
* aliases to address portions of it, mostly for backwards compatibility
@@ -1006,7 +998,17 @@ void pc_memory_init(MemoryRegion *system_memory,
memory_region_add_subregion(system_memory, 0x100000000ULL,
ram_above_4g);
}
+}
+void pc_bios_init(const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ ram_addr_t below_4g_mem_size,
+ MemoryRegion *rom_memory)
+{
+ MemoryRegion *option_rom_mr;
+ void *fw_cfg;
+ int linux_boot, i;
/* Initialize PC system firmware */
pc_system_firmware_init(rom_memory);
@@ -1019,6 +1021,8 @@ void pc_memory_init(MemoryRegion *system_memory,
option_rom_mr,
1);
+ linux_boot = (kernel_filename != NULL);
+
fw_cfg = bochs_bios_init();
rom_set_fw(fw_cfg);
diff --git a/hw/pc.h b/hw/pc.h
index 8ccf202..33ab689 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -107,13 +107,14 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
void pc_cpus_init(const char *cpu_model);
void pc_memory_init(MemoryRegion *system_memory,
- const char *kernel_filename,
- const char *kernel_cmdline,
- const char *initrd_filename,
ram_addr_t below_4g_mem_size,
ram_addr_t above_4g_mem_size,
- MemoryRegion *rom_memory,
MemoryRegion **ram_memory);
+void pc_bios_init(const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ ram_addr_t below_4g_mem_size,
+ MemoryRegion *rom_memory);
qemu_irq *pc_allocate_cpu_irq(void);
DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus);
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index fb86f27..21b4f59 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -176,10 +176,8 @@ static void pc_init1(MemoryRegion *system_memory,
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
- pc_memory_init(system_memory,
- kernel_filename, kernel_cmdline, initrd_filename,
- below_4g_mem_size, above_4g_mem_size,
- pci_enabled ? rom_memory : system_memory, &ram_memory);
+ pc_memory_init(system_memory, below_4g_mem_size, above_4g_mem_size,
+ &ram_memory);
}
gsi_state = g_malloc0(sizeof(*gsi_state));
@@ -287,6 +285,12 @@ static void pc_init1(MemoryRegion *system_memory,
if (pci_enabled) {
pc_pci_device_init(pci_bus);
}
+
+ if (!xen_enabled()) {
+ pc_bios_init(kernel_filename, kernel_cmdline, initrd_filename,
+ below_4g_mem_size,
+ pci_enabled ? rom_memory : system_memory);
+ }
}
static void pc_init_pci(ram_addr_t ram_size,
--
1.7.4.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 3/9] isa: Add a way to query for a free interrupt
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
2012-07-02 19:44 ` [PATCH 1/9] smbios: Add a function to directly add an entry minyard
2012-07-02 19:44 ` [PATCH 2/9] pc: move SMBIOS setup to after device init minyard
@ 2012-07-02 19:44 ` minyard
2012-07-02 19:44 ` [PATCH 4/9] Add a base IPMI interface minyard
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
This lets devices that don't care about their interrupt number, like
IPMI, just grab any unused interrupt.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
hw/isa-bus.c | 13 +++++++++++++
hw/isa.h | 2 ++
2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/hw/isa-bus.c b/hw/isa-bus.c
index 5a43f03..f561f21 100644
--- a/hw/isa-bus.c
+++ b/hw/isa-bus.c
@@ -71,6 +71,7 @@ qemu_irq isa_get_irq(ISADevice *dev, int isairq)
if (isairq < 0 || isairq > 15) {
hw_error("isa irq %d invalid", isairq);
}
+ isabus->irq_inuse[isairq] = 1;
return isabus->irqs[isairq];
}
@@ -82,6 +83,18 @@ void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq)
dev->nirqs++;
}
+int isa_find_free_irq(ISABus *bus)
+{
+ unsigned int i;
+
+ /* 0 and 1 are called for, 2 is the chain interrupt */
+ for (i = 3; i < ISA_NUM_IRQS; i++) {
+ if (!bus->irq_inuse[i])
+ return i;
+ }
+ return 0;
+}
+
static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport)
{
if (dev && (dev->ioport_id == 0 || ioport < dev->ioport_id)) {
diff --git a/hw/isa.h b/hw/isa.h
index f7bc4b5..9447296 100644
--- a/hw/isa.h
+++ b/hw/isa.h
@@ -28,6 +28,7 @@ struct ISABus {
BusState qbus;
MemoryRegion *address_space_io;
qemu_irq *irqs;
+ int irq_inuse[ISA_NUM_IRQS];
};
struct ISADevice {
@@ -41,6 +42,7 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io);
void isa_bus_irqs(ISABus *bus, qemu_irq *irqs);
qemu_irq isa_get_irq(ISADevice *dev, int isairq);
void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq);
+int isa_find_free_irq(ISABus *bus);
MemoryRegion *isa_address_space(ISADevice *dev);
ISADevice *isa_create(ISABus *bus, const char *name);
ISADevice *isa_try_create(ISABus *bus, const char *name);
--
1.7.4.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 4/9] Add a base IPMI interface
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
` (2 preceding siblings ...)
2012-07-02 19:44 ` [PATCH 3/9] isa: Add a way to query for a free interrupt minyard
@ 2012-07-02 19:44 ` minyard
2012-07-02 19:44 ` [PATCH 5/9] IPMI: Add a PC ISA type structure minyard
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
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 <cminyard@mvista.com>
---
default-configs/i386-softmmu.mak | 1 +
default-configs/x86_64-softmmu.mak | 1 +
hw/Makefile.objs | 2 +
hw/ipmi.c | 147 +++++++++++++++++++++++++++
hw/ipmi.h | 192 ++++++++++++++++++++++++++++++++++++
qemu-doc.texi | 2 +
qemu-options.hx | 35 +++++++
sysemu.h | 8 ++
vl.c | 46 +++++++++
9 files changed, 434 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 30c0b78..0d55997 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..86a097b
--- /dev/null
+++ b/hw/ipmi.c
@@ -0,0 +1,147 @@
+/*
+ * 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"
+
+static void (*ipmi_init_handlers[4])(IPMIState *s);
+
+void register_ipmi_type(unsigned int type, void (*init)(IPMIState *s))
+{
+ ipmi_init_handlers[type] = init;
+}
+
+static void (*ipmi_sim_init_handlers[2])(IPMIState *s);
+
+void register_ipmi_sim(unsigned int iftype, void (*init)(IPMIState *s))
+{
+ ipmi_sim_init_handlers[iftype] = init;
+}
+
+
+#ifdef DO_IPMI_THREAD
+static void *ipmi_thread(void *opaque)
+{
+ IPMIState *s = opaque;
+ int64_t wait_until;
+
+ ipmi_lock();
+ 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();
+ return NULL;
+}
+#endif
+
+static int ipmi_do_hw_op(IPMIState *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(IPMIState *s, int val)
+{
+ s->irqs_enabled = val;
+}
+
+static void ipmi_reset_handler(void *opaque)
+{
+ IPMIState *s = opaque;
+
+ if (s->handle_reset)
+ s->handle_reset(s);
+}
+
+void ipmi_init(unsigned int type, IPMIState *s)
+{
+ s->do_hw_op = ipmi_do_hw_op;
+ s->set_irq_enable = ipmi_set_irq_enable;
+
+ if (!ipmi_init_handlers[type]) {
+ fprintf(stderr, "IPMI type is not supported\n");
+ abort();
+ }
+ ipmi_init_handlers[type](s);
+
+ if (!s->slave_addr)
+ s->slave_addr = 0x20;
+
+ if (s->chropts) {
+ if (!ipmi_sim_init_handlers[IPMI_SIM_EXTERNAL]) {
+ fprintf(stderr, "IPMI: external interface requested, but"
+ " no external interface is compiled in.\n");
+ abort();
+ }
+ ipmi_sim_init_handlers[IPMI_SIM_EXTERNAL](s);
+ } else {
+ if (!ipmi_sim_init_handlers[IPMI_SIM_INTERNAL]) {
+ fprintf(stderr, "IPMI: local interface requested, but"
+ " no local interface is compiled in.\n");
+ abort();
+ }
+ ipmi_sim_init_handlers[IPMI_SIM_INTERNAL](s);
+ }
+
+ qemu_register_reset(ipmi_reset_handler, s);
+
+#ifdef DO_IPMI_THREAD
+ qemu_mutex_init(&s->lock);
+ qemu_cond_init(&s->waker);
+ qemu_thread_create(&s->thread, ipmi_thread, s, 0);
+#endif
+}
diff --git a/hw/ipmi.h b/hw/ipmi.h
new file mode 100644
index 0000000..5517198
--- /dev/null
+++ b/hw/ipmi.h
@@ -0,0 +1,192 @@
+/*
+ * IPMI general stuff
+ *
+ * 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 "qemu-char.h"
+
+/* #define DO_IPMI_THREAD */
+
+#ifdef DO_IPMI_THREAD
+#include "qemu-thread.h"
+
+#define ipmi_lock() qemu_mutex_lock(&s->lock)
+#define ipmi_unlock() qemu_mutex_unlock(&s->lock)
+#define ipmi_signal() do { s->do_wake=1; qemu_cond_signal(&s->waker); } while(0)
+
+#else
+#define ipmi_lock() do {} while(0)
+#define ipmi_unlock() do {} while(0)
+#define ipmi_signal() \
+do { \
+ s->do_wake = 1; \
+ while (s->do_wake) { \
+ s->do_wake = 0; \
+ 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
+
+/* Phyical interface types. */
+#define IPMI_KCS 1
+#define IPMI_BT 2
+#define IPMI_SMIC 3
+
+#define IPMI_SMBIOS_KCS 0x01
+#define IPMI_SMBIOS_SMIC 0x02
+#define IPMI_SMBIOS_BT 0x03
+#define IPMI_SMBIOS_SSIF 0x04
+
+/* Simulation types */
+#define IPMI_SIM_INTERNAL 0
+#define IPMI_SIM_EXTERNAL 1
+
+/*
+ * Create a separate thread for the IPMI device itself. This is a
+ * better simulation and lets the IPMI device do things asynchronously
+ * if necessary.
+ */
+
+typedef struct IPMIState {
+#ifdef DO_IPMI_THREAD
+ QemuThread thread;
+ QemuCond waker;
+ QemuMutex lock;
+#endif
+ int do_wake;
+
+ int obf_irq_set;
+ int atn_irq_set;
+ qemu_irq irq;
+ int use_irq;
+ int irqs_enabled;
+
+ unsigned int smbios_type;
+
+ 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;
+
+ /*
+ * If we have an external interface, it's chardev and options go here.
+ * Othersize chropts is NULL.
+ */
+ void *chropts; /* Really QemuOpts */
+
+ /*
+ * The interface (local simulator or external interface) puts
+ * its data here.
+ */
+ void *ifdata;
+
+ /* Called when the system resets to report to the interface. */
+ void (*handle_reset)(struct IPMIState *s);
+
+ /* The individual types (kcs, bt) store their data here. */
+ void *typeinfo;
+
+ void (*handle_if_event)(struct IPMIState *s);
+
+ /*
+ * Handle a command to the other end. Must be called
+ * with ipmi_lock held.
+ */
+ void (*handle_command)(struct IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ unsigned int max_cmd_len,
+ uint8_t msg_id);
+
+ /*
+ * The interfaces use this to perform certain ops
+ */
+ void (*set_atn)(struct IPMIState *s, int val, int irq);
+
+ /* 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 IPMIState *s, enum ipmi_op op, int checkonly);
+
+ /*
+ * Handle a response from the other end. Must be called
+ * with ipmi_lock held.
+ */
+ void (*handle_rsp)(struct IPMIState *s, uint8_t msg_id,
+ unsigned char *rsp, unsigned int rsp_len);
+
+ void (*set_irq_enable)(struct IPMIState *s, int val);
+} IPMIState;
+
+void register_ipmi_type(unsigned int type, void (*init)(IPMIState *s));
+void register_ipmi_sim(unsigned int iftype, void (*init)(IPMIState *s));
+void ipmi_init(unsigned int type, IPMIState *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
diff --git a/qemu-options.hx b/qemu-options.hx
index 125a4da..823f6bc 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2204,6 +2204,41 @@ Three button serial mouse. Configure the guest to use Microsoft protocol.
@end table
ETEXI
+DEF("ipmi", HAS_ARG, QEMU_OPTION_ipmi, \
+ "-ipmi [kcs|bt,]dev|local|none IPMI interface to the dev, or internal BMC\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -ipmi [bt|kcs,]@var{dev}|local|none
+@findex -ipmi
+Set up an IPMI interface. The physical interface may either be
+KCS or BT, the default is KCS. Two options are available for
+simulation of the IPMI BMC. If @code{local} is specified, then a
+minimal internal BMC is used. This BMC is basically useful as a
+watchdog timer and for fooling a system into thinking IPMI is there.
+
+If @var{dev} is specified (see the serial section above for details on
+what can be specified for @var{dev}) then a connection to an external IPMI
+simulator is made. This interface has the ability to do power control
+and reset, so it can do the normal IPMI types of things required.
+
+The OpenIPMI project's lanserv simulator is capable of providing
+this interface. It is also capable of an IPMI LAN interface, and
+you can do power control (the lanserv simulator is capable of starting
+a VM, too) and reset of a virtual machine over a standard remote LAN
+interface. For details on this, see OpenIPMI.
+
+The remote connection to a LAN interface will reconnect if disconnected,
+so if a remote BMC fails and restarts, it will still be usable.
+
+For instance, to connect to an external interface on the local machine
+port 9002 with a BT physical interface, do the following:
+@table @code
+@item -ipmi bt,tcp:localhost:9002
+@end table
+
+Use @code{-ipmi none} to disable IPMI.
+ETEXI
+
DEF("parallel", HAS_ARG, QEMU_OPTION_parallel, \
"-parallel dev redirect the parallel port to char device 'dev'\n",
QEMU_ARCH_ALL)
diff --git a/sysemu.h b/sysemu.h
index eb3da5a..b71cd7a 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -172,6 +172,14 @@ int do_pcie_aer_inject_error(Monitor *mon,
extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+/* IPMI devices */
+
+#define MAX_IPMI_DEVICES 1
+
+extern int do_local_ipmi;
+extern int ipmi_types[MAX_IPMI_DEVICES];
+extern QemuOpts *ipmi_hds[MAX_IPMI_DEVICES];
+
/* parallel ports */
#define MAX_PARALLEL_PORTS 3
diff --git a/vl.c b/vl.c
index 2e59f21..ea3decd 100644
--- a/vl.c
+++ b/vl.c
@@ -120,6 +120,7 @@ int main(int argc, char **argv)
#include "hw/xen.h"
#include "hw/qdev.h"
#include "hw/loader.h"
+#include "hw/ipmi.h"
#include "bt-host.h"
#include "net.h"
#include "net/slirp.h"
@@ -193,6 +194,9 @@ static int no_frame = 0;
#endif
int no_quit = 0;
CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+int do_local_ipmi;
+int ipmi_types[MAX_IPMI_DEVICES];
+QemuOpts *ipmi_hds[MAX_IPMI_DEVICES];
CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
int win2k_install_hack = 0;
@@ -1936,6 +1940,7 @@ struct device_config {
DEV_VIRTCON, /* -virtioconsole */
DEV_DEBUGCON, /* -debugcon */
DEV_GDB, /* -gdb, -s */
+ DEV_IPMI, /* -ipmi */
} type;
const char *cmdline;
Location loc;
@@ -1993,6 +1998,42 @@ static int serial_parse(const char *devname)
return 0;
}
+static int ipmi_parse(const char *devname)
+{
+ static int index = 0;
+
+ if (strcmp(devname, "none") == 0)
+ return 0;
+ if (index >= MAX_IPMI_DEVICES) {
+ fprintf(stderr, "qemu: too many IPMI devices\n");
+ exit(1);
+ }
+ if (strncmp(devname, "kcs,", 4) == 0) {
+ ipmi_types[index] = IPMI_KCS;
+ devname += 4;
+ } else if (strncmp(devname, "bt,", 3) == 0) {
+ ipmi_types[index] = IPMI_BT;
+ devname += 3;
+ } else if (strncmp(devname, "smic,", 5) == 0) {
+ ipmi_types[index] = IPMI_SMIC;
+ devname += 5;
+ } else
+ ipmi_types[index] = IPMI_KCS;
+ if (strcmp(devname, "local") == 0) {
+ do_local_ipmi = 1;
+ index++;
+ return 0;
+ }
+ ipmi_hds[index] = qemu_chr_parse_compat("ipmi", devname);
+ if (!ipmi_hds[index]) {
+ fprintf(stderr, "qemu: could parse ipmi device '%s': %s\n",
+ devname, strerror(errno));
+ return -1;
+ }
+ index++;
+ return 0;
+}
+
static int parallel_parse(const char *devname)
{
static int index = 0;
@@ -2875,6 +2916,9 @@ int main(int argc, char **argv, char **envp)
default_monitor = 0;
}
break;
+ case QEMU_OPTION_ipmi:
+ add_device_config(DEV_IPMI, optarg);
+ break;
case QEMU_OPTION_watchdog:
if (watchdog) {
fprintf(stderr,
@@ -3520,6 +3564,8 @@ int main(int argc, char **argv, char **envp)
if (foreach_device_config(DEV_SERIAL, serial_parse) < 0)
exit(1);
+ if (foreach_device_config(DEV_IPMI, ipmi_parse) < 0)
+ exit(1);
if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0)
exit(1);
if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0)
--
1.7.4.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 5/9] IPMI: Add a PC ISA type structure
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
` (3 preceding siblings ...)
2012-07-02 19:44 ` [PATCH 4/9] Add a base IPMI interface minyard
@ 2012-07-02 19:44 ` minyard
2012-07-02 19:44 ` [PATCH 6/9] IPMI: Add a KCS low-level interface minyard
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
This provides the base infrastructure to tie IPMI low-level
interfaces into a PC ISA bus.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
default-configs/i386-softmmu.mak | 1 +
default-configs/x86_64-softmmu.mak | 1 +
hw/Makefile.objs | 1 +
hw/isa_ipmi.c | 138 ++++++++++++++++++++++++++++++++++++
hw/pc.c | 12 +++
hw/pc.h | 18 +++++
hw/smbios.h | 12 +++
7 files changed, 183 insertions(+), 0 deletions(-)
create mode 100644 hw/isa_ipmi.c
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index eb17afc..c0aff0d 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -8,6 +8,7 @@ CONFIG_VGA_CIRRUS=y
CONFIG_VMWARE_VGA=y
CONFIG_VMMOUSE=y
CONFIG_IPMI=y
+CONFIG_ISA_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 e4e3e4f..615e4f2 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -8,6 +8,7 @@ CONFIG_VGA_CIRRUS=y
CONFIG_VMWARE_VGA=y
CONFIG_VMMOUSE=y
CONFIG_IPMI=y
+CONFIG_ISA_IPMI=y
CONFIG_SERIAL=y
CONFIG_PARALLEL=y
CONFIG_I8254=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 0d55997..8f27ffe 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -21,6 +21,7 @@ hw-obj-$(CONFIG_ESCC) += escc.o
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_SERIAL) += serial.o
hw-obj-$(CONFIG_PARALLEL) += parallel.o
diff --git a/hw/isa_ipmi.c b/hw/isa_ipmi.c
new file mode 100644
index 0000000..cad78b0
--- /dev/null
+++ b/hw/isa_ipmi.c
@@ -0,0 +1,138 @@
+/*
+ * QEMU ISA 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 "isa.h"
+#include "pc.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "smbios.h"
+#include "ipmi.h"
+
+
+typedef struct ISAIPMIState {
+ ISADevice dev;
+ uint32_t type;
+ uint32_t iobase;
+ uint32_t isairq;
+ uint8_t slave_addr;
+ IPMIState state;
+} ISAIPMIState;
+
+static int ipmi_isa_initfn(ISADevice *dev)
+{
+ ISAIPMIState *isa = DO_UPCAST(ISAIPMIState, dev, dev);
+ struct smbios_type_38 smb38;
+
+ if (isa->iobase == -1) {
+ /* If no I/O base is specified, set the defaults */
+ switch (isa->type) {
+ case IPMI_KCS:
+ isa->iobase = 0xca2;
+ break;
+ case IPMI_BT:
+ isa->iobase = 0xe4;
+ break;
+ case IPMI_SMIC:
+ isa->iobase = 0xca9;
+ break;
+ default:
+ fprintf(stderr, "Unknown IPMI type: %d\n", isa->type);
+ abort();
+ }
+ }
+
+ isa->state.slave_addr = isa->slave_addr;
+
+ qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 3);
+
+ ipmi_init(isa->type, &isa->state);
+
+ if (isa->isairq > 0) {
+ isa_init_irq(dev, &isa->state.irq, isa->isairq);
+ isa->state.use_irq = 1;
+ }
+
+ isa_register_ioport(dev, &isa->state.io, isa->iobase);
+
+ smb38.header.type = 38;
+ smb38.header.length = sizeof(smb38);
+ smb38.header.handle = 0x3000;
+ smb38.interface_type = isa->state.smbios_type;
+ smb38.ipmi_version = 0x20;
+ smb38.i2c_slave_addr = isa->state.slave_addr;
+ smb38.nv_storage_dev_addr = 0;
+
+ /* or 1 to set it to I/O space */
+ smb38.base_addr = isa->iobase | 1;
+
+ /* 1-byte boundaries, addr bit0=0, level triggered irq */
+ smb38.base_addr_mod_and_irq_info = 1;
+ smb38.interrupt_number = isa->isairq;
+ smbios_table_entry_add((struct smbios_structure_header *) &smb38);
+
+ return 0;
+}
+
+static Property ipmi_isa_properties[] = {
+ DEFINE_PROP_HEX32("type", ISAIPMIState, type, IPMI_KCS),
+ DEFINE_PROP_HEX32("iobase", ISAIPMIState, iobase, -1),
+ DEFINE_PROP_UINT32("irq", ISAIPMIState, isairq, 0),
+ DEFINE_PROP_UINT8("slave_addr", ISAIPMIState, slave_addr, 0),
+ DEFINE_PROP_PTR("charopts", ISAIPMIState, state.chropts),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_isa_ipmi = {
+ .name = "isa-ipmi",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(state, ISAIPMIState, 0, vmstate_isa_ipmi,
+ IPMIState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ipmi_isa_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+ ic->init = ipmi_isa_initfn;
+ dc->vmsd = &vmstate_isa_ipmi;
+ dc->props = ipmi_isa_properties;
+}
+
+static TypeInfo ipmi_isa_info = {
+ .name = "isa-ipmi",
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(ISAIPMIState),
+ .class_init = ipmi_isa_class_initfn,
+};
+
+static void ipmi_register_types(void)
+{
+ type_register_static(&ipmi_isa_info);
+}
+
+type_init(ipmi_register_types)
diff --git a/hw/pc.c b/hw/pc.c
index c0acb6a..965e053 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1173,6 +1173,18 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
fd[i] = drive_get(IF_FLOPPY, 0, i);
}
*floppy = fdctrl_init_isa(isa_bus, fd);
+
+ i = 0;
+ if (do_local_ipmi) {
+ ipmi_isa_init(isa_bus, isa_find_free_irq(isa_bus), ipmi_types[i], NULL);
+ i++;
+ }
+ for(; i < MAX_IPMI_DEVICES; i++) {
+ if (ipmi_hds[i]) {
+ ipmi_isa_init(isa_bus, isa_find_free_irq(isa_bus),
+ ipmi_types[i], ipmi_hds[i]);
+ }
+ }
}
void pc_pci_device_init(PCIBus *pci_bus)
diff --git a/hw/pc.h b/hw/pc.h
index 33ab689..5b6d947 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -223,6 +223,24 @@ static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd)
return true;
}
+/* IPMI */
+static inline bool ipmi_isa_init(ISABus *bus, int irq,
+ int type, QemuOpts *opts)
+{
+ ISADevice *dev;
+
+ dev = isa_try_create(bus, "isa-ipmi");
+ if (!dev) {
+ return false;
+ }
+ qdev_prop_set_uint32(&dev->qdev, "type", type);
+ qdev_prop_set_uint32(&dev->qdev, "irq", irq);
+ qdev_prop_set_ptr(&dev->qdev, "charopts", opts);
+ if (qdev_init(&dev->qdev) < 0) {
+ return false;
+ }
+ return true;
+}
/* pc_sysfw.c */
void pc_system_firmware_init(MemoryRegion *rom_memory);
diff --git a/hw/smbios.h b/hw/smbios.h
index 6431a15..b9e4a61 100644
--- a/hw/smbios.h
+++ b/hw/smbios.h
@@ -155,6 +155,18 @@ struct smbios_type_32 {
uint8_t boot_status;
} QEMU_PACKED;
+/* SMBIOS type 38 - IPMI Device Information */
+struct smbios_type_38 {
+ struct smbios_structure_header header;
+ uint8_t interface_type;
+ uint8_t ipmi_version;
+ uint8_t i2c_slave_addr;
+ uint8_t nv_storage_dev_addr;
+ uint64_t base_addr;
+ uint8_t base_addr_mod_and_irq_info;
+ uint8_t interrupt_number;
+} QEMU_PACKED;
+
/* SMBIOS type 127 -- End-of-table */
struct smbios_type_127 {
struct smbios_structure_header header;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 6/9] IPMI: Add a KCS low-level interface
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
` (4 preceding siblings ...)
2012-07-02 19:44 ` [PATCH 5/9] IPMI: Add a PC ISA type structure minyard
@ 2012-07-02 19:44 ` minyard
2012-07-02 19:44 ` [PATCH 7/9] IPMI: Add a BT " minyard
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
This provides the simulation of the KCS hardware interface.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
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
--
1.7.4.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 7/9] IPMI: Add a BT low-level interface
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
` (5 preceding siblings ...)
2012-07-02 19:44 ` [PATCH 6/9] IPMI: Add a KCS low-level interface minyard
@ 2012-07-02 19:44 ` minyard
2012-07-02 19:44 ` [PATCH 8/9] IPMI: Add a local BMC simulation minyard
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
This provides the simulation of the BT hardware interface for
IPMI.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
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
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 8/9] IPMI: Add a local BMC simulation
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
` (6 preceding siblings ...)
2012-07-02 19:44 ` [PATCH 7/9] IPMI: Add a BT " minyard
@ 2012-07-02 19:44 ` minyard
2012-07-02 19:44 ` [PATCH 9/9] IPMI: Add an external connection simulation interface minyard
2012-07-09 13:23 ` First shot at adding IPMI to qemu Corey Minyard
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
This provides a minimal local BMC, basically enough to comply with the
spec and provide a complete watchdog timer (including a sensor, SDR,
and event).
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
default-configs/i386-softmmu.mak | 1 +
default-configs/x86_64-softmmu.mak | 1 +
hw/Makefile.objs | 1 +
hw/ipmi_sim.c | 1273 ++++++++++++++++++++++++++++++++++++
hw/ipmi_sim.h | 270 ++++++++
5 files changed, 1546 insertions(+), 0 deletions(-)
create mode 100644 hw/ipmi_sim.c
create mode 100644 hw/ipmi_sim.h
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index f8f8e6d..8c99d5d 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -11,6 +11,7 @@ CONFIG_IPMI=y
CONFIG_ISA_IPMI=y
CONFIG_IPMI_KCS=y
CONFIG_IPMI_BT=y
+CONFIG_IPMI_LOCAL=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 8c1177d..4d01883 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -11,6 +11,7 @@ CONFIG_IPMI=y
CONFIG_ISA_IPMI=y
CONFIG_IPMI_KCS=y
CONFIG_IPMI_BT=y
+CONFIG_IPMI_LOCAL=y
CONFIG_SERIAL=y
CONFIG_PARALLEL=y
CONFIG_I8254=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index e1d30cc..193227d 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -24,6 +24,7 @@ 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_IPMI_LOCAL) += ipmi_sim.o
hw-obj-$(CONFIG_SERIAL) += serial.o
hw-obj-$(CONFIG_PARALLEL) += parallel.o
diff --git a/hw/ipmi_sim.c b/hw/ipmi_sim.c
new file mode 100644
index 0000000..6813a86
--- /dev/null
+++ b/hw/ipmi_sim.c
@@ -0,0 +1,1273 @@
+/*
+ * IPMI BMC 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 <stdio.h>
+#include <string.h>
+#include "ipmi_sim.h"
+
+static void ipmi_sim_handle_timeout(IPMIState *s);
+
+static void ipmi_gettime(struct ipmi_time *time)
+{
+ int64_t stime;
+
+ stime = get_clock_realtime();
+ time->tv_sec = stime / 1000000000LL;
+ time->tv_nsec = stime % 1000000000LL;
+}
+
+static int64_t ipmi_getmonotime(void)
+{
+ return qemu_get_clock_ns(vm_clock);
+}
+
+static void ipmi_timeout(void *opaque)
+{
+ IPMIState *s = opaque;
+
+ ipmi_sim_handle_timeout(s);
+}
+
+static void set_timestamp(IPMISimState *ss, uint8_t *ts)
+{
+ unsigned int val;
+ struct ipmi_time now;
+
+ ipmi_gettime(&now);
+ val = now.tv_sec + ss->sel.time_offset;
+ ts[0] = val & 0xff;
+ ts[1] = (val >> 8) & 0xff;
+ ts[2] = (val >> 16) & 0xff;
+ ts[3] = (val >> 24) & 0xff;
+}
+
+static void sdr_inc_reservation(IPMISdr *sdr)
+{
+ sdr->reservation++;
+ if (sdr->reservation == 0)
+ sdr->reservation = 1;
+}
+
+static int sdr_add_entry(IPMISimState *ss, const uint8_t *entry,
+ unsigned int len, uint16_t *recid)
+{
+ if ((len < 5) || (len > 255))
+ return 1;
+
+ if (entry[ss->sdr.next_free + 4] != len)
+ return 1;
+
+ if (ss->sdr.next_free + len > MAX_SDR_SIZE) {
+ ss->sdr.overflow = 1;
+ return 1;
+ }
+
+ memcpy(ss->sdr.sdr + ss->sdr.next_free, entry, len);
+ ss->sdr.sdr[ss->sdr.next_free] = ss->sdr.next_rec_id & 0xff;
+ ss->sdr.sdr[ss->sdr.next_free+1] = (ss->sdr.next_rec_id >> 8) & 0xff;
+ ss->sdr.sdr[ss->sdr.next_free+2] = 0x51; /* Conform to IPMI 1.5 spec */
+
+ if (recid)
+ *recid = ss->sdr.next_rec_id;
+ ss->sdr.next_rec_id++;
+ set_timestamp(ss, ss->sdr.last_addition);
+ ss->sdr.next_free += len;
+ sdr_inc_reservation(&ss->sdr);
+ return 0;
+}
+
+static int sdr_find_entry(IPMISdr *sdr, uint16_t recid,
+ unsigned int *retpos, uint16_t *nextrec)
+{
+ unsigned int pos = *retpos;
+
+ while (pos < sdr->next_free) {
+ uint16_t trec = sdr->sdr[pos] | (sdr->sdr[pos + 1] << 8);
+ unsigned int nextpos = pos + sdr->sdr[pos + 4];
+
+ if (trec == recid) {
+ if (nextrec) {
+ if (nextpos >= sdr->next_free)
+ *nextrec = 0xffff;
+ else
+ *nextrec = (sdr->sdr[nextpos] |
+ (sdr->sdr[nextpos + 1] << 8));
+ }
+ *retpos = pos;
+ return 0;
+ }
+ pos = nextpos;
+ }
+ return 1;
+}
+
+static void sel_inc_reservation(IPMISel *sel)
+{
+ sel->reservation++;
+ if (sel->reservation == 0)
+ sel->reservation = 1;
+}
+
+/* Returns 1 if the SEL is full and can't hold the event. */
+static int sel_add_event(IPMISimState *ss, uint8_t *event)
+{
+ event[0] = 0xff;
+ event[1] = 0xff;
+ set_timestamp(ss, event + 3);
+ if (ss->sel.next_free == MAX_SEL_SIZE) {
+ ss->sel.overflow = 1;
+ return 1;
+ }
+ event[0] = ss->sel.next_free & 0xff;
+ event[1] = (ss->sel.next_free >> 8) & 0xff;
+ memcpy(ss->sel.last_addition, event + 3, 4);
+ memcpy(ss->sel.sel[ss->sel.next_free], event, 16);
+ ss->sel.next_free++;
+ sel_inc_reservation(&ss->sel);
+ return 0;
+}
+
+static int attn_irq_enabled(IPMISimState *s)
+{
+ return (IPMI_BMC_MSG_INTS_ON(s) && IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s))
+ || (IPMI_BMC_EVBUF_FULL_INT_ENABLED(s) &&
+ IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(s));
+}
+
+static void gen_event(IPMIState *s, unsigned int sens_num, uint8_t deassert,
+ uint8_t evd1, uint8_t evd2, uint8_t evd3)
+{
+ IPMISimState *ss = s->ifdata;
+ uint8_t evt[16];
+ IPMISensor *sens = ss->sensors + sens_num;
+
+ if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ss))
+ return;
+ if (!IPMI_SENSOR_GET_EVENTS_ON(sens))
+ return;
+
+ evt[2] = 0x2; /* System event record */
+ evt[7] = s->slave_addr;
+ evt[8] = 0;
+ evt[9] = 0x04; /* Format version */
+ evt[10] = sens->sensor_type;
+ evt[11] = sens_num;
+ evt[12] = sens->evt_reading_type_code | (!!deassert << 7);
+ evt[13] = evd1;
+ evt[14] = evd2;
+ evt[15] = evd3;
+
+ if (IPMI_BMC_EVENT_LOG_ENABLED(ss))
+ sel_add_event(ss, evt);
+
+ if (ss->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)
+ goto out;
+
+ ss->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL;
+ memcpy(ss->evtbuf, evt, 16);
+ if (s->set_atn &&
+ ~(ss->msg_flags & IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK))
+ s->set_atn(s, 1, attn_irq_enabled(ss));
+ out:
+ return;
+}
+
+static void sensor_set_discrete_bit(IPMIState *s, unsigned int sensor,
+ unsigned int bit, unsigned int val,
+ uint8_t evd1, uint8_t evd2, uint8_t evd3)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMISensor *sens;
+ uint16_t mask;
+
+ if (sensor >= MAX_SENSORS)
+ return;
+ if (bit >= 16)
+ return;
+
+ mask = (1 << bit);
+ sens = ss->sensors + sensor;
+ if (val) {
+ sens->states |= mask & sens->states_suppt;
+ if (sens->assert_states & mask)
+ return; /* Already asserted */
+ sens->assert_states |= mask & sens->assert_suppt;
+ if (sens->assert_enable & mask & sens->assert_states) {
+ /* Send an event on assert */
+ gen_event(s, sensor, 0, evd1, evd2, evd3);
+ }
+ } else {
+ sens->states &= ~(mask & sens->states_suppt);
+ if (sens->deassert_states & mask)
+ return; /* Already deasserted */
+ sens->deassert_states |= mask & sens->deassert_suppt;
+ if (sens->deassert_enable & mask & sens->deassert_states) {
+ /* Send an event on deassert */
+ gen_event(s, sensor, 1, evd1, evd2, evd3);
+ }
+ }
+}
+
+static void ipmi_init_sensors_from_sdrs(IPMISimState *s)
+{
+ unsigned int i, pos;
+ IPMISensor *sens;
+
+ for (i = 0; i < MAX_SENSORS; i++)
+ memset(s->sensors + i, 0, sizeof(*sens));
+
+ pos = 0;
+ for (i = 0; !sdr_find_entry(&s->sdr, i, &pos, NULL); i++) {
+ uint8_t *sdr = s->sdr.sdr + pos;
+ unsigned int len = sdr[4];
+
+ if (len < 20)
+ continue;
+ if ((sdr[3] < 1) || (sdr[3] > 2))
+ continue; /* Not a sensor SDR we set from */
+
+ if (sdr[7] > MAX_SENSORS)
+ continue;
+ sens = s->sensors + sdr[7];
+
+ IPMI_SENSOR_SET_PRESENT(sens, 1);
+ IPMI_SENSOR_SET_SCAN_ON(sens, (sdr[10] >> 6) & 1);
+ IPMI_SENSOR_SET_EVENTS_ON(sens, (sdr[10] >> 5) & 1);
+ sens->assert_suppt = sdr[14] | (sdr[15] << 8);
+ sens->deassert_suppt = sdr[16] | (sdr[17] << 8);
+ sens->states_suppt = sdr[18] | (sdr[19] << 8);
+ sens->sensor_type = sdr[12];
+ sens->evt_reading_type_code = sdr[13] & 0x7f;
+
+ /* Enable all the events that are supported. */
+ sens->assert_enable = sens->assert_suppt;
+ sens->deassert_enable = sens->deassert_suppt;
+ }
+}
+
+int ipmi_register_netfn(IPMISimState *s, unsigned int netfn, IPMINetfn *netfnd)
+{
+ if ((netfn & 1) || (netfn > MAX_NETFNS) || (s->netfns[netfn / 2]))
+ return -1;
+ s->netfns[netfn / 2] = netfnd;
+ return 0;
+}
+
+int ipmi_register_cmd(IPMISimState *s, unsigned int netfn, unsigned int cmd,
+ IPMICmdHandler handler)
+{
+ if ((netfn | 1) || (netfn > MAX_NETFNS) || (!s->netfns[netfn / 2]))
+ return -1;
+ if (cmd >= s->netfns[netfn / 2]->cmd_nums)
+ return -1;
+ if (s->netfns[netfn / 2]->cmd_handlers[cmd])
+ return -1;
+ s->netfns[netfn / 2]->cmd_handlers[cmd] = handler;
+ return 0;
+}
+
+static void next_timeout(IPMISimState *ss)
+{
+ int64_t next;
+ if (ss->watchdog_running) {
+ next = ss->watchdog_expiry;
+ } else {
+ /* Wait a minute */
+ next = ipmi_getmonotime() + 60 * 1000000000LL;
+ }
+ qemu_mod_timer_ns(ss->timer, next);
+}
+
+static void ipmi_sim_handle_command(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ unsigned int max_cmd_len,
+ uint8_t msg_id)
+{
+ IPMISimState *ss = s->ifdata;
+ unsigned int netfn;
+ uint8_t rsp[MAX_IPMI_MSG_SIZE];
+ unsigned int rsp_len_holder =- 0;
+ unsigned int *rsp_len = &rsp_len_holder;
+ unsigned int max_rsp_len = sizeof(rsp);
+
+ /* Set up the response, set the low bit of NETFN. */
+ /* Note that max_rsp_len must be at least 3 */
+ IPMI_ADD_RSP_DATA(cmd[0] | 0x04);
+ IPMI_ADD_RSP_DATA(cmd[1]);
+ IPMI_ADD_RSP_DATA(0); /* Assume success */
+
+ /* If it's too short or it was truncated, return an error. */
+ if (cmd_len < 2) {
+ rsp[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
+ goto out;
+ }
+ if (cmd_len > max_cmd_len) {
+ rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED;
+ goto out;
+ }
+
+ if ((cmd[0] & 0x03) != 0) {
+ /* Only have stuff on LUN 0 */
+ rsp[2] = IPMI_CC_COMMAND_INVALID_FOR_LUN;
+ goto out;
+ }
+
+ netfn = cmd[0] >> 2;
+
+ /* Odd netfns are not valid, make sure the command is registered */
+ if ((netfn & 1) || !ss->netfns[netfn / 2] ||
+ (cmd[1] >= ss->netfns[netfn / 2]->cmd_nums) ||
+ (!ss->netfns[netfn / 2]->cmd_handlers[cmd[1]])) {
+ rsp[2] = IPMI_CC_INVALID_CMD;
+ goto out;
+ }
+
+ ss->netfns[netfn / 2]->cmd_handlers[cmd[1]](s, cmd, cmd_len, rsp, rsp_len,
+ max_rsp_len);
+
+ out:
+ s->handle_rsp(s, msg_id, rsp, *rsp_len);
+
+ next_timeout(ss);
+}
+
+static void ipmi_sim_handle_timeout(IPMIState *s)
+{
+ IPMISimState *ss = s->ifdata;
+
+ if (!ss->watchdog_running)
+ goto out;
+
+ if (! ss->watchdog_preaction_ran) {
+ switch (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ss)) {
+ case IPMI_BMC_WATCHDOG_PRE_NMI:
+ ss->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK;
+ s->do_hw_op(s, IPMI_SEND_NMI, 0);
+ sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 8, 1,
+ 0xc8, (2 << 4) | 0xf, 0xff);
+ break;
+
+ case IPMI_BMC_WATCHDOG_PRE_MSG_INT:
+ ss->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK;
+ if (s->set_atn &&
+ !(ss->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL))
+ s->set_atn(s, 1, attn_irq_enabled(ss));
+ sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 8, 1,
+ 0xc8, (3 << 4) | 0xf, 0xff);
+ break;
+
+ default:
+ goto do_full_expiry;
+ }
+
+ ss->watchdog_preaction_ran = 1;
+ /* Issued the pretimeout, do the rest of the timeout now. */
+ ss->watchdog_expiry = ipmi_getmonotime();
+ ss->watchdog_expiry += ss->watchdog_pretimeout * 1000000000LL;
+ goto out;
+ }
+
+ do_full_expiry:
+ ss->watchdog_running = 0; /* Stop the watchdog on a timeout */
+ ss->watchdog_expired |= (1 << IPMI_BMC_WATCHDOG_GET_USE(ss));
+ switch (IPMI_BMC_WATCHDOG_GET_ACTION(ss)) {
+ case IPMI_BMC_WATCHDOG_ACTION_NONE:
+ sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 0, 1,
+ 0xc0, ss->watchdog_use & 0xf, 0xff);
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_RESET:
+ sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 1, 1,
+ 0xc1, ss->watchdog_use & 0xf, 0xff);
+ s->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN:
+ sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 2, 1,
+ 0xc2, ss->watchdog_use & 0xf, 0xff);
+ s->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE:
+ sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 2, 1,
+ 0xc3, ss->watchdog_use & 0xf, 0xff);
+ s->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0);
+ break;
+ }
+
+ out:
+ next_timeout(ss);
+}
+
+static void chassis_capabilities(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMI_ADD_RSP_DATA(0);
+ IPMI_ADD_RSP_DATA(s->slave_addr);
+ IPMI_ADD_RSP_DATA(s->slave_addr);
+ IPMI_ADD_RSP_DATA(s->slave_addr);
+ IPMI_ADD_RSP_DATA(s->slave_addr);
+ out:
+ return;
+}
+
+static void chassis_status(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMI_ADD_RSP_DATA(0x61); /* Unknown power restore, power is on */
+ IPMI_ADD_RSP_DATA(0);
+ IPMI_ADD_RSP_DATA(0);
+ IPMI_ADD_RSP_DATA(0);
+ out:
+ return;
+}
+
+static void chassis_control(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMI_CHECK_CMD_LEN(3);
+ switch (cmd[2] & 0xf) {
+ case 0: /* power down */
+ rsp[2] = s->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
+ break;
+ case 1: /* power up */
+ rsp[2] = s->do_hw_op(s, IPMI_POWERON_CHASSIS, 0);
+ break;
+ case 2: /* power cycle */
+ rsp[2] = s->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0);
+ break;
+ case 3: /* hard reset */
+ rsp[2] = s->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
+ break;
+ case 4: /* pulse diagnostic interrupt */
+ rsp[2] = s->do_hw_op(s, IPMI_PULSE_DIAG_IRQ, 0);
+ break;
+ case 5: /* soft shutdown via ACPI by overtemp emulation */
+ rsp[2] = s->do_hw_op(s,
+ IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 0);
+ break;
+ default:
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ out:
+ return;
+}
+
+static void get_device_id(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_ADD_RSP_DATA(ss->device_id);
+ IPMI_ADD_RSP_DATA(ss->device_rev & 0xf);
+ IPMI_ADD_RSP_DATA(ss->fwrev1 & 0x7f);
+ IPMI_ADD_RSP_DATA(ss->fwrev2);
+ IPMI_ADD_RSP_DATA(ss->ipmi_version);
+ IPMI_ADD_RSP_DATA(0x07); /* sensor, SDR, and SEL. */
+ IPMI_ADD_RSP_DATA(ss->mfg_id[0]);
+ IPMI_ADD_RSP_DATA(ss->mfg_id[1]);
+ IPMI_ADD_RSP_DATA(ss->mfg_id[2]);
+ IPMI_ADD_RSP_DATA(ss->product_id[0]);
+ IPMI_ADD_RSP_DATA(ss->product_id[1]);
+ out:
+ return;
+}
+
+static void set_bmc_global_enables(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ uint8_t old_val = ss->bmc_global_enables;
+ IPMI_CHECK_CMD_LEN(3);
+ ss->bmc_global_enables = cmd[2];
+ if ((old_val & (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT))
+ != (cmd[2] & (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT))) {
+ s->set_irq_enable(s,
+ !!(cmd[2] & (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT)));
+ }
+ out:
+ return;
+}
+
+static void get_bmc_global_enables(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_ADD_RSP_DATA(ss->bmc_global_enables);
+ out:
+ return;
+}
+
+static void clr_msg_flags(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_CHECK_CMD_LEN(3);
+ if (cmd[2] & IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK) {
+ if (s->set_atn &&
+ !(ss->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL))
+ s->set_atn(s, 0, attn_irq_enabled(ss));
+ }
+ ss->msg_flags &= ~cmd[2];
+ out:
+ return;
+}
+
+static void get_msg_flags(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_ADD_RSP_DATA(ss->msg_flags);
+ out:
+ return;
+}
+
+static void read_evt_msg_buf(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ unsigned int i;
+
+ if (!(ss->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)) {
+ rsp[2] = 0x80;
+ goto out;
+ }
+ for (i = 0; i < 16; i++)
+ IPMI_ADD_RSP_DATA(ss->evtbuf[i]);
+ ss->msg_flags &= ~IPMI_BMC_MSG_FLAG_EVT_BUF_FULL;
+ if (s->set_atn &&
+ ~(ss->msg_flags & IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK))
+ s->set_atn(s, 0, attn_irq_enabled(ss));
+ out:
+ return;
+}
+
+static void do_watchdog_reset(IPMISimState *ss)
+{
+ if (IPMI_BMC_WATCHDOG_GET_ACTION(ss) ==
+ IPMI_BMC_WATCHDOG_ACTION_NONE) {
+ ss->watchdog_running = 0;
+ return;
+ }
+ ss->watchdog_preaction_ran = 0;
+
+
+ /* Timeout is in tenths of a second, offset is in seconds */
+ ss->watchdog_expiry = ipmi_getmonotime();
+ ss->watchdog_expiry += ss->watchdog_timeout * 100000000LL;
+ if (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ss) != IPMI_BMC_WATCHDOG_PRE_NONE) {
+ ss->watchdog_expiry -= ss->watchdog_pretimeout * 1000000000LL;
+ }
+ ss->watchdog_running = 1;
+}
+
+static void reset_watchdog_timer(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ if (!ss->watchdog_initialized) {
+ rsp[2] = 0x80;
+ goto out;
+ }
+ do_watchdog_reset(ss);
+ out:
+ return;
+}
+
+static void set_watchdog_timer(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ unsigned int val;
+
+ IPMI_CHECK_CMD_LEN(8);
+ val = cmd[2] & 0x7; /* Validate use */
+ if (val == 0 || val > 5) {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ val = cmd[3] & 0x7; /* Validate action */
+ switch (val) {
+ case IPMI_BMC_WATCHDOG_ACTION_NONE:
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_RESET:
+ rsp[2] = s->do_hw_op(s, IPMI_RESET_CHASSIS, 1);
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN:
+ rsp[2] = s->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1);
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE:
+ rsp[2] = s->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 1);
+ break;
+
+ default:
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ }
+ if (rsp[2]) {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+
+ val = (cmd[3] >> 4) & 0x7; /* Validate preaction */
+ switch (val) {
+ case IPMI_BMC_WATCHDOG_PRE_MSG_INT:
+ case IPMI_BMC_WATCHDOG_PRE_NONE:
+ break;
+
+ case IPMI_BMC_WATCHDOG_PRE_NMI:
+ if (!s->do_hw_op(s, IPMI_SEND_NMI, 1)) {
+ /* NMI not supported. */
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ default:
+ /* We don't support PRE_SMI */
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+
+ ss->watchdog_initialized = 1;
+ ss->watchdog_use = cmd[2] & IPMI_BMC_WATCHDOG_USE_MASK;
+ ss->watchdog_action = cmd[3] & IPMI_BMC_WATCHDOG_ACTION_MASK;
+ ss->watchdog_pretimeout = cmd[4];
+ ss->watchdog_expired &= ~cmd[5];
+ ss->watchdog_timeout = cmd[6] | (((uint16_t) cmd[7]) << 8);
+ if (ss->watchdog_running & IPMI_BMC_WATCHDOG_GET_DONT_STOP(ss))
+ do_watchdog_reset(ss);
+ else
+ ss->watchdog_running = 0;
+ out:
+ return;
+}
+
+static void get_watchdog_timer(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_ADD_RSP_DATA(ss->watchdog_use);
+ IPMI_ADD_RSP_DATA(ss->watchdog_action);
+ IPMI_ADD_RSP_DATA(ss->watchdog_pretimeout);
+ IPMI_ADD_RSP_DATA(ss->watchdog_expired);
+ if (ss->watchdog_running) {
+ long timeout;
+ timeout = ((ss->watchdog_expiry - ipmi_getmonotime() + 50000000)
+ / 100000000);
+ IPMI_ADD_RSP_DATA(timeout & 0xff);
+ IPMI_ADD_RSP_DATA((timeout >> 8) & 0xff);
+ } else {
+ IPMI_ADD_RSP_DATA(0);
+ IPMI_ADD_RSP_DATA(0);
+ }
+ out:
+ return;
+}
+
+static void get_sdr_rep_info(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ unsigned int i;
+
+ IPMI_ADD_RSP_DATA(0x51); /* Conform to IPMI 1.5 spec */
+ IPMI_ADD_RSP_DATA(ss->sdr.next_rec_id & 0xff);
+ IPMI_ADD_RSP_DATA((ss->sdr.next_rec_id >> 8) & 0xff);
+ IPMI_ADD_RSP_DATA((MAX_SDR_SIZE - ss->sdr.next_free) & 0xff);
+ IPMI_ADD_RSP_DATA(((MAX_SDR_SIZE - ss->sdr.next_free) >> 8) & 0xff);
+ for (i = 0; i < 4; i++)
+ IPMI_ADD_RSP_DATA(ss->sdr.last_addition[i]);
+ for (i = 0; i < 4; i++)
+ IPMI_ADD_RSP_DATA(ss->sdr.last_clear[i]);
+ /* Only modal support, reserve supported */
+ IPMI_ADD_RSP_DATA((ss->sdr.overflow << 7) | 0x22);
+ out:
+ return;
+}
+
+static void reserve_sdr_rep(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_ADD_RSP_DATA(ss->sdr.reservation & 0xff);
+ IPMI_ADD_RSP_DATA((ss->sdr.reservation >> 8) & 0xff);
+ out:
+ return;
+}
+
+static void get_sdr(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ unsigned int pos;
+ uint16_t nextrec;
+
+ IPMI_CHECK_CMD_LEN(8);
+ if (cmd[6])
+ IPMI_CHECK_RESERVATION(2, ss->sdr.reservation);
+ pos = 0;
+ if (sdr_find_entry(&ss->sdr, cmd[4] | (cmd[5] << 8),
+ &pos, &nextrec)) {
+ rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+ goto out;
+ }
+ if (cmd[6] > (ss->sdr.sdr[pos + 4])) {
+ rsp[2] = IPMI_CC_PARM_OUT_OF_RANGE;
+ goto out;
+ }
+
+ IPMI_ADD_RSP_DATA(nextrec & 0xff);
+ IPMI_ADD_RSP_DATA((nextrec >> 8) & 0xff);
+
+ if (cmd[7] == 0xff)
+ cmd[7] = ss->sdr.sdr[pos + 4] - cmd[6];
+
+ if ((cmd[7] + *rsp_len) > max_rsp_len) {
+ rsp[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
+ goto out;
+ }
+ memcpy(rsp + *rsp_len, ss->sdr.sdr + pos + cmd[6], cmd[7]);
+ *rsp_len += cmd[7];
+ out:
+ return;
+}
+
+static void add_sdr(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ uint16_t recid;
+
+ if (sdr_add_entry(ss, cmd + 2, cmd_len - 2, &recid)) {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ IPMI_ADD_RSP_DATA(recid & 0xff);
+ IPMI_ADD_RSP_DATA((recid >> 8) & 0xff);
+ out:
+ return;
+}
+
+static void clear_sdr_rep(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_CHECK_CMD_LEN(8);
+ IPMI_CHECK_RESERVATION(2, ss->sdr.reservation);
+ if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ if (cmd[7] == 0xaa) {
+ ss->sdr.next_free = 0;
+ ss->sdr.overflow = 0;
+ set_timestamp(ss, ss->sdr.last_clear);
+ IPMI_ADD_RSP_DATA(1); /* Erasure complete */
+ sdr_inc_reservation(&ss->sdr);
+ } else if (cmd[7] == 0) {
+ IPMI_ADD_RSP_DATA(1); /* Erasure complete */
+ } else {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ out:
+ return;
+}
+
+static void get_sel_info(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ unsigned int i, val;
+
+ IPMI_ADD_RSP_DATA(0x51); /* Conform to IPMI 1.5 */
+ IPMI_ADD_RSP_DATA(ss->sel.next_free & 0xff);
+ IPMI_ADD_RSP_DATA((ss->sel.next_free >> 8) & 0xff);
+ val = (MAX_SEL_SIZE - ss->sel.next_free) * 16;
+ IPMI_ADD_RSP_DATA(val & 0xff);
+ IPMI_ADD_RSP_DATA((val >> 8) & 0xff);
+ for (i = 0; i < 4; i++)
+ IPMI_ADD_RSP_DATA(ss->sel.last_addition[i]);
+ for (i = 0; i < 4; i++)
+ IPMI_ADD_RSP_DATA(ss->sel.last_clear[i]);
+ /* Only support Reserve SEL */
+ IPMI_ADD_RSP_DATA((ss->sel.overflow << 7) | 0x02);
+ out:
+ return;
+}
+
+static void reserve_sel(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_ADD_RSP_DATA(ss->sel.reservation & 0xff);
+ IPMI_ADD_RSP_DATA((ss->sel.reservation >> 8) & 0xff);
+ out:
+ return;
+}
+
+static void get_sel_entry(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ unsigned int val;
+
+ IPMI_CHECK_CMD_LEN(8);
+ if (cmd[6])
+ IPMI_CHECK_RESERVATION(2, ss->sel.reservation);
+ if (ss->sel.next_free == 0) {
+ rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+ goto out;
+ }
+ if (cmd[6] > 15) {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ if (cmd[7] == 0xff)
+ cmd[7] = 16;
+ else if ((cmd[7] + cmd[6]) > 16) {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ } else
+ cmd[7] += cmd[6];
+
+ val = cmd[4] | (cmd[5] << 8);
+ if (val == 0xffff)
+ val = ss->sel.next_free - 1;
+ else if (val >= ss->sel.next_free) {
+ rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+ goto out;
+ }
+ if ((val + 1) == ss->sel.next_free) {
+ IPMI_ADD_RSP_DATA(0xff);
+ IPMI_ADD_RSP_DATA(0xff);
+ } else {
+ IPMI_ADD_RSP_DATA((val + 1) & 0xff);
+ IPMI_ADD_RSP_DATA(((val + 1) >> 8) & 0xff);
+ }
+ for (; cmd[6] < cmd[7]; cmd[6]++)
+ IPMI_ADD_RSP_DATA(ss->sel.sel[val][cmd[6]]);
+ out:
+ return;
+}
+
+static void add_sel_entry(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_CHECK_CMD_LEN(18);
+ if (sel_add_event(ss, cmd + 2)) {
+ rsp[2] = IPMI_CC_OUT_OF_SPACE;
+ goto out;
+ }
+ /* sel_add_event fills in the record number. */
+ IPMI_ADD_RSP_DATA(cmd[2]);
+ IPMI_ADD_RSP_DATA(cmd[3]);
+ out:
+ return;
+}
+
+static void clear_sel(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMI_CHECK_CMD_LEN(8);
+ IPMI_CHECK_RESERVATION(2, ss->sel.reservation);
+ if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ if (cmd[7] == 0xaa) {
+ ss->sel.next_free = 0;
+ ss->sel.overflow = 0;
+ set_timestamp(ss, ss->sdr.last_clear);
+ IPMI_ADD_RSP_DATA(1); /* Erasure complete */
+ sel_inc_reservation(&ss->sel);
+ } else if (cmd[7] == 0) {
+ IPMI_ADD_RSP_DATA(1); /* Erasure complete */
+ } else {
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ out:
+ return;
+}
+
+static void get_sel_time(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ uint32_t val;
+ struct ipmi_time now;
+
+ ipmi_gettime(&now);
+ val = now.tv_sec + ss->sel.time_offset;
+ IPMI_ADD_RSP_DATA(val & 0xff);
+ IPMI_ADD_RSP_DATA((val >> 8) & 0xff);
+ IPMI_ADD_RSP_DATA((val >> 16) & 0xff);
+ IPMI_ADD_RSP_DATA((val >> 24) & 0xff);
+ out:
+ return;
+}
+
+static void set_sel_time(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ uint32_t val;
+ struct ipmi_time now;
+
+ IPMI_CHECK_CMD_LEN(6);
+ val = cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24);
+ ipmi_gettime(&now);
+ ss->sel.time_offset = now.tv_sec - ((long) val);
+ out:
+ return;
+}
+
+static void set_sensor_evt_enable(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMISensor *sens;
+
+ IPMI_CHECK_CMD_LEN(4);
+ if ((cmd[2] > MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) {
+ rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+ goto out;
+ }
+ sens = ss->sensors + cmd[2];
+ switch ((cmd[3] >> 4) & 0x3) {
+ case 0: /* Do not change */
+ break;
+ case 1: /* Enable bits */
+ if (cmd_len > 4)
+ sens->assert_enable |= cmd[4];
+ if (cmd_len > 5)
+ sens->assert_enable |= cmd[5] << 8;
+ if (cmd_len > 6)
+ sens->deassert_enable |= cmd[6];
+ if (cmd_len > 7)
+ sens->deassert_enable |= cmd[7] << 8;
+ break;
+ case 2: /* Disable bits */
+ if (cmd_len > 4)
+ sens->assert_enable &= ~cmd[4];
+ if (cmd_len > 5)
+ sens->assert_enable &= ~(cmd[5] << 8);
+ if (cmd_len > 6)
+ sens->deassert_enable &= ~cmd[6];
+ if (cmd_len > 7)
+ sens->deassert_enable &= ~(cmd[7] << 8);
+ break;
+ case 3:
+ rsp[2] = IPMI_CC_INVALID_DATA_FIELD;
+ goto out;
+ }
+ IPMI_SENSOR_SET_RET_STATUS(sens, cmd[3]);
+ out:
+ return;
+}
+
+static void get_sensor_evt_enable(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMISensor *sens;
+
+ IPMI_CHECK_CMD_LEN(3);
+ if ((cmd[2] > MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) {
+ rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+ goto out;
+ }
+ sens = ss->sensors + cmd[2];
+ IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens));
+ IPMI_ADD_RSP_DATA(sens->assert_enable & 0xff);
+ IPMI_ADD_RSP_DATA((sens->assert_enable >> 8) & 0xff);
+ IPMI_ADD_RSP_DATA(sens->deassert_enable & 0xff);
+ IPMI_ADD_RSP_DATA((sens->deassert_enable >> 8) & 0xff);
+ out:
+ return;
+}
+
+static void rearm_sensor_evts(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMISensor *sens;
+
+ IPMI_CHECK_CMD_LEN(4);
+ if ((cmd[2] > MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) {
+ rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+ goto out;
+ }
+ sens = ss->sensors + cmd[2];
+
+ if ((cmd[3] & 0x80) == 0) {
+ /* Just clear everything */
+ sens->states = 0;
+ goto out;
+ }
+ out:
+ return;
+}
+
+static void get_sensor_evt_status(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMISensor *sens;
+
+ IPMI_CHECK_CMD_LEN(3);
+ if ((cmd[2] > MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) {
+ rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+ goto out;
+ }
+ sens = ss->sensors + cmd[2];
+ IPMI_ADD_RSP_DATA(sens->reading);
+ IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens));
+ IPMI_ADD_RSP_DATA(sens->assert_states & 0xff);
+ IPMI_ADD_RSP_DATA((sens->assert_states >> 8) & 0xff);
+ IPMI_ADD_RSP_DATA(sens->deassert_states & 0xff);
+ IPMI_ADD_RSP_DATA((sens->deassert_states >> 8) & 0xff);
+ out:
+ return;
+}
+
+static void get_sensor_reading(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len)
+{
+ IPMISimState *ss = s->ifdata;
+ IPMISensor *sens;
+
+ IPMI_CHECK_CMD_LEN(3);
+ if ((cmd[2] > MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) {
+ rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT;
+ goto out;
+ }
+ sens = ss->sensors + cmd[2];
+ IPMI_ADD_RSP_DATA(sens->reading);
+ IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens));
+ IPMI_ADD_RSP_DATA(sens->states & 0xff);
+ if (IPMI_SENSOR_IS_DISCRETE(sens))
+ IPMI_ADD_RSP_DATA((sens->states >> 8) & 0xff);
+ out:
+ return;
+}
+
+static IPMICmdHandler chassis_cmds[IPMI_NETFN_CHASSIS_MAXCMD] = {
+ [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = chassis_capabilities,
+ [IPMI_CMD_GET_CHASSIS_STATUS] = chassis_status,
+ [IPMI_CMD_CHASSIS_CONTROL] = chassis_control
+};
+static IPMINetfn chassis_netfn = {
+ .cmd_nums = IPMI_NETFN_CHASSIS_MAXCMD,
+ .cmd_handlers = chassis_cmds
+};
+
+static IPMICmdHandler sensor_event_cmds[IPMI_NETFN_SENSOR_EVENT_MAXCMD] = {
+ [IPMI_CMD_SET_SENSOR_EVT_ENABLE] = set_sensor_evt_enable,
+ [IPMI_CMD_GET_SENSOR_EVT_ENABLE] = get_sensor_evt_enable,
+ [IPMI_CMD_REARM_SENSOR_EVTS] = rearm_sensor_evts,
+ [IPMI_CMD_GET_SENSOR_EVT_STATUS] = get_sensor_evt_status,
+ [IPMI_CMD_GET_SENSOR_READING] = get_sensor_reading
+};
+static IPMINetfn sensor_event_netfn = {
+ .cmd_nums = IPMI_NETFN_SENSOR_EVENT_MAXCMD,
+ .cmd_handlers = sensor_event_cmds
+};
+
+static IPMICmdHandler app_cmds[IPMI_NETFN_APP_MAXCMD] = {
+ [IPMI_CMD_GET_DEVICE_ID] = get_device_id,
+ [IPMI_CMD_SET_BMC_GLOBAL_ENABLES] = set_bmc_global_enables,
+ [IPMI_CMD_GET_BMC_GLOBAL_ENABLES] = get_bmc_global_enables,
+ [IPMI_CMD_CLR_MSG_FLAGS] = clr_msg_flags,
+ [IPMI_CMD_GET_MSG_FLAGS] = get_msg_flags,
+ [IPMI_CMD_READ_EVT_MSG_BUF] = read_evt_msg_buf,
+ [IPMI_CMD_RESET_WATCHDOG_TIMER] = reset_watchdog_timer,
+ [IPMI_CMD_SET_WATCHDOG_TIMER] = set_watchdog_timer,
+ [IPMI_CMD_GET_WATCHDOG_TIMER] = get_watchdog_timer,
+};
+static IPMINetfn app_netfn = {
+ .cmd_nums = IPMI_NETFN_APP_MAXCMD,
+ .cmd_handlers = app_cmds
+};
+
+static IPMICmdHandler storage_cmds[IPMI_NETFN_STORAGE_MAXCMD] = {
+ [IPMI_CMD_GET_SDR_REP_INFO] = get_sdr_rep_info,
+ [IPMI_CMD_RESERVE_SDR_REP] = reserve_sdr_rep,
+ [IPMI_CMD_GET_SDR] = get_sdr,
+ [IPMI_CMD_ADD_SDR] = add_sdr,
+ [IPMI_CMD_CLEAR_SDR_REP] = clear_sdr_rep,
+ [IPMI_CMD_GET_SEL_INFO] = get_sel_info,
+ [IPMI_CMD_RESERVE_SEL] = reserve_sel,
+ [IPMI_CMD_GET_SEL_ENTRY] = get_sel_entry,
+ [IPMI_CMD_ADD_SEL_ENTRY] = add_sel_entry,
+ [IPMI_CMD_CLEAR_SEL] = clear_sel,
+ [IPMI_CMD_GET_SEL_TIME] = get_sel_time,
+ [IPMI_CMD_SET_SEL_TIME] = set_sel_time,
+};
+static IPMINetfn storage_netfn = {
+ .cmd_nums = IPMI_NETFN_STORAGE_MAXCMD,
+ .cmd_handlers = storage_cmds
+};
+
+static void register_cmds(IPMISimState *s)
+{
+ ipmi_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn);
+ ipmi_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn);
+ ipmi_register_netfn(s, IPMI_NETFN_APP, &app_netfn);
+ ipmi_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn);
+}
+
+static const uint8_t init_sdrs[] = {
+ /* Watchdog device */
+ 0x00, 0x00, 0x51, 0x02, 40, 0x20, 0x00, 0x00,
+ 0x23, 0x01, 0x63, 0x00, 0x23, 0x6f, 0x0f, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8,
+ 'W', 'a', 't', 'c', 'h', 'd', 'o', 'g',
+ /* End */
+ 0xff, 0xff, 0x00, 0x00, 0x00
+};
+
+static void ipmi_sim_init(IPMIState *s)
+{
+ unsigned int i;
+ unsigned int next_recid, recid;
+ IPMISimState *ss;
+
+ ss = g_malloc0(sizeof(*ss));
+ ss->bmc_global_enables = (1 << IPMI_BMC_EVENT_LOG_BIT);
+ ss->device_id = 0x20;
+ ss->ipmi_version = 0x02; /* IPMI 2.0 */
+ for (i = 0; i < 4; i++) {
+ ss->sel.last_addition[i] = 0xff;
+ ss->sel.last_clear[i] = 0xff;
+ ss->sdr.last_addition[i] = 0xff;
+ ss->sdr.last_clear[i] = 0xff;
+ }
+
+ next_recid = 0;
+ for (i = 0;;) {
+ int len;
+ if ((i + 5) > sizeof(init_sdrs)) {
+ ipmi_debug("Problem with recid 0x%4.4x\n", i);
+ break;
+ }
+ len = init_sdrs[i + 4];
+ recid = init_sdrs[i] | (init_sdrs[i + 1] << 8);
+ if (recid == 0xffff)
+ break;
+ if ((i + len) > sizeof(init_sdrs)) {
+ ipmi_debug("Problem with recid 0x%4.4x\n", i);
+ break;
+ }
+ if (recid != next_recid) {
+ ipmi_debug("Problem with recid 0x%4.4x\n", i);
+ break;
+ }
+ sdr_add_entry(ss, init_sdrs + i, len, NULL);
+ i += len;
+ }
+
+ ipmi_init_sensors_from_sdrs(ss);
+ register_cmds(ss);
+
+ s->ifdata = ss;
+ s->handle_command = ipmi_sim_handle_command;
+
+ ss->timer = qemu_new_timer_ns(vm_clock, ipmi_timeout, s);
+}
+
+static void ipmi_sim_register_types(void)
+{
+ register_ipmi_sim(IPMI_SIM_INTERNAL, ipmi_sim_init);
+}
+
+type_init(ipmi_sim_register_types)
diff --git a/hw/ipmi_sim.h b/hw/ipmi_sim.h
new file mode 100644
index 0000000..498036e
--- /dev/null
+++ b/hw/ipmi_sim.h
@@ -0,0 +1,270 @@
+/*
+ * IPMI BMC 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_SIM_H
+#define HW_IPMI_SIM_H
+
+#include <stdint.h>
+#include "qemu-timer.h"
+#include "ipmi.h"
+
+#define IPMI_NETFN_CHASSIS 0x00
+#define IPMI_NETFN_CHASSIS_MAXCMD 0x03
+
+#define IPMI_CMD_GET_CHASSIS_CAPABILITIES 0x00
+#define IPMI_CMD_GET_CHASSIS_STATUS 0x01
+#define IPMI_CMD_CHASSIS_CONTROL 0x02
+
+#define IPMI_NETFN_SENSOR_EVENT 0x04
+#define IPMI_NETFN_SENSOR_EVENT_MAXCMD 0x2e
+
+#define IPMI_CMD_SET_SENSOR_EVT_ENABLE 0x28
+#define IPMI_CMD_GET_SENSOR_EVT_ENABLE 0x29
+#define IPMI_CMD_REARM_SENSOR_EVTS 0x2a
+#define IPMI_CMD_GET_SENSOR_EVT_STATUS 0x2b
+#define IPMI_CMD_GET_SENSOR_READING 0x2d
+
+/* #define IPMI_NETFN_APP 0x06 In ipmi.h */
+#define IPMI_NETFN_APP_MAXCMD 0x36
+
+#define IPMI_CMD_GET_DEVICE_ID 0x01
+#define IPMI_CMD_RESET_WATCHDOG_TIMER 0x22
+#define IPMI_CMD_SET_WATCHDOG_TIMER 0x24
+#define IPMI_CMD_GET_WATCHDOG_TIMER 0x25
+#define IPMI_CMD_SET_BMC_GLOBAL_ENABLES 0x2e
+#define IPMI_CMD_GET_BMC_GLOBAL_ENABLES 0x2f
+#define IPMI_CMD_CLR_MSG_FLAGS 0x30
+#define IPMI_CMD_GET_MSG_FLAGS 0x31
+#define IPMI_CMD_READ_EVT_MSG_BUF 0x35
+
+#define IPMI_NETFN_STORAGE 0x0a
+#define IPMI_NETFN_STORAGE_MAXCMD 0x4a
+
+#define IPMI_CMD_GET_SDR_REP_INFO 0x20
+#define IPMI_CMD_GET_SDR_REP_ALLOC_INFO 0x21
+#define IPMI_CMD_RESERVE_SDR_REP 0x22
+#define IPMI_CMD_GET_SDR 0x23
+#define IPMI_CMD_ADD_SDR 0x24
+#define IPMI_CMD_PARTIAL_ADD_SDR 0x25
+#define IPMI_CMD_DELETE_SDR 0x26
+#define IPMI_CMD_CLEAR_SDR_REP 0x27
+#define IPMI_CMD_GET_SDR_REP_TIME 0x28
+#define IPMI_CMD_SET_SDR_REP_TIME 0x29
+#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A
+#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B
+#define IPMI_CMD_RUN_INIT_AGENT 0x2C
+#define IPMI_CMD_GET_SEL_INFO 0x40
+#define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41
+#define IPMI_CMD_RESERVE_SEL 0x42
+#define IPMI_CMD_GET_SEL_ENTRY 0x43
+#define IPMI_CMD_ADD_SEL_ENTRY 0x44
+#define IPMI_CMD_PARTIAL_ADD_SEL_ENTRY 0x45
+#define IPMI_CMD_DELETE_SEL_ENTRY 0x46
+#define IPMI_CMD_CLEAR_SEL 0x47
+#define IPMI_CMD_GET_SEL_TIME 0x48
+#define IPMI_CMD_SET_SEL_TIME 0x49
+
+
+/* Same as a timespec struct. */
+struct ipmi_time
+{
+ long tv_sec;
+ long tv_nsec;
+};
+
+#define MAX_SEL_SIZE 128
+
+typedef struct IPMISel {
+ uint8_t sel[MAX_SEL_SIZE][16];
+ unsigned int next_free;
+ long time_offset;
+ uint16_t reservation;
+ uint8_t last_addition[4];
+ uint8_t last_clear[4];
+ uint8_t overflow;
+} IPMISel;
+
+#define MAX_SDR_SIZE 16384
+
+typedef struct IPMISdr {
+ uint8_t sdr[MAX_SDR_SIZE];
+ unsigned int next_free;
+ uint16_t next_rec_id;
+ uint16_t reservation;
+ uint8_t last_addition[4];
+ uint8_t last_clear[4];
+ uint8_t overflow;
+} IPMISdr;
+
+typedef struct IPMISensor {
+ uint8_t status;
+ uint8_t reading;
+ uint16_t states_suppt;
+ uint16_t assert_suppt;
+ uint16_t deassert_suppt;
+ uint16_t states;
+ uint16_t assert_states;
+ uint16_t deassert_states;
+ uint16_t assert_enable;
+ uint16_t deassert_enable;
+ uint8_t sensor_type;
+ uint8_t evt_reading_type_code;
+} IPMISensor;
+#define IPMI_SENSOR_GET_PRESENT(s) ((s)->status & 0x01)
+#define IPMI_SENSOR_SET_PRESENT(s,v) ((s)->status = (s->status & ~0x01) | \
+ !!(v))
+#define IPMI_SENSOR_GET_SCAN_ON(s) ((s)->status & 0x40)
+#define IPMI_SENSOR_SET_SCAN_ON(s,v) ((s)->status = (s->status & ~0x40) | \
+ ((!!(v)) << 6))
+#define IPMI_SENSOR_GET_EVENTS_ON(s) ((s)->status & 0x80)
+#define IPMI_SENSOR_SET_EVENTS_ON(s,v) ((s)->status = (s->status & ~0x80) | \
+ ((!!(v)) << 7))
+#define IPMI_SENSOR_GET_RET_STATUS(s) ((s)->status & 0xc0)
+#define IPMI_SENSOR_SET_RET_STATUS(s,v) ((s)->status = (s->status & ~0xc0) | \
+ (v & 0xc0))
+#define IPMI_SENSOR_IS_DISCRETE(s) ((s)->evt_reading_type_code != 1)
+
+#define MAX_SENSORS 20
+#define IPMI_WATCHDOG_SENSOR 0
+
+typedef struct IPMISimState IPMISimState;
+
+#define MAX_NETFNS 64
+typedef void (*IPMICmdHandler)(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ uint8_t *rsp, unsigned int *rsp_len,
+ unsigned int max_rsp_len);
+typedef struct IPMINetfn {
+ unsigned int cmd_nums;
+ IPMICmdHandler *cmd_handlers;
+} IPMINetfn;
+
+struct IPMISimState {
+ QEMUTimer *timer;
+
+ uint8_t bmc_global_enables;
+ uint8_t msg_flags;
+
+ int8_t watchdog_initialized;
+ uint8_t watchdog_use;
+ uint8_t watchdog_action;
+ uint8_t watchdog_pretimeout; /* In seconds */
+ uint8_t watchdog_expired;
+ uint16_t watchdog_timeout; /* in 100's of milliseconds */
+
+ int8_t watchdog_running;
+ int8_t watchdog_preaction_ran;
+ int64_t watchdog_expiry;
+
+ uint8_t device_id;
+ uint8_t ipmi_version;
+ uint8_t device_rev;
+ uint8_t fwrev1;
+ uint8_t fwrev2;
+ uint8_t mfg_id[3];
+ uint8_t product_id[2];
+
+ IPMISel sel;
+ IPMISdr sdr;
+ IPMISensor sensors[MAX_SENSORS];
+
+ /* Odd netfns are for responses, so we only need the even ones. */
+ IPMINetfn *netfns[MAX_NETFNS / 2];
+
+ /* We allow one event in the buffer */
+ uint8_t evtbuf[16];
+};
+
+#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK (1 << 3)
+#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL (1 << 1)
+#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE (1 << 0)
+#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(s) \
+ (IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK & (s)->msg_flags)
+#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(s) \
+ (IPMI_BMC_MSG_FLAG_EVT_BUF_FULL & (s)->msg_flags)
+#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s) \
+ (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE & (s)->msg_flags)
+
+#define IPMI_BMC_RCV_MSG_QUEUE_INT_BIT 0
+#define IPMI_BMC_EVBUF_FULL_INT_BIT 1
+#define IPMI_BMC_EVENT_MSG_BUF_BIT 2
+#define IPMI_BMC_EVENT_LOG_BIT 3
+#define IPMI_BMC_MSG_INTS_ON(s) ((s)->bmc_global_enables & \
+ (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT))
+#define IPMI_BMC_EVBUF_FULL_INT_ENABLED(s) ((s)->bmc_global_enables & \
+ (1 << IPMI_BMC_EVBUF_FULL_INT_BIT))
+#define IPMI_BMC_EVENT_LOG_ENABLED(s) ((s)->bmc_global_enables & \
+ (1 << IPMI_BMC_EVENT_LOG_BIT))
+#define IPMI_BMC_EVENT_MSG_BUF_ENABLED(s) ((s)->bmc_global_enables & \
+ (1 << IPMI_BMC_EVENT_MSG_BUF_BIT))
+
+#define IPMI_BMC_WATCHDOG_USE_MASK 0xc7
+#define IPMI_BMC_WATCHDOG_ACTION_MASK 0x77
+#define IPMI_BMC_WATCHDOG_GET_USE(s) ((s)->watchdog_use & 0x7)
+#define IPMI_BMC_WATCHDOG_GET_DONT_LOG(s) (((s)->watchdog_use >> 7) & 0x1)
+#define IPMI_BMC_WATCHDOG_GET_DONT_STOP(s) (((s)->watchdog_use >> 6) & 0x1)
+#define IPMI_BMC_WATCHDOG_GET_PRE_ACTION(s) (((s)->watchdog_action >> 4) & 0x7)
+#define IPMI_BMC_WATCHDOG_PRE_NONE 0
+#define IPMI_BMC_WATCHDOG_PRE_SMI 1
+#define IPMI_BMC_WATCHDOG_PRE_NMI 2
+#define IPMI_BMC_WATCHDOG_PRE_MSG_INT 3
+#define IPMI_BMC_WATCHDOG_GET_ACTION(s) ((s)->watchdog_action & 0x7)
+#define IPMI_BMC_WATCHDOG_ACTION_NONE 0
+#define IPMI_BMC_WATCHDOG_ACTION_RESET 1
+#define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN 2
+#define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE 3
+
+
+int ipmi_register_netfn(IPMISimState *s, unsigned int netfn, IPMINetfn *netfnd);
+int ipmi_register_cmd(IPMISimState *s, unsigned int netfn, unsigned int cmd,
+ IPMICmdHandler handler);
+
+/* Add a byte to the response. */
+#define IPMI_ADD_RSP_DATA(b) \
+ do { \
+ if (*rsp_len >= max_rsp_len) { \
+ rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED; \
+ goto out; \
+ } \
+ rsp[(*rsp_len)++] = (b); \
+ } while(0)
+
+/* Verify that the received command is a certain length. */
+#define IPMI_CHECK_CMD_LEN(l) \
+ if (cmd_len < l) { \
+ rsp[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; \
+ goto out; \
+ }
+
+/* Check that the reservation in the command is valid. */
+#define IPMI_CHECK_RESERVATION(off, r) \
+ do { \
+ if ((cmd[off] | (cmd[off + 1] << 8)) != r) { \
+ rsp[2] = IPMI_CC_INVALID_RESERVATION; \
+ goto out; \
+ } \
+ } while(0)
+
+
+#endif
--
1.7.4.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 9/9] IPMI: Add an external connection simulation interface
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
` (7 preceding siblings ...)
2012-07-02 19:44 ` [PATCH 8/9] IPMI: Add a local BMC simulation minyard
@ 2012-07-02 19:44 ` minyard
2012-07-09 13:23 ` First shot at adding IPMI to qemu Corey Minyard
9 siblings, 0 replies; 12+ messages in thread
From: minyard @ 2012-07-02 19:44 UTC (permalink / raw)
To: kvm; +Cc: Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
This adds an interface for IPMI that connects to a remote
BMC over a chardev (generally a TCP socket). The OpenIPMI
lanserv simulator describes this interface, see that for
interface details.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
default-configs/i386-softmmu.mak | 1 +
default-configs/x86_64-softmmu.mak | 1 +
hw/Makefile.objs | 1 +
hw/ipmi_extern.c | 421 ++++++++++++++++++++++++++++++++++++
hw/ipmi_extern.h | 75 +++++++
5 files changed, 499 insertions(+), 0 deletions(-)
create mode 100644 hw/ipmi_extern.c
create mode 100644 hw/ipmi_extern.h
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 8c99d5d..325f92e 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -12,6 +12,7 @@ CONFIG_ISA_IPMI=y
CONFIG_IPMI_KCS=y
CONFIG_IPMI_BT=y
CONFIG_IPMI_LOCAL=y
+CONFIG_IPMI_EXTERN=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 4d01883..2ac9177 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -12,6 +12,7 @@ CONFIG_ISA_IPMI=y
CONFIG_IPMI_KCS=y
CONFIG_IPMI_BT=y
CONFIG_IPMI_LOCAL=y
+CONFIG_IPMI_EXTERN=y
CONFIG_SERIAL=y
CONFIG_PARALLEL=y
CONFIG_I8254=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 193227d..06757b0 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -25,6 +25,7 @@ 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_IPMI_LOCAL) += ipmi_sim.o
+hw-obj-$(CONFIG_IPMI_EXTERN) += ipmi_extern.o
hw-obj-$(CONFIG_SERIAL) += serial.o
hw-obj-$(CONFIG_PARALLEL) += parallel.o
diff --git a/hw/ipmi_extern.c b/hw/ipmi_extern.c
new file mode 100644
index 0000000..bbb8469
--- /dev/null
+++ b/hw/ipmi_extern.c
@@ -0,0 +1,421 @@
+/*
+ * IPMI BMC external connection
+ *
+ * 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.
+ */
+
+/*
+ * This is designed to connect with OpenIPMI's lanserv serial interface
+ * using the "VM" connection type. See that for details.
+ */
+
+#include "ipmi_extern.h"
+
+static int can_receive(void *opaque);
+static void receive(void *opaque, const uint8_t *buf, int size);
+static void chr_event(void *opaque, int event);
+
+static unsigned char
+ipmb_checksum(const unsigned char *data, int size, unsigned char start)
+{
+ unsigned char csum = start;
+
+ for (; size > 0; size--, data++)
+ csum += *data;
+
+ return csum;
+}
+
+static void continue_send(IPMIState *s, IPMIExternState *es)
+{
+ if (es->outlen == 0)
+ goto check_reset;
+
+ send:
+ es->outpos += qemu_chr_fe_write(es->chr, es->outbuf + es->outpos,
+ es->outlen - es->outpos);
+ if (es->outpos < es->outlen) {
+ /* Not fully transmitted, try again in a 10ms */
+ qemu_mod_timer(es->extern_timer,
+ qemu_get_clock_ns(vm_clock) + 10000000);
+ } else {
+ /* Sent */
+ es->outlen = 0;
+ es->outpos = 0;
+ if (!es->sending_cmd)
+ es->waiting_rsp = 1;
+ else
+ es->sending_cmd = 0;
+
+ check_reset:
+ if (es->send_reset) {
+ /* Send the reset */
+ es->outbuf[0] = VM_CMD_RESET;
+ es->outbuf[1] = VM_CMD_CHAR;
+ es->outlen = 2;
+ es->outpos = 0;
+ es->send_reset = 0;
+ es->sending_cmd = 1;
+ goto send;
+ }
+
+ if (es->waiting_rsp)
+ /* Make sure we get a response within 4 seconds. */
+ qemu_mod_timer(es->extern_timer,
+ qemu_get_clock_ns(vm_clock) + 4000000000ULL);
+ }
+ return;
+}
+
+static void extern_timeout(void *opaque)
+{
+ IPMIState *s = opaque;
+ IPMIExternState *es = s->ifdata;
+
+ ipmi_lock();
+ if (!es->connected) {
+ if (es->is_listen)
+ goto out;
+ /* Try to reconnect every 10 seconds. */
+ qemu_mod_timer(es->extern_timer,
+ qemu_get_clock_ns(vm_clock) + 10000000000ULL);
+ es->chr = qemu_chr_new_from_opts(s->chropts, NULL);
+ if (es->chr)
+ qemu_chr_add_handlers(es->chr, can_receive, receive, chr_event, s);
+ } else if (es->waiting_rsp && (es->outlen == 0)) {
+ /* The message response timed out, return an error. */
+ es->waiting_rsp = 0;
+ es->inbuf[1] = es->outbuf[1] | 0x04;
+ es->inbuf[2] = es->outbuf[2];
+ es->inbuf[3] = IPMI_CC_TIMEOUT;
+ s->handle_rsp(s, es->outbuf[0], es->inbuf + 1, 3);
+ } else {
+ continue_send(s, es);
+ }
+ out:
+ ipmi_unlock();
+}
+
+static void addchar(IPMIExternState *es, unsigned char ch)
+{
+ switch (ch) {
+ case VM_MSG_CHAR:
+ case VM_CMD_CHAR:
+ case VM_ESCAPE_CHAR:
+ es->outbuf[es->outlen] = VM_ESCAPE_CHAR;
+ es->outlen++;
+ ch |= 0x10;
+ /* No break */
+
+ default:
+ es->outbuf[es->outlen] = ch;
+ es->outlen++;
+ }
+}
+
+static void ipmi_extern_handle_command(IPMIState *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ unsigned int max_cmd_len,
+ uint8_t msg_id)
+{
+ IPMIExternState *es = s->ifdata;
+ uint8_t err = 0, csum;
+ unsigned int i;
+
+ ipmi_lock();
+ if (es->outlen) {
+ /* We already have a command queued. Shouldn't ever happen. */
+ fprintf(stderr, "IPMI KCS: Got command when not finished with the"
+ " previous commmand\n");
+ abort();
+ }
+
+ /* If it's too short or it was truncated, return an error. */
+ if (cmd_len < 2)
+ err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
+ else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE))
+ err = IPMI_CC_REQUEST_DATA_TRUNCATED;
+ else if (!es->connected)
+ err = IPMI_CC_BMC_INIT_IN_PROGRESS;
+ if (err) {
+ unsigned char rsp[3];
+ rsp[0] = cmd[0] | 0x04;
+ rsp[1] = cmd[1];
+ rsp[2] = err;
+ es->waiting_rsp = 0;
+ s->handle_rsp(s, msg_id, rsp, 3);
+ goto out;
+ }
+
+ addchar(es, msg_id);
+ for (i = 0; i < cmd_len; i++)
+ addchar(es, cmd[i]);
+
+ csum = ipmb_checksum(&msg_id, 1, 0);
+ addchar(es, -ipmb_checksum(cmd, cmd_len, csum));
+
+ es->outbuf[es->outlen] = VM_MSG_CHAR;
+ es->outlen++;
+
+ /* Start the transmit */
+ continue_send(s, es);
+
+ out:
+ ipmi_unlock();
+ return;
+}
+
+static void handle_hw_op(IPMIState *s, unsigned char hw_op)
+{
+ switch (hw_op) {
+ case VM_CMD_VERSION:
+ /* We only support one version at this time. */
+ break;
+
+ case VM_CMD_NOATTN:
+ s->set_atn(s, 0, 0);
+ break;
+
+ case VM_CMD_ATTN:
+ s->set_atn(s, 1, 0);
+ break;
+
+ case VM_CMD_ATTN_IRQ:
+ s->set_atn(s, 1, 1);
+ break;
+
+ case VM_CMD_POWEROFF:
+ s->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
+ break;
+
+ case VM_CMD_RESET:
+ s->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
+ break;
+
+ case VM_CMD_ENABLE_IRQ:
+ s->set_irq_enable(s, 1);
+ break;
+
+ case VM_CMD_DISABLE_IRQ:
+ s->set_irq_enable(s, 0);
+ break;
+
+ case VM_CMD_SEND_NMI:
+ s->do_hw_op(s, IPMI_SEND_NMI, 0);
+ break;
+ }
+}
+
+static void handle_msg(IPMIState *s, IPMIExternState *es)
+{
+ if (es->in_escape) {
+ ipmi_debug("msg escape not ended\n");
+ return;
+ }
+ if (es->inpos < 5) {
+ ipmi_debug("msg too short\n");
+ return;
+ }
+ if (es->in_too_many) {
+ es->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED;
+ es->inpos = 4;
+ } else if (ipmb_checksum(es->inbuf, es->inpos, 0) != 0) {
+ ipmi_debug("msg checksum failure\n");
+ return;
+ } else
+ es->inpos--; /* Remove checkum */
+
+ qemu_del_timer(es->extern_timer);
+ es->waiting_rsp = 0;
+ s->handle_rsp(s, es->inbuf[0], es->inbuf + 1, es->inpos - 1);
+}
+
+static int can_receive(void *opaque)
+{
+ return 1;
+}
+
+static void receive(void *opaque, const uint8_t *buf, int size)
+{
+ IPMIState *s = opaque;
+ IPMIExternState *es = s->ifdata;
+ int i;
+ unsigned char hw_op;
+
+ ipmi_lock();
+ for (i = 0; i < size; i++) {
+ unsigned char ch = buf[i];
+
+ switch (ch) {
+ case VM_MSG_CHAR:
+ handle_msg(s, es);
+ es->in_too_many = 0;
+ es->inpos = 0;
+ break;
+
+ case VM_CMD_CHAR:
+ if (es->in_too_many) {
+ ipmi_debug("cmd in too many\n");
+ es->in_too_many = 0;
+ es->inpos = 0;
+ break;
+ }
+ if (es->in_escape) {
+ ipmi_debug("cmd in escape\n");
+ es->in_too_many = 0;
+ es->inpos = 0;
+ es->in_escape = 0;
+ break;
+ }
+ es->in_too_many = 0;
+ if (es->inpos < 1)
+ break;
+ hw_op = es->inbuf[0];
+ es->inpos = 0;
+ goto out_hw_op;
+ break;
+
+ case VM_ESCAPE_CHAR:
+ es->in_escape = 1;
+ break;
+
+ default:
+ if (es->in_escape) {
+ ch &= ~0x10;
+ es->in_escape = 0;
+ }
+
+ if (es->in_too_many)
+ break;
+
+ if (es->inpos >= sizeof(es->inbuf)) {
+ es->in_too_many = 1;
+ break;
+ }
+
+ es->inbuf[es->inpos] = ch;
+ es->inpos++;
+ break;
+ }
+ }
+ ipmi_unlock();
+ return;
+
+ out_hw_op:
+ ipmi_unlock();
+ /*
+ * We don't want to handle hardware operations while holding the
+ * lock, that may call back into this code to report a reset.
+ */
+ handle_hw_op(s, hw_op);
+}
+
+static void chr_event(void *opaque, int event)
+{
+ IPMIState *s = opaque;
+ IPMIExternState *es = s->ifdata;
+ unsigned char v;
+
+ ipmi_lock();
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ es->connected = 1;
+ es->outpos = 0;
+ es->outlen = 0;
+ addchar(es, VM_CMD_VERSION);
+ addchar(es, VM_PROTOCOL_VERSION);
+ es->outbuf[es->outlen] = VM_CMD_CHAR;
+ es->outlen++;
+ addchar(es, VM_CMD_CAPABILITIES);
+ v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN;
+ if (s->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1) == 0)
+ v |= VM_CAPABILITIES_POWER;
+ if (s->do_hw_op(s, IPMI_RESET_CHASSIS, 1) == 0)
+ v |= VM_CAPABILITIES_RESET;
+ if (s->do_hw_op(s, IPMI_SEND_NMI, 1) == 0)
+ v |= VM_CAPABILITIES_NMI;
+ addchar(es, v);
+ es->outbuf[es->outlen] = VM_CMD_CHAR;
+ es->outlen++;
+ es->sending_cmd = 0;
+ continue_send(s, es);
+ break;
+
+ case CHR_EVENT_CLOSED:
+ if (!es->connected)
+ return;
+ es->connected = 0;
+ if (es->is_listen)
+ return;
+ qemu_chr_delete(es->chr);
+ es->chr = NULL;
+ if (es->waiting_rsp) {
+ es->waiting_rsp = 0;
+ es->inbuf[1] = es->outbuf[1] | 0x04;
+ es->inbuf[2] = es->outbuf[2];
+ es->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
+ s->handle_rsp(s, es->outbuf[0], es->inbuf + 1, 3);
+ }
+ /* Try to open in a bit */
+ qemu_mod_timer(es->extern_timer,
+ qemu_get_clock_ns(vm_clock) + 10000000000ULL);
+ break;
+ }
+ ipmi_unlock();
+}
+
+static void ipmi_extern_handle_reset(IPMIState *s)
+{
+ IPMIExternState *es = s->ifdata;
+
+ ipmi_lock();
+ es->send_reset = 1;
+ continue_send(s, es);
+ ipmi_unlock();
+}
+
+static void ipmi_extern_init(IPMIState *s)
+{
+ IPMIExternState *es;
+
+ es = g_malloc0(sizeof(*es));
+ s->handle_command = ipmi_extern_handle_command;
+
+ s->ifdata = es;
+ es->is_listen = qemu_opt_get_bool(s->chropts, "server", 0);
+ es->extern_timer = qemu_new_timer_ns(vm_clock, extern_timeout, s);
+ if (!es->is_listen)
+ /* Make sure we connect in time. */
+ qemu_mod_timer(es->extern_timer,
+ qemu_get_clock_ns(vm_clock) + 10000000000ULL);
+ es->chr = qemu_chr_new_from_opts(s->chropts, NULL);
+ if (es->chr)
+ qemu_chr_add_handlers(es->chr, can_receive, receive, chr_event, s);
+
+ s->handle_reset = ipmi_extern_handle_reset;
+}
+
+static void ipmi_extern_register_types(void)
+{
+ register_ipmi_sim(IPMI_SIM_EXTERNAL, ipmi_extern_init);
+}
+
+type_init(ipmi_extern_register_types)
diff --git a/hw/ipmi_extern.h b/hw/ipmi_extern.h
new file mode 100644
index 0000000..65a0834
--- /dev/null
+++ b/hw/ipmi_extern.h
@@ -0,0 +1,75 @@
+/*
+ * IPMI BMC external connection
+ *
+ * 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_EXTERN_H
+#define HW_IPMI_EXTERN_H
+
+#include <stdint.h>
+#include "qemu-timer.h"
+#include "ipmi.h"
+
+#define VM_MSG_CHAR 0xA0 /* Marks end of message */
+#define VM_CMD_CHAR 0xA1 /* Marks end of a command */
+#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */
+
+#define VM_PROTOCOL_VERSION 1
+#define VM_CMD_VERSION 0xff /* A version number byte follows */
+#define VM_CMD_NOATTN 0x00
+#define VM_CMD_ATTN 0x01
+#define VM_CMD_ATTN_IRQ 0x02
+#define VM_CMD_POWEROFF 0x03
+#define VM_CMD_RESET 0x04
+#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */
+#define VM_CMD_DISABLE_IRQ 0x06
+#define VM_CMD_SEND_NMI 0x07
+#define VM_CMD_CAPABILITIES 0x08
+#define VM_CAPABILITIES_POWER 0x01
+#define VM_CAPABILITIES_RESET 0x02
+#define VM_CAPABILITIES_IRQ 0x04
+#define VM_CAPABILITIES_NMI 0x08
+#define VM_CAPABILITIES_ATTN 0x10
+
+typedef struct IPMIExternState {
+ int connected;
+ int is_listen;
+ CharDriverState *chr;
+
+ unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2];
+ unsigned int inpos;
+ int in_escape;
+ int in_too_many;
+ int waiting_rsp;
+ int sending_cmd;
+
+ unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1];
+ unsigned int outpos;
+ unsigned int outlen;
+
+ struct QEMUTimer *extern_timer;
+
+ /* A reset event is pending to be sent upstream. */
+ int send_reset;
+} IPMIExternState;
+
+#endif
--
1.7.4.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: First shot at adding IPMI to qemu
2012-07-02 19:44 First shot at adding IPMI to qemu minyard
` (8 preceding siblings ...)
2012-07-02 19:44 ` [PATCH 9/9] IPMI: Add an external connection simulation interface minyard
@ 2012-07-09 13:23 ` Corey Minyard
2012-07-09 14:05 ` Daniel P. Berrange
9 siblings, 1 reply; 12+ messages in thread
From: Corey Minyard @ 2012-07-09 13:23 UTC (permalink / raw)
To: kvm
I haven't heard anything about these patches. Any comments, good or
bad? Has anyone tried these?
Thanks,
-corey
On 07/02/2012 02:44 PM, minyard@acm.org wrote:
> I had asked about getting an IPMI device into qemu and received some
> interest, and it's useful to me, so I've done some work to add it.
> The following patch set has a set of patches to add an IPMI KCS
> device, and IPMI BT device, a built-in BMC (IPMI management controller),
> and a way to attach an external BMC through a chardev.
>
> There was some discussion on whether to make the BMC internal or
> external, but I went ahead and added both. The internal one is
> fairly basic and not extensible, at least without adding code.
> I've modified the OpenIPMI library simulator to work with the
> external interface to allow it to receive connections from the
> qemu external simulator with a fairly basic protocol.
>
> I've also added the ability for the OpenIPMI library to manage
> a VM to power it on, power it off, reset it, and handle an IPMI
> watchdog timer. So it looks quite like a real system. Instructions
> for using it are in the OpenIPMI release candidate I uploaded to
> https://sourceforge.net/projects/openipmi
>
> Since IPMI can advertise it's presence via SMBIOS, I added a
> way for a driver to add an SMBIOS entry. I also added a way
> to query a free interrupt from the ISA bus, since the interrupt
> is in the SMBIOS entry and nobody really cares which one is used.
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 12+ messages in thread