Skip to contentSkip to conversation

Add an IPMI device to qemu

PATCH In review [email protected] 4 participants 24 messages Dec 12, 2014 – Feb 22, 2015

lore
Share & tools
lore
#0 lore raw

cover Add an IPMI device to qemu

This set of patches adds an IPMI device to qemu. This is good for systems that require an IPMI device to work correctly, for simulating scenarios that require IPMI and testing software that uses IPMI, and of course, for the Linux IPMI driver maintainer to use to reproduce issues that could not be easily reproduced otherwise :).

For those that don't know, IPMI is a standard for doing sensor monitoring and basic machine maintenance. It has local interfaces on the host system (four different types, SMIC, KCS, BT, and SSIF) that can be in I/O space, memory space, and PCI space, and in the case of SSIF, is on an I2C bus. It can also have network interfaces for out of band maintenance, though that's not directly relevant here. It is a message-based interface; all IPMI operations are done by sending command messages, results come back as response messages.

The maintenance actions are done by a small controller on the system called a Baseboard Management Controller (BMC). These devices are always on when the system is plugged in, even if the system is off.

The first 9 patches add the device itself on an ISA interface. This adds a KCS device and a BT device (two of the four standard IPMI interfaces). It also adds software to have a small BMC simulated inside of QEMU, and a way to connected to an external BMC (like openipmi library's lanserv) over the network. Those patches should be pretty straightforward, though I'm still not 100% sure about migration.

Patch 10 postpones sending the SMBIOS and ACPI tables to the firmware until after the devices have initialized. This lets the IPMI driver add entries to those tables when it starts. It might be possible to pre-parse the IPMI parameters very early, but that seems like a lot more work, and there's no harm in sending the tables to the firmware later, as far as I can tell.

Patches 11 and 12 add an SMBIOS entry for the IPMI device.

Patches 13-15 are about adding ACPI tables for the IPMI device. This adds code that can dynamically create ACPI tables, then uses that to generate an SSDT for an IPMI device in the ISA bus space. I opted for this over a hard coded table because I would need several different tables depending on configuration (I/O or memory space, interrupt or none, PCI space, I2C space if that gets added, IPMI device type). Generating them on the fly is fairly simple.

Patch 16 moves running the local BMC simulation to it's own thread. I'm not really sure of the value, besides a more accurate simulation, but perhaps there is to offload the main execution of qemu.

-corey

#1 lore raw

PATCH 1/16 Add a base IPMI interface

From: Corey Minyard <[email protected]>

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.

---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/Makefile.objs                   |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/ipmi.c                     | 135 +++++++++++++++++++++++
 hw/ipmi/ipmi.h                     | 215 +++++++++++++++++++++++++++++++++++++
 qemu-doc.texi                      |   2 +
 7 files changed, 356 insertions(+)
 create mode 100644 hw/ipmi/Makefile.objs
 create mode 100644 hw/ipmi/ipmi.c
 create mode 100644 hw/ipmi/ipmi.h
default-configs/i386-softmmu.mak +1 -0
index 8e08841..e7179a6 100644
@@ -10,6 +10,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
default-configs/x86_64-softmmu.mak +1 -0
index 66557ac..d04705d 100644
@@ -10,6 +10,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
hw/Makefile.objs +1 -0
index 52a1464..16a0893 100644
@@ -13,6 +13,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ide/
 devices-dirs-$(CONFIG_SOFTMMU) += input/
 devices-dirs-$(CONFIG_SOFTMMU) += intc/
 devices-dirs-$(CONFIG_IPACK) += ipack/
+devices-dirs-$(CONFIG_SOFTMMU) += ipmi/
 devices-dirs-$(CONFIG_SOFTMMU) += isa/
 devices-dirs-$(CONFIG_SOFTMMU) += misc/
 devices-dirs-$(CONFIG_SOFTMMU) += net/
hw/ipmi/Makefile.objs new file +1 -0
new file mode 100644
index 0000000..65bde11
@@ -0,0 +1 @@
+common-obj-$(CONFIG_IPMI) += ipmi.o
hw/ipmi/ipmi.c new file +135 -0

135 lines not on this page · raw message

hw/ipmi/ipmi.h new file +215 -0

215 lines not on this page · raw message

qemu-doc.texi +2 -0
index ad418f8..14ac004 100644
@@ -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
Signature · 1 line
1.8.3.1
#2 lore raw

PATCH 2/16 ipmi: Add a PC ISA type structure

In reply to [email protected]

From: Corey Minyard <[email protected]>

This provides the base infrastructure to tie IPMI low-level interfaces into a PC ISA bus.

---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/isa_ipmi.c                 | 144 +++++++++++++++++++++++++++++++++++++
 include/hw/nvram/fw_cfg.h          |  11 ++-
 5 files changed, 157 insertions(+), 1 deletion(-)
 create mode 100644 hw/ipmi/isa_ipmi.c
default-configs/i386-softmmu.mak +1 -0
index e7179a6..7ee1789 100644
@@ -11,6 +11,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
default-configs/x86_64-softmmu.mak +1 -0
index d04705d..8003d91 100644
@@ -11,6 +11,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
hw/ipmi/Makefile.objs +1 -0
index 65bde11..3494b70 100644
@@ -1 +1,2 @@
 common-obj-$(CONFIG_IPMI) += ipmi.o
+common-obj-$(CONFIG_ISA_IPMI) += isa_ipmi.o
hw/ipmi/isa_ipmi.c new file +144 -0

144 lines not on this page · raw message

include/hw/nvram/fw_cfg.h +10 -1
index 56e1ed7..217d1e4 100644
@@ -38,7 +38,16 @@
 
 #define FW_CFG_FILE_FIRST       0x20
 #define FW_CFG_FILE_SLOTS       0x10
-#define FW_CFG_MAX_ENTRY        (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS)
+
+#define FW_CFG_IPMI_INTERFACE   0x30
+#define FW_CFG_IPMI_BASE_ADDR   0x31
+#define FW_CFG_IPMI_REG_SPACE   0x32
+#define FW_CFG_IPMI_REG_SPACING 0x33
+#define FW_CFG_IPMI_SLAVE_ADDR  0x34
+#define FW_CFG_IPMI_IRQ         0x35
+#define FW_CFG_IPMI_VERSION     0x36
+
+#define FW_CFG_MAX_ENTRY        (FW_CFG_IPMI_VERSION + 1)
 
 #define FW_CFG_WRITE_CHANNEL    0x4000
 #define FW_CFG_ARCH_LOCAL       0x8000
Signature · 1 line
1.8.3.1
#3 lore raw

PATCH 3/16 ipmi: Add a KCS low-level interface

In reply to [email protected]

From: Corey Minyard <[email protected]>

This provides the simulation of the KCS hardware interface.

---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/ipmi_kcs.c                 | 337 +++++++++++++++++++++++++++++++++++++
 4 files changed, 340 insertions(+)
 create mode 100644 hw/ipmi/ipmi_kcs.c
default-configs/i386-softmmu.mak +1 -0
index 7ee1789..4f28a4c 100644
@@ -12,6 +12,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
default-configs/x86_64-softmmu.mak +1 -0
index 8003d91..64bc16b 100644
@@ -12,6 +12,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
hw/ipmi/Makefile.objs +1 -0
index 3494b70..3c811ae 100644
@@ -1,2 +1,3 @@
 common-obj-$(CONFIG_IPMI) += ipmi.o
 common-obj-$(CONFIG_ISA_IPMI) += isa_ipmi.o
+common-obj-$(CONFIG_IPMI_KCS) += ipmi_kcs.o
hw/ipmi/ipmi_kcs.c new file +337 -0

337 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#4 lore raw

PATCH 4/16 ipmi: Add a BT low-level interface

In reply to [email protected]

From: Corey Minyard <[email protected]>

This provides the simulation of the BT hardware interface for IPMI.

---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/ipmi_bt.c                  | 374 +++++++++++++++++++++++++++++++++++++
 4 files changed, 377 insertions(+)
 create mode 100644 hw/ipmi/ipmi_bt.c
default-configs/i386-softmmu.mak +1 -0
index 4f28a4c..42dbdc8 100644
@@ -13,6 +13,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
default-configs/x86_64-softmmu.mak +1 -0
index 64bc16b..1c02ce7 100644
@@ -13,6 +13,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
hw/ipmi/Makefile.objs +1 -0
index 3c811ae..a8cd463 100644
@@ -1,3 +1,4 @@
 common-obj-$(CONFIG_IPMI) += ipmi.o
 common-obj-$(CONFIG_ISA_IPMI) += isa_ipmi.o
 common-obj-$(CONFIG_IPMI_KCS) += ipmi_kcs.o
+common-obj-$(CONFIG_IPMI_BT) += ipmi_bt.o
hw/ipmi/ipmi_bt.c new file +374 -0

374 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#5 lore raw

PATCH 5/16 ipmi: Add a local BMC simulation

In reply to [email protected]

From: Corey Minyard <[email protected]>

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).

---
 default-configs/i386-softmmu.mak   |    1 +
 default-configs/x86_64-softmmu.mak |    1 +
 hw/ipmi/Makefile.objs              |    1 +
 hw/ipmi/ipmi_sim.c                 | 1728 ++++++++++++++++++++++++++++++++++++
 4 files changed, 1731 insertions(+)
 create mode 100644 hw/ipmi/ipmi_sim.c
default-configs/i386-softmmu.mak +1 -0
index 42dbdc8..47a1b3b 100644
@@ -14,6 +14,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
default-configs/x86_64-softmmu.mak +1 -0
index 1c02ce7..9cda1d9 100644
@@ -14,6 +14,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
hw/ipmi/Makefile.objs +1 -0
index a8cd463..2366160 100644
@@ -2,3 +2,4 @@ common-obj-$(CONFIG_IPMI) += ipmi.o
 common-obj-$(CONFIG_ISA_IPMI) += isa_ipmi.o
 common-obj-$(CONFIG_IPMI_KCS) += ipmi_kcs.o
 common-obj-$(CONFIG_IPMI_BT) += ipmi_bt.o
+common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_sim.o
hw/ipmi/ipmi_sim.c new file +1728 -0

1,728 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#6 lore raw

PATCH 6/16 ipmi: Add an external connection simulation interface

In reply to [email protected]

From: Corey Minyard <[email protected]>

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.

---
 default-configs/i386-softmmu.mak   |   1 +
 default-configs/x86_64-softmmu.mak |   1 +
 hw/ipmi/Makefile.objs              |   1 +
 hw/ipmi/ipmi_extern.c              | 457 +++++++++++++++++++++++++++++++++++++
 4 files changed, 460 insertions(+)
 create mode 100644 hw/ipmi/ipmi_extern.c
default-configs/i386-softmmu.mak +1 -0
index 47a1b3b..4fb0456 100644
@@ -15,6 +15,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
default-configs/x86_64-softmmu.mak +1 -0
index 9cda1d9..623c328 100644
@@ -15,6 +15,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
hw/ipmi/Makefile.objs +1 -0
index 2366160..9dd05a8 100644
@@ -3,3 +3,4 @@ common-obj-$(CONFIG_ISA_IPMI) += isa_ipmi.o
 common-obj-$(CONFIG_IPMI_KCS) += ipmi_kcs.o
 common-obj-$(CONFIG_IPMI_BT) += ipmi_bt.o
 common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_sim.o
+common-obj-$(CONFIG_IPMI_EXTERN) += ipmi_extern.o
hw/ipmi/ipmi_extern.c new file +457 -0

457 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#7 lore raw

PATCH 7/16 ipmi: Add tests

In reply to [email protected]

From: Corey Minyard <[email protected]>

Test the KCS interface with a local BMC and a BT interface with an external BMC.

---
 tests/Makefile        |   4 +
 tests/ipmi-bt-test.c  | 440 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/ipmi-kcs-test.c | 294 +++++++++++++++++++++++++++++++++
 3 files changed, 738 insertions(+)
 create mode 100644 tests/ipmi-bt-test.c
 create mode 100644 tests/ipmi-kcs-test.c
tests/Makefile +4 -0
index 16f0e4c..54cfda5 100644
@@ -138,6 +138,8 @@ gcov-files-i386-y += hw/block/hd-geometry.c
 check-qtest-i386-y += tests/boot-order-test$(EXESUF)
 check-qtest-i386-y += tests/bios-tables-test$(EXESUF)
 check-qtest-i386-y += tests/rtc-test$(EXESUF)
+check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF)
+check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF)
 check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
@@ -310,6 +312,8 @@ tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y)
 tests/fdc-test$(EXESUF): tests/fdc-test.o
 tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
 tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y)
+tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o
+tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
 tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y)
tests/ipmi-bt-test.c new file +440 -0

440 lines not on this page · raw message

tests/ipmi-kcs-test.c new file +294 -0

294 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#8 lore raw

PATCH 8/16 ipmi: Add documentation

In reply to [email protected]

From: Corey Minyard <[email protected]>

Add some basic documentation for the IPMI device.

---
 qemu-options.hx | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
qemu-options.hx +35 -0
index 10b9568..84c3a5d 100644
@@ -350,6 +350,41 @@ Add device @var{driver}.  @var{prop}=@var{value} sets driver
 properties.  Valid properties depend on the driver.  To get help on
 possible drivers and properties, use @code{-device help} and
 @code{-device @var{driver},help}.
+
+Some drivers are:
+@item -device isa-ipmi[,interface=kcs|bt][,iobase=@var{val}][,irq=@var{val}][,slave_addr=@var{val}][,chardev=name]
+
+Add an IPMI device.  This also adds a corresponding SMBIOS entry to the
+SMBIOS tables for x86.  The following options are handled:
+@table @option
+@item interface=kcs|bt
+Define the interface type to use.  Currently the IPMI-defined KCS and
+BT interfaces are handled.  The default is KCS.
+@item iobase=@var{val}
+Define the I/O address of the interface.  The default is 0xca0 for KCS
+and 0xe4 for BT.
+@item irq=@var{val}
+Define the interrupt to use.  The default is 5.  To disable interrupts,
+set this to 0.
+@item slave_addr=@var{val}
+The IPMI slave address to use for the BMC.  The default is 0x20.
+@item chardev=name
+If a chardev is not specified, the IPMI driver uses a built-in baseboard
+management controller (BMC) simulator.  It provides a basic BMC with a
+watchdog timer and associated sensor.
+
+If a chardev is specified, A connection is made to an external BMC
+simulator.  If you do this, it is strongly recommended that you use
+the "reconnect=" chardev option to reconnect to the simulator if the
+connection is lost.  Note that if this is not used carefully, it can
+be a security issue, as the interface has the ability to send resets,
+NMIs, and power off the VM.  It's best if QEMU makes a connection to
+an external simulator running on a secure port on localhost, so
+neither the simulator nor QEMU is exposed to any outside network.
+
+See the "lanserv/README.vm" file in the OpenIPMI library for more
+details on the external interface.
+@end table
 ETEXI
 
 DEF("name", HAS_ARG, QEMU_OPTION_name,
Signature · 1 line
1.8.3.1
#9 lore raw

PATCH 9/16 ipmi: Add migration capability to the IPMI device.

In reply to [email protected]

From: Corey Minyard <[email protected]>

---
 hw/ipmi/ipmi.c        | 17 +++++++++++++++++
 hw/ipmi/ipmi.h        |  2 ++
 hw/ipmi/ipmi_bt.c     | 14 ++++++++++++++
 hw/ipmi/ipmi_extern.c | 42 ++++++++++++++++++++++++++++++++++++++----
 hw/ipmi/ipmi_kcs.c    | 15 +++++++++++++++
 hw/ipmi/ipmi_sim.c    | 30 ++++++++++++++++++++++++++++++
 hw/ipmi/isa_ipmi.c    | 12 ++++++++++++
 7 files changed, 128 insertions(+), 4 deletions(-)
hw/ipmi/ipmi.c +17 -0
index b046517..f3e5e9e 100644
@@ -118,6 +118,23 @@ void ipmi_bmc_init(IPMIBmc *s, Error **errp)
     }
 }
 
+const VMStateDescription vmstate_IPMIInterface = {
+    .name = TYPE_IPMI_INTERFACE,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(obf_irq_set, IPMIInterface),
+        VMSTATE_BOOL(atn_irq_set, IPMIInterface),
+        VMSTATE_BOOL(use_irq, IPMIInterface),
+        VMSTATE_BOOL(irqs_enabled, IPMIInterface),
+        VMSTATE_UINT32(outpos, IPMIInterface),
+        VMSTATE_UINT32(outlen, IPMIInterface),
+        VMSTATE_VBUFFER_UINT32(inmsg, IPMIInterface, 1, NULL, 0, inlen),
+        VMSTATE_BOOL(write_end, IPMIInterface),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static TypeInfo ipmi_bmc_type_info = {
     .name = TYPE_IPMI_BMC,
     .parent = TYPE_OBJECT,
hw/ipmi/ipmi.h +2 -0
index 6b2a3e9..56cb423 100644
@@ -153,6 +153,8 @@ typedef struct IPMIInterfaceClass {
                        unsigned char *rsp, unsigned int rsp_len);
 } IPMIInterfaceClass;
 
+extern const VMStateDescription vmstate_IPMIInterface;
+
 void ipmi_interface_init(IPMIInterface *s, Error **errp);
 void ipmi_interface_reset(IPMIInterface *s);
 
hw/ipmi/ipmi_bt.c +14 -0
index 95beb71..105b978 100644
@@ -335,6 +335,19 @@ static void ipmi_bt_handle_reset(IPMIInterface *s, bool is_cold)
     }
 }
 
+static const VMStateDescription vmstate_ipmi_bt = {
+    .name = TYPE_IPMI_INTERFACE_BT,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(control_reg, IPMIBtInterface),
+        VMSTATE_UINT8(mask_reg, IPMIBtInterface),
+        VMSTATE_UINT8(waiting_rsp, IPMIBtInterface),
+        VMSTATE_UINT8(waiting_seq, IPMIBtInterface),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void ipmi_bt_init(IPMIInterface *s, Error **errp)
 {
     IPMIBtInterface *bt = IPMI_INTERFACE_BT(s);
@@ -345,6 +358,7 @@ static void ipmi_bt_init(IPMIInterface *s, Error **errp)
     s->io_length = 3;
 
     memory_region_init_io(&s->io, NULL, &ipmi_bt_io_ops, bt, "ipmi-bt", 3);
+    vmstate_register(NULL, 0, &vmstate_ipmi_bt, bt);
 }
 
 static void ipmi_bt_class_init(ObjectClass *klass, void *data)
hw/ipmi/ipmi_extern.c +38 -4

60 lines not on this page · raw message

hw/ipmi/ipmi_kcs.c +15 -0
index 411799e..e3af39e 100644
@@ -299,6 +299,20 @@ static void ipmi_kcs_set_atn(IPMIInterface *s, int val, int irq)
     }
 }
 
+static const VMStateDescription vmstate_ipmi_kcs = {
+    .name = TYPE_IPMI_INTERFACE_KCS,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(status_reg, IPMIKcsInterface),
+        VMSTATE_UINT8(data_out_reg, IPMIKcsInterface),
+        VMSTATE_INT16(data_in_reg, IPMIKcsInterface),
+        VMSTATE_INT16(cmd_reg, IPMIKcsInterface),
+        VMSTATE_UINT8(waiting_rsp, IPMIKcsInterface),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void ipmi_kcs_init(IPMIInterface *s, Error **errp)
 {
     IPMIKcsInterface *kcs = IPMI_INTERFACE_KCS(s);
@@ -309,6 +323,7 @@ static void ipmi_kcs_init(IPMIInterface *s, Error **errp)
     s->io_length = 2;
 
     memory_region_init_io(&s->io, NULL, &ipmi_kcs_io_ops, kcs, "ipmi-kcs", 2);
+    vmstate_register(NULL, 0, &vmstate_ipmi_kcs, kcs);
 }
 
 static void ipmi_kcs_class_init(ObjectClass *class, void *data)
hw/ipmi/ipmi_sim.c +30 -0
index 8028f40..7480d00 100644
@@ -1657,6 +1657,34 @@ static const uint8_t init_sdrs[] = {
     0xff, 0xff, 0x00, 0x00, 0x00
 };
 
+static const VMStateDescription vmstate_ipmi_sim = {
+    .name = TYPE_IPMI_BMC_SIMULATOR,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(bmc_global_enables, IPMISimBmc),
+        VMSTATE_UINT8(msg_flags, IPMISimBmc),
+        VMSTATE_BOOL(watchdog_initialized, IPMISimBmc),
+        VMSTATE_UINT8(watchdog_use, IPMISimBmc),
+        VMSTATE_UINT8(watchdog_action, IPMISimBmc),
+        VMSTATE_UINT8(watchdog_pretimeout, IPMISimBmc),
+        VMSTATE_BOOL(watchdog_expired, IPMISimBmc),
+        VMSTATE_UINT16(watchdog_timeout, IPMISimBmc),
+        VMSTATE_BOOL(watchdog_running, IPMISimBmc),
+        VMSTATE_BOOL(watchdog_preaction_ran, IPMISimBmc),
+        VMSTATE_INT64(watchdog_expiry, IPMISimBmc),
+        VMSTATE_UINT8_ARRAY(evtbuf, IPMISimBmc, 16),
+        VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].status, IPMISimBmc),
+        VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].reading, IPMISimBmc),
+        VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].states, IPMISimBmc),
+        VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_states, IPMISimBmc),
+        VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].deassert_states,
+                       IPMISimBmc),
+        VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_enable, IPMISimBmc),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void ipmi_sim_init(IPMIBmc *b, Error **errp)
 {
     unsigned int i;
@@ -1703,6 +1731,8 @@ static void ipmi_sim_init(IPMIBmc *b, Error **errp)
     register_cmds(ss);
 
     ss->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ss);
+
+    vmstate_register(NULL, 0, &vmstate_ipmi_sim, ss);
 }
 
 static void ipmi_sim_class_init(ObjectClass *klass, void *data)
hw/ipmi/isa_ipmi.c +12 -0
index 1c1ab8d..e62f744 100644
@@ -121,11 +121,23 @@ static Property ipmi_isa_properties[] = {
     DEFINE_PROP_END_OF_LIST(),
 };
 
+static const VMStateDescription vmstate_isa_ipmi = {
+    .name = TYPE_ISA_IPMI,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT_POINTER(intf, ISAIPMIDevice, vmstate_IPMIInterface,
+                               IPMIInterface),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void ipmi_isa_class_initfn(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     dc->realize = ipmi_isa_realizefn;
     dc->reset = ipmi_isa_reset;
+    dc->vmsd = &vmstate_isa_ipmi;
     dc->props = ipmi_isa_properties;
 }
 
Signature · 1 line
1.8.3.1
#10 lore raw

PATCH 10/16 pc: Postpone adding ACPI and SMBIOS to fw_cfg

In reply to [email protected]

From: Corey Minyard <[email protected]>

Postpone the addition of the ACPI and SMBIOS tables until after device initialization. This allows devices to add entries to these tables.

---
 hw/i386/pc.c | 58 ++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 40 insertions(+), 18 deletions(-)
hw/i386/pc.c +40 -18
index f31d55e..831d421 100644
@@ -639,11 +639,45 @@ static unsigned int pc_apic_id_limit(unsigned int max_cpus)
     return x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
 }
 
-static FWCfgState *bochs_bios_init(void)
+struct pc_bios_post_init {
+    Notifier post_init;
+    void *fw_cfg;
+};
+static struct pc_bios_post_init post_init;
+
+/* Add the ACPI and SMBIOS tables after all the hardware has been initialized.
+ * This gives devices a chance to add to those tables.
+ */
+static void pc_bios_post_initfn(Notifier *n, void *opaque)
 {
-    FWCfgState *fw_cfg;
+    struct pc_bios_post_init *p = container_of(n, struct pc_bios_post_init,
+                                               post_init);
     uint8_t *smbios_tables, *smbios_anchor;
     size_t smbios_tables_len, smbios_anchor_len;
+    FWCfgState *fw_cfg = p->fw_cfg;
+
+    smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
+    if (smbios_tables) {
+        fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
+                         smbios_tables, smbios_tables_len);
+    }
+
+    smbios_get_tables(&smbios_tables, &smbios_tables_len,
+                      &smbios_anchor, &smbios_anchor_len);
+    if (smbios_anchor) {
+        fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
+                        smbios_tables, smbios_tables_len);
+        fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
+                        smbios_anchor, smbios_anchor_len);
+    }
+
+    fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES,
+                     acpi_tables, acpi_tables_len);
+}
+
+static FWCfgState *bochs_bios_init(void)
+{
+    FWCfgState *fw_cfg;
     uint64_t *numa_fw_cfg;
     int i, j;
     unsigned int apic_id_limit = pc_apic_id_limit(max_cpus);
@@ -666,24 +700,8 @@ static FWCfgState *bochs_bios_init(void)
     fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit);
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
-    fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES,
-                     acpi_tables, acpi_tables_len);
     fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override());
 
-    smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
-    if (smbios_tables) {
-        fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
-                         smbios_tables, smbios_tables_len);
-    }
-
-    smbios_get_tables(&smbios_tables, &smbios_tables_len,
-                      &smbios_anchor, &smbios_anchor_len);
-    if (smbios_anchor) {
-        fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
-                        smbios_tables, smbios_tables_len);
-        fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
-                        smbios_anchor, smbios_anchor_len);
-    }
 
     fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE,
                      &e820_reserve, sizeof(e820_reserve));
@@ -714,6 +732,10 @@ static FWCfgState *bochs_bios_init(void)
                      (1 + apic_id_limit + nb_numa_nodes) *
                      sizeof(*numa_fw_cfg));
 
+    post_init.fw_cfg = fw_cfg;
+    post_init.post_init.notify = pc_bios_post_initfn;
+    qemu_add_machine_init_done_notifier(&post_init.post_init);
+
     return fw_cfg;
 }
 
Signature · 1 line
1.8.3.1
#11 lore raw

PATCH 11/16 smbios: Add a function to directly add an entry

In reply to [email protected]

From: Corey Minyard <[email protected]>

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.

---
 hw/i386/smbios.c         | 149 ++++++++++++++++++++++++++++++-----------------
 include/hw/i386/smbios.h |  13 +++++
 2 files changed, 110 insertions(+), 52 deletions(-)
hw/i386/smbios.c +97 -52
index 024e594..1081d3c 100644
@@ -825,6 +825,25 @@ static void smbios_entry_point_setup(void)
     ep.structure_table_address = cpu_to_le32(0);
 }
 
+struct smbios_device_handler {
+    void (*handle_device_table)(void *opaque);
+    void *opaque;
+    struct smbios_device_handler *prev;
+};
+static struct smbios_device_handler *device_handlers;
+
+void smbios_register_device_table_handler(void (*handle_device_table)
+                                                (void *opaque),
+                                          void *opaque)
+{
+    struct smbios_device_handler *handler = g_malloc(sizeof(*handler));
+
+    handler->handle_device_table = handle_device_table;
+    handler->opaque = opaque;
+    handler->prev = device_handlers;
+    device_handlers = handler;
+}
+
 void smbios_get_tables(uint8_t **tables, size_t *tables_len,
                        uint8_t **anchor, size_t *anchor_len)
 {
@@ -868,6 +887,14 @@ void smbios_get_tables(uint8_t **tables, size_t *tables_len,
         }
 
         smbios_build_type_32_table();
+
+        while (device_handlers) {
+            struct smbios_device_handler *handler = device_handlers;
+            device_handlers = handler->prev;
+            handler->handle_device_table(handler->opaque);
+            g_free(handler);
+        }
+
         smbios_build_type_127_table();
 
         smbios_validate_table();
@@ -891,6 +918,71 @@ static void save_opt(const char **dest, QemuOpts *opts, const char *name)
     }
 }
 
+int smbios_table_entry_add(void *data, int size, bool append_zeros)
+{
+    struct smbios_structure_header *header;
+    struct smbios_table *table; /* legacy mode only */
+
+    /*
+     * NOTE: standard double '\0' terminator expected, per smbios spec.
+     * (except in legacy mode, where the second '\0' is implicit and
+     *  will be inserted by the BIOS).
+     */
+    if (append_zeros) {
+        size += 2;
+    }
+    smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size);
+    header = (struct smbios_structure_header *)(smbios_tables +
+                                                smbios_tables_len);
+
+    memcpy(header, data, size);
+
+    if (test_bit(header->type, have_fields_bitmap)) {
+        error_report("can't load type %d struct, fields already specified!",
+                     header->type);
+        exit(1);
+    }
+    set_bit(header->type, have_binfile_bitmap);
+
+    if (header->type == 4) {
+        smbios_type4_count++;
+    }
+
+    smbios_tables_len += size;
+    if (size > smbios_table_max) {
+        smbios_table_max = size;
+    }
+    smbios_table_cnt++;
+
+    /* add a copy of the newly loaded blob to legacy smbios_entries */
+    /* NOTE: This code runs before smbios_set_defaults(), so we don't
+     *       yet know which mode (legacy vs. aggregate-table) will be
+     *       required. We therefore add the binary blob to both legacy
+     *       (smbios_entries) and aggregate (smbios_tables) tables, and
+     *       delete the one we don't need from smbios_set_defaults(),
+     *       once we know which machine version has been requested.
+     */
+    if (!smbios_entries) {
+        smbios_entries_len = sizeof(uint16_t);
+        smbios_entries = g_malloc0(smbios_entries_len);
+    }
+    if (append_zeros) {
+        size -= 1; /* The BIOS adds the second zero in legacy mode. */
+    }
+    smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
+                               size + sizeof(*table));
+    table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
+    table->header.type = SMBIOS_TABLE_ENTRY;
+    table->header.length = cpu_to_le16(sizeof(*table) + size);
+    memcpy(table->data, header, size);
+    smbios_entries_len += sizeof(*table) + size;
+    (*(uint16_t *)smbios_entries) =
+                cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+    /* end: add a copy of the newly loaded blob to legacy smbios_entries */
+
+    return 0;
+}
+
 void smbios_entry_add(QemuOpts *opts)
 {
     Error *local_err = NULL;
@@ -900,9 +992,8 @@ void smbios_entry_add(QemuOpts *opts)
 
     val = qemu_opt_get(opts, "file");
     if (val) {
-        struct smbios_structure_header *header;
         int size;
-        struct smbios_table *table; /* legacy mode only */
+        uint8_t *data;
 
         qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err);
         if (local_err) {
@@ -916,60 +1007,14 @@ void smbios_entry_add(QemuOpts *opts)
             exit(1);
         }
 
-        /*
-         * NOTE: standard double '\0' terminator expected, per smbios spec.
-         * (except in legacy mode, where the second '\0' is implicit and
-         *  will be inserted by the BIOS).
-         */
-        smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size);
-        header = (struct smbios_structure_header *)(smbios_tables +
-                                                    smbios_tables_len);
-
-        if (load_image(val, (uint8_t *)header) != size) {
+        data = g_malloc(size);
+        if (load_image(val, data) != size) {
             error_report("Failed to load SMBIOS file %s", val);
             exit(1);
         }
 
-        if (test_bit(header->type, have_fields_bitmap)) {
-            error_report("can't load type %d struct, fields already specified!",
-                         header->type);
-            exit(1);
-        }
-        set_bit(header->type, have_binfile_bitmap);
-
-        if (header->type == 4) {
-            smbios_type4_count++;
-        }
-
-        smbios_tables_len += size;
-        if (size > smbios_table_max) {
-            smbios_table_max = size;
-        }
-        smbios_table_cnt++;
-
-        /* add a copy of the newly loaded blob to legacy smbios_entries */
-        /* NOTE: This code runs before smbios_set_defaults(), so we don't
-         *       yet know which mode (legacy vs. aggregate-table) will be
-         *       required. We therefore add the binary blob to both legacy
-         *       (smbios_entries) and aggregate (smbios_tables) tables, and
-         *       delete the one we don't need from smbios_set_defaults(),
-         *       once we know which machine version has been requested.
-         */
-        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 +
-                                                   size + sizeof(*table));
-        table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
-        table->header.type = SMBIOS_TABLE_ENTRY;
-        table->header.length = cpu_to_le16(sizeof(*table) + size);
-        memcpy(table->data, header, size);
-        smbios_entries_len += sizeof(*table) + size;
-        (*(uint16_t *)smbios_entries) =
-                cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
-        /* end: add a copy of the newly loaded blob to legacy smbios_entries */
-
+        smbios_table_entry_add(data, size, false);
+        g_free(data);
         return;
     }
 
include/hw/i386/smbios.h +13 -0
index d2850be..656327d 100644
@@ -27,6 +27,19 @@ void smbios_get_tables(uint8_t **tables, size_t *tables_len,
                        uint8_t **anchor, size_t *anchor_len);
 
 /*
+ * Add an external entry to the SMBIOS table.  Can only be called
+ * from a registered device table handler.
+ */
+int smbios_table_entry_add(void *data, int size, bool append_zeros);
+
+/*
+ * When constructing SMBIOS tables, call a function at the end of the
+ * add process to allow devices to add their own SMBIOS table entries.
+ */
+void smbios_register_device_table_handler(void (*handle_device_table)
+                                                (void *opaque),
+                                          void *opaque);
+/*
  * SMBIOS spec defined tables
  */
 
Signature · 1 line
1.8.3.1
#12 lore raw

PATCH 12/16 ipmi: Add SMBIOS table entry

In reply to [email protected]

From: Corey Minyard <[email protected]>

Add an IPMI table entry to the SMBIOS.

---
 hw/ipmi/isa_ipmi.c | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
hw/ipmi/isa_ipmi.c +43 -0
index e62f744..83ea706 100644
@@ -27,6 +27,7 @@
 #include "qemu/timer.h"
 #include "sysemu/char.h"
 #include "sysemu/sysemu.h"
+#include "hw/i386/smbios.h"
 #include "ipmi.h"
 
 /* This is the type the user specifies on the -device command line */
@@ -36,13 +37,50 @@
 typedef struct ISAIPMIDevice {
     ISADevice dev;
     char *interface;
+    int intftype;
     uint32_t iobase;
     int32 isairq;
     uint8_t slave_addr;
+    uint8_t version;
     CharDriverState *chr;
     IPMIInterface *intf;
 } ISAIPMIDevice;
 
+/* SMBIOS type 38 - IPMI */
+struct smbios_type_38 {
+    struct smbios_structure_header header;
+    uint8_t interface_type;
+    uint8_t ipmi_spec_revision;
+    uint8_t i2c_slave_address;
+    uint8_t nv_storage_device_address;
+    uint64_t base_address;
+    uint8_t base_address_modifier;
+    uint8_t interrupt_number;
+} QEMU_PACKED;
+
+static void ipmi_encode_smbios(void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    struct smbios_type_38 smb38;
+
+    smb38.header.type = 38;
+    smb38.header.length = sizeof(smb38);
+    smb38.header.handle = cpu_to_le16(0x3000);
+    smb38.interface_type = info->intftype;
+    smb38.ipmi_spec_revision = info->version;
+    smb38.i2c_slave_address = info->slave_addr;
+    smb38.nv_storage_device_address = 0;
+
+    /* or 1 to set it to I/O space */
+    smb38.base_address = cpu_to_le64(info->iobase | 1);
+
+     /* 1-byte boundaries, addr bit0=0, level triggered irq */
+    smb38.base_address_modifier = 1;
+    smb38.interrupt_number = info->isairq;
+    smbios_table_entry_add((struct smbios_structure_header *) &smb38,
+                           sizeof(smb38), true);
+}
+
 static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
 {
     ISADevice *isadev = ISA_DEVICE(dev);
@@ -50,6 +88,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     char typename[20];
     Object *intfobj;
     IPMIInterface *intf;
+    IPMIInterfaceClass *intfk;
     Object *bmcobj;
     IPMIBmc *bmc;
 
@@ -68,10 +107,13 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
              TYPE_IPMI_INTERFACE_PREFIX "%s", ipmi->interface);
     intfobj = object_new(typename);
     intf = IPMI_INTERFACE(intfobj);
+    intfk = IPMI_INTERFACE_GET_CLASS(intf);
     bmc->intf = intf;
     intf->bmc = bmc;
     intf->io_base = ipmi->iobase;
     intf->slave_addr = ipmi->slave_addr;
+    ipmi->intftype = intfk->smbios_type;
+    ipmi->version = 0x20; /* Version 2.0 */
     ipmi_interface_init(intf, errp);
     if (*errp) {
         return;
@@ -103,6 +145,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     qdev_set_legacy_instance_id(dev, intf->io_base, intf->io_length);
 
     isa_register_ioport(isadev, &intf->io, intf->io_base);
+    smbios_register_device_table_handler(ipmi_encode_smbios, ipmi);
 }
 
 static void ipmi_isa_reset(DeviceState *qdev)
Signature · 1 line
1.8.3.1
#13 lore raw

Re: [Qemu-devel] [PATCH 12/16] ipmi: Add SMBIOS table entry

Quoted 3 lines · On Fri, 2014-12-12 at 13:15 -0600, [email protected] wrote:

From: Corey Minyard <[email protected]>

Add an IPMI table entry to the SMBIOS.

Can this be made optional ? I'd like to use the BMC on an OpenPower model I'm working on but that has no smbios..

Cheers,
Ben.

Quoted 98 lines
---
 hw/ipmi/isa_ipmi.c | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
hw/ipmi/isa_ipmi.c +43 -0
index e62f744..83ea706 100644
@@ -27,6 +27,7 @@
 #include "qemu/timer.h"
 #include "sysemu/char.h"
 #include "sysemu/sysemu.h"
+#include "hw/i386/smbios.h"
 #include "ipmi.h"
 
 /* This is the type the user specifies on the -device command line */
@@ -36,13 +37,50 @@
 typedef struct ISAIPMIDevice {
     ISADevice dev;
     char *interface;
+    int intftype;
     uint32_t iobase;
     int32 isairq;
     uint8_t slave_addr;
+    uint8_t version;
     CharDriverState *chr;
     IPMIInterface *intf;
 } ISAIPMIDevice;
 
+/* SMBIOS type 38 - IPMI */
+struct smbios_type_38 {
+    struct smbios_structure_header header;
+    uint8_t interface_type;
+    uint8_t ipmi_spec_revision;
+    uint8_t i2c_slave_address;
+    uint8_t nv_storage_device_address;
+    uint64_t base_address;
+    uint8_t base_address_modifier;
+    uint8_t interrupt_number;
+} QEMU_PACKED;
+
+static void ipmi_encode_smbios(void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    struct smbios_type_38 smb38;
+
+    smb38.header.type = 38;
+    smb38.header.length = sizeof(smb38);
+    smb38.header.handle = cpu_to_le16(0x3000);
+    smb38.interface_type = info->intftype;
+    smb38.ipmi_spec_revision = info->version;
+    smb38.i2c_slave_address = info->slave_addr;
+    smb38.nv_storage_device_address = 0;
+
+    /* or 1 to set it to I/O space */
+    smb38.base_address = cpu_to_le64(info->iobase | 1);
+
+     /* 1-byte boundaries, addr bit0=0, level triggered irq */
+    smb38.base_address_modifier = 1;
+    smb38.interrupt_number = info->isairq;
+    smbios_table_entry_add((struct smbios_structure_header *) &smb38,
+                           sizeof(smb38), true);
+}
+
 static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
 {
     ISADevice *isadev = ISA_DEVICE(dev);
@@ -50,6 +88,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     char typename[20];
     Object *intfobj;
     IPMIInterface *intf;
+    IPMIInterfaceClass *intfk;
     Object *bmcobj;
     IPMIBmc *bmc;
 
@@ -68,10 +107,13 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
              TYPE_IPMI_INTERFACE_PREFIX "%s", ipmi->interface);
     intfobj = object_new(typename);
     intf = IPMI_INTERFACE(intfobj);
+    intfk = IPMI_INTERFACE_GET_CLASS(intf);
     bmc->intf = intf;
     intf->bmc = bmc;
     intf->io_base = ipmi->iobase;
     intf->slave_addr = ipmi->slave_addr;
+    ipmi->intftype = intfk->smbios_type;
+    ipmi->version = 0x20; /* Version 2.0 */
     ipmi_interface_init(intf, errp);
     if (*errp) {
         return;
@@ -103,6 +145,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     qdev_set_legacy_instance_id(dev, intf->io_base, intf->io_length);
 
     isa_register_ioport(isadev, &intf->io, intf->io_base);
+    smbios_register_device_table_handler(ipmi_encode_smbios, ipmi);
 }
 
 static void ipmi_isa_reset(DeviceState *qdev)
#14 lore raw

PATCH 13/16 acpi: Add a way to extend tables

In reply to [email protected]

From: Corey Minyard <[email protected]>

Add a function that can extend the contents of a given ACPI table to add on new entries. This way devices that have ACPI entries can add them.

---
 hw/acpi/core.c         | 106 +++++++++++++++++++++++++++++++++----------------
 include/hw/acpi/acpi.h |   2 +
 2 files changed, 74 insertions(+), 34 deletions(-)
hw/acpi/core.c +72 -34
index 51913d6..a4cb485 100644
@@ -78,33 +78,16 @@ static int acpi_checksum(const uint8_t *data, int len)
     return (-sum) & 0xff;
 }
 
-
-/* Install a copy of the ACPI table specified in @blob.
- *
- * If @has_header is set, @blob starts with the System Description Table Header
- * structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field
- * is optionally overwritten from @hdrs.
- *
- * It is valid to call this function with
- * (@blob == NULL && bloblen == 0 && !has_header).
- *
- * @hdrs->file and @hdrs->data are ignored.
- *
- * SIZE_MAX is considered "infinity" in this function.
- *
- * The number of tables that can be installed is not limited, but the 16-bit
- * counter at the beginning of "acpi_tables" wraps around after UINT16_MAX.
- */
-static void acpi_table_install(const char unsigned *blob, size_t bloblen,
-                               bool has_header,
-                               const struct AcpiTableOptions *hdrs,
-                               Error **errp)
+static struct acpi_table_header *acpi_new_table(const unsigned char *blob,
+                                                size_t bloblen,
+                                                bool has_header,
+                                                size_t *rpayload_size,
+                                                Error **errp)
 {
     size_t body_start;
-    const char unsigned *hdr_src;
+    const unsigned char *hdr_src;
     size_t body_size, acpi_payload_size;
     struct acpi_table_header *ext_hdr;
-    unsigned changed_fields;
 
     /* Calculate where the ACPI table body starts within the blob, plus where
      * to copy the ACPI table header from.
@@ -124,7 +107,7 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen,
             error_setg(errp, "ACPI table claiming to have header is too "
                        "short, available: %zu, expected: %zu", bloblen,
                        body_start);
-            return;
+            return NULL;
         }
         hdr_src = blob;
     } else {
@@ -145,7 +128,7 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen,
     if (acpi_payload_size > UINT16_MAX) {
         error_setg(errp, "ACPI table too big, requested: %zu, max: %u",
                    acpi_payload_size, (unsigned)UINT16_MAX);
-        return;
+        return NULL;
     }
 
     /* We won't fail from here on. Initialize / extend the globals. */
@@ -173,29 +156,63 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen,
     stw_le_p(acpi_tables, lduw_le_p(acpi_tables) + 1u);
 
     /* Update the header fields. The strings need not be NUL-terminated. */
-    changed_fields = 0;
     ext_hdr->_length = cpu_to_le16(acpi_payload_size);
 
-    if (hdrs->has_sig) {
-        strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig);
-        ++changed_fields;
-    }
-
     if (has_header && le32_to_cpu(ext_hdr->length) != acpi_payload_size) {
         fprintf(stderr,
                 "warning: ACPI table has wrong length, header says "
                 "%" PRIu32 ", actual size %zu bytes\n",
                 le32_to_cpu(ext_hdr->length), acpi_payload_size);
     }
+
     ext_hdr->length = cpu_to_le32(acpi_payload_size);
+    *rpayload_size = acpi_payload_size;
+
+    return ext_hdr;
+}
+
+/* Install a copy of the ACPI table specified in @blob.
+ *
+ * If @has_header is set, @blob starts with the System Description Table Header
+ * structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field
+ * is optionally overwritten from @hdrs.
+ *
+ * It is valid to call this function with
+ * (@blob == NULL && bloblen == 0 && !has_header).
+ *
+ * @hdrs->file and @hdrs->data are ignored.
+ *
+ * SIZE_MAX is considered "infinity" in this function.
+ *
+ * The number of tables that can be installed is not limited, but the 16-bit
+ * counter at the beginning of "acpi_tables" wraps around after UINT16_MAX.
+ */
+static void acpi_table_install(const char unsigned *blob, size_t bloblen,
+                               bool has_header,
+                               const struct AcpiTableOptions *hdrs,
+                               Error **errp)
+{
+    struct acpi_table_header *ext_hdr;
+    size_t payload_size = 0;
+    unsigned changed_fields = 0;
+
+    ext_hdr = acpi_new_table(blob, bloblen, has_header, &payload_size, errp);
+    if (errp && !*errp) {
+        return;
+    }
+
+    ext_hdr->checksum = 0;
+
+    if (hdrs->has_sig) {
+        strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig);
+        ++changed_fields;
+    }
 
     if (hdrs->has_rev) {
         ext_hdr->revision = hdrs->rev;
         ++changed_fields;
     }
 
-    ext_hdr->checksum = 0;
-
     if (hdrs->has_oem_id) {
         strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id);
         ++changed_fields;
@@ -225,7 +242,28 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen,
 
     /* recalculate checksum */
     ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr +
-                                      ACPI_TABLE_PFX_SIZE, acpi_payload_size);
+                                      ACPI_TABLE_PFX_SIZE, payload_size);
+}
+
+/*
+ * Add a table from an internal driver.
+ */
+void acpi_append_to_table(const char *sig, void *blob, size_t bloblen,
+                          Error **errp)
+{
+    struct acpi_table_header *ext_hdr;
+    size_t payload_size = 0;
+
+    ext_hdr = acpi_new_table(blob, bloblen, false, &payload_size, errp);
+    if (errp && *errp) {
+        return;
+    }
+
+    strncpy(ext_hdr->sig, sig, sizeof ext_hdr->sig);
+
+    /* recalculate checksum */
+    ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr +
+                                      ACPI_TABLE_PFX_SIZE, payload_size);
 }
 
 void acpi_table_add(const QemuOpts *opts, Error **errp)
include/hw/acpi/acpi.h +2 -0
index 1f678b4..acdd432 100644
@@ -184,5 +184,7 @@ uint8_t *acpi_table_next(uint8_t *current);
 unsigned acpi_table_len(void *current);
 void acpi_table_add(const QemuOpts *opts, Error **errp);
 void acpi_table_add_builtin(const QemuOpts *opts, Error **errp);
+void acpi_append_to_table(const char *sig, void *blob, size_t bloblen,
+                          Error **errp);
 
 #endif /* !QEMU_HW_ACPI_H */
Signature · 1 line
1.8.3.1
#15 lore raw

PATCH 14/16 acpi: Add table construction tools

In reply to [email protected]

From: Corey Minyard <[email protected]>

Add a set of functions to allow construction of ACPI elements dynamically.

---
 hw/acpi/Makefile.objs           |   1 +
 hw/acpi/acpi-elements.c         | 470 ++++++++++++++++++++++++++++++++++++++++
 include/hw/acpi/acpi-elements.h |  80 +++++++
 3 files changed, 551 insertions(+)
 create mode 100644 hw/acpi/acpi-elements.c
 create mode 100644 include/hw/acpi/acpi-elements.h
hw/acpi/Makefile.objs +1 -0
index acd2389..628deac 100644
@@ -1,3 +1,4 @@
 common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o cpu_hotplug.o
 common-obj-$(CONFIG_ACPI) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
+common-obj-$(CONFIG_ACPI) += acpi-elements.o
hw/acpi/acpi-elements.c new file +470 -0

470 lines not on this page · raw message

include/hw/acpi/acpi-elements.h new file +80 -0

80 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#16 lore raw

PATCH 15/16 ipmi: Add ACPI table entries for BMCs

In reply to [email protected]

From: Corey Minyard <[email protected]>

Use the new ACPI table construction tools to create an ACPI entry for IPMI.

---
 hw/ipmi/isa_ipmi.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 145 insertions(+)
hw/ipmi/isa_ipmi.c +145 -0
index 83ea706..1652166 100644
@@ -23,6 +23,8 @@
  */
 #include "hw/hw.h"
 #include "hw/isa/isa.h"
+#include "hw/acpi/acpi-elements.h"
+#include "hw/acpi/acpi.h"
 #include "hw/i386/pc.h"
 #include "qemu/timer.h"
 #include "sysemu/char.h"
@@ -39,6 +41,8 @@ typedef struct ISAIPMIDevice {
     char *interface;
     int intftype;
     uint32_t iobase;
+    uint32_t iolength;
+    uint8_t regspacing;
     int32 isairq;
     uint8_t slave_addr;
     uint8_t version;
@@ -58,6 +62,143 @@ struct smbios_type_38 {
     uint8_t interrupt_number;
 } QEMU_PACKED;
 
+static int
+acpi_ipmi_crs_ops(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len, rv;
+    uint8_t regspacing = info->regspacing;
+
+    if (regspacing == 1) {
+        regspacing = 0;
+    }
+
+    /* IO(Decode16, x, y, z, c) */
+    len = acpi_add_IO16(data, dlen, info->iobase,
+                        info->iobase + info->iolength - 1,
+                        regspacing, info->iolength);
+    if (len < 0) {
+        return len;
+    }
+
+    if (info->isairq) {
+        /* Interrupt(ResourceConsumer,Level,ActiveHigh,Exclusive) {n} */
+        rv = acpi_add_Interrupt(data, dlen, info->isairq,
+                                ACPI_RESOURCE_CONSUMER,
+                                ACPI_INTERRUPT_MODE_LEVEL,
+                                ACPI_INTERRUPT_POLARITY_ACTIVE_HIGH,
+                                ACPI_INTERRUPT_EXCLUSIVE);
+        if (rv < 0) {
+            return rv;
+        }
+        len += rv;
+    }
+    rv = acpi_add_EndResource(data, dlen);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    return len;
+}
+
+static int
+acpi_ipmi_crs(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len;
+
+    len = acpi_add_BufferOp(NULL, 0, acpi_ipmi_crs_ops, info);
+    if (len < 0) {
+        return len;
+    }
+    if (len <= dlen) {
+        acpi_add_BufferOp(data, dlen, acpi_ipmi_crs_ops, info);
+    }
+    return len;
+}
+
+static int
+acpi_ipmi_dev(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len, rv;
+    char *name;
+    uint64_t val;
+
+    name = g_strdup_printf("ipmi_%s", info->interface);
+
+    /* Name(_HID, EISAID("IPI0001")) */
+    len = acpi_add_Name(data, dlen, "_HID", acpi_add_EISAID,
+                        (void *) "IPI0001");
+    if (len < 0) {
+        return len;
+    }
+    /* Name(_STR, Unicode("ipmi_xxx")) */
+    rv = acpi_add_Name(data, dlen, "_STR", acpi_add_Unicode, name);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = 0;
+    /* Name(_UID, 0) */
+    rv = acpi_add_Name(data, dlen, "_UID", acpi_add_Integer, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    /* Name(_CRS, ResourceTemplate() { */
+    rv = acpi_add_Name(data, dlen, "_CRS", acpi_ipmi_crs, info);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = info->intftype;
+    /* Method(_IFT) { Return(i) } */
+    rv = acpi_add_Method(data, dlen, "_IFT", 0, acpi_add_Return, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = ((info->version & 0xf0) << 4) | (info->version & 0x0f);
+    /* Method(_SRV) { Return(version) } */
+    rv = acpi_add_Method(data, dlen, "_SRV", 0, acpi_add_Return, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    return len;
+}
+
+static int
+acpi_ipmi_scope(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+
+    /* Device(MI0) { */
+    return acpi_add_Device(data, dlen, "MI0", acpi_ipmi_dev, info);
+    /* } */
+}
+
+static void
+ipmi_encode_acpi(ISAIPMIDevice *info)
+{
+    char ipmitable[200];
+    char *tblptr = ipmitable;
+    int rc;
+    Error *err = NULL;
+
+    /* Scope(\_SB.PCI0.ISA) { */
+    rc = acpi_add_Scope(&tblptr, sizeof(ipmitable), "\\_SB.PCI0.ISA",
+                         acpi_ipmi_scope, info);
+    /* } */
+    if (rc < 0) {
+        fprintf(stderr, "Unable to format IPMI ACPI table entry\n");
+        return;
+    }
+
+    acpi_append_to_table("SSDT", ipmitable, rc, &err);
+}
+
 static void ipmi_encode_smbios(void *opaque)
 {
     ISAIPMIDevice *info = opaque;
@@ -79,6 +220,8 @@ static void ipmi_encode_smbios(void *opaque)
     smb38.interrupt_number = info->isairq;
     smbios_table_entry_add((struct smbios_structure_header *) &smb38,
                            sizeof(smb38), true);
+
+    ipmi_encode_acpi(info);
 }
 
 static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
@@ -110,6 +253,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     intfk = IPMI_INTERFACE_GET_CLASS(intf);
     bmc->intf = intf;
     intf->bmc = bmc;
+    ipmi->regspacing = 1;
     intf->io_base = ipmi->iobase;
     intf->slave_addr = ipmi->slave_addr;
     ipmi->intftype = intfk->smbios_type;
@@ -118,6 +262,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     if (*errp) {
         return;
     }
+    ipmi->iolength = intf->io_length;
     ipmi_bmc_init(bmc, errp);
     if (*errp) {
         return;
Signature · 1 line
1.8.3.1
#17 lore raw

Re: [Qemu-devel] [PATCH 15/16] ipmi: Add ACPI table entries for BMCs

Quoted 4 lines · On Fri, 2014-12-12 at 13:15 -0600, [email protected] wrote:

From: Corey Minyard <[email protected]>

Use the new ACPI table construction tools to create an ACPI entry for IPMI.

Same question as for the smbios business... Can this be made optional possibly via instanciation attributes for the BMC device ?

Ben.

Quoted 196 lines
---
 hw/ipmi/isa_ipmi.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 145 insertions(+)
hw/ipmi/isa_ipmi.c +145 -0
index 83ea706..1652166 100644
@@ -23,6 +23,8 @@
  */
 #include "hw/hw.h"
 #include "hw/isa/isa.h"
+#include "hw/acpi/acpi-elements.h"
+#include "hw/acpi/acpi.h"
 #include "hw/i386/pc.h"
 #include "qemu/timer.h"
 #include "sysemu/char.h"
@@ -39,6 +41,8 @@ typedef struct ISAIPMIDevice {
     char *interface;
     int intftype;
     uint32_t iobase;
+    uint32_t iolength;
+    uint8_t regspacing;
     int32 isairq;
     uint8_t slave_addr;
     uint8_t version;
@@ -58,6 +62,143 @@ struct smbios_type_38 {
     uint8_t interrupt_number;
 } QEMU_PACKED;
 
+static int
+acpi_ipmi_crs_ops(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len, rv;
+    uint8_t regspacing = info->regspacing;
+
+    if (regspacing == 1) {
+        regspacing = 0;
+    }
+
+    /* IO(Decode16, x, y, z, c) */
+    len = acpi_add_IO16(data, dlen, info->iobase,
+                        info->iobase + info->iolength - 1,
+                        regspacing, info->iolength);
+    if (len < 0) {
+        return len;
+    }
+
+    if (info->isairq) {
+        /* Interrupt(ResourceConsumer,Level,ActiveHigh,Exclusive) {n} */
+        rv = acpi_add_Interrupt(data, dlen, info->isairq,
+                                ACPI_RESOURCE_CONSUMER,
+                                ACPI_INTERRUPT_MODE_LEVEL,
+                                ACPI_INTERRUPT_POLARITY_ACTIVE_HIGH,
+                                ACPI_INTERRUPT_EXCLUSIVE);
+        if (rv < 0) {
+            return rv;
+        }
+        len += rv;
+    }
+    rv = acpi_add_EndResource(data, dlen);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    return len;
+}
+
+static int
+acpi_ipmi_crs(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len;
+
+    len = acpi_add_BufferOp(NULL, 0, acpi_ipmi_crs_ops, info);
+    if (len < 0) {
+        return len;
+    }
+    if (len <= dlen) {
+        acpi_add_BufferOp(data, dlen, acpi_ipmi_crs_ops, info);
+    }
+    return len;
+}
+
+static int
+acpi_ipmi_dev(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len, rv;
+    char *name;
+    uint64_t val;
+
+    name = g_strdup_printf("ipmi_%s", info->interface);
+
+    /* Name(_HID, EISAID("IPI0001")) */
+    len = acpi_add_Name(data, dlen, "_HID", acpi_add_EISAID,
+                        (void *) "IPI0001");
+    if (len < 0) {
+        return len;
+    }
+    /* Name(_STR, Unicode("ipmi_xxx")) */
+    rv = acpi_add_Name(data, dlen, "_STR", acpi_add_Unicode, name);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = 0;
+    /* Name(_UID, 0) */
+    rv = acpi_add_Name(data, dlen, "_UID", acpi_add_Integer, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    /* Name(_CRS, ResourceTemplate() { */
+    rv = acpi_add_Name(data, dlen, "_CRS", acpi_ipmi_crs, info);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = info->intftype;
+    /* Method(_IFT) { Return(i) } */
+    rv = acpi_add_Method(data, dlen, "_IFT", 0, acpi_add_Return, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = ((info->version & 0xf0) << 4) | (info->version & 0x0f);
+    /* Method(_SRV) { Return(version) } */
+    rv = acpi_add_Method(data, dlen, "_SRV", 0, acpi_add_Return, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    return len;
+}
+
+static int
+acpi_ipmi_scope(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+
+    /* Device(MI0) { */
+    return acpi_add_Device(data, dlen, "MI0", acpi_ipmi_dev, info);
+    /* } */
+}
+
+static void
+ipmi_encode_acpi(ISAIPMIDevice *info)
+{
+    char ipmitable[200];
+    char *tblptr = ipmitable;
+    int rc;
+    Error *err = NULL;
+
+    /* Scope(\_SB.PCI0.ISA) { */
+    rc = acpi_add_Scope(&tblptr, sizeof(ipmitable), "\\_SB.PCI0.ISA",
+                         acpi_ipmi_scope, info);
+    /* } */
+    if (rc < 0) {
+        fprintf(stderr, "Unable to format IPMI ACPI table entry\n");
+        return;
+    }
+
+    acpi_append_to_table("SSDT", ipmitable, rc, &err);
+}
+
 static void ipmi_encode_smbios(void *opaque)
 {
     ISAIPMIDevice *info = opaque;
@@ -79,6 +220,8 @@ static void ipmi_encode_smbios(void *opaque)
     smb38.interrupt_number = info->isairq;
     smbios_table_entry_add((struct smbios_structure_header *) &smb38,
                            sizeof(smb38), true);
+
+    ipmi_encode_acpi(info);
 }
 
 static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
@@ -110,6 +253,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     intfk = IPMI_INTERFACE_GET_CLASS(intf);
     bmc->intf = intf;
     intf->bmc = bmc;
+    ipmi->regspacing = 1;
     intf->io_base = ipmi->iobase;
     intf->slave_addr = ipmi->slave_addr;
     ipmi->intftype = intfk->smbios_type;
@@ -118,6 +262,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     if (*errp) {
         return;
     }
+    ipmi->iolength = intf->io_length;
     ipmi_bmc_init(bmc, errp);
     if (*errp) {
         return;
#18 lore raw

Re: [Qemu-devel] [PATCH 15/16] ipmi: Add ACPI table entries for BMCs

Quoted 7 lines · Benjamin Herrenschmidt
Quoted 4 lines · On Fri, 2014-12-12 at 13:15 -0600, [email protected] wrote:

From: Corey Minyard <[email protected]>

Use the new ACPI table construction tools to create an ACPI entry for IPMI.

Same question as for the smbios business... Can this be made optional possibly via instanciation attributes for the BMC device ?

I was thinking about how to do this. I wasn't too terribly concerned right now because none of this ACPI stuff is going in as is, there are other patches for handling this and there's a lot to work out for SMBIOS and ACPI.

However, there is another pressing concern, assuming your device is on the ISA bus, and that's the default address, interrupt, etc. It may vary from platform to platform, and it would be nice to have a way for it to be set more automatically.

If it's not on an ISA bus, another interface will need to be written.

-corey

Quoted 200 lines

Ben.

Quoted 196 lines
---
 hw/ipmi/isa_ipmi.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 145 insertions(+)
hw/ipmi/isa_ipmi.c +145 -0
index 83ea706..1652166 100644
@@ -23,6 +23,8 @@
  */
 #include "hw/hw.h"
 #include "hw/isa/isa.h"
+#include "hw/acpi/acpi-elements.h"
+#include "hw/acpi/acpi.h"
 #include "hw/i386/pc.h"
 #include "qemu/timer.h"
 #include "sysemu/char.h"
@@ -39,6 +41,8 @@ typedef struct ISAIPMIDevice {
     char *interface;
     int intftype;
     uint32_t iobase;
+    uint32_t iolength;
+    uint8_t regspacing;
     int32 isairq;
     uint8_t slave_addr;
     uint8_t version;
@@ -58,6 +62,143 @@ struct smbios_type_38 {
     uint8_t interrupt_number;
 } QEMU_PACKED;
 
+static int
+acpi_ipmi_crs_ops(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len, rv;
+    uint8_t regspacing = info->regspacing;
+
+    if (regspacing == 1) {
+        regspacing = 0;
+    }
+
+    /* IO(Decode16, x, y, z, c) */
+    len = acpi_add_IO16(data, dlen, info->iobase,
+                        info->iobase + info->iolength - 1,
+                        regspacing, info->iolength);
+    if (len < 0) {
+        return len;
+    }
+
+    if (info->isairq) {
+        /* Interrupt(ResourceConsumer,Level,ActiveHigh,Exclusive) {n} */
+        rv = acpi_add_Interrupt(data, dlen, info->isairq,
+                                ACPI_RESOURCE_CONSUMER,
+                                ACPI_INTERRUPT_MODE_LEVEL,
+                                ACPI_INTERRUPT_POLARITY_ACTIVE_HIGH,
+                                ACPI_INTERRUPT_EXCLUSIVE);
+        if (rv < 0) {
+            return rv;
+        }
+        len += rv;
+    }
+    rv = acpi_add_EndResource(data, dlen);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    return len;
+}
+
+static int
+acpi_ipmi_crs(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len;
+
+    len = acpi_add_BufferOp(NULL, 0, acpi_ipmi_crs_ops, info);
+    if (len < 0) {
+        return len;
+    }
+    if (len <= dlen) {
+        acpi_add_BufferOp(data, dlen, acpi_ipmi_crs_ops, info);
+    }
+    return len;
+}
+
+static int
+acpi_ipmi_dev(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+    int len, rv;
+    char *name;
+    uint64_t val;
+
+    name = g_strdup_printf("ipmi_%s", info->interface);
+
+    /* Name(_HID, EISAID("IPI0001")) */
+    len = acpi_add_Name(data, dlen, "_HID", acpi_add_EISAID,
+                        (void *) "IPI0001");
+    if (len < 0) {
+        return len;
+    }
+    /* Name(_STR, Unicode("ipmi_xxx")) */
+    rv = acpi_add_Name(data, dlen, "_STR", acpi_add_Unicode, name);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = 0;
+    /* Name(_UID, 0) */
+    rv = acpi_add_Name(data, dlen, "_UID", acpi_add_Integer, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    /* Name(_CRS, ResourceTemplate() { */
+    rv = acpi_add_Name(data, dlen, "_CRS", acpi_ipmi_crs, info);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = info->intftype;
+    /* Method(_IFT) { Return(i) } */
+    rv = acpi_add_Method(data, dlen, "_IFT", 0, acpi_add_Return, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    val = ((info->version & 0xf0) << 4) | (info->version & 0x0f);
+    /* Method(_SRV) { Return(version) } */
+    rv = acpi_add_Method(data, dlen, "_SRV", 0, acpi_add_Return, &val);
+    if (rv < 0) {
+        return rv;
+    }
+    len += rv;
+    return len;
+}
+
+static int
+acpi_ipmi_scope(char **data, int dlen, void *opaque)
+{
+    ISAIPMIDevice *info = opaque;
+
+    /* Device(MI0) { */
+    return acpi_add_Device(data, dlen, "MI0", acpi_ipmi_dev, info);
+    /* } */
+}
+
+static void
+ipmi_encode_acpi(ISAIPMIDevice *info)
+{
+    char ipmitable[200];
+    char *tblptr = ipmitable;
+    int rc;
+    Error *err = NULL;
+
+    /* Scope(\_SB.PCI0.ISA) { */
+    rc = acpi_add_Scope(&tblptr, sizeof(ipmitable), "\\_SB.PCI0.ISA",
+                         acpi_ipmi_scope, info);
+    /* } */
+    if (rc < 0) {
+        fprintf(stderr, "Unable to format IPMI ACPI table entry\n");
+        return;
+    }
+
+    acpi_append_to_table("SSDT", ipmitable, rc, &err);
+}
+
 static void ipmi_encode_smbios(void *opaque)
 {
     ISAIPMIDevice *info = opaque;
@@ -79,6 +220,8 @@ static void ipmi_encode_smbios(void *opaque)
     smb38.interrupt_number = info->isairq;
     smbios_table_entry_add((struct smbios_structure_header *) &smb38,
                            sizeof(smb38), true);
+
+    ipmi_encode_acpi(info);
 }
 
 static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
@@ -110,6 +253,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     intfk = IPMI_INTERFACE_GET_CLASS(intf);
     bmc->intf = intf;
     intf->bmc = bmc;
+    ipmi->regspacing = 1;
     intf->io_base = ipmi->iobase;
     intf->slave_addr = ipmi->slave_addr;
     ipmi->intftype = intfk->smbios_type;
@@ -118,6 +262,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     if (*errp) {
         return;
     }
+    ipmi->iolength = intf->io_length;
     ipmi_bmc_init(bmc, errp);
     if (*errp) {
         return;
#19 lore raw

Re: [Qemu-devel] [PATCH 15/16] ipmi: Add ACPI table entries for BMCs

Quoted 6 lines · Corey Minyard

However, there is another pressing concern, assuming your device is on the ISA bus, and that's the default address, interrupt, etc. It may vary from platform to platform, and it would be nice to have a way for it to be set more automatically.

If it's not on an ISA bus, another interface will need to be written.

It's on the LPC/ISA bus, and the address/interrupt are in the device-tree which qemu provides but yes, it would make sense to have them be qdev properties to configure them to mimmic actual HW.

In fact I would like them to be qobject properties that can be changed at runtime, but I'll look into that later. The reason is that I want to emulate a bit more of the BMC since our FW uses a few other things in there, and among others, some registers that you can use to ... set/change the ISA address of the BT or KCS interfaces. (In fact same goes with the UART).

But that's for much later, I still have a lot of work to do to get the basic shape of the P8 model in a mergable state, so my comment was more a generic idea of trying to keep the more generic BMC bits reasonably separate from the SMBIOS/ACPI specific portions.

Cheers,
Ben.

#20 lore raw

PATCH 16/16 ipmi: Add a thread to better simulate a BMC

In reply to [email protected]

From: Corey Minyard <[email protected]>

Run the IPMI BMC in a separate thread. This provides a little better simulation, since a BMC will normally be asynchronous to the rest of the system.

---
 hw/ipmi/ipmi.c        | 33 +++++++++++++++++++++++++++++++++
 hw/ipmi/ipmi.h        | 47 +++++++++++++++++++++++++++++++++++++++++++----
 hw/ipmi/ipmi_bt.c     |  6 ++++++
 hw/ipmi/ipmi_extern.c | 17 +++++++++++++++++
 hw/ipmi/ipmi_kcs.c    |  5 +++++
 hw/ipmi/isa_ipmi.c    |  3 +++
 6 files changed, 107 insertions(+), 4 deletions(-)
hw/ipmi/ipmi.c +33 -0
index f3e5e9e..27cf888 100644
@@ -27,6 +27,27 @@
 #include "sysemu/sysemu.h"
 #include "qmp-commands.h"
 
+/*
+ * Create a separate thread for the IPMI interface itself.  This is a
+ * better simulation and lets the IPMI interface do things asynchronously
+ * if necessary.
+ */
+static void *ipmi_thread(void *opaque)
+{
+    IPMIInterface *s = opaque;
+
+    qemu_mutex_lock(&s->lock);
+    for (;;) {
+        qemu_cond_wait(&s->waker, &s->lock);
+        while (s->do_wake) {
+            s->do_wake = 0;
+            (IPMI_INTERFACE_GET_CLASS(s))->handle_if_event(s);
+        }
+    }
+    qemu_mutex_unlock(&s->lock);
+    return NULL;
+}
+
 static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly)
 {
     switch (op) {
@@ -90,6 +111,18 @@ void ipmi_interface_init(IPMIInterface *s, Error **errp)
     if (!s->slave_addr) {
         s->slave_addr = 0x20;
     }
+
+    if (s->threaded_bmc) {
+        qemu_mutex_init(&s->lock);
+        qemu_cond_init(&s->waker);
+        qemu_thread_create(&s->thread, "ipmi-bmc", ipmi_thread, s, 0);
+    }
+
+    if (s->threaded_bmc) {
+        qemu_mutex_init(&s->lock);
+        qemu_cond_init(&s->waker);
+        qemu_thread_create(&s->thread, "ipmi-bmc", ipmi_thread, s, 0);
+    }
 }
 
 static void ipmi_interface_class_init(ObjectClass *class, void *data)
hw/ipmi/ipmi.h +43 -4

85 lines not on this page · raw message

hw/ipmi/ipmi_bt.c +6 -0
index 105b978..aecf6c9 100644
@@ -107,6 +107,7 @@ static void ipmi_bt_handle_event(IPMIInterface *s)
 {
     IPMIBtInterface *bt = IPMI_INTERFACE_BT(s);
 
+    /* ipmi_lock is already claimed. */
     if (s->inlen < 4) {
         goto out;
     }
@@ -165,6 +166,7 @@ static void ipmi_bt_handle_rsp(IPMIInterface *s, uint8_t msg_id,
 {
     IPMIBtInterface *bt = IPMI_INTERFACE_BT(s);
 
+    /* ipmi_lock is already claimed. */
     if (bt->waiting_rsp == msg_id) {
         bt->waiting_rsp++;
         if (rsp_len > (sizeof(s->outmsg) - 2)) {
@@ -199,6 +201,7 @@ static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size)
     IPMIInterface *s = &bt->intf;
     uint32_t ret = 0xff;
 
+    ipmi_lock(s);
     switch (addr & 3) {
     case 0:
         ret = bt->control_reg;
@@ -219,6 +222,7 @@ static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size)
         ret = bt->mask_reg;
         break;
     }
+    ipmi_unlock(s);
     return ret;
 }
 
@@ -228,6 +232,7 @@ static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val,
     IPMIBtInterface *bt = opaque;
     IPMIInterface *s = &bt->intf;
 
+    ipmi_lock(s);
     switch (addr & 3) {
     case 0:
         if (IPMI_BT_GET_CLR_WR(val)) {
@@ -284,6 +289,7 @@ static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val,
         }
         break;
     }
+    ipmi_unlock(s);
 }
 
 static const MemoryRegionOps ipmi_bt_io_ops = {
hw/ipmi/ipmi_extern.c +17 -0
index aace0b3..981a97b 100644
@@ -140,6 +140,7 @@ static void extern_timeout(void *opaque)
     IPMIExternBmc *es = opaque;
     IPMIInterface *s = es->parent.intf;
 
+    ipmi_lock(s);
     if (es->connected) {
         if (es->waiting_rsp && (es->outlen == 0)) {
             IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
@@ -153,6 +154,7 @@ static void extern_timeout(void *opaque)
             continue_send(es);
         }
     }
+    ipmi_unlock(s);
 }
 
 static void addchar(IPMIExternBmc *es, unsigned char ch)
@@ -182,6 +184,7 @@ static void ipmi_extern_handle_command(IPMIBmc *b,
     uint8_t err = 0, csum;
     unsigned int i;
 
+    ipmi_lock(s);
     if (es->outlen) {
         /* We already have a command queued.  Shouldn't ever happen. */
         fprintf(stderr, "IPMI KCS: Got command when not finished with the"
@@ -222,6 +225,7 @@ static void ipmi_extern_handle_command(IPMIBmc *b,
     continue_send(es);
 
  out:
+    ipmi_unlock(s);
     return;
 }
 
@@ -304,9 +308,11 @@ static int can_receive(void *opaque)
 static void receive(void *opaque, const uint8_t *buf, int size)
 {
     IPMIExternBmc *es = opaque;
+    IPMIInterface *s = es->parent.intf;
     int i;
     unsigned char hw_op;
 
+    ipmi_lock(s);
     for (i = 0; i < size; i++) {
         unsigned char ch = buf[i];
 
@@ -361,9 +367,15 @@ static void receive(void *opaque, const uint8_t *buf, int size)
             break;
         }
     }
+    ipmi_unlock(s);
     return;
 
  out_hw_op:
+    ipmi_unlock(s);
+    /*
+     * 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(es, hw_op);
 }
 
@@ -374,6 +386,7 @@ static void chr_event(void *opaque, int event)
     IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
     unsigned char v;
 
+    ipmi_lock(s);
     switch (event) {
     case CHR_EVENT_OPENED:
         es->connected = 1;
@@ -415,14 +428,18 @@ static void chr_event(void *opaque, int event)
         }
         break;
     }
+    ipmi_unlock(s);
 }
 
 static void ipmi_extern_handle_reset(IPMIBmc *b)
 {
     IPMIExternBmc *es = IPMI_BMC_EXTERN(b);
+    IPMIInterface *s = es->parent.intf;
 
+    ipmi_lock(s);
     es->send_reset = 1;
     continue_send(es);
+    ipmi_unlock(s);
 }
 
 static int ipmi_extern_post_migrate(void *opaque, int version_id)
hw/ipmi/ipmi_kcs.c +5 -0
index e3af39e..ef50b83 100644
@@ -197,6 +197,7 @@ static void ipmi_kcs_handle_rsp(IPMIInterface *s, uint8_t msg_id,
 {
     IPMIKcsInterface *kcs = IPMI_INTERFACE_KCS(s);
 
+    /* ipmi_lock is already claimed. */
     if (kcs->waiting_rsp == msg_id) {
         kcs->waiting_rsp++;
         if (rsp_len > sizeof(s->outmsg)) {
@@ -220,6 +221,7 @@ static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size)
     IPMIKcsInterface *kcs = opaque;
     uint32_t ret;
 
+    ipmi_lock(&kcs->intf);
     switch (addr & 1) {
     case 0:
         ret = kcs->data_out_reg;
@@ -241,6 +243,7 @@ static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size)
         }
         break;
     }
+    ipmi_unlock(&kcs->intf);
     return ret;
 }
 
@@ -254,6 +257,7 @@ static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val,
         return;
     }
 
+    ipmi_lock(s);
     switch (addr & 1) {
     case 0:
         kcs->data_in_reg = val;
@@ -265,6 +269,7 @@ static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val,
     }
     IPMI_KCS_SET_IBF(kcs->status_reg, 1);
     ipmi_signal(s);
+    ipmi_unlock(s);
 }
 
 const MemoryRegionOps ipmi_kcs_io_ops = {
hw/ipmi/isa_ipmi.c +3 -0
index 1652166..aa4bb3c 100644
@@ -46,6 +46,7 @@ typedef struct ISAIPMIDevice {
     int32 isairq;
     uint8_t slave_addr;
     uint8_t version;
+    bool threaded_bmc;
     CharDriverState *chr;
     IPMIInterface *intf;
 } ISAIPMIDevice;
@@ -258,6 +259,7 @@ static void ipmi_isa_realizefn(DeviceState *dev, Error **errp)
     intf->slave_addr = ipmi->slave_addr;
     ipmi->intftype = intfk->smbios_type;
     ipmi->version = 0x20; /* Version 2.0 */
+    intf->threaded_bmc = ipmi->threaded_bmc;
     ipmi_interface_init(intf, errp);
     if (*errp) {
         return;
@@ -306,6 +308,7 @@ static Property ipmi_isa_properties[] = {
     DEFINE_PROP_INT32("irq",   ISAIPMIDevice, isairq,  5),
     DEFINE_PROP_UINT8("slave_addr", ISAIPMIDevice, slave_addr,  0),
     DEFINE_PROP_CHR("chardev",  ISAIPMIDevice, chr),
+    DEFINE_PROP_BOOL("threadbmc",  ISAIPMIDevice, threaded_bmc, false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
Signature · 1 line
1.8.3.1
#21 lore raw

Re: [Qemu-devel] [PATCH 16/16] ipmi: Add a thread to better simulate a BMC

hw/ipmi/ipmi.c void ipmi_interface_init(IPMIInterface *s, Error **errp)

Quoted 12 lines · On 12/12/2014 20:15, [email protected] wrote:
hw/ipmi/ipmi.c +12 -0
+    if (s->threaded_bmc) {
+        qemu_mutex_init(&s->lock);
+        qemu_cond_init(&s->waker);
+        qemu_thread_create(&s->thread, "ipmi-bmc", ipmi_thread, s, 0);
+    }
+
+    if (s->threaded_bmc) {
+        qemu_mutex_init(&s->lock);
+        qemu_cond_init(&s->waker);
+        qemu_thread_create(&s->thread, "ipmi-bmc", ipmi_thread, s, 0);
+    }
+

Duplicated bit---probably a rebase hiccup?

Paolo

#22 lore raw

Re: [Qemu-devel] [PATCH 16/16] ipmi: Add a thread to better simulate a BMC

Quoted 15 lines · Paolo Bonzini

hw/ipmi/ipmi.c void ipmi_interface_init(IPMIInterface *s, Error **errp)

Quoted 12 lines · On 12/12/2014 20:15, [email protected] wrote:
hw/ipmi/ipmi.c +12 -0
+    if (s->threaded_bmc) {
+        qemu_mutex_init(&s->lock);
+        qemu_cond_init(&s->waker);
+        qemu_thread_create(&s->thread, "ipmi-bmc", ipmi_thread, s, 0);
+    }
+
+    if (s->threaded_bmc) {
+        qemu_mutex_init(&s->lock);
+        qemu_cond_init(&s->waker);
+        qemu_thread_create(&s->thread, "ipmi-bmc", ipmi_thread, s, 0);
+    }
+

Duplicated bit---probably a rebase hiccup?

Almost certainly. Got it, Thanks.

-corey

#23 lore raw
In reply to [email protected]
Quoted 20 lines · On 12/12/2014 20:15, [email protected] wrote:

This set of patches adds an IPMI device to qemu. This is good for systems that require an IPMI device to work correctly, for simulating scenarios that require IPMI and testing software that uses IPMI, and of course, for the Linux IPMI driver maintainer to use to reproduce issues that could not be easily reproduced otherwise :).

For those that don't know, IPMI is a standard for doing sensor monitoring and basic machine maintenance. It has local interfaces on the host system (four different types, SMIC, KCS, BT, and SSIF) that can be in I/O space, memory space, and PCI space, and in the case of SSIF, is on an I2C bus. It can also have network interfaces for out of band maintenance, though that's not directly relevant here. It is a message-based interface; all IPMI operations are done by sending command messages, results come back as response messages.

The maintenance actions are done by a small controller on the system called a Baseboard Management Controller (BMC). These devices are always on when the system is plugged in, even if the system is off.

Quoted 7 lines

The first 9 patches add the device itself on an ISA interface. This adds a KCS device and a BT device (two of the four standard IPMI interfaces). It also adds software to have a small BMC simulated inside of QEMU, and a way to connected to an external BMC (like openipmi library's lanserv) over the network. Those patches should be pretty straightforward, though I'm still not 100% sure about migration.

Indeed patches 1-8 should be pretty much done. And 9 looks like it's in good shape too, though I have not reviewed it carefully enough.

I haven't checked that the patches pass checkpatch.pl; also the preferred style over this:

+ (IPMI_INTERFACE_GET_CLASS(s))->handle_if_event(s);

is to have a function:

    ipmi_interface_handle_if_event(s);

that is written like

void ipmi_interface_handle_if_event(IPMIInterface *s)

{
    IPMIInterfaceClass *sc = IPMI_INTERFACE_GET_CLASS(s);
    sc->handle_if_event(s);
}

Also, a function like ipmi_signal would be in the .c file. There should be no use of *_GET_CLASS outside the .c file.

For patches 10-15, there is already code in QEMU for ACPI table generation. Right now it uses iasl, but Igor Mammedov is also working on an API similar to yours (the duplication is unfortunate, indeed). So it would probably be better to integrate table construction with "regular" SSDT construction in hw/i386/acpi-build.c. ACPI tables are already sent to the firmware after device initialization; IIUC they are built on demand, so there should be no change required for this anymore.

SMBIOS tables are also built in QEMU now, which simplified your work a bit. Moving the work to a machine_done_notifier is a good idea and matches ACPI. The only thing I'd change is using a NotifierList instead of rolling your own smbios_device_handler struct.

BTW, any reason to choose ISA over PCI or I2C (which is supported for i386 via the ACPI SMBus controller)?

Paolo