KVM: PPC: Book3S: Allow only implemented hcalls to be enabled or disabled

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>
This commit is contained in:
Paul Mackerras 2014-06-02 11:03:00 +10:00 committed by Alexander Graf
parent 699a0ea082
commit ae2113a4f1
10 changed files with 88 additions and 5 deletions

View 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 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 disable others of a group of related hcalls, but KVM does not prevent
userspace from doing that. 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.

View 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 int kvmppc_mmu_hpte_sysinit(void);
extern void kvmppc_mmu_hpte_sysexit(void); extern void kvmppc_mmu_hpte_sysexit(void);
extern int kvmppc_mmu_hv_init(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_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); 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 ulong kvmppc_alignment_dar(struct kvm_vcpu *vcpu, unsigned int inst);
extern int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd); extern int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd);
extern void kvmppc_pr_init_default_hcalls(struct kvm *kvm); 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, extern void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu,
struct kvm_vcpu *vcpu); struct kvm_vcpu *vcpu);
extern void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu, extern void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,

View File

@ -228,7 +228,7 @@ struct kvmppc_ops {
void (*fast_vcpu_kick)(struct kvm_vcpu *vcpu); void (*fast_vcpu_kick)(struct kvm_vcpu *vcpu);
long (*arch_vm_ioctl)(struct file *filp, unsigned int ioctl, long (*arch_vm_ioctl)(struct file *filp, unsigned int ioctl,
unsigned long arg); unsigned long arg);
int (*hcall_implemented)(unsigned long hcall);
}; };
extern struct kvmppc_ops *kvmppc_hv_ops; extern struct kvmppc_ops *kvmppc_hv_ops;

View File

@ -925,6 +925,11 @@ int kvmppc_core_check_processor_compat(void)
return 0; 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) static int kvmppc_book3s_init(void)
{ {
int r; int r;

View File

@ -645,6 +645,28 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
return RESUME_GUEST; 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, static int kvmppc_handle_exit_hv(struct kvm_run *run, struct kvm_vcpu *vcpu,
struct task_struct *tsk) struct task_struct *tsk)
{ {
@ -2451,9 +2473,13 @@ static unsigned int default_hcall_list[] = {
static void init_default_hcalls(void) static void init_default_hcalls(void)
{ {
int i; int i;
unsigned int hcall;
for (i = 0; default_hcall_list[i]; ++i) for (i = 0; default_hcall_list[i]; ++i) {
__set_bit(default_hcall_list[i] / 4, default_enabled_hcalls); 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 = { 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, .emulate_mfspr = kvmppc_core_emulate_mfspr_hv,
.fast_vcpu_kick = kvmppc_fast_vcpu_kick_hv, .fast_vcpu_kick = kvmppc_fast_vcpu_kick_hv,
.arch_vm_ioctl = kvm_arch_vm_ioctl_hv, .arch_vm_ioctl = kvm_arch_vm_ioctl_hv,
.hcall_implemented = kvmppc_hcall_impl_hv,
}; };
static int kvmppc_book3s_init_hv(void) static int kvmppc_book3s_init_hv(void)

View File

@ -212,3 +212,16 @@ bool kvm_hv_mode_active(void)
{ {
return atomic_read(&hv_vm_count) != 0; 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);

View File

@ -2042,6 +2042,7 @@ hcall_real_table:
.long 0 /* 0x12c */ .long 0 /* 0x12c */
.long 0 /* 0x130 */ .long 0 /* 0x130 */
.long DOTSYM(kvmppc_h_set_xdabr) - hcall_real_table .long DOTSYM(kvmppc_h_set_xdabr) - hcall_real_table
.globl hcall_real_table_end
hcall_real_table_end: hcall_real_table_end:
ignore_hdec: ignore_hdec:

View File

@ -1670,6 +1670,9 @@ static struct kvmppc_ops kvm_ops_pr = {
.emulate_mfspr = kvmppc_core_emulate_mfspr_pr, .emulate_mfspr = kvmppc_core_emulate_mfspr_pr,
.fast_vcpu_kick = kvm_vcpu_kick, .fast_vcpu_kick = kvm_vcpu_kick,
.arch_vm_ioctl = kvm_arch_vm_ioctl_pr, .arch_vm_ioctl = kvm_arch_vm_ioctl_pr,
#ifdef CONFIG_PPC_BOOK3S_64
.hcall_implemented = kvmppc_hcall_impl_pr,
#endif
}; };

View File

@ -309,6 +309,27 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd)
return EMULATE_FAIL; 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. * 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) void kvmppc_pr_init_default_hcalls(struct kvm *kvm)
{ {
int i; int i;
unsigned int hcall;
for (i = 0; default_hcall_list[i]; ++i) for (i = 0; default_hcall_list[i]; ++i) {
__set_bit(default_hcall_list[i] / 4, kvm->arch.enabled_hcalls); hcall = default_hcall_list[i];
WARN_ON(!kvmppc_hcall_impl_pr(hcall));
__set_bit(hcall / 4, kvm->arch.enabled_hcalls);
}
} }

View File

@ -1119,6 +1119,8 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
if (hcall > MAX_HCALL_OPCODE || (hcall & 3) || if (hcall > MAX_HCALL_OPCODE || (hcall & 3) ||
cap->args[1] > 1) cap->args[1] > 1)
break; break;
if (!kvmppc_book3s_hcall_implemented(kvm, hcall))
break;
if (cap->args[1]) if (cap->args[1])
set_bit(hcall / 4, kvm->arch.enabled_hcalls); set_bit(hcall / 4, kvm->arch.enabled_hcalls);
else else