MFC r273856,273863,273963-273965
- Add two new functions to the AGP driver KPI to bind/unbind arbitrary sets of pages into the GTT. - Avoid possible overflow in agp_generic_alloc_memory. - In agp(4) avoid the need to flush all cpu caches with wbinvd between updating the GTT and flushing the AGP TLB by storing the GTT in write-combining memory. - In agp_amd_bind_page don't flush the AGP TLB. It's done by the calling function. - agp_generic_unbind_memory: flush AGP TLB before unwiring pages agp_bind_pages: assert that pages have been wired down
This commit is contained in:
parent
0e426a7bb8
commit
3add3ea367
@ -50,6 +50,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/pci/pcireg.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/vm_page.h>
|
||||
@ -84,14 +86,6 @@ static devclass_t agp_devclass;
|
||||
|
||||
/* Helper functions for implementing chipset mini drivers. */
|
||||
|
||||
void
|
||||
agp_flush_cache()
|
||||
{
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
wbinvd();
|
||||
#endif
|
||||
}
|
||||
|
||||
u_int8_t
|
||||
agp_find_caps(device_t dev)
|
||||
{
|
||||
@ -158,17 +152,16 @@ agp_alloc_gatt(device_t dev)
|
||||
return 0;
|
||||
|
||||
gatt->ag_entries = entries;
|
||||
gatt->ag_virtual = contigmalloc(entries * sizeof(u_int32_t), M_AGP, 0,
|
||||
0, ~0, PAGE_SIZE, 0);
|
||||
gatt->ag_virtual = (void *)kmem_alloc_contig(kernel_arena,
|
||||
entries * sizeof(u_int32_t), M_NOWAIT | M_ZERO, 0, ~0, PAGE_SIZE,
|
||||
0, VM_MEMATTR_WRITE_COMBINING);
|
||||
if (!gatt->ag_virtual) {
|
||||
if (bootverbose)
|
||||
device_printf(dev, "contiguous allocation failed\n");
|
||||
free(gatt, M_AGP);
|
||||
return 0;
|
||||
}
|
||||
bzero(gatt->ag_virtual, entries * sizeof(u_int32_t));
|
||||
gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
|
||||
agp_flush_cache();
|
||||
|
||||
return gatt;
|
||||
}
|
||||
@ -176,8 +169,8 @@ agp_alloc_gatt(device_t dev)
|
||||
void
|
||||
agp_free_gatt(struct agp_gatt *gatt)
|
||||
{
|
||||
contigfree(gatt->ag_virtual,
|
||||
gatt->ag_entries * sizeof(u_int32_t), M_AGP);
|
||||
kmem_free(kernel_arena, (vm_offset_t)gatt->ag_virtual,
|
||||
gatt->ag_entries * sizeof(u_int32_t));
|
||||
free(gatt, M_AGP);
|
||||
}
|
||||
|
||||
@ -280,7 +273,6 @@ agp_free_res(device_t dev)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, sc->as_aperture_rid,
|
||||
sc->as_aperture);
|
||||
mtx_destroy(&sc->as_lock);
|
||||
agp_flush_cache();
|
||||
}
|
||||
|
||||
int
|
||||
@ -485,7 +477,7 @@ agp_generic_alloc_memory(device_t dev, int type, vm_size_t size)
|
||||
if ((size & (AGP_PAGE_SIZE - 1)) != 0)
|
||||
return 0;
|
||||
|
||||
if (sc->as_allocated + size > sc->as_maxmem)
|
||||
if (size > sc->as_maxmem - sc->as_allocated)
|
||||
return 0;
|
||||
|
||||
if (type != 0) {
|
||||
@ -604,12 +596,6 @@ agp_generic_bind_memory(device_t dev, struct agp_memory *mem,
|
||||
}
|
||||
VM_OBJECT_WUNLOCK(mem->am_obj);
|
||||
|
||||
/*
|
||||
* Flush the cpu cache since we are providing a new mapping
|
||||
* for these pages.
|
||||
*/
|
||||
agp_flush_cache();
|
||||
|
||||
/*
|
||||
* Make sure the chipset gets the new mappings.
|
||||
*/
|
||||
@ -659,6 +645,9 @@ agp_generic_unbind_memory(device_t dev, struct agp_memory *mem)
|
||||
*/
|
||||
for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
|
||||
AGP_UNBIND_PAGE(dev, mem->am_offset + i);
|
||||
|
||||
AGP_FLUSH_TLB(dev);
|
||||
|
||||
VM_OBJECT_WLOCK(mem->am_obj);
|
||||
for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
|
||||
m = vm_page_lookup(mem->am_obj, atop(i));
|
||||
@ -667,9 +656,6 @@ agp_generic_unbind_memory(device_t dev, struct agp_memory *mem)
|
||||
vm_page_unlock(m);
|
||||
}
|
||||
VM_OBJECT_WUNLOCK(mem->am_obj);
|
||||
|
||||
agp_flush_cache();
|
||||
AGP_FLUSH_TLB(dev);
|
||||
|
||||
mem->am_offset = 0;
|
||||
mem->am_is_bound = 0;
|
||||
@ -996,3 +982,76 @@ void agp_memory_info(device_t dev, void *handle, struct
|
||||
mi->ami_offset = mem->am_offset;
|
||||
mi->ami_is_bound = mem->am_is_bound;
|
||||
}
|
||||
|
||||
int
|
||||
agp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size,
|
||||
vm_offset_t offset)
|
||||
{
|
||||
struct agp_softc *sc;
|
||||
vm_offset_t i, j, k, pa;
|
||||
vm_page_t m;
|
||||
int error;
|
||||
|
||||
if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
|
||||
(offset & (AGP_PAGE_SIZE - 1)) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->as_lock);
|
||||
for (i = 0; i < size; i += PAGE_SIZE) {
|
||||
m = pages[OFF_TO_IDX(i)];
|
||||
KASSERT(m->wire_count > 0,
|
||||
("agp_bind_pages: page %p hasn't been wired", m));
|
||||
|
||||
/*
|
||||
* Install entries in the GATT, making sure that if
|
||||
* AGP_PAGE_SIZE < PAGE_SIZE and size is not
|
||||
* aligned to PAGE_SIZE, we don't modify too many GATT
|
||||
* entries.
|
||||
*/
|
||||
for (j = 0; j < PAGE_SIZE && i + j < size; j += AGP_PAGE_SIZE) {
|
||||
pa = VM_PAGE_TO_PHYS(m) + j;
|
||||
AGP_DPF("binding offset %#jx to pa %#jx\n",
|
||||
(uintmax_t)offset + i + j, (uintmax_t)pa);
|
||||
error = AGP_BIND_PAGE(dev, offset + i + j, pa);
|
||||
if (error) {
|
||||
/*
|
||||
* Bail out. Reverse all the mappings.
|
||||
*/
|
||||
for (k = 0; k < i + j; k += AGP_PAGE_SIZE)
|
||||
AGP_UNBIND_PAGE(dev, offset + k);
|
||||
|
||||
mtx_unlock(&sc->as_lock);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AGP_FLUSH_TLB(dev);
|
||||
|
||||
mtx_unlock(&sc->as_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
agp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset)
|
||||
{
|
||||
struct agp_softc *sc;
|
||||
vm_offset_t i;
|
||||
|
||||
if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
|
||||
(offset & (AGP_PAGE_SIZE - 1)) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->as_lock);
|
||||
for (i = 0; i < size; i += AGP_PAGE_SIZE)
|
||||
AGP_UNBIND_PAGE(dev, offset + i);
|
||||
|
||||
AGP_FLUSH_TLB(dev);
|
||||
|
||||
mtx_unlock(&sc->as_lock);
|
||||
return (0);
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/pci/pcireg.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <machine/bus.h>
|
||||
@ -92,34 +94,35 @@ agp_amd_alloc_gatt(device_t dev)
|
||||
|
||||
/*
|
||||
* The AMD751 uses a page directory to map a non-contiguous
|
||||
* gatt so we don't need to use contigmalloc.
|
||||
* Malloc individual gatt pages and map them into the page
|
||||
* gatt so we don't need to use kmem_alloc_contig.
|
||||
* Allocate individual GATT pages and map them into the page
|
||||
* directory.
|
||||
*/
|
||||
gatt->ag_entries = entries;
|
||||
gatt->ag_virtual = malloc(entries * sizeof(u_int32_t),
|
||||
M_AGP, M_NOWAIT);
|
||||
gatt->ag_virtual = (void *)kmem_alloc_attr(kernel_arena,
|
||||
entries * sizeof(u_int32_t), M_NOWAIT | M_ZERO, 0, ~0,
|
||||
VM_MEMATTR_WRITE_COMBINING);
|
||||
if (!gatt->ag_virtual) {
|
||||
if (bootverbose)
|
||||
device_printf(dev, "allocation failed\n");
|
||||
free(gatt, M_AGP);
|
||||
return 0;
|
||||
}
|
||||
bzero(gatt->ag_virtual, entries * sizeof(u_int32_t));
|
||||
|
||||
/*
|
||||
* Allocate the page directory.
|
||||
*/
|
||||
gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT);
|
||||
gatt->ag_vdir = (void *)kmem_alloc_attr(kernel_arena, AGP_PAGE_SIZE,
|
||||
M_NOWAIT | M_ZERO, 0, ~0, VM_MEMATTR_WRITE_COMBINING);
|
||||
if (!gatt->ag_vdir) {
|
||||
if (bootverbose)
|
||||
device_printf(dev,
|
||||
"failed to allocate page directory\n");
|
||||
free(gatt->ag_virtual, M_AGP);
|
||||
kmem_free(kernel_arena, (vm_offset_t)gatt->ag_virtual,
|
||||
entries * sizeof(u_int32_t));
|
||||
free(gatt, M_AGP);
|
||||
return 0;
|
||||
}
|
||||
bzero(gatt->ag_vdir, AGP_PAGE_SIZE);
|
||||
|
||||
gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir);
|
||||
if(bootverbose)
|
||||
@ -158,19 +161,15 @@ agp_amd_alloc_gatt(device_t dev)
|
||||
gatt->ag_vdir[i + pdir_offset] = pa | 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the chipset can see everything.
|
||||
*/
|
||||
agp_flush_cache();
|
||||
|
||||
return gatt;
|
||||
}
|
||||
|
||||
static void
|
||||
agp_amd_free_gatt(struct agp_amd_gatt *gatt)
|
||||
{
|
||||
free(gatt->ag_virtual, M_AGP);
|
||||
free(gatt->ag_vdir, M_AGP);
|
||||
kmem_free(kernel_arena, (vm_offset_t)gatt->ag_vdir, AGP_PAGE_SIZE);
|
||||
kmem_free(kernel_arena, (vm_offset_t)gatt->ag_virtual,
|
||||
gatt->ag_entries * sizeof(u_int32_t));
|
||||
free(gatt, M_AGP);
|
||||
}
|
||||
|
||||
@ -348,9 +347,6 @@ agp_amd_bind_page(device_t dev, vm_offset_t offset, vm_offset_t physical)
|
||||
return EINVAL;
|
||||
|
||||
sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1;
|
||||
|
||||
/* invalidate the cache */
|
||||
AGP_FLUSH_TLB(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -241,8 +241,6 @@ agp_amd64_attach(device_t dev)
|
||||
4);
|
||||
}
|
||||
|
||||
agp_flush_cache();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -224,8 +224,6 @@ agp_apple_bind_page(device_t dev, vm_offset_t offset, vm_offset_t physical)
|
||||
return EINVAL;
|
||||
|
||||
sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical;
|
||||
__asm __volatile("dcbst 0,%0; sync" ::
|
||||
"r"(&sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT]) : "memory");
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -238,8 +236,6 @@ agp_apple_unbind_page(device_t dev, vm_offset_t offset)
|
||||
return EINVAL;
|
||||
|
||||
sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
|
||||
__asm __volatile("dcbst 0,%0; sync" ::
|
||||
"r"(&sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT]) : "memory");
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/pci/pcireg.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <machine/bus.h>
|
||||
@ -129,20 +131,23 @@ agp_ati_alloc_gatt(device_t dev)
|
||||
|
||||
/* Alloc the GATT -- pointers to pages of AGP memory */
|
||||
sc->ag_entries = entries;
|
||||
sc->ag_virtual = malloc(entries * sizeof(u_int32_t), M_AGP,
|
||||
M_NOWAIT | M_ZERO);
|
||||
sc->ag_virtual = (void *)kmem_alloc_attr(kernel_arena,
|
||||
entries * sizeof(u_int32_t), M_NOWAIT | M_ZERO, 0, ~0,
|
||||
VM_MEMATTR_WRITE_COMBINING);
|
||||
if (sc->ag_virtual == NULL) {
|
||||
if (bootverbose)
|
||||
device_printf(dev, "aperture allocation failed\n");
|
||||
device_printf(dev, "GATT allocation failed\n");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* Alloc the page directory -- pointers to each page of the GATT */
|
||||
sc->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT | M_ZERO);
|
||||
sc->ag_vdir = (void *)kmem_alloc_attr(kernel_arena, AGP_PAGE_SIZE,
|
||||
M_NOWAIT | M_ZERO, 0, ~0, VM_MEMATTR_WRITE_COMBINING);
|
||||
if (sc->ag_vdir == NULL) {
|
||||
if (bootverbose)
|
||||
device_printf(dev, "pagedir allocation failed\n");
|
||||
free(sc->ag_virtual, M_AGP);
|
||||
kmem_free(kernel_arena, (vm_offset_t)sc->ag_virtual,
|
||||
entries * sizeof(u_int32_t));
|
||||
return ENOMEM;
|
||||
}
|
||||
sc->ag_pdir = vtophys((vm_offset_t)sc->ag_vdir);
|
||||
@ -158,11 +163,6 @@ agp_ati_alloc_gatt(device_t dev)
|
||||
sc->ag_vdir[apbase_offset + i] = pa | 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the chipset can see everything.
|
||||
*/
|
||||
agp_flush_cache();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -264,8 +264,9 @@ agp_ati_detach(device_t dev)
|
||||
temp = pci_read_config(dev, apsize_reg, 4);
|
||||
pci_write_config(dev, apsize_reg, temp & ~1, 4);
|
||||
|
||||
free(sc->ag_vdir, M_AGP);
|
||||
free(sc->ag_virtual, M_AGP);
|
||||
kmem_free(kernel_arena, (vm_offset_t)sc->ag_vdir, AGP_PAGE_SIZE);
|
||||
kmem_free(kernel_arena, (vm_offset_t)sc->ag_virtual,
|
||||
sc->ag_entries * sizeof(u_int32_t));
|
||||
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, ATI_GART_MMADDR, sc->regs);
|
||||
agp_free_res(dev);
|
||||
|
@ -66,6 +66,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/pci/pci_private.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/vm_page.h>
|
||||
@ -1449,17 +1451,16 @@ agp_i810_install_gatt(device_t dev)
|
||||
sc->dcache_size = 0;
|
||||
|
||||
/* According to the specs the gatt on the i810 must be 64k. */
|
||||
sc->gatt->ag_virtual = contigmalloc(64 * 1024, M_AGP, 0, 0, ~0,
|
||||
PAGE_SIZE, 0);
|
||||
sc->gatt->ag_virtual = (void *)kmem_alloc_contig(kernel_arena,
|
||||
64 * 1024, M_NOWAIT | M_ZERO, 0, ~0, PAGE_SIZE,
|
||||
0, VM_MEMATTR_WRITE_COMBINING);
|
||||
if (sc->gatt->ag_virtual == NULL) {
|
||||
if (bootverbose)
|
||||
device_printf(dev, "contiguous allocation failed\n");
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
bzero(sc->gatt->ag_virtual, sc->gatt->ag_entries * sizeof(u_int32_t));
|
||||
sc->gatt->ag_physical = vtophys((vm_offset_t)sc->gatt->ag_virtual);
|
||||
agp_flush_cache();
|
||||
/* Install the GATT. */
|
||||
bus_write_4(sc->sc_res[0], AGP_I810_PGTBL_CTL,
|
||||
sc->gatt->ag_physical | 1);
|
||||
@ -1591,7 +1592,7 @@ agp_i810_deinstall_gatt(device_t dev)
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
bus_write_4(sc->sc_res[0], AGP_I810_PGTBL_CTL, 0);
|
||||
contigfree(sc->gatt->ag_virtual, 64 * 1024, M_AGP);
|
||||
kmem_free(kernel_arena, (vm_offset_t)sc->gatt->ag_virtual, 64 * 1024);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2146,7 +2147,6 @@ agp_i810_bind_memory(device_t dev, struct agp_memory *mem, vm_offset_t offset)
|
||||
sc->match->driver->install_gtt_pte(dev, (offset + i) >>
|
||||
AGP_PAGE_SHIFT, mem->am_physical + i, 0);
|
||||
}
|
||||
agp_flush_cache();
|
||||
mem->am_offset = offset;
|
||||
mem->am_is_bound = 1;
|
||||
mtx_unlock(&sc->agp.as_lock);
|
||||
@ -2187,7 +2187,6 @@ agp_i810_unbind_memory(device_t dev, struct agp_memory *mem)
|
||||
sc->match->driver->install_gtt_pte(dev,
|
||||
(mem->am_offset + i) >> AGP_PAGE_SHIFT, 0, 0);
|
||||
}
|
||||
agp_flush_cache();
|
||||
mem->am_is_bound = 0;
|
||||
mtx_unlock(&sc->agp.as_lock);
|
||||
return (0);
|
||||
|
@ -83,7 +83,6 @@ struct agp_gatt {
|
||||
vm_offset_t ag_physical;
|
||||
};
|
||||
|
||||
void agp_flush_cache(void);
|
||||
u_int8_t agp_find_caps(device_t dev);
|
||||
struct agp_gatt *agp_alloc_gatt(device_t dev);
|
||||
void agp_set_aperture_resource(device_t dev, int rid);
|
||||
|
@ -122,6 +122,19 @@ int agp_unbind_memory(device_t dev, void *handle);
|
||||
*/
|
||||
void agp_memory_info(device_t dev, void *handle, struct agp_memory_info *mi);
|
||||
|
||||
/*
|
||||
* Bind a set of pages at a given offset within the AGP aperture.
|
||||
* Returns EINVAL if the given size or offset is not at an AGP page boundary.
|
||||
*/
|
||||
int agp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size,
|
||||
vm_offset_t offset);
|
||||
|
||||
/*
|
||||
* Unbind a set of pages from the AGP aperture.
|
||||
* Returns EINVAL if the given size or offset is not at an AGP page boundary.
|
||||
*/
|
||||
int agp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset);
|
||||
|
||||
#define AGP_NORMAL_MEMORY 0
|
||||
|
||||
#define AGP_USER_TYPES (1 << 16)
|
||||
|
Loading…
Reference in New Issue
Block a user