Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Aug 2013 18:06:18 +0000 (UTC)
From:      Neel Natu <neel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r254575 - in projects/bhyve_npt_pmap/sys/amd64: include vmm vmm/io
Message-ID:  <201308201806.r7KI6Ib4039481@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201308201806.r7KI6Ib4039481>