KVM: MIPS: Use uaccess to read/modify guest instructions
Now that we have GVA page tables, use standard user accesses with page faults disabled to read & modify guest instructions. This should be more robust (than the rather dodgy method of accessing guest mapped segments by just directly addressing them) and will also work with Enhanced Virtual Addressing (EVA) host kernel configurations where dedicated instructions are needed for accessing user mode memory. For simplicity and speed we do this regardless of the guest segment the address resides in, rather than handling guest KSeg0 specially with kmap_atomic() as before. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: "Radim Krčmář" <rkrcmar@redhat.com> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: linux-mips@linux-mips.org Cc: kvm@vger.kernel.org
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/bootmem.h>
|
||||
@@ -29,28 +30,15 @@
|
||||
static int kvm_mips_trans_replace(struct kvm_vcpu *vcpu, u32 *opc,
|
||||
union mips_instruction replace)
|
||||
{
|
||||
unsigned long paddr, flags;
|
||||
void *vaddr;
|
||||
unsigned long vaddr = (unsigned long)opc;
|
||||
int err;
|
||||
|
||||
if (KVM_GUEST_KSEGX((unsigned long)opc) == KVM_GUEST_KSEG0) {
|
||||
paddr = kvm_mips_translate_guest_kseg0_to_hpa(vcpu,
|
||||
(unsigned long)opc);
|
||||
vaddr = kmap_atomic(pfn_to_page(PHYS_PFN(paddr)));
|
||||
vaddr += paddr & ~PAGE_MASK;
|
||||
memcpy(vaddr, (void *)&replace, sizeof(u32));
|
||||
local_flush_icache_range((unsigned long)vaddr,
|
||||
(unsigned long)vaddr + 32);
|
||||
kunmap_atomic(vaddr);
|
||||
} else if (KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) {
|
||||
local_irq_save(flags);
|
||||
memcpy((void *)opc, (void *)&replace, sizeof(u32));
|
||||
__local_flush_icache_user_range((unsigned long)opc,
|
||||
(unsigned long)opc + 32);
|
||||
local_irq_restore(flags);
|
||||
} else {
|
||||
err = put_user(replace.word, opc);
|
||||
if (unlikely(err)) {
|
||||
kvm_err("%s: Invalid address: %p\n", __func__, opc);
|
||||
return -EFAULT;
|
||||
return err;
|
||||
}
|
||||
__local_flush_icache_user_range(vaddr, vaddr + 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user