From: Christian Ehrhardt <ehrhardt@xxxxxxxxxxxxxxxxxx> This adds the host part of the magic page registration. This is a memory area of the guest granted to the host. The patch just introduces the infrastruture to receive and map the guest paddr. This is later used storage area a guest can read unprivileged (using binary rewriting to change privileges instructions). Signed-off-by: Christian Ehrhardt <ehrhardt@xxxxxxxxxxxxxxxxxx> --- [diffstat] arch/powerpc/kvm/booke_guest.c | 13 +++++++++++++ arch/powerpc/kvm/emulate.c | 9 +++++++++ arch/powerpc/kvm/powerpc.c | 20 ++++++++++++++++++-- include/asm-powerpc/kvm_host.h | 5 +++++ include/asm-powerpc/kvm_para.h | 24 ++++++++++++++++++++++++ include/linux/kvm.h | 5 +++++ 6 files changed, 74 insertions(+), 2 deletions(-) [diff] diff --git a/arch/powerpc/kvm/booke_guest.c b/arch/powerpc/kvm/booke_guest.c --- a/arch/powerpc/kvm/booke_guest.c +++ b/arch/powerpc/kvm/booke_guest.c @@ -21,6 +21,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/kvm_host.h> +#include <linux/kvm_para.h> #include <linux/module.h> #include <linux/vmalloc.h> #include <linux/fs.h> @@ -44,6 +45,7 @@ { "itlb_v", VCPU_STAT(itlb_virt_miss_exits) }, { "dtlb_r", VCPU_STAT(dtlb_real_miss_exits) }, { "dtlb_v", VCPU_STAT(dtlb_virt_miss_exits) }, + { "dtlb_pv", VCPU_STAT(dtlb_pvmem_miss_exits) }, { "sysc", VCPU_STAT(syscall_exits) }, { "hcall", VCPU_STAT(hcall_exits) }, { "isi", VCPU_STAT(isi_exits) }, @@ -344,6 +346,15 @@ unsigned long eaddr = vcpu->arch.fault_dear; gfn_t gfn; + if (kvmppc_is_pvmem(vcpu, eaddr)) { + kvmppc_mmu_map(vcpu, eaddr, + vcpu->arch.pvmem_gpaddr >> KVM_PPCPV_MAGIC_PAGE_SHIFT, + 0, KVM_PPCPV_MAGIC_PAGE_FLAGS); + vcpu->stat.dtlb_pvmem_miss_exits++; + r = RESUME_GUEST; + break; + } + /* Check the guest TLB. */ gtlbe = kvmppc_44x_dtlb_search(vcpu, eaddr); if (!gtlbe) { @@ -495,6 +506,8 @@ vcpu->arch.shadow_pid = 1; + vcpu->arch.pvmem = NULL; + /* Eye-catching number so we know if the guest takes an interrupt * before it's programmed its own IVPR. */ vcpu->arch.ivpr = 0x55550000; diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -21,6 +21,7 @@ #include <linux/timer.h> #include <linux/types.h> #include <linux/string.h> +#include <linux/highmem.h> #include <linux/kvm_host.h> #include <linux/kvm_para.h> @@ -207,8 +208,16 @@ int kvmppc_do_hypercall(struct kvm_vcpu *vcpu) { u32 ret = 0; + struct page *pvmem_page; switch (vcpu->arch.gpr[11]) { + case KVM_HCALL_RESERVE_MAGICPAGE: + vcpu->arch.pvmem_gvaddr = vcpu->arch.gpr[3]; + vcpu->arch.pvmem_gpaddr = vcpu->arch.gpr[4]; + pvmem_page = gfn_to_page(vcpu->kvm, + vcpu->arch.pvmem_gpaddr >> KVM_PPCPV_MAGIC_PAGE_SHIFT); + vcpu->arch.pvmem = kmap(pvmem_page); + break; default: printk(KERN_ERR "unknown hypercall %d\n", vcpu->arch.gpr[11]); kvmppc_dump_vcpu(vcpu); diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -21,6 +21,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/kvm_host.h> +#include <linux/kvm_para.h> #include <linux/module.h> #include <linux/vmalloc.h> #include <linux/fs.h> @@ -148,18 +149,33 @@ case KVM_CAP_COALESCED_MMIO: r = KVM_COALESCED_MMIO_PAGE_OFFSET; break; + case KVM_CAP_PPCPV_MAGICPAGE: + r = 1; + break; default: r = 0; break; } return r; - } long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { - return -EINVAL; + long r = -EINVAL; + + switch (ioctl) { + case KVM_GET_PPCPV_MAGICPAGE_SIZE: + if (arg) + goto out; + r = KVM_PPCPV_MAGIC_PAGE_SIZE; + break; + default: + break; + } +out: + return r; + } int kvm_arch_set_memory_region(struct kvm *kvm, diff --git a/include/asm-powerpc/kvm_host.h b/include/asm-powerpc/kvm_host.h --- a/include/asm-powerpc/kvm_host.h +++ b/include/asm-powerpc/kvm_host.h @@ -54,6 +54,7 @@ u32 itlb_real_miss_exits; u32 itlb_virt_miss_exits; u32 dtlb_real_miss_exits; + u32 dtlb_pvmem_miss_exits; u32 dtlb_virt_miss_exits; u32 syscall_exits; u32 hcall_exits; @@ -152,6 +153,10 @@ struct timer_list dec_timer; unsigned long pending_exceptions; + + long unsigned int pvmem_gvaddr; + long unsigned int pvmem_gpaddr; + void *pvmem; /* host mapping of pvmem */ }; struct kvm_guest_debug { diff --git a/include/asm-powerpc/kvm_para.h b/include/asm-powerpc/kvm_para.h --- a/include/asm-powerpc/kvm_para.h +++ b/include/asm-powerpc/kvm_para.h @@ -22,7 +22,19 @@ #ifdef __KERNEL__ +#include <linux/kvm_host.h> + #define KVM_HYPERCALL_BIN 0x44000022 + +#define KVM_HCALL_RESERVE_MAGICPAGE 0 + +/* + * the guest guarantees alignment to requested size, choosing page size here + * easens tlb handling which is handled by host for the magic page + */ +#define KVM_PPCPV_MAGIC_PAGE_SIZE 4096 +#define KVM_PPCPV_MAGIC_PAGE_SHIFT 12 +#define KVM_PPCPV_MAGIC_PAGE_FLAGS 0x3f static inline int kvm_para_available(void) { @@ -36,6 +48,18 @@ extern int kvmppc_do_hypercall(struct kvm_vcpu *vcpu); +static inline int kvmppc_is_pvmem(struct kvm_vcpu *vcpu, unsigned long eaddr) +{ + return vcpu->arch.pvmem && + (eaddr >> KVM_PPCPV_MAGIC_PAGE_SHIFT) == + (vcpu->arch.pvmem_gvaddr >> KVM_PPCPV_MAGIC_PAGE_SHIFT); +} + +static inline int kvmppc_has_pvmem(struct kvm_vcpu *vcpu) +{ + return !!vcpu->arch.pvmem; +} + #endif /* __KERNEL__ */ #endif /* __POWERPC_KVM_PARA_H__ */ diff --git a/include/linux/kvm.h b/include/linux/kvm.h --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -365,6 +365,10 @@ #define KVM_TRACE_PAUSE _IO(KVMIO, 0x07) #define KVM_TRACE_DISABLE _IO(KVMIO, 0x08) /* + * ioctls for powerpc paravirtualization extensions + */ +#define KVM_GET_PPCPV_MAGICPAGE_SIZE _IO(KVMIO, 0x09) +/* * Extension capability list. */ #define KVM_CAP_IRQCHIP 0 @@ -382,6 +386,7 @@ #define KVM_CAP_PV_MMU 13 #define KVM_CAP_MP_STATE 14 #define KVM_CAP_COALESCED_MMIO 15 +#define KVM_CAP_PPCPV_MAGICPAGE 16 /* * ioctls for VM fds -- To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html