svn commit: r254575 - in projects/bhyve_npt_pmap/sys/amd64: include vmm vmm/io
Neel Natu
neel at FreeBSD.org
Tue Aug 20 18:06:19 UTC 2013
Author: neel
Date: Tue Aug 20 18:06:18 2013
New Revision: 254575
URL: http://svnweb.freebsd.org/changeset/base/254575
Log:
Virtual machines with PCI passthru devices get special treatment:
- the entire guest address space is wired
- iommu mappings need to be created to map guest physical addresses to the
wired host physical pages.
This happens automatically when the first PCI passthru device is attached
to the guest. The reverse happens automatically when the last PCI passthru
device is detached from the guest.
Modified:
projects/bhyve_npt_pmap/sys/amd64/include/vmm.h
projects/bhyve_npt_pmap/sys/amd64/vmm/io/ppt.c
projects/bhyve_npt_pmap/sys/amd64/vmm/io/ppt.h
projects/bhyve_npt_pmap/sys/amd64/vmm/vmm.c
projects/bhyve_npt_pmap/sys/amd64/vmm/vmm_dev.c
Modified: projects/bhyve_npt_pmap/sys/amd64/include/vmm.h
==============================================================================
--- projects/bhyve_npt_pmap/sys/amd64/include/vmm.h Tue Aug 20 18:05:31 2013 (r254574)
+++ projects/bhyve_npt_pmap/sys/amd64/include/vmm.h Tue Aug 20 18:06:18 2013 (r254575)
@@ -153,6 +153,8 @@ vcpu_is_running(struct vm *vm, int vcpu,
void *vcpu_stats(struct vm *vm, int vcpu);
void vm_interrupt_hostcpu(struct vm *vm, int vcpu);
struct vmspace *vm_get_vmspace(struct vm *vm);
+int vm_assign_pptdev(struct vm *vm, int bus, int slot, int func);
+int vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func);
#endif /* KERNEL */
#include <machine/vmm_instruction_emul.h>
Modified: projects/bhyve_npt_pmap/sys/amd64/vmm/io/ppt.c
==============================================================================
--- projects/bhyve_npt_pmap/sys/amd64/vmm/io/ppt.c Tue Aug 20 18:05:31 2013 (r254574)
+++ projects/bhyve_npt_pmap/sys/amd64/vmm/io/ppt.c Tue Aug 20 18:06:18 2013 (r254575)
@@ -282,6 +282,43 @@ ppt_teardown_msix(struct pptdev *ppt)
}
int
+ppt_num_devices(struct vm *vm)
+{
+ int i, num;
+
+ num = 0;
+ for (i = 0; i < num_pptdevs; i++) {
+ if (pptdevs[i].vm == vm)
+ num++;
+ }
+ return (num);
+}
+
+boolean_t
+ppt_is_mmio(struct vm *vm, vm_paddr_t gpa)
+{
+ int i, n;
+ struct pptdev *ppt;
+ struct vm_memory_segment *seg;
+
+ for (n = 0; n < num_pptdevs; n++) {
+ ppt = &pptdevs[n];
+ if (ppt->vm != vm)
+ continue;
+
+ for (i = 0; i < MAX_MMIOSEGS; i++) {
+ seg = &ppt->mmio[i];
+ if (seg->len == 0)
+ continue;
+ if (gpa >= seg->gpa && gpa < seg->gpa + seg->len)
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+int
ppt_assign_device(struct vm *vm, int bus, int slot, int func)
{
struct pptdev *ppt;
@@ -336,7 +373,7 @@ ppt_unassign_all(struct vm *vm)
bus = pci_get_bus(dev);
slot = pci_get_slot(dev);
func = pci_get_function(dev);
- ppt_unassign_device(vm, bus, slot, func);
+ vm_unassign_pptdev(vm, bus, slot, func);
}
}
@@ -591,4 +628,3 @@ ppt_setup_msix(struct vm *vm, int vcpu,
return (0);
}
-
Modified: projects/bhyve_npt_pmap/sys/amd64/vmm/io/ppt.h
==============================================================================
--- projects/bhyve_npt_pmap/sys/amd64/vmm/io/ppt.h Tue Aug 20 18:05:31 2013 (r254574)
+++ projects/bhyve_npt_pmap/sys/amd64/vmm/io/ppt.h Tue Aug 20 18:06:18 2013 (r254575)
@@ -29,13 +29,20 @@
#ifndef _IO_PPT_H_
#define _IO_PPT_H_
-int ppt_assign_device(struct vm *vm, int bus, int slot, int func);
-int ppt_unassign_device(struct vm *vm, int bus, int slot, int func);
int ppt_unassign_all(struct vm *vm);
int ppt_map_mmio(struct vm *vm, int bus, int slot, int func,
vm_paddr_t gpa, size_t len, vm_paddr_t hpa);
int ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func,
int destcpu, int vector, int numvec);
int ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func,
- int idx, uint32_t msg, uint32_t vector_control, uint64_t addr);
+ int idx, uint32_t msg, uint32_t vector_control, uint64_t addr);
+int ppt_num_devices(struct vm *vm);
+boolean_t ppt_is_mmio(struct vm *vm, vm_paddr_t gpa);
+
+/*
+ * The following functions should never be called directly.
+ * Use 'vm_assign_pptdev()' and 'vm_unassign_pptdev()' instead.
+ */
+int ppt_assign_device(struct vm *vm, int bus, int slot, int func);
+int ppt_unassign_device(struct vm *vm, int bus, int slot, int func);
#endif
Modified: projects/bhyve_npt_pmap/sys/amd64/vmm/vmm.c
==============================================================================
--- projects/bhyve_npt_pmap/sys/amd64/vmm/vmm.c Tue Aug 20 18:05:31 2013 (r254574)
+++ projects/bhyve_npt_pmap/sys/amd64/vmm/vmm.c Tue Aug 20 18:06:18 2013 (r254575)
@@ -99,6 +99,7 @@ struct vcpu {
struct mem_seg {
vm_paddr_t gpa;
size_t len;
+ boolean_t wired;
vm_object_t object;
};
#define VM_MAX_MEMORY_SEGMENTS 2
@@ -311,9 +312,6 @@ vm_create(const char *name, struct vm **
return (0);
}
-/*
- * XXX need to deal with iommu mappings
- */
static void
vm_free_mem_seg(struct vm *vm, struct mem_seg *seg)
{
@@ -357,15 +355,20 @@ vm_name(struct vm *vm)
int
vm_map_mmio(struct vm *vm, vm_paddr_t gpa, size_t len, vm_paddr_t hpa)
{
+ vm_object_t obj;
- return (ENXIO); /* XXX fixme */
+ if ((obj = vmm_mmio_alloc(vm->vmspace, gpa, len, hpa)) == NULL)
+ return (ENOMEM);
+ else
+ return (0);
}
int
vm_unmap_mmio(struct vm *vm, vm_paddr_t gpa, size_t len)
{
- return (ENXIO); /* XXX fixme */
+ vmm_mmio_free(vm->vmspace, gpa, len);
+ return (0);
}
boolean_t
@@ -378,15 +381,15 @@ vm_mem_allocated(struct vm *vm, vm_paddr
gpabase = vm->mem_segs[i].gpa;
gpalimit = gpabase + vm->mem_segs[i].len;
if (gpa >= gpabase && gpa < gpalimit)
- return (TRUE);
+ return (TRUE); /* 'gpa' is regular memory */
}
+ if (ppt_is_mmio(vm, gpa))
+ return (TRUE); /* 'gpa' is pci passthru mmio */
+
return (FALSE);
}
-/*
- * XXX need to deal with iommu
- */
int
vm_malloc(struct vm *vm, vm_paddr_t gpa, size_t len)
{
@@ -434,12 +437,159 @@ vm_malloc(struct vm *vm, vm_paddr_t gpa,
seg->gpa = gpa;
seg->len = len;
seg->object = object;
+ seg->wired = FALSE;
vm->num_mem_segs++;
return (0);
}
+static void
+vm_gpa_unwire(struct vm *vm)
+{
+ int i, rv;
+ struct mem_seg *seg;
+
+ for (i = 0; i < vm->num_mem_segs; i++) {
+ seg = &vm->mem_segs[i];
+ if (!seg->wired)
+ continue;
+
+ rv = vm_map_unwire(&vm->vmspace->vm_map,
+ seg->gpa, seg->gpa + seg->len,
+ VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES);
+ KASSERT(rv == KERN_SUCCESS, ("vm(%s) memory segment "
+ "%#lx/%ld could not be unwired: %d",
+ vm_name(vm), seg->gpa, seg->len, rv));
+
+ seg->wired = FALSE;
+ }
+}
+
+static int
+vm_gpa_wire(struct vm *vm)
+{
+ int i, rv;
+ struct mem_seg *seg;
+
+ for (i = 0; i < vm->num_mem_segs; i++) {
+ seg = &vm->mem_segs[i];
+ if (seg->wired)
+ continue;
+
+ /* XXX rlimits? */
+ rv = vm_map_wire(&vm->vmspace->vm_map,
+ seg->gpa, seg->gpa + seg->len,
+ VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES);
+ if (rv != KERN_SUCCESS)
+ break;
+
+ seg->wired = TRUE;
+ }
+
+ if (i < vm->num_mem_segs) {
+ /*
+ * Undo the wiring before returning an error.
+ */
+ vm_gpa_unwire(vm);
+ return (EAGAIN);
+ }
+
+ return (0);
+}
+
+static void
+vm_iommu_modify(struct vm *vm, boolean_t map)
+{
+ int i, sz;
+ vm_paddr_t gpa, hpa;
+ struct mem_seg *seg;
+ void *vp, *cookie, *host_domain;
+
+ sz = PAGE_SIZE;
+ host_domain = iommu_host_domain();
+
+ for (i = 0; i < vm->num_mem_segs; i++) {
+ seg = &vm->mem_segs[i];
+ KASSERT(seg->wired,
+ ("vm(%s) memory segment %#lx/%ld not wired",
+ vm_name(vm), seg->gpa, seg->len));
+
+ gpa = seg->gpa;
+ while (gpa < seg->gpa + seg->len) {
+ vp = vm_gpa_hold(vm, gpa, PAGE_SIZE, VM_PROT_WRITE,
+ &cookie);
+ KASSERT(vp != NULL, ("vm(%s) could not map gpa %#lx",
+ vm_name(vm), gpa));
+
+ vm_gpa_release(cookie);
+
+ hpa = DMAP_TO_PHYS((uintptr_t)vp);
+ if (map) {
+ iommu_create_mapping(vm->iommu, gpa, hpa, sz);
+ iommu_remove_mapping(host_domain, hpa, sz);
+ } else {
+ iommu_remove_mapping(vm->iommu, gpa, sz);
+ iommu_create_mapping(host_domain, hpa, hpa, sz);
+ }
+
+ gpa += PAGE_SIZE;
+ }
+ }
+
+ /*
+ * Invalidate the cached translations associated with the domain
+ * from which pages were removed.
+ */
+ if (map)
+ iommu_invalidate_tlb(host_domain);
+ else
+ iommu_invalidate_tlb(vm->iommu);
+}
+
+#define vm_iommu_unmap(vm) vm_iommu_modify((vm), FALSE)
+#define vm_iommu_map(vm) vm_iommu_modify((vm), TRUE)
+
+int
+vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func)
+{
+ int error;
+
+ error = ppt_unassign_device(vm, bus, slot, func);
+ if (error)
+ return (error);
+
+ if (ppt_num_devices(vm) == 0) {
+ vm_iommu_unmap(vm);
+ vm_gpa_unwire(vm);
+ }
+ return (0);
+}
+
+int
+vm_assign_pptdev(struct vm *vm, int bus, int slot, int func)
+{
+ int error;
+
+ /*
+ * Virtual machines with pci passthru devices get special treatment:
+ * - the guest physical memory is wired
+ * - the iommu is programmed to do the 'gpa' to 'hpa' translation
+ *
+ * We need to do this before the first pci passthru device is attached.
+ */
+ if (ppt_num_devices(vm) == 0) {
+ error = vm_gpa_wire(vm);
+ if (error)
+ return (error);
+
+ vm_iommu_map(vm);
+ }
+
+ error = ppt_assign_device(vm, bus, slot, func);
+ return (error);
+}
+
void *
vm_gpa_hold(struct vm *vm, vm_paddr_t gpa, size_t len, int reqprot,
void **cookie)
@@ -483,6 +633,7 @@ vm_gpabase2memseg(struct vm *vm, vm_padd
if (gpabase == vm->mem_segs[i].gpa) {
seg->gpa = vm->mem_segs[i].gpa;
seg->len = vm->mem_segs[i].len;
+ seg->wired = vm->mem_segs[i].wired;
return (0);
}
}
Modified: projects/bhyve_npt_pmap/sys/amd64/vmm/vmm_dev.c
==============================================================================
--- projects/bhyve_npt_pmap/sys/amd64/vmm/vmm_dev.c Tue Aug 20 18:05:31 2013 (r254574)
+++ projects/bhyve_npt_pmap/sys/amd64/vmm/vmm_dev.c Tue Aug 20 18:06:18 2013 (r254575)
@@ -271,13 +271,13 @@ vmmdev_ioctl(struct cdev *cdev, u_long c
break;
case VM_BIND_PPTDEV:
pptdev = (struct vm_pptdev *)data;
- error = ppt_assign_device(sc->vm, pptdev->bus, pptdev->slot,
- pptdev->func);
+ error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot,
+ pptdev->func);
break;
case VM_UNBIND_PPTDEV:
pptdev = (struct vm_pptdev *)data;
- error = ppt_unassign_device(sc->vm, pptdev->bus, pptdev->slot,
- pptdev->func);
+ error = vm_unassign_pptdev(sc->vm, pptdev->bus, pptdev->slot,
+ pptdev->func);
break;
case VM_INJECT_EVENT:
vmevent = (struct vm_event *)data;
More information about the svn-src-projects
mailing list