diff options
author | nwhitehorn <nwhitehorn@FreeBSD.org> | 2010-10-03 16:02:53 +0000 |
---|---|---|
committer | nwhitehorn <nwhitehorn@FreeBSD.org> | 2010-10-03 16:02:53 +0000 |
commit | 91a938a7c56ab798b0e49b73d0efc3791f267969 (patch) | |
tree | 5324c5eeadb4a4688f7919bb59218d875fd448ba /sys | |
parent | e68de2436ecf5f86bace8ec1f2e9608ee66d4796 (diff) | |
download | FreeBSD-src-91a938a7c56ab798b0e49b73d0efc3791f267969.zip FreeBSD-src-91a938a7c56ab798b0e49b73d0efc3791f267969.tar.gz |
Add a memory-range interface to /dev/mem on PowerPC using PAT attributes.
Unlike actual MTRR, this only controls the mapping attributes for
subsequent mmap() of /dev/mem. Nonetheless, the support is sufficiently
MTRR-like that Xorg can use it, which translates into an enormous increase
in graphics performance on PowerPC.
MFC after: 2 weeks
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/files.powerpc | 1 | ||||
-rw-r--r-- | sys/modules/mem/Makefile | 2 | ||||
-rw-r--r-- | sys/powerpc/include/memdev.h | 2 | ||||
-rw-r--r-- | sys/powerpc/powerpc/mem.c | 152 |
4 files changed, 154 insertions, 3 deletions
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 1a4a8ff..e7d1d4d 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -27,6 +27,7 @@ dev/fb/fb.c optional sc dev/fdt/fdt_powerpc.c optional fdt dev/hwpmc/hwpmc_powerpc.c optional hwpmc dev/kbd/kbd.c optional sc +dev/mem/memutil.c optional mem dev/ofw/openfirm.c optional aim | fdt dev/ofw/openfirmio.c optional aim | fdt dev/ofw/ofw_bus_if.m optional aim | fdt diff --git a/sys/modules/mem/Makefile b/sys/modules/mem/Makefile index b4fdb9c..c24e18f 100644 --- a/sys/modules/mem/Makefile +++ b/sys/modules/mem/Makefile @@ -6,7 +6,7 @@ KMOD= mem SRCS= memdev.c mem.c -.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" +.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} = "powerpc" SRCS+= memutil.c .endif .if ${MACHINE_CPUARCH} == "i386" diff --git a/sys/powerpc/include/memdev.h b/sys/powerpc/include/memdev.h index 584eb4c..97000c9 100644 --- a/sys/powerpc/include/memdev.h +++ b/sys/powerpc/include/memdev.h @@ -31,7 +31,7 @@ d_open_t memopen; d_read_t memrw; -#define memioctl (d_ioctl_t *)NULL +d_ioctl_t memioctl; d_mmap_t memmmap; void dev_mem_md_init(void); diff --git a/sys/powerpc/powerpc/mem.c b/sys/powerpc/powerpc/mem.c index e4b70c6..b4636ec 100644 --- a/sys/powerpc/powerpc/mem.c +++ b/sys/powerpc/powerpc/mem.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <sys/fcntl.h> #include <sys/kernel.h> #include <sys/lock.h> +#include <sys/ioccom.h> #include <sys/malloc.h> #include <sys/memrange.h> #include <sys/module.h> @@ -68,7 +69,21 @@ __FBSDID("$FreeBSD$"); #include <machine/memdev.h> -struct mem_range_softc mem_range_softc; +static void ppc_mrinit(struct mem_range_softc *); +static int ppc_mrset(struct mem_range_softc *, struct mem_range_desc *, int *); + +MALLOC_DEFINE(M_MEMDESC, "memdesc", "memory range descriptors"); + +static struct mem_range_ops ppc_mem_range_ops = { + ppc_mrinit, + ppc_mrset, + NULL, + NULL +}; +struct mem_range_softc mem_range_softc = { + &ppc_mem_range_ops, + 0, 0, 0 +}; /* ARGSUSED */ int @@ -162,6 +177,8 @@ int memmmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { + int i; + /* * /dev/mem is the only one that makes sense through this * interface. For /dev/kmem any physaddr we return here @@ -178,10 +195,143 @@ memmmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, *paddr = offset; + for (i = 0; i < mem_range_softc.mr_ndesc; i++) { + if (!(mem_range_softc.mr_desc[i].mr_flags & MDF_ACTIVE)) + continue; + + if (offset >= mem_range_softc.mr_desc[i].mr_base && + offset < mem_range_softc.mr_desc[i].mr_base + + mem_range_softc.mr_desc[i].mr_len) { + switch (mem_range_softc.mr_desc[i].mr_flags & + MDF_ATTRMASK) { + case MDF_WRITEBACK: + *memattr = VM_MEMATTR_WRITE_BACK; + break; + case MDF_WRITECOMBINE: + *memattr = VM_MEMATTR_WRITE_COMBINING; + break; + case MDF_UNCACHEABLE: + *memattr = VM_MEMATTR_UNCACHEABLE; + break; + case MDF_WRITETHROUGH: + *memattr = VM_MEMATTR_WRITE_THROUGH; + break; + } + + break; + } + } + return (0); } void dev_mem_md_init(void) { + mem_range_softc.mr_op->init(&mem_range_softc); +} + +static void +ppc_mrinit(struct mem_range_softc *sc) +{ + sc->mr_cap = 0; + sc->mr_ndesc = 8; /* XXX: Should be dynamically expandable */ + sc->mr_desc = malloc(sc->mr_ndesc * sizeof(struct mem_range_desc), + M_MEMDESC, M_NOWAIT | M_ZERO); + if (sc->mr_desc == NULL) + panic("%s: malloc returns NULL", __func__); +} + +static int +ppc_mrset(struct mem_range_softc *sc, struct mem_range_desc *desc, int *arg) +{ + int i; + + switch(*arg) { + case MEMRANGE_SET_UPDATE: + for (i = 0; i < sc->mr_ndesc; i++) { + if (!sc->mr_desc[i].mr_len) { + sc->mr_desc[i] = *desc; + sc->mr_desc[i].mr_flags |= MDF_ACTIVE; + return (0); + } + if (sc->mr_desc[i].mr_base == desc->mr_base && + sc->mr_desc[i].mr_len == desc->mr_len) + return (EEXIST); + } + return (ENOSPC); + case MEMRANGE_SET_REMOVE: + for (i = 0; i < sc->mr_ndesc; i++) + if (sc->mr_desc[i].mr_base == desc->mr_base && + sc->mr_desc[i].mr_len == desc->mr_len) { + bzero(&sc->mr_desc[i], sizeof(sc->mr_desc[i])); + return (0); + } + return (ENOENT); + default: + return (EOPNOTSUPP); + } + + return (0); +} + +/* + * Operations for changing memory attributes. + * + * This is basically just an ioctl shim for mem_range_attr_get + * and mem_range_attr_set. + */ +/* ARGSUSED */ +int +memioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int flags, + struct thread *td) +{ + int nd, error = 0; + struct mem_range_op *mo = (struct mem_range_op *)data; + struct mem_range_desc *md; + + /* is this for us? */ + if ((cmd != MEMRANGE_GET) && + (cmd != MEMRANGE_SET)) + return (ENOTTY); + + /* any chance we can handle this? */ + if (mem_range_softc.mr_op == NULL) + return (EOPNOTSUPP); + + /* do we have any descriptors? */ + if (mem_range_softc.mr_ndesc == 0) + return (ENXIO); + + switch (cmd) { + case MEMRANGE_GET: + nd = imin(mo->mo_arg[0], mem_range_softc.mr_ndesc); + if (nd > 0) { + md = (struct mem_range_desc *) + malloc(nd * sizeof(struct mem_range_desc), + M_MEMDESC, M_WAITOK); + error = mem_range_attr_get(md, &nd); + if (!error) + error = copyout(md, mo->mo_desc, + nd * sizeof(struct mem_range_desc)); + free(md, M_MEMDESC); + } + else + nd = mem_range_softc.mr_ndesc; + mo->mo_arg[0] = nd; + break; + + case MEMRANGE_SET: + md = (struct mem_range_desc *)malloc(sizeof(struct mem_range_desc), + M_MEMDESC, M_WAITOK); + error = copyin(mo->mo_desc, md, sizeof(struct mem_range_desc)); + /* clamp description string */ + md->mr_owner[sizeof(md->mr_owner) - 1] = 0; + if (error == 0) + error = mem_range_attr_set(md, &mo->mo_arg[0]); + free(md, M_MEMDESC); + break; + } + return (error); } + |