[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v5 9/9] ioreq-server: bring the PCI hotplug controller implementation into Xen



Because we may now have more than one emulator, the implementation of the
PCI hotplug controller needs to be done by Xen. Happily the code is very
short and simple and it also removes the need for a different ACPI DSDT
when using different variants of QEMU.

As a precaution, we obscure the IO ranges used by QEMU traditional's gpe
and hotplug controller implementations to avoid the possibility of it
raising an SCI which will never be cleared.

VMs started on an older host and then migrated in will not use the in-Xen
controller as the AML may still point at QEMU traditional's hotplug
controller implementation. This means xc_hvm_pci_hotplug() will fail
with EOPNOTSUPP and it is up to the caller to decide whether this is a
problem or not. libxl will ignore EOPNOTSUPP as it is always hotplugging
via QEMU so it does not matter whether it is Xen or QEMU providing the
implementation.

Signed-off-by: Paul Durrant <paul.durrant@xxxxxxxxxx>
Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
Cc: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
Cc: Ian Campbell <ian.campbell@xxxxxxxxxx>
Cc: Jan Beulich <jbeulich@xxxxxxxx>
---
 tools/firmware/hvmloader/acpi/Makefile  |    2 +-
 tools/firmware/hvmloader/acpi/mk_dsdt.c |  193 +++++---------------------
 tools/libxc/xc_domain.c                 |   27 ++++
 tools/libxc/xc_domain_restore.c         |    1 +
 tools/libxc/xc_hvm_build_x86.c          |    1 +
 tools/libxc/xenctrl.h                   |   24 ++++
 tools/libxl/libxl_pci.c                 |   14 ++
 xen/arch/x86/hvm/Makefile               |    1 +
 xen/arch/x86/hvm/hotplug.c              |  224 +++++++++++++++++++++++++++++++
 xen/arch/x86/hvm/hvm.c                  |   40 ++++++
 xen/include/asm-x86/hvm/domain.h        |   11 ++
 xen/include/asm-x86/hvm/io.h            |    8 +-
 xen/include/public/hvm/hvm_op.h         |   12 ++
 xen/include/public/hvm/ioreq.h          |    4 +
 14 files changed, 402 insertions(+), 160 deletions(-)
 create mode 100644 xen/arch/x86/hvm/hotplug.c

diff --git a/tools/firmware/hvmloader/acpi/Makefile 
b/tools/firmware/hvmloader/acpi/Makefile
index 2c50851..5fc4ebd 100644
--- a/tools/firmware/hvmloader/acpi/Makefile
+++ b/tools/firmware/hvmloader/acpi/Makefile
@@ -36,7 +36,7 @@ mk_dsdt: mk_dsdt.c
 
 dsdt_anycpu_qemu_xen.asl: dsdt.asl mk_dsdt
        awk 'NR > 1 {print s} {s=$$0}' $< > $@
-       ./mk_dsdt --dm-version qemu-xen >> $@
+       ./mk_dsdt >> $@
 
 # NB. awk invocation is a portable alternative to 'head -n -1'
 dsdt_%cpu.asl: dsdt.asl mk_dsdt
diff --git a/tools/firmware/hvmloader/acpi/mk_dsdt.c 
b/tools/firmware/hvmloader/acpi/mk_dsdt.c
index a4b693b..1de88ac 100644
--- a/tools/firmware/hvmloader/acpi/mk_dsdt.c
+++ b/tools/firmware/hvmloader/acpi/mk_dsdt.c
@@ -8,11 +8,6 @@
 
 static unsigned int indent_level;
 
-typedef enum dm_version {
-    QEMU_XEN_TRADITIONAL,
-    QEMU_XEN,
-} dm_version;
-
 static void indent(void)
 {
     unsigned int i;
@@ -58,38 +53,14 @@ static void pop_block(void)
     printf("}\n");
 }
 
-static void pci_hotplug_notify(unsigned int slt)
-{
-    stmt("Notify", "\\_SB.PCI0.S%02X, EVT", slt);
-}
-
-static void decision_tree(
-    unsigned int s, unsigned int e, char *var, void (*leaf)(unsigned int))
-{
-    if ( s == (e-1) )
-    {
-        (*leaf)(s);
-        return;
-    }
-
-    push_block("If", "And(%s, 0x%02x)", var, (e-s)/2);
-    decision_tree((s+e)/2, e, var, leaf);
-    pop_block();
-    push_block("Else", NULL);
-    decision_tree(s, (s+e)/2, var, leaf);
-    pop_block();
-}
-
 static struct option options[] = {
     { "maxcpu", 1, 0, 'c' },
-    { "dm-version", 1, 0, 'q' },
     { 0, 0, 0, 0 }
 };
 
 int main(int argc, char **argv)
 {
     unsigned int slot, dev, intx, link, cpu, max_cpus = HVM_MAX_VCPUS;
-    dm_version dm_version = QEMU_XEN_TRADITIONAL;
 
     for ( ; ; )
     {
@@ -115,16 +86,6 @@ int main(int argc, char **argv)
             }
             break;
         }
-        case 'q':
-            if (strcmp(optarg, "qemu-xen") == 0) {
-                dm_version = QEMU_XEN;
-            } else if (strcmp(optarg, "qemu-xen-traditional") == 0) {
-                dm_version = QEMU_XEN_TRADITIONAL;
-            } else {
-                fprintf(stderr, "Unknown device model version `%s'.\n", 
optarg);
-                return -1;
-            }
-            break;
         default:
             return -1;
         }
@@ -222,11 +183,8 @@ int main(int argc, char **argv)
 
     /* Define GPE control method. */
     push_block("Scope", "\\_GPE");
-    if (dm_version == QEMU_XEN_TRADITIONAL) {
-        push_block("Method", "_L02");
-    } else {
-        push_block("Method", "_E02");
-    }
+    push_block("Method", "_L02");
+
     stmt("Return", "\\_SB.PRSC()");
     pop_block();
     pop_block();
@@ -237,23 +195,17 @@ int main(int argc, char **argv)
     push_block("Scope", "\\_SB.PCI0");
 
     /*
-     * Reserve the IO port ranges [0x10c0, 0x1101] and [0xb044, 0xb047].
-     * Or else, for a hotplugged-in device, the port IO BAR assigned
-     * by guest OS may conflict with the ranges here.
+     * Reserve the IO port ranges used by PCI hotplug controller or else,
+     * for a hotplugged-in device, the port IO BAR assigned by guest OS may
+     * conflict with the ranges here.
      */
     push_block("Device", "HP0"); {
         stmt("Name", "_HID, EISAID(\"PNP0C02\")");
-        if (dm_version == QEMU_XEN_TRADITIONAL) {
-            stmt("Name", "_CRS, ResourceTemplate() {"
-                 "  IO (Decode16, 0x10c0, 0x10c0, 0x00, 0x82)"
-                 "  IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)"
-                 "}");
-        } else {
-            stmt("Name", "_CRS, ResourceTemplate() {"
-                 "  IO (Decode16, 0xae00, 0xae00, 0x00, 0x10)"
-                 "  IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)"
-                 "}");
-        }
+        stmt("Name", "_CRS, ResourceTemplate() {"
+             "  IO (Decode16, 0x10c0, 0x10c0, 0x00, 0x82)"
+             "  IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)"
+             "  IO (Decode16, 0xae00, 0xae00, 0x00, 0x10)"
+             "}");
     } pop_block();
 
     /*** PCI-ISA link definitions ***/
@@ -322,64 +274,21 @@ int main(int argc, char **argv)
                    dev, intx, ((dev*4+dev/8+intx)&31)+16);
     printf("})\n");
 
-    /*
-     * Each PCI hotplug slot needs at least two methods to handle
-     * the ACPI event:
-     *  _EJ0: eject a device
-     *  _STA: return a device's status, e.g. enabled or removed
-     * 
-     * Eject button would generate a general-purpose event, then the
-     * control method for this event uses Notify() to inform OSPM which
-     * action happened and on which device.
-     *
-     * Pls. refer "6.3 Device Insertion, Removal, and Status Objects"
-     * in ACPI spec 3.0b for details.
-     *
-     * QEMU provides a simple hotplug controller with some I/O to handle
-     * the hotplug action and status, which is beyond the ACPI scope.
-     */
-    if (dm_version == QEMU_XEN_TRADITIONAL) {
-        for ( slot = 0; slot < 0x100; slot++ )
-        {
-            push_block("Device", "S%02X", slot);
-            /* _ADR == dev:fn (16:16) */
-            stmt("Name", "_ADR, 0x%08x", ((slot & ~7) << 13) | (slot & 7));
-            /* _SUN == dev */
-            stmt("Name", "_SUN, 0x%08x", slot >> 3);
-            push_block("Method", "_EJ0, 1");
-            stmt("Store", "0x%02x, \\_GPE.DPT1", slot);
-            stmt("Store", "0x88, \\_GPE.DPT2");
-            stmt("Store", "0x%02x, \\_GPE.PH%02X", /* eject */
-                 (slot & 1) ? 0x10 : 0x01, slot & ~1);
-            pop_block();
-            push_block("Method", "_STA, 0");
-            stmt("Store", "0x%02x, \\_GPE.DPT1", slot);
-            stmt("Store", "0x89, \\_GPE.DPT2");
-            if ( slot & 1 )
-                stmt("ShiftRight", "0x4, \\_GPE.PH%02X, Local1", slot & ~1);
-            else
-                stmt("And", "\\_GPE.PH%02X, 0x0f, Local1", slot & ~1);
-            stmt("Return", "Local1"); /* IN status as the _STA */
-            pop_block();
-            pop_block();
-        }
-    } else {
-        stmt("OperationRegion", "SEJ, SystemIO, 0xae08, 0x04");
-        push_block("Field", "SEJ, DWordAcc, NoLock, WriteAsZeros");
-        indent(); printf("B0EJ, 32,\n");
-        pop_block();
+    stmt("OperationRegion", "SEJ, SystemIO, 0xae08, 0x04");
+    push_block("Field", "SEJ, DWordAcc, NoLock, WriteAsZeros");
+    indent(); printf("B0EJ, 32,\n");
+    pop_block();
 
-        /* hotplug_slot */
-        for (slot = 1; slot <= 31; slot++) {
-            push_block("Device", "S%i", slot); {
-                stmt("Name", "_ADR, %#06x0000", slot);
-                push_block("Method", "_EJ0,1"); {
-                    stmt("Store", "ShiftLeft(1, %#06x), B0EJ", slot);
-                    stmt("Return", "0x0");
-                } pop_block();
-                stmt("Name", "_SUN, %i", slot);
+    /* hotplug_slot */
+    for (slot = 1; slot <= 31; slot++) {
+        push_block("Device", "S%i", slot); {
+            stmt("Name", "_ADR, %#06x0000", slot);
+            push_block("Method", "_EJ0,1"); {
+                stmt("Store", "ShiftLeft(1, %#06x), B0EJ", slot);
+                stmt("Return", "0x0");
             } pop_block();
-        }
+            stmt("Name", "_SUN, %i", slot);
+        } pop_block();
     }
 
     pop_block();
@@ -389,26 +298,11 @@ int main(int argc, char **argv)
     /**** GPE start ****/
     push_block("Scope", "\\_GPE");
 
-    if (dm_version == QEMU_XEN_TRADITIONAL) {
-        stmt("OperationRegion", "PHP, SystemIO, 0x10c0, 0x82");
-
-        push_block("Field", "PHP, ByteAcc, NoLock, Preserve");
-        indent(); printf("PSTA, 8,\n"); /* hotplug controller event reg */
-        indent(); printf("PSTB, 8,\n"); /* hotplug controller slot reg */
-        for ( slot = 0; slot < 0x100; slot += 2 )
-        {
-            indent();
-            /* Each hotplug control register manages a pair of pci functions. 
*/
-            printf("PH%02X, 8,\n", slot);
-        }
-        pop_block();
-    } else {
-        stmt("OperationRegion", "PCST, SystemIO, 0xae00, 0x08");
-        push_block("Field", "PCST, DWordAcc, NoLock, WriteAsZeros");
-        indent(); printf("PCIU, 32,\n");
-        indent(); printf("PCID, 32,\n");
-        pop_block();
-    }
+    stmt("OperationRegion", "PCST, SystemIO, 0xae00, 0x08");
+    push_block("Field", "PCST, DWordAcc, NoLock, WriteAsZeros");
+    indent(); printf("PCIU, 32,\n");
+    indent(); printf("PCID, 32,\n");
+    pop_block();
 
     stmt("OperationRegion", "DG1, SystemIO, 0xb044, 0x04");
 
@@ -416,33 +310,16 @@ int main(int argc, char **argv)
     indent(); printf("DPT1, 8, DPT2, 8\n");
     pop_block();
 
-    if (dm_version == QEMU_XEN_TRADITIONAL) {
-        push_block("Method", "_L03, 0, Serialized");
-        /* Detect slot and event (remove/add). */
-        stmt("Name", "SLT, 0x0");
-        stmt("Name", "EVT, 0x0");
-        stmt("Store", "PSTA, Local1");
-        stmt("And", "Local1, 0xf, EVT");
-        stmt("Store", "PSTB, Local1"); /* XXX: Store (PSTB, SLT) ? */
-        stmt("And", "Local1, 0xff, SLT");
-        /* Debug */
-        stmt("Store", "SLT, DPT1");
-        stmt("Store", "EVT, DPT2");
-        /* Decision tree */
-        decision_tree(0x00, 0x100, "SLT", pci_hotplug_notify);
+    push_block("Method", "_E01");
+    for (slot = 1; slot <= 31; slot++) {
+        push_block("If", "And(PCIU, ShiftLeft(1, %i))", slot);
+        stmt("Notify", "\\_SB.PCI0.S%i, 1", slot);
         pop_block();
-    } else {
-        push_block("Method", "_E01");
-        for (slot = 1; slot <= 31; slot++) {
-            push_block("If", "And(PCIU, ShiftLeft(1, %i))", slot);
-            stmt("Notify", "\\_SB.PCI0.S%i, 1", slot);
-            pop_block();
-            push_block("If", "And(PCID, ShiftLeft(1, %i))", slot);
-            stmt("Notify", "\\_SB.PCI0.S%i, 3", slot);
-            pop_block();
-        }
+        push_block("If", "And(PCID, ShiftLeft(1, %i))", slot);
+        stmt("Notify", "\\_SB.PCI0.S%i, 3", slot);
         pop_block();
     }
+    pop_block();
 
     pop_block();
     /**** GPE end ****/
diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c
index 67090bd..f9b3c07 100644
--- a/tools/libxc/xc_domain.c
+++ b/tools/libxc/xc_domain.c
@@ -1532,6 +1532,33 @@ int xc_hvm_set_ioreq_server_state(xc_interface *xch,
     return rc;
 }
 
+int xc_hvm_pci_hotplug(xc_interface *xch,
+                       domid_t domid,
+                       uint32_t slot,
+                       int enable)
+{
+    DECLARE_HYPERCALL;
+    DECLARE_HYPERCALL_BUFFER(xen_hvm_pci_hotplug_t, arg);
+    int rc;
+
+    arg = xc_hypercall_buffer_alloc(xch, arg, sizeof(*arg));
+    if ( arg == NULL )
+        return -1;
+
+    hypercall.op     = __HYPERVISOR_hvm_op;
+    hypercall.arg[0] = HVMOP_pci_hotplug;
+    hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg);
+
+    arg->domid = domid;
+    arg->slot = slot;
+    arg->enable = !!enable;
+
+    rc = do_xen_hypercall(xch, &hypercall);
+
+    xc_hypercall_buffer_free(xch, arg);
+    return rc;
+}
+
 int xc_domain_setdebugging(xc_interface *xch,
                            uint32_t domid,
                            unsigned int enable)
diff --git a/tools/libxc/xc_domain_restore.c b/tools/libxc/xc_domain_restore.c
index af2bf3a..9b49509 100644
--- a/tools/libxc/xc_domain_restore.c
+++ b/tools/libxc/xc_domain_restore.c
@@ -1784,6 +1784,7 @@ int xc_domain_restore(xc_interface *xch, int io_fd, 
uint32_t dom,
      */
     if (pagebuf.nr_ioreq_server_pages != 0) {
         if (pagebuf.ioreq_server_pfn != 0) {
+            /* Setting this parameter also enables the hotplug controller */
             xc_set_hvm_param(xch, dom, HVM_PARAM_NR_IOREQ_SERVER_PAGES, 
                              pagebuf.nr_ioreq_server_pages);
             xc_set_hvm_param(xch, dom, HVM_PARAM_IOREQ_SERVER_PFN,
diff --git a/tools/libxc/xc_hvm_build_x86.c b/tools/libxc/xc_hvm_build_x86.c
index 3564e8b..31d288f 100644
--- a/tools/libxc/xc_hvm_build_x86.c
+++ b/tools/libxc/xc_hvm_build_x86.c
@@ -526,6 +526,7 @@ static int setup_guest(xc_interface *xch,
     /* Tell the domain where the pages are and how many there are */
     xc_set_hvm_param(xch, dom, HVM_PARAM_IOREQ_SERVER_PFN,
                      ioreq_server_pfn(0));
+    /* Setting this parameter also enables the hotplug controller */
     xc_set_hvm_param(xch, dom, HVM_PARAM_NR_IOREQ_SERVER_PAGES,
                      NR_IOREQ_SERVER_PAGES);
 
diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h
index 60f6abc..fad66cb 100644
--- a/tools/libxc/xenctrl.h
+++ b/tools/libxc/xenctrl.h
@@ -1927,6 +1927,30 @@ int xc_hvm_set_ioreq_server_state(xc_interface *xch,
                                   ioservid_t id,
                                   int enabled);
 
+/*
+ * Hotplug controller API
+ */
+
+/**
+ * This function either enables or disables a hotplug PCI slot
+ *
+ * @parm xch a handle to an open hypervisor interface.
+ * @parm domid the domain id to be serviced
+ * @parm slot the slot number
+ * @parm enable enable/disable the slot
+ * @return 0 on success, -1 on failure.
+ *
+ * VMs started on an old version of Xen may not have a hotplug
+ * controller, in which case this function will fail with errno
+ * set to EOPNOTSUPP. Such a failure can be safely ignored if
+ * the device in question is being emulated by QEMU since it
+ * will providing the hotplug controller implementation.
+ */
+int xc_hvm_pci_hotplug(xc_interface *xch,
+                       domid_t domid,
+                       uint32_t slot,
+                       int enable);
+
 /* HVM guest pass-through */
 int xc_assign_device(xc_interface *xch,
                      uint32_t domid,
diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c
index 44d0453..55cb8a2 100644
--- a/tools/libxl/libxl_pci.c
+++ b/tools/libxl/libxl_pci.c
@@ -867,6 +867,13 @@ static int do_pci_add(libxl__gc *gc, uint32_t domid, 
libxl_device_pci *pcidev, i
         }
         if ( rc )
             return ERROR_FAIL;
+
+        rc = xc_hvm_pci_hotplug(CTX->xch, domid, pcidev->dev, 1);
+        if (rc < 0 && errno != EOPNOTSUPP) {
+            LOGE(ERROR, "Error: xc_hvm_pci_hotplug enable failed");
+            return ERROR_FAIL;
+        }
+
         break;
     case LIBXL_DOMAIN_TYPE_PV:
     {
@@ -1188,6 +1195,13 @@ static int do_pci_remove(libxl__gc *gc, uint32_t domid,
                                          NULL, NULL, NULL) < 0)
             goto out_fail;
 
+        rc = xc_hvm_pci_hotplug(CTX->xch, domid, pcidev->dev, 0);
+        if (rc < 0 && errno != EOPNOTSUPP) {
+            LOGE(ERROR, "Error: xc_hvm_pci_hotplug disable failed");
+            rc = ERROR_FAIL;
+            goto out_fail;
+        }
+
         switch (libxl__device_model_version_running(gc, domid)) {
         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
             rc = qemu_pci_remove_xenstore(gc, domid, pcidev, force);
diff --git a/xen/arch/x86/hvm/Makefile b/xen/arch/x86/hvm/Makefile
index eea5555..48efddb 100644
--- a/xen/arch/x86/hvm/Makefile
+++ b/xen/arch/x86/hvm/Makefile
@@ -3,6 +3,7 @@ subdir-y += vmx
 
 obj-y += asid.o
 obj-y += emulate.o
+obj-y += hotplug.o
 obj-y += hpet.o
 obj-y += hvm.o
 obj-y += i8254.o
diff --git a/xen/arch/x86/hvm/hotplug.c b/xen/arch/x86/hvm/hotplug.c
new file mode 100644
index 0000000..0b139ad
--- /dev/null
+++ b/xen/arch/x86/hvm/hotplug.c
@@ -0,0 +1,224 @@
+/*
+ * hvm/hotplug.c
+ *
+ * Copyright (c) 2014, Citrix Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <xen/types.h>
+#include <xen/spinlock.h>
+#include <xen/xmalloc.h>
+#include <asm/hvm/io.h>
+#include <asm/hvm/support.h>
+
+#define SCI_IRQ 9
+
+/* pci status bit: \_GPE._L02 - i.e. level sensitive, bit 2 */
+#define GPE_PCI_HOTPLUG_STATUS  2
+
+#define PCI_UP      0
+#define PCI_DOWN    4
+#define PCI_EJECT   8
+
+static void gpe_update_sci(struct hvm_hotplug *hp)
+{
+    struct domain *d;
+
+    d = container_of(
+            container_of(
+                container_of(hp, struct hvm_domain, hotplug),
+                struct arch_domain, hvm_domain),
+            struct domain, arch);
+
+    if ( (hp->gpe_sts_en[0] & hp->gpe_sts_en[ACPI_GPE0_BLK_LEN_V1 / 2]) &
+         GPE_PCI_HOTPLUG_STATUS )
+        hvm_isa_irq_assert(d, SCI_IRQ);
+    else
+        hvm_isa_irq_deassert(d, SCI_IRQ);
+}
+
+static uint8_t gpe_read_port(struct hvm_hotplug *hp, uint32_t port)
+{
+    return hp->gpe_sts_en[port];
+}
+
+static void gpe_write_port(struct hvm_hotplug *hp, uint32_t port, uint8_t val)
+{
+    if ( port < ACPI_GPE0_BLK_LEN_V1 / 2 )
+        hp->gpe_sts_en[port] &= ~val;
+    else
+        hp->gpe_sts_en[port] = val;
+}
+
+static int handle_gpe_io(
+    int dir, uint32_t port, uint32_t bytes, uint32_t *val)
+{
+    struct vcpu *v = current;
+    struct domain *d = v->domain;
+    struct hvm_hotplug *hp = &d->arch.hvm_domain.hotplug;
+
+    port -= ACPI_GPE0_BLK_ADDRESS_V1;
+
+    if ( dir == IOREQ_READ )
+    {
+        unsigned int i;
+
+        *val = 0;
+        for ( i = 0; i < bytes; i++ )
+            *val |= gpe_read_port(hp, port + i) << (i * 8);
+    }
+    else
+    {
+        unsigned int i;
+
+        for ( i = 0; i < bytes; i++ )
+            gpe_write_port(hp, port + i, (*val) >> (i * 8));
+
+        gpe_update_sci(hp);
+    }
+
+    return X86EMUL_OKAY;
+}
+
+static void pci_hotplug_eject(struct hvm_hotplug *hp, uint32_t mask)
+{
+    int slot = ffs(mask) - 1;
+
+    gdprintk(XENLOG_INFO, "%s: %d\n", __func__, slot);
+
+    hp->slot_down &= ~(1u  << slot);
+    hp->slot_up &= ~(1u  << slot);
+}
+
+static int handle_pci_hotplug_io(
+    int dir, uint32_t port, uint32_t bytes, uint32_t *val)
+{
+    struct vcpu *v = current;
+    struct domain *d = v->domain;
+    struct hvm_hotplug  *hp = &d->arch.hvm_domain.hotplug;
+
+    /* ASL specifies DWordAcc */
+    if ( bytes != 4 )
+    {
+        gdprintk(XENLOG_WARNING, "%s: bad access\n", __func__);
+        goto done;
+    }
+
+    port -= ACPI_PCI_HOTPLUG_ADDRESS_V1;
+
+    if ( dir == IOREQ_READ )
+    {
+        switch ( port )
+        {
+        case PCI_UP:
+            *val = hp->slot_up;
+            break;
+        case PCI_DOWN:
+            *val = hp->slot_down;
+            break;
+        default:
+            break;
+        }
+    }
+    else
+    {   
+        switch ( port )
+        {
+        case PCI_EJECT:
+            pci_hotplug_eject(hp, *val);
+            break;
+        default:
+            break;
+        }
+    }
+
+ done:
+    return X86EMUL_OKAY;
+}
+
+static int null_io(
+    int dir, uint32_t port, uint32_t bytes, uint32_t *val)
+{
+    /* Make it look like this IO range is non-existent */
+    if ( dir == IOREQ_READ )
+        *val = ~0u;
+
+    return X86EMUL_OKAY;
+}
+
+int pci_hotplug(struct domain *d, int slot, bool_t enable)
+{
+    struct hvm_hotplug  *hp = &d->arch.hvm_domain.hotplug;
+
+    gdprintk(XENLOG_DEBUG, "%s: %d:%d %s\n", __func__,
+             d->domain_id, slot, enable ? "enable" : "disable");
+
+    if ( !hp->gpe_sts_en )
+        return -EOPNOTSUPP;
+
+    if ( enable )
+        hp->slot_up |= (1u << slot);
+    else
+        hp->slot_down |= (1u << slot);
+
+    hp->gpe_sts_en[0] |= GPE_PCI_HOTPLUG_STATUS;
+    gpe_update_sci(hp);
+
+    return 0;
+}
+
+int gpe_init(struct domain *d)
+{
+    struct hvm_hotplug  *hp = &d->arch.hvm_domain.hotplug;
+
+    hp->gpe_sts_en = xzalloc_array(uint8_t, ACPI_GPE0_BLK_LEN_V1);
+    if ( hp->gpe_sts_en == NULL )
+        return -ENOMEM;
+
+    register_portio_handler(d, ACPI_GPE0_BLK_ADDRESS_V1,
+                            ACPI_GPE0_BLK_LEN_V1, handle_gpe_io);
+    register_portio_handler(d, ACPI_PCI_HOTPLUG_ADDRESS_V1,
+                            ACPI_PCI_HOTPLUG_LEN_V1, handle_pci_hotplug_io);
+
+    /*
+     * We should make sure that the old GPE and hotplug controller ranges
+     * used by qemu traditional are obscured to avoid confusion.
+     */
+    register_portio_handler(d, ACPI_GPE0_BLK_ADDRESS_V0,
+                            ACPI_GPE0_BLK_LEN_V0, null_io);
+    register_portio_handler(d, ACPI_PCI_HOTPLUG_ADDRESS_V0,
+                            ACPI_PCI_HOTPLUG_LEN_V0, null_io);
+
+
+    return 0;
+}
+
+void gpe_deinit(struct domain *d)
+{
+    struct hvm_hotplug  *hp = &d->arch.hvm_domain.hotplug;
+
+    xfree(hp->gpe_sts_en);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * c-tab-always-indent: nil
+ * End:
+ */
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index e734adb..517a789 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -1571,6 +1571,7 @@ void hvm_domain_destroy(struct domain *d)
         return;
 
     hvm_funcs.domain_destroy(d);
+    gpe_deinit(d);
     rtc_deinit(d);
     stdvga_deinit(d);
     vioapic_deinit(d);
@@ -5307,6 +5308,31 @@ static int hvmop_destroy_ioreq_server(
     return rc;
 }
 
+static int hvmop_pci_hotplug(
+    XEN_GUEST_HANDLE_PARAM(xen_hvm_pci_hotplug_t) uop)
+{
+    xen_hvm_pci_hotplug_t op;
+    struct domain *d;
+    int rc;
+
+    if ( copy_from_guest(&op, uop, 1) )
+        return -EFAULT;
+
+    rc = rcu_lock_remote_domain_by_id(op.domid, &d);
+    if ( rc != 0 )
+        return rc;
+
+    rc = -EINVAL;
+    if ( !is_hvm_domain(d) )
+        goto out;
+
+    rc = pci_hotplug(d, op.slot, !!op.enable);
+
+out:
+    rcu_unlock_domain(d);
+    return rc;
+}
+
 #define HVMOP_op_mask 0xff
 
 long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE_PARAM(void) arg)
@@ -5348,6 +5374,11 @@ long do_hvm_op(unsigned long op, 
XEN_GUEST_HANDLE_PARAM(void) arg)
             guest_handle_cast(arg, xen_hvm_destroy_ioreq_server_t));
         break;
     
+    case HVMOP_pci_hotplug:
+        rc = hvmop_pci_hotplug(
+            guest_handle_cast(arg, xen_hvm_pci_hotplug_t));
+        break;
+
     case HVMOP_set_param:
     case HVMOP_get_param:
     {
@@ -5519,6 +5550,15 @@ long do_hvm_op(unsigned long op, 
XEN_GUEST_HANDLE_PARAM(void) arg)
                     break;
                 }
                 d->arch.hvm_domain.ioreq_gmfn_count = a.value;
+
+                /*
+                 * Since secondary emulators are now possible, enable
+                 * the hotplug controller.
+                 */
+                rc = gpe_init(d);
+                if ( rc == -EEXIST )
+                    rc = 0;
+
                 break;
             }
 
diff --git a/xen/include/asm-x86/hvm/domain.h b/xen/include/asm-x86/hvm/domain.h
index 9220e2f..b80cfaf 100644
--- a/xen/include/asm-x86/hvm/domain.h
+++ b/xen/include/asm-x86/hvm/domain.h
@@ -71,6 +71,15 @@ struct hvm_ioreq_server {
     bool_t                 enabled;
 };
 
+struct hvm_hotplug {
+    /* Lower half of block are status bits */
+    uint8_t         *gpe_sts_en;
+
+    /* PCI hotplug */
+    uint32_t        slot_up;
+    uint32_t        slot_down;
+};
+
 struct hvm_domain {
     /* Guest page range used for non-default ioreq servers */
     unsigned long           ioreq_gmfn_base;
@@ -88,6 +97,8 @@ struct hvm_domain {
     uint32_t                pci_cf8;
     spinlock_t              pci_lock;
 
+    struct hvm_hotplug      hotplug;
+
     struct pl_time         pl_time;
 
     struct hvm_io_handler *io_handler;
diff --git a/xen/include/asm-x86/hvm/io.h b/xen/include/asm-x86/hvm/io.h
index 86db58d..7f88a62 100644
--- a/xen/include/asm-x86/hvm/io.h
+++ b/xen/include/asm-x86/hvm/io.h
@@ -25,7 +25,7 @@
 #include <public/hvm/ioreq.h>
 #include <public/event_channel.h>
 
-#define MAX_IO_HANDLER             16
+#define MAX_IO_HANDLER             32
 
 #define HVM_PORTIO                  0
 #define HVM_BUFFERED_IO             2
@@ -142,5 +142,11 @@ void stdvga_init(struct domain *d);
 void stdvga_deinit(struct domain *d);
 
 extern void hvm_dpci_msi_eoi(struct domain *d, int vector);
+
+int gpe_init(struct domain *d);
+void gpe_deinit(struct domain *d);
+
+int pci_hotplug(struct domain *d, int slot, bool_t enable);
+
 #endif /* __ASM_X86_HVM_IO_H__ */
 
diff --git a/xen/include/public/hvm/hvm_op.h b/xen/include/public/hvm/hvm_op.h
index ab4b583..c08aeda 100644
--- a/xen/include/public/hvm/hvm_op.h
+++ b/xen/include/public/hvm/hvm_op.h
@@ -362,6 +362,18 @@ struct xen_hvm_set_ioreq_server_state {
 typedef struct xen_hvm_set_ioreq_server_state xen_hvm_set_ioreq_server_state_t;
 DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_ioreq_server_state_t);
 
+/*
+ * HVMOP_pci_hotplug: enable/disable <slot> of domain <domid>.
+ */
+#define HVMOP_pci_hotplug 23
+struct xen_hvm_pci_hotplug {
+    domid_t domid;  /* IN - domain to be serviced */
+    uint8_t enable; /* IN - enable or disable? */
+    uint32_t slot;  /* IN - slot to enable/disable */
+};
+typedef struct xen_hvm_pci_hotplug xen_hvm_pci_hotplug_t;
+DEFINE_XEN_GUEST_HANDLE(xen_hvm_pci_hotplug_t);
+
 #endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */
 
 #endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
diff --git a/xen/include/public/hvm/ioreq.h b/xen/include/public/hvm/ioreq.h
index 533089d..3c3a8c1 100644
--- a/xen/include/public/hvm/ioreq.h
+++ b/xen/include/public/hvm/ioreq.h
@@ -102,6 +102,8 @@ typedef struct buffered_iopage buffered_iopage_t;
 #define ACPI_PM_TMR_BLK_ADDRESS_V0   (ACPI_PM1A_EVT_BLK_ADDRESS_V0 + 0x08)
 #define ACPI_GPE0_BLK_ADDRESS_V0     (ACPI_PM_TMR_BLK_ADDRESS_V0 + 0x20)
 #define ACPI_GPE0_BLK_LEN_V0         0x08
+#define ACPI_PCI_HOTPLUG_ADDRESS_V0  0x10c0
+#define ACPI_PCI_HOTPLUG_LEN_V0      0x82 /* NR_PHP_SLOT_REG in piix4acpi.c */
 
 /* Version 1: Locations preferred by modern Qemu. */
 #define ACPI_PM1A_EVT_BLK_ADDRESS_V1 0xb000
@@ -109,6 +111,8 @@ typedef struct buffered_iopage buffered_iopage_t;
 #define ACPI_PM_TMR_BLK_ADDRESS_V1   (ACPI_PM1A_EVT_BLK_ADDRESS_V1 + 0x08)
 #define ACPI_GPE0_BLK_ADDRESS_V1     0xafe0
 #define ACPI_GPE0_BLK_LEN_V1         0x04
+#define ACPI_PCI_HOTPLUG_ADDRESS_V1  0xae00
+#define ACPI_PCI_HOTPLUG_LEN_V1      0x10
 
 /* Compatibility definitions for the default location (version 0). */
 #define ACPI_PM1A_EVT_BLK_ADDRESS    ACPI_PM1A_EVT_BLK_ADDRESS_V0
-- 
1.7.10.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.