KVM: PPC: Book3S: Allow only implemented hcalls to be enabled or disabled
authorPaul Mackerras <paulus@samba.org>
Mon, 2 Jun 2014 01:03:00 +0000 (11:03 +1000)
committerAlexander Graf <agraf@suse.de>
Mon, 28 Jul 2014 13:22:18 +0000 (15:22 +0200)
This adds code to check that when the KVM_CAP_PPC_ENABLE_HCALL
capability is used to enable or disable in-kernel handling of an
hcall, that the hcall is actually implemented by the kernel.
If not an EINVAL error is returned.

This also checks the default-enabled list of hcalls and prints a
warning if any hcall there is not actually implemented.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
Documentation/virtual/kvm/api.txt
arch/powerpc/include/asm/kvm_book3s.h
arch/powerpc/include/asm/kvm_ppc.h
arch/powerpc/kvm/book3s.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_hv_builtin.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/book3s_pr.c
arch/powerpc/kvm/book3s_pr_papr.c
arch/powerpc/kvm/powerpc.c

index 5c54d196f4c8066cc66c857fd352d7d990563b91..69553183ef0f833bf7aff22c32eee46de80c9b0f 100644 (file)
@@ -3039,3 +3039,7 @@ not to attempt to handle the hcall, but will always exit to userspace
 to handle it.  Note that it may not make sense to enable some and
 disable others of a group of related hcalls, but KVM does not prevent
 userspace from doing that.
+
+If the hcall number specified is not one that has an in-kernel
+implementation, the KVM_ENABLE_CAP ioctl will fail with an EINVAL
+error.
index 052ab2ad49b51cc22de5961fdf4e69e4e5da54fd..ceb70aaad6d45f060b6646af6e60092b60dda7cf 100644 (file)
@@ -146,6 +146,7 @@ extern void kvmppc_mmu_invalidate_pte(struct kvm_vcpu *vcpu, struct hpte_cache *
 extern int kvmppc_mmu_hpte_sysinit(void);
 extern void kvmppc_mmu_hpte_sysexit(void);
 extern int kvmppc_mmu_hv_init(void);
+extern int kvmppc_book3s_hcall_implemented(struct kvm *kvm, unsigned long hc);
 
 extern int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr, bool data);
 extern int kvmppc_st(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr, bool data);
@@ -188,6 +189,8 @@ extern u32 kvmppc_alignment_dsisr(struct kvm_vcpu *vcpu, unsigned int inst);
 extern ulong kvmppc_alignment_dar(struct kvm_vcpu *vcpu, unsigned int inst);
 extern int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd);
 extern void kvmppc_pr_init_default_hcalls(struct kvm *kvm);
+extern int kvmppc_hcall_impl_pr(unsigned long cmd);
+extern int kvmppc_hcall_impl_hv_realmode(unsigned long cmd);
 extern void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu,
                                 struct kvm_vcpu *vcpu);
 extern void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,
index 9c89cdd067a643a6a0572042e90f0d77066dc1d4..e2fd5a133b9cdb3b4173a6b4b054c6acee9b0299 100644 (file)
@@ -228,7 +228,7 @@ struct kvmppc_ops {
        void (*fast_vcpu_kick)(struct kvm_vcpu *vcpu);
        long (*arch_vm_ioctl)(struct file *filp, unsigned int ioctl,
                              unsigned long arg);
-
+       int (*hcall_implemented)(unsigned long hcall);
 };
 
 extern struct kvmppc_ops *kvmppc_hv_ops;
index 90aa5c750e08edb42a09e3a6517dc898e1cafcde..bd75902b38ba4d918d860495e0d829b1b16d2dd8 100644 (file)
@@ -925,6 +925,11 @@ int kvmppc_core_check_processor_compat(void)
        return 0;
 }
 
+int kvmppc_book3s_hcall_implemented(struct kvm *kvm, unsigned long hcall)
+{
+       return kvm->arch.kvm_ops->hcall_implemented(hcall);
+}
+
 static int kvmppc_book3s_init(void)
 {
        int r;
index cf445d22570fe48c54e0c8fa989b7e759e4f76c4..c4377c70787a1634c8b26a5248f52bdd0de8baa6 100644 (file)
@@ -645,6 +645,28 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
        return RESUME_GUEST;
 }
 
+static int kvmppc_hcall_impl_hv(unsigned long cmd)
+{
+       switch (cmd) {
+       case H_CEDE:
+       case H_PROD:
+       case H_CONFER:
+       case H_REGISTER_VPA:
+#ifdef CONFIG_KVM_XICS
+       case H_XIRR:
+       case H_CPPR:
+       case H_EOI:
+       case H_IPI:
+       case H_IPOLL:
+       case H_XIRR_X:
+#endif
+               return 1;
+       }
+
+       /* See if it's in the real-mode table */
+       return kvmppc_hcall_impl_hv_realmode(cmd);
+}
+
 static int kvmppc_handle_exit_hv(struct kvm_run *run, struct kvm_vcpu *vcpu,
                                 struct task_struct *tsk)
 {
@@ -2451,9 +2473,13 @@ static unsigned int default_hcall_list[] = {
 static void init_default_hcalls(void)
 {
        int i;
+       unsigned int hcall;
 
-       for (i = 0; default_hcall_list[i]; ++i)
-               __set_bit(default_hcall_list[i] / 4, default_enabled_hcalls);
+       for (i = 0; default_hcall_list[i]; ++i) {
+               hcall = default_hcall_list[i];
+               WARN_ON(!kvmppc_hcall_impl_hv(hcall));
+               __set_bit(hcall / 4, default_enabled_hcalls);
+       }
 }
 
 static struct kvmppc_ops kvm_ops_hv = {
@@ -2488,6 +2514,7 @@ static struct kvmppc_ops kvm_ops_hv = {
        .emulate_mfspr = kvmppc_core_emulate_mfspr_hv,
        .fast_vcpu_kick = kvmppc_fast_vcpu_kick_hv,
        .arch_vm_ioctl  = kvm_arch_vm_ioctl_hv,
+       .hcall_implemented = kvmppc_hcall_impl_hv,
 };
 
 static int kvmppc_book3s_init_hv(void)
index 7cde8a6652056c26f4e05343439c90ca43328188..3b41447482e54f595a498ef170895ccf0295f52c 100644 (file)
@@ -212,3 +212,16 @@ bool kvm_hv_mode_active(void)
 {
        return atomic_read(&hv_vm_count) != 0;
 }
+
+extern int hcall_real_table[], hcall_real_table_end[];
+
+int kvmppc_hcall_impl_hv_realmode(unsigned long cmd)
+{
+       cmd /= 4;
+       if (cmd < hcall_real_table_end - hcall_real_table &&
+           hcall_real_table[cmd])
+               return 1;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(kvmppc_hcall_impl_hv_realmode);
index 33aaadef71390d55ea7f069cf739685c102ef54e..e66c1e388ddf5adf1c0fbd87a38e24c3f24696c4 100644 (file)
@@ -2042,6 +2042,7 @@ hcall_real_table:
        .long   0               /* 0x12c */
        .long   0               /* 0x130 */
        .long   DOTSYM(kvmppc_h_set_xdabr) - hcall_real_table
+       .globl  hcall_real_table_end
 hcall_real_table_end:
 
 ignore_hdec:
index 123ac7dc5e1f78fe132b13e43a6efae27c52e33d..15fd6c25179cffdd8b371c762ac68d504d673eb9 100644 (file)
@@ -1670,6 +1670,9 @@ static struct kvmppc_ops kvm_ops_pr = {
        .emulate_mfspr = kvmppc_core_emulate_mfspr_pr,
        .fast_vcpu_kick = kvm_vcpu_kick,
        .arch_vm_ioctl  = kvm_arch_vm_ioctl_pr,
+#ifdef CONFIG_PPC_BOOK3S_64
+       .hcall_implemented = kvmppc_hcall_impl_pr,
+#endif
 };
 
 
index eacaa6e4876e422881d772d5540e9f1f19777f1e..6d0143fbeb63879cb31118a2535226a7a5a406d5 100644 (file)
@@ -309,6 +309,27 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd)
        return EMULATE_FAIL;
 }
 
+int kvmppc_hcall_impl_pr(unsigned long cmd)
+{
+       switch (cmd) {
+       case H_ENTER:
+       case H_REMOVE:
+       case H_PROTECT:
+       case H_BULK_REMOVE:
+       case H_PUT_TCE:
+       case H_CEDE:
+#ifdef CONFIG_KVM_XICS
+       case H_XIRR:
+       case H_CPPR:
+       case H_EOI:
+       case H_IPI:
+       case H_IPOLL:
+       case H_XIRR_X:
+#endif
+               return 1;
+       }
+       return 0;
+}
 
 /*
  * List of hcall numbers to enable by default.
@@ -337,7 +358,11 @@ static unsigned int default_hcall_list[] = {
 void kvmppc_pr_init_default_hcalls(struct kvm *kvm)
 {
        int i;
+       unsigned int hcall;
 
-       for (i = 0; default_hcall_list[i]; ++i)
-               __set_bit(default_hcall_list[i] / 4, kvm->arch.enabled_hcalls);
+       for (i = 0; default_hcall_list[i]; ++i) {
+               hcall = default_hcall_list[i];
+               WARN_ON(!kvmppc_hcall_impl_pr(hcall));
+               __set_bit(hcall / 4, kvm->arch.enabled_hcalls);
+       }
 }
index 3222a4d08a6fe126960b8e0e88702ce5eb45c1b7..7efc2b71140404b9da1c5f289a10914899058847 100644 (file)
@@ -1119,6 +1119,8 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
                if (hcall > MAX_HCALL_OPCODE || (hcall & 3) ||
                    cap->args[1] > 1)
                        break;
+               if (!kvmppc_book3s_hcall_implemented(kvm, hcall))
+                       break;
                if (cap->args[1])
                        set_bit(hcall / 4, kvm->arch.enabled_hcalls);
                else