KVM: s390: Handle hosts not supporting s390-virtio.
authorCornelia Huck <cornelia.huck@de.ibm.com>
Fri, 14 Dec 2012 16:02:16 +0000 (17:02 +0100)
committerBen Hutchings <ben@decadent.org.uk>
Wed, 6 Mar 2013 03:22:34 +0000 (03:22 +0000)
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: Ben Hutchings <ben@decadent.org.uk>
drivers/s390/kvm/kvm_virtio.c

index 94f49ff..b1e8f6c 100644 (file)
@@ -413,6 +413,26 @@ static void kvm_extint_handler(unsigned int ext_int_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
@@ -424,21 +444,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();