From 5f8754c077a3b4b35eba921d7175ce0944de8e8e Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Mon, 26 Feb 2018 19:19:05 +0000 Subject: [PATCH] Add a new variant of the GLA2GPA ioctl for use by the debug server. Unlike the existing GLA2GPA ioctl, GLA2GPA_NOFAULT does not modify the guest. In particular, it does not inject any faults or modify PTEs in the guest when performing an address space translation. This is used by bhyve's debug server to read and write memory for the remote debugger. Reviewed by: grehan MFC after: 1 month Differential Revision: https://reviews.freebsd.org/D14075 --- lib/libvmmapi/vmmapi.c | 22 +++++++++ lib/libvmmapi/vmmapi.h | 3 ++ sys/amd64/include/vmm_dev.h | 3 ++ sys/amd64/include/vmm_instruction_emul.h | 7 +++ sys/amd64/vmm/vmm_dev.c | 8 +++ sys/amd64/vmm/vmm_instruction_emul.c | 62 +++++++++++++++++------- 6 files changed, 88 insertions(+), 17 deletions(-) diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c index 4580ddc3dbb6..292e749eaaa2 100644 --- a/lib/libvmmapi/vmmapi.c +++ b/lib/libvmmapi/vmmapi.c @@ -1233,6 +1233,27 @@ vm_gla2gpa(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, return (error); } +int +vm_gla2gpa_nofault(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, + uint64_t gla, int prot, uint64_t *gpa, int *fault) +{ + struct vm_gla2gpa gg; + int error; + + bzero(&gg, sizeof(struct vm_gla2gpa)); + gg.vcpuid = vcpu; + gg.prot = prot; + gg.gla = gla; + gg.paging = *paging; + + error = ioctl(ctx->fd, VM_GLA2GPA_NOFAULT, &gg); + if (error == 0) { + *fault = gg.fault; + *gpa = gg.gpa; + } + return (error); +} + #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif @@ -1479,6 +1500,7 @@ vm_get_ioctls(size_t *len) VM_PPTDEV_MSIX, VM_INJECT_NMI, VM_STATS, VM_STAT_DESC, VM_SET_X2APIC_STATE, VM_GET_X2APIC_STATE, VM_GET_HPET_CAPABILITIES, VM_GET_GPA_PMAP, VM_GLA2GPA, + VM_GLA2GPA_NOFAULT, VM_ACTIVATE_CPU, VM_GET_CPUS, VM_SET_INTINFO, VM_GET_INTINFO, VM_RTC_WRITE, VM_RTC_READ, VM_RTC_SETTIME, VM_RTC_GETTIME, VM_RESTART_INSTRUCTION }; diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h index 9650d2ec1b53..e391d9131e86 100644 --- a/lib/libvmmapi/vmmapi.h +++ b/lib/libvmmapi/vmmapi.h @@ -113,6 +113,9 @@ void *vm_map_gpa(struct vmctx *ctx, vm_paddr_t gaddr, size_t len); int vm_get_gpa_pmap(struct vmctx *, uint64_t gpa, uint64_t *pte, int *num); int vm_gla2gpa(struct vmctx *, int vcpuid, struct vm_guest_paging *paging, uint64_t gla, int prot, uint64_t *gpa, int *fault); +int vm_gla2gpa_nofault(struct vmctx *, int vcpuid, + struct vm_guest_paging *paging, uint64_t gla, int prot, + uint64_t *gpa, int *fault); uint32_t vm_get_lowmem_limit(struct vmctx *ctx); void vm_set_lowmem_limit(struct vmctx *ctx, uint32_t limit); void vm_set_memflags(struct vmctx *ctx, int flags); diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h index c667c45ed077..28de79e225ba 100644 --- a/sys/amd64/include/vmm_dev.h +++ b/sys/amd64/include/vmm_dev.h @@ -243,6 +243,7 @@ enum { IOCNUM_GET_MEMSEG = 15, IOCNUM_MMAP_MEMSEG = 16, IOCNUM_MMAP_GETNEXT = 17, + IOCNUM_GLA2GPA_NOFAULT = 18, /* register/state accessors */ IOCNUM_SET_REGISTER = 20, @@ -379,6 +380,8 @@ enum { _IOWR('v', IOCNUM_GET_GPA_PMAP, struct vm_gpa_pte) #define VM_GLA2GPA \ _IOWR('v', IOCNUM_GLA2GPA, struct vm_gla2gpa) +#define VM_GLA2GPA_NOFAULT \ + _IOWR('v', IOCNUM_GLA2GPA_NOFAULT, struct vm_gla2gpa) #define VM_ACTIVATE_CPU \ _IOW('v', IOCNUM_ACTIVATE_CPU, struct vm_activate_cpu) #define VM_GET_CPUS \ diff --git a/sys/amd64/include/vmm_instruction_emul.h b/sys/amd64/include/vmm_instruction_emul.h index 261be571a071..bca2a02e286c 100644 --- a/sys/amd64/include/vmm_instruction_emul.h +++ b/sys/amd64/include/vmm_instruction_emul.h @@ -97,6 +97,13 @@ int vmm_fetch_instruction(struct vm *vm, int cpuid, int vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, uint64_t gla, int prot, uint64_t *gpa, int *is_fault); +/* + * Like vm_gla2gpa, but no exceptions are injected into the guest and + * PTEs are not changed. + */ +int vm_gla2gpa_nofault(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, + uint64_t gla, int prot, uint64_t *gpa, int *is_fault); + void vie_init(struct vie *vie, const char *inst_bytes, int inst_length); /* diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c index 6071da7f0e1f..fe633e3eb096 100644 --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -375,6 +375,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, case VM_PPTDEV_MSIX: case VM_SET_X2APIC_STATE: case VM_GLA2GPA: + case VM_GLA2GPA_NOFAULT: case VM_ACTIVATE_CPU: case VM_SET_INTINFO: case VM_GET_INTINFO: @@ -665,6 +666,13 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, ("%s: vm_gla2gpa unknown error %d", __func__, error)); break; } + case VM_GLA2GPA_NOFAULT: + gg = (struct vm_gla2gpa *)data; + error = vm_gla2gpa_nofault(sc->vm, gg->vcpuid, &gg->paging, + gg->gla, gg->prot, &gg->gpa, &gg->fault); + KASSERT(error == 0 || error == EFAULT, + ("%s: vm_gla2gpa unknown error %d", __func__, error)); + break; case VM_ACTIVATE_CPU: vac = (struct vm_activate_cpu *)data; error = vm_activate_cpu(sc->vm, vac->vcpuid); diff --git a/sys/amd64/vmm/vmm_instruction_emul.c b/sys/amd64/vmm/vmm_instruction_emul.c index c8c44672b2d3..21c6a1373e6b 100644 --- a/sys/amd64/vmm/vmm_instruction_emul.c +++ b/sys/amd64/vmm/vmm_instruction_emul.c @@ -1718,9 +1718,9 @@ ptp_hold(struct vm *vm, int vcpu, vm_paddr_t ptpphys, size_t len, void **cookie) return (ptr); } -int -vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, - uint64_t gla, int prot, uint64_t *gpa, int *guest_fault) +static int +_vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, + uint64_t gla, int prot, uint64_t *gpa, int *guest_fault, bool check_only) { int nlevels, pfcode, ptpshift, ptpindex, retval, usermode, writable; u_int retries; @@ -1746,7 +1746,8 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, * XXX assuming a non-stack reference otherwise a stack fault * should be generated. */ - vm_inject_gp(vm, vcpuid); + if (!check_only) + vm_inject_gp(vm, vcpuid); goto fault; } @@ -1776,9 +1777,11 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, if ((pte32 & PG_V) == 0 || (usermode && (pte32 & PG_U) == 0) || (writable && (pte32 & PG_RW) == 0)) { - pfcode = pf_error_code(usermode, prot, 0, - pte32); - vm_inject_pf(vm, vcpuid, pfcode, gla); + if (!check_only) { + pfcode = pf_error_code(usermode, prot, 0, + pte32); + vm_inject_pf(vm, vcpuid, pfcode, gla); + } goto fault; } @@ -1789,7 +1792,7 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, * is only set at the last level providing the guest * physical address. */ - if ((pte32 & PG_A) == 0) { + if (!check_only && (pte32 & PG_A) == 0) { if (atomic_cmpset_32(&ptpbase32[ptpindex], pte32, pte32 | PG_A) == 0) { goto restart; @@ -1804,7 +1807,7 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, } /* Set the dirty bit in the page table entry if necessary */ - if (writable && (pte32 & PG_M) == 0) { + if (!check_only && writable && (pte32 & PG_M) == 0) { if (atomic_cmpset_32(&ptpbase32[ptpindex], pte32, pte32 | PG_M) == 0) { goto restart; @@ -1831,8 +1834,10 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, pte = ptpbase[ptpindex]; if ((pte & PG_V) == 0) { - pfcode = pf_error_code(usermode, prot, 0, pte); - vm_inject_pf(vm, vcpuid, pfcode, gla); + if (!check_only) { + pfcode = pf_error_code(usermode, prot, 0, pte); + vm_inject_pf(vm, vcpuid, pfcode, gla); + } goto fault; } @@ -1858,13 +1863,15 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, if ((pte & PG_V) == 0 || (usermode && (pte & PG_U) == 0) || (writable && (pte & PG_RW) == 0)) { - pfcode = pf_error_code(usermode, prot, 0, pte); - vm_inject_pf(vm, vcpuid, pfcode, gla); + if (!check_only) { + pfcode = pf_error_code(usermode, prot, 0, pte); + vm_inject_pf(vm, vcpuid, pfcode, gla); + } goto fault; } /* Set the accessed bit in the page table entry */ - if ((pte & PG_A) == 0) { + if (!check_only && (pte & PG_A) == 0) { if (atomic_cmpset_64(&ptpbase[ptpindex], pte, pte | PG_A) == 0) { goto restart; @@ -1873,8 +1880,11 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, if (nlevels > 0 && (pte & PG_PS) != 0) { if (pgsize > 1 * GB) { - pfcode = pf_error_code(usermode, prot, 1, pte); - vm_inject_pf(vm, vcpuid, pfcode, gla); + if (!check_only) { + pfcode = pf_error_code(usermode, prot, 1, + pte); + vm_inject_pf(vm, vcpuid, pfcode, gla); + } goto fault; } break; @@ -1884,7 +1894,7 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, } /* Set the dirty bit in the page table entry if necessary */ - if (writable && (pte & PG_M) == 0) { + if (!check_only && writable && (pte & PG_M) == 0) { if (atomic_cmpset_64(&ptpbase[ptpindex], pte, pte | PG_M) == 0) goto restart; } @@ -1905,6 +1915,24 @@ vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, goto done; } +int +vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, + uint64_t gla, int prot, uint64_t *gpa, int *guest_fault) +{ + + return (_vm_gla2gpa(vm, vcpuid, paging, gla, prot, gpa, guest_fault, + false)); +} + +int +vm_gla2gpa_nofault(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, + uint64_t gla, int prot, uint64_t *gpa, int *guest_fault) +{ + + return (_vm_gla2gpa(vm, vcpuid, paging, gla, prot, gpa, guest_fault, + true)); +} + int vmm_fetch_instruction(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, uint64_t rip, int inst_length, struct vie *vie, int *faultptr)