summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjake <jake@FreeBSD.org>2003-03-17 18:53:02 +0000
committerjake <jake@FreeBSD.org>2003-03-17 18:53:02 +0000
commitbf1f3783a0fc86740dd608391f6cbfb7ce93d386 (patch)
tree3df7f9cfa36baaaed5aa883345e9944c1f493fa0
parentef60146aeb5110d712d91ea00a2e945ea1ca89c8 (diff)
downloadFreeBSD-src-bf1f3783a0fc86740dd608391f6cbfb7ce93d386.zip
FreeBSD-src-bf1f3783a0fc86740dd608391f6cbfb7ce93d386.tar.gz
Clean up /dev/mem now that pmap handles illegal aliases properly. Don't
allow access to device memory through /dev/mem, or try to make modifying kernel text through /dev/mem safe (it is not).
-rw-r--r--sys/sparc64/sparc64/mem.c124
1 files changed, 70 insertions, 54 deletions
diff --git a/sys/sparc64/sparc64/mem.c b/sys/sparc64/sparc64/mem.c
index 6d9bda3..b838b96 100644
--- a/sys/sparc64/sparc64/mem.c
+++ b/sys/sparc64/sparc64/mem.c
@@ -64,11 +64,15 @@
#include <vm/vm.h>
#include <vm/vm_param.h>
+#include <vm/vm_page.h>
+#include <vm/vm_kern.h>
#include <vm/pmap.h>
#include <vm/vm_extern.h>
#include <machine/cache.h>
+#include <machine/md_var.h>
#include <machine/pmap.h>
+#include <machine/tlb.h>
#include <machine/upa.h>
static dev_t memdev, kmemdev;
@@ -115,19 +119,26 @@ mmopen(dev_t dev, int flags, int fmt, struct thread *td)
return (0);
}
-#define IOSTART UPA_MEMSTART
-
/*ARGSUSED*/
static int
mmrw(dev_t dev, struct uio *uio, int flags)
{
struct iovec *iov;
- int error = 0;
- vm_offset_t addr, eaddr, o, v = 0;
+ vm_offset_t eva;
+ vm_offset_t off;
+ vm_offset_t ova;
+ vm_offset_t pa;
+ vm_offset_t va;
vm_prot_t prot;
- vm_size_t c = 0;
- u_long asi;
- char *buf = NULL;
+ vm_size_t cnt;
+ vm_page_t m;
+ int color;
+ int error;
+ int i;
+
+ cnt = 0;
+ error = 0;
+ ova = 0;
GIANT_REQUIRED;
@@ -143,69 +154,74 @@ mmrw(dev_t dev, struct uio *uio, int flags)
switch (minor(dev)) {
case 0:
/* mem (physical memory) */
- if (buf == NULL) {
- buf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
- if (buf == NULL) {
- error = ENOMEM;
+ pa = uio->uio_offset & ~PAGE_MASK;
+ if (!is_physical_memory(pa)) {
+ error = EFAULT;
+ break;
+ }
+
+ off = uio->uio_offset & PAGE_MASK;
+ cnt = PAGE_SIZE - ((vm_offset_t)iov->iov_base &
+ PAGE_MASK);
+ cnt = min(cnt, PAGE_SIZE - off);
+ cnt = min(cnt, iov->iov_len);
+
+ m = NULL;
+ for (i = 0; phys_avail[i] != 0; i += 2) {
+ if (pa >= phys_avail[i] &&
+ pa < phys_avail[i + 1]) {
+ m = PHYS_TO_VM_PAGE(pa);
break;
}
}
- v = uio->uio_offset;
- asi = ASI_PHYS_USE_EC;
- /* Access device memory noncacheable. */
- if (v >= IOSTART)
- asi = ASI_PHYS_BYPASS_EC_WITH_EBIT;
- o = v & PAGE_MASK;
- c = ulmin(iov->iov_len, PAGE_SIZE - o);
- /*
- * This double copy could be avoided, at the cost of
- * inlining a version of uiomove. Since this is not
- * performance-critical, it is probably not worth it.
- */
- if (uio->uio_rw == UIO_READ)
- ascopyfrom(asi, v, buf, c);
- error = uiomove(buf, c, uio);
- if (error == 0 && uio->uio_rw == UIO_WRITE)
- ascopyto(buf, asi, v, c);
- /*
- * If a write was evil enough to change kernel code,
- * I$ must be flushed. Also, D$ must be flushed if there
- * is a chance that there is a cacheable mapping to
- * avoid working with stale data.
- */
- if (v < IOSTART && uio->uio_rw == UIO_WRITE) {
- icache_inval_phys(v, v + c);
- dcache_inval_phys(v, v + c);
+
+ if (m != NULL) {
+ if (ova == 0) {
+ ova = kmem_alloc_wait(kernel_map,
+ PAGE_SIZE * DCACHE_COLORS);
+ }
+ if ((color = m->md.color) == -1)
+ va = ova;
+ else
+ va = ova + color * PAGE_SIZE;
+ pmap_qenter(va, &m, 1);
+ error = uiomove((void *)(va + off), cnt,
+ uio);
+ pmap_qremove(va, 1);
+ } else {
+ va = TLB_PHYS_TO_DIRECT(pa);
+ error = uiomove((void *)(va + off), cnt,
+ uio);
}
break;
case 1:
/* kmem (kernel memory) */
- c = iov->iov_len;
+ va = trunc_page(uio->uio_offset);
+ eva = round_page(uio->uio_offset + iov->iov_len);
/*
- * Make sure that all of the pages are currently resident so
- * that we don't create any zero-fill pages.
+ * Make sure that all of the pages are currently
+ * resident so we don't create any zero fill pages.
*/
- addr = trunc_page(uio->uio_offset);
- eaddr = round_page(uio->uio_offset + c);
-
- for (; addr < eaddr; addr += PAGE_SIZE)
- if (pmap_extract(kernel_pmap, addr) == 0)
- return EFAULT;
+ for (; va < eva; va += PAGE_SIZE)
+ if (pmap_kextract(va) == 0)
+ return (EFAULT);
prot = (uio->uio_rw == UIO_READ) ? VM_PROT_READ :
VM_PROT_WRITE;
- v = uio->uio_offset;
- if (v < VM_MIN_DIRECT_ADDRESS &&
- kernacc((caddr_t)v, c, prot) == FALSE)
+ va = uio->uio_offset;
+ if (va < VM_MIN_DIRECT_ADDRESS &&
+ kernacc((void *)va, iov->iov_len, prot) == FALSE)
return (EFAULT);
- error = uiomove((caddr_t)v, c, uio);
- if (uio->uio_rw == UIO_WRITE)
- icache_flush(v, v + c);
+
+ error = uiomove((void *)va, iov->iov_len, uio);
+ break;
+ default:
+ return (ENODEV);
}
}
- if (buf != NULL)
- free(buf, M_DEVBUF);
+ if (ova != 0)
+ kmem_free_wakeup(kernel_map, ova, PAGE_SIZE * DCACHE_COLORS);
return (error);
}
OpenPOWER on IntegriCloud