KVM: s390: Handle hosts not supporting s390-virtio.
Cornelia Huck [Fri, 14 Dec 2012 16:02:16 +0000 (17:02 +0100)]
commit 55c171a6d90dc0574021f9c836127cfd1a7d2e30 upstream.

Running under a kvm host does not necessarily imply the presence of
a page mapped above the main memory with the virtio information;
however, the code includes a hard coded access to that page.

Instead, check for the presence of the page and exit gracefully
before we hit an addressing exception if it does not exist.

Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
Reviewed-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

drivers/s390/kvm/kvm_virtio.c

index d74e9ae..f97b2aa 100644 (file)
@@ -418,6 +418,26 @@ static void kvm_extint_handler(struct ext_code ext_code,
 }
 
 /*
+ * For s390-virtio, we expect a page above main storage containing
+ * the virtio configuration. Try to actually load from this area
+ * in order to figure out if the host provides this page.
+ */
+static int __init test_devices_support(unsigned long addr)
+{
+       int ret = -EIO;
+
+       asm volatile(
+               "0:     lura    0,%1\n"
+               "1:     xgr     %0,%0\n"
+               "2:\n"
+               EX_TABLE(0b,2b)
+               EX_TABLE(1b,2b)
+               : "+d" (ret)
+               : "a" (addr)
+               : "0", "cc");
+       return ret;
+}
+/*
  * Init function for virtio
  * devices are in a single page above top of "normal" mem
  */
@@ -428,21 +448,23 @@ static int __init kvm_devices_init(void)
        if (!MACHINE_IS_KVM)
                return -ENODEV;
 
+       if (test_devices_support(real_memory_size) < 0)
+               return -ENODEV;
+
+       rc = vmem_add_mapping(real_memory_size, PAGE_SIZE);
+       if (rc)
+               return rc;
+
+       kvm_devices = (void *) real_memory_size;
+
        kvm_root = root_device_register("kvm_s390");
        if (IS_ERR(kvm_root)) {
                rc = PTR_ERR(kvm_root);
                printk(KERN_ERR "Could not register kvm_s390 root device");
+               vmem_remove_mapping(real_memory_size, PAGE_SIZE);
                return rc;
        }
 
-       rc = vmem_add_mapping(real_memory_size, PAGE_SIZE);
-       if (rc) {
-               root_device_unregister(kvm_root);
-               return rc;
-       }
-
-       kvm_devices = (void *) real_memory_size;
-
        INIT_WORK(&hotplug_work, hotplug_devices);
 
        service_subclass_irq_register();