]> git.karo-electronics.de Git - linux-beck.git/commitdiff
KVM: s390: guest large pages
authorChristian Borntraeger <borntraeger@de.ibm.com>
Wed, 12 Jun 2013 11:54:53 +0000 (13:54 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 17 Jun 2013 15:05:07 +0000 (17:05 +0200)
This patch enables kvm to give large pages to the guest. The heavy
lifting is done by the hardware, the host only has to take care
of the PFMF instruction, which is also part of EDAT-1.

We also support the non-quiescing key setting facility if the host
supports it, to behave similar to the interpretation of sske.

Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/s390/include/asm/kvm_host.h
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/kvm-s390.h
arch/s390/kvm/priv.c

index 9a809f935580eb1edf0102aea0a0117538cf96e0..43207dd45fabdf0eb0dd24eb85498f1b3727abd0 100644 (file)
@@ -62,6 +62,7 @@ struct sca_block {
 #define CPUSTAT_MCDS       0x00000100
 #define CPUSTAT_SM         0x00000080
 #define CPUSTAT_G          0x00000008
+#define CPUSTAT_GED        0x00000004
 #define CPUSTAT_J          0x00000002
 #define CPUSTAT_P          0x00000001
 
@@ -96,7 +97,8 @@ struct kvm_s390_sie_block {
        __u32   scaoh;                  /* 0x005c */
        __u8    reserved60;             /* 0x0060 */
        __u8    ecb;                    /* 0x0061 */
-       __u8    reserved62[2];          /* 0x0062 */
+       __u8    ecb2;                   /* 0x0062 */
+       __u8    reserved63[1];          /* 0x0063 */
        __u32   scaol;                  /* 0x0064 */
        __u8    reserved68[4];          /* 0x0068 */
        __u32   todpr;                  /* 0x006c */
@@ -136,6 +138,7 @@ struct kvm_vcpu_stat {
        u32 deliver_program_int;
        u32 deliver_io_int;
        u32 exit_wait_state;
+       u32 instruction_pfmf;
        u32 instruction_stidp;
        u32 instruction_spx;
        u32 instruction_stpx;
index 3b597e590a756712fefb517a437f3abe473cd499..426e259b6a6915790addd3bae46183057cfba63e 100644 (file)
@@ -59,6 +59,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "deliver_restart_signal", VCPU_STAT(deliver_restart_signal) },
        { "deliver_program_interruption", VCPU_STAT(deliver_program_int) },
        { "exit_wait_state", VCPU_STAT(exit_wait_state) },
+       { "instruction_pfmf", VCPU_STAT(instruction_pfmf) },
        { "instruction_stidp", VCPU_STAT(instruction_stidp) },
        { "instruction_spx", VCPU_STAT(instruction_spx) },
        { "instruction_stpx", VCPU_STAT(instruction_stpx) },
@@ -381,8 +382,10 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
 {
        atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH |
                                                    CPUSTAT_SM |
-                                                   CPUSTAT_STOPPED);
+                                                   CPUSTAT_STOPPED |
+                                                   CPUSTAT_GED);
        vcpu->arch.sie_block->ecb   = 6;
+       vcpu->arch.sie_block->ecb2  = 8;
        vcpu->arch.sie_block->eca   = 0xC1002001U;
        vcpu->arch.sie_block->fac   = (int) (long) facilities;
        hrtimer_init(&vcpu->arch.ckc_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
@@ -1125,7 +1128,7 @@ static int __init kvm_s390_init(void)
                return -ENOMEM;
        }
        memcpy(facilities, S390_lowcore.stfle_fac_list, 16);
-       facilities[0] &= 0xff00fff3f47c0000ULL;
+       facilities[0] &= 0xff82fff3f47c0000ULL;
        facilities[1] &= 0x001c000000000000ULL;
        return 0;
 }
index 269b523d0f6e87486a6eb82360631bffa1969cc0..15795b8f8ff5964174b745e9f62cc5b310b74858 100644 (file)
@@ -86,6 +86,12 @@ static inline void kvm_s390_get_base_disp_sse(struct kvm_vcpu *vcpu,
        *address2 = (base2 ? vcpu->run->s.regs.gprs[base2] : 0) + disp2;
 }
 
+static inline void kvm_s390_get_regs_rre(struct kvm_vcpu *vcpu, int *r1, int *r2)
+{
+       *r1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 20;
+       *r2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16;
+}
+
 static inline u64 kvm_s390_get_base_disp_rsy(struct kvm_vcpu *vcpu)
 {
        u32 base2 = vcpu->arch.sie_block->ipb >> 28;
index ecc58a694df7516b7aed8cd89768e9236c08ea0f..bda9c9b494f0243fd06b019a63ae8094fe8e68e4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * handling privileged instructions
  *
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008, 2013
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License (version 2 only)
@@ -20,6 +20,9 @@
 #include <asm/debug.h>
 #include <asm/ebcdic.h>
 #include <asm/sysinfo.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/io.h>
 #include <asm/ptrace.h>
 #include <asm/compat.h>
 #include "gaccess.h"
@@ -212,7 +215,7 @@ static int handle_stfl(struct kvm_vcpu *vcpu)
 
        vcpu->stat.instruction_stfl++;
        /* only pass the facility bits, which we can handle */
-       facility_list = S390_lowcore.stfl_fac_list & 0xff00fff3;
+       facility_list = S390_lowcore.stfl_fac_list & 0xff82fff3;
 
        rc = copy_to_guest(vcpu, offsetof(struct _lowcore, stfl_fac_list),
                           &facility_list, sizeof(facility_list));
@@ -468,9 +471,88 @@ static int handle_epsw(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+#define PFMF_RESERVED   0xfffc0101UL
+#define PFMF_SK         0x00020000UL
+#define PFMF_CF         0x00010000UL
+#define PFMF_UI         0x00008000UL
+#define PFMF_FSC        0x00007000UL
+#define PFMF_NQ         0x00000800UL
+#define PFMF_MR         0x00000400UL
+#define PFMF_MC         0x00000200UL
+#define PFMF_KEY        0x000000feUL
+
+static int handle_pfmf(struct kvm_vcpu *vcpu)
+{
+       int reg1, reg2;
+       unsigned long start, end;
+
+       vcpu->stat.instruction_pfmf++;
+
+       kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
+
+       if (!MACHINE_HAS_PFMF)
+               return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
+
+       if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+               return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OPERATION);
+
+       if (vcpu->run->s.regs.gprs[reg1] & PFMF_RESERVED)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+       /* Only provide non-quiescing support if the host supports it */
+       if (vcpu->run->s.regs.gprs[reg1] & PFMF_NQ &&
+           S390_lowcore.stfl_fac_list & 0x00020000)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+       /* No support for conditional-SSKE */
+       if (vcpu->run->s.regs.gprs[reg1] & (PFMF_MR | PFMF_MC))
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+       start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
+       switch (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
+       case 0x00000000:
+               end = (start + (1UL << 12)) & ~((1UL << 12) - 1);
+               break;
+       case 0x00001000:
+               end = (start + (1UL << 20)) & ~((1UL << 20) - 1);
+               break;
+       /* We dont support EDAT2
+       case 0x00002000:
+               end = (start + (1UL << 31)) & ~((1UL << 31) - 1);
+               break;*/
+       default:
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+       }
+       while (start < end) {
+               unsigned long useraddr;
+
+               useraddr = gmap_translate(start, vcpu->arch.gmap);
+               if (IS_ERR((void *)useraddr))
+                       return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+
+               if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
+                       if (clear_user((void __user *)useraddr, PAGE_SIZE))
+                               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+               }
+
+               if (vcpu->run->s.regs.gprs[reg1] & PFMF_SK) {
+                       if (set_guest_storage_key(current->mm, useraddr,
+                                       vcpu->run->s.regs.gprs[reg1] & PFMF_KEY,
+                                       vcpu->run->s.regs.gprs[reg1] & PFMF_NQ))
+                               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+               }
+
+               start += PAGE_SIZE;
+       }
+       if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC)
+               vcpu->run->s.regs.gprs[reg2] = end;
+       return 0;
+}
+
 static const intercept_handler_t b9_handlers[256] = {
        [0x8d] = handle_epsw,
        [0x9c] = handle_io_inst,
+       [0xaf] = handle_pfmf,
 };
 
 int kvm_s390_handle_b9(struct kvm_vcpu *vcpu)