summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/urw.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/common/os/urw.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/uts/common/os/urw.c')
-rw-r--r--usr/src/uts/common/os/urw.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/usr/src/uts/common/os/urw.c b/usr/src/uts/common/os/urw.c
new file mode 100644
index 0000000000..a0195a6db3
--- /dev/null
+++ b/usr/src/uts/common/os/urw.c
@@ -0,0 +1,327 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/atomic.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cpuvar.h>
+#include <sys/kmem.h>
+#include <sys/strsubr.h>
+#include <sys/sysmacros.h>
+#include <sys/frame.h>
+#include <sys/stack.h>
+#include <sys/proc.h>
+#include <sys/priv.h>
+#include <sys/policy.h>
+#include <sys/ontrap.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <vm/seg_dev.h>
+#include <vm/seg_vn.h>
+#include <vm/seg_spt.h>
+#include <vm/seg_kmem.h>
+
+extern struct seg_ops segdev_ops; /* needs a header file */
+extern struct seg_ops segspt_shmops; /* needs a header file */
+
+static int
+page_valid(struct seg *seg, caddr_t addr)
+{
+ struct segvn_data *svd;
+ vnode_t *vp;
+ vattr_t vattr;
+
+ /*
+ * Fail if the page doesn't map to a page in the underlying
+ * mapped file, if an underlying mapped file exists.
+ */
+ vattr.va_mask = AT_SIZE;
+ if (seg->s_ops == &segvn_ops &&
+ SEGOP_GETVP(seg, addr, &vp) == 0 &&
+ vp != NULL && vp->v_type == VREG &&
+ VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) {
+ u_offset_t size = roundup(vattr.va_size, (u_offset_t)PAGESIZE);
+ u_offset_t offset = SEGOP_GETOFFSET(seg, addr);
+
+ if (offset >= size)
+ return (0);
+ }
+
+ /*
+ * Fail if this is an ISM shared segment and the address is
+ * not within the real size of the spt segment that backs it.
+ */
+ if (seg->s_ops == &segspt_shmops &&
+ addr >= seg->s_base + spt_realsize(seg))
+ return (0);
+
+ /*
+ * Fail if the segment is mapped from /dev/null.
+ * The key is that the mapping comes from segdev and the
+ * type is neither MAP_SHARED nor MAP_PRIVATE.
+ */
+ if (seg->s_ops == &segdev_ops &&
+ ((SEGOP_GETTYPE(seg, addr) & (MAP_SHARED | MAP_PRIVATE)) == 0))
+ return (0);
+
+ /*
+ * Fail if the page is a MAP_NORESERVE page that has
+ * not actually materialized.
+ * We cheat by knowing that segvn is the only segment
+ * driver that supports MAP_NORESERVE.
+ */
+ if (seg->s_ops == &segvn_ops &&
+ (svd = (struct segvn_data *)seg->s_data) != NULL &&
+ (svd->vp == NULL || svd->vp->v_type != VREG) &&
+ (svd->flags & MAP_NORESERVE)) {
+ /*
+ * Guilty knowledge here. We know that
+ * segvn_incore returns more than just the
+ * low-order bit that indicates the page is
+ * actually in memory. If any bits are set,
+ * then there is backing store for the page.
+ */
+ char incore = 0;
+ (void) SEGOP_INCORE(seg, addr, PAGESIZE, &incore);
+ if (incore == 0)
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Map address "addr" in address space "as" into a kernel virtual address.
+ * The memory is guaranteed to be resident and locked down.
+ */
+static caddr_t
+mapin(struct as *as, caddr_t addr, int writing)
+{
+ page_t *pp;
+ caddr_t kaddr;
+ pfn_t pfnum;
+
+ /*
+ * NB: Because of past mistakes, we have bits being returned
+ * by getpfnum that are actually the page type bits of the pte.
+ * When the object we are trying to map is a memory page with
+ * a page structure everything is ok and we can use the optimal
+ * method, ppmapin. Otherwise, we have to do something special.
+ */
+ pfnum = hat_getpfnum(as->a_hat, addr);
+ if (pf_is_memory(pfnum)) {
+ pp = page_numtopp_nolock(pfnum);
+ if (pp != NULL) {
+ ASSERT(PAGE_LOCKED(pp));
+ kaddr = ppmapin(pp, writing ?
+ (PROT_READ | PROT_WRITE) : PROT_READ,
+ (caddr_t)-1);
+ return (kaddr + ((uintptr_t)addr & PAGEOFFSET));
+ }
+ }
+
+ /*
+ * Oh well, we didn't have a page struct for the object we were
+ * trying to map in; ppmapin doesn't handle devices, but allocating a
+ * heap address allows ppmapout to free virutal space when done.
+ */
+ kaddr = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
+
+ hat_devload(kas.a_hat, kaddr, PAGESIZE, pfnum,
+ writing ? (PROT_READ | PROT_WRITE) : PROT_READ, HAT_LOAD_LOCK);
+
+ return (kaddr + ((uintptr_t)addr & PAGEOFFSET));
+}
+
+/*ARGSUSED*/
+static void
+mapout(struct as *as, caddr_t addr, caddr_t vaddr, int writing)
+{
+ vaddr = (caddr_t)(uintptr_t)((uintptr_t)vaddr & PAGEMASK);
+ ppmapout(vaddr);
+}
+
+/*
+ * Perform I/O to a given process. This will return EIO if we dectect
+ * corrupt memory and ENXIO if there is no such mapped address in the
+ * user process's address space.
+ */
+static int
+urw(proc_t *p, int writing, void *buf, size_t len, uintptr_t a)
+{
+ caddr_t addr = (caddr_t)a;
+ caddr_t page;
+ caddr_t vaddr;
+ struct seg *seg;
+ int error = 0;
+ int err = 0;
+ uint_t prot;
+ uint_t prot_rw = writing ? PROT_WRITE : PROT_READ;
+ int protchanged;
+ on_trap_data_t otd;
+ int retrycnt;
+ struct as *as = p->p_as;
+ enum seg_rw rw;
+
+ /*
+ * Locate segment containing address of interest.
+ */
+ page = (caddr_t)(uintptr_t)((uintptr_t)addr & PAGEMASK);
+ retrycnt = 0;
+ AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER);
+retry:
+ if ((seg = as_segat(as, page)) == NULL ||
+ !page_valid(seg, page)) {
+ AS_LOCK_EXIT(as, &as->a_lock);
+ return (ENXIO);
+ }
+ SEGOP_GETPROT(seg, page, 0, &prot);
+
+ protchanged = 0;
+ if ((prot & prot_rw) == 0) {
+ protchanged = 1;
+ err = SEGOP_SETPROT(seg, page, PAGESIZE, prot | prot_rw);
+
+ if (err == IE_RETRY) {
+ protchanged = 0;
+ ASSERT(retrycnt == 0);
+ retrycnt++;
+ goto retry;
+ }
+
+ if (err != 0) {
+ AS_LOCK_EXIT(as, &as->a_lock);
+ return (ENXIO);
+ }
+ }
+
+ /*
+ * segvn may do a copy-on-write for F_SOFTLOCK/S_READ case to break
+ * sharing to avoid a copy on write of a softlocked page by another
+ * thread. But since we locked the address space as a writer no other
+ * thread can cause a copy on write. S_READ_NOCOW is passed as the
+ * access type to tell segvn that it's ok not to do a copy-on-write
+ * for this SOFTLOCK fault.
+ */
+ if (writing)
+ rw = S_WRITE;
+ else if (seg->s_ops == &segvn_ops)
+ rw = S_READ_NOCOW;
+ else
+ rw = S_READ;
+
+ if (SEGOP_FAULT(as->a_hat, seg, page, PAGESIZE, F_SOFTLOCK, rw)) {
+ if (protchanged)
+ (void) SEGOP_SETPROT(seg, page, PAGESIZE, prot);
+ AS_LOCK_EXIT(as, &as->a_lock);
+ return (ENXIO);
+ }
+ CPU_STATS_ADD_K(vm, softlock, 1);
+
+ /*
+ * Make sure we're not trying to read or write off the end of the page.
+ */
+ ASSERT(len <= page + PAGESIZE - addr);
+
+ /*
+ * Map in the locked page, copy to our local buffer,
+ * then map the page out and unlock it.
+ */
+ vaddr = mapin(as, addr, writing);
+
+ /*
+ * Since we are copying memory on behalf of the user process,
+ * protect against memory error correction faults.
+ */
+ if (!on_trap(&otd, OT_DATA_EC)) {
+ if (seg->s_ops == &segdev_ops) {
+ /*
+ * Device memory can behave strangely; invoke
+ * a segdev-specific copy operation instead.
+ */
+ if (writing) {
+ if (segdev_copyto(seg, addr, buf, vaddr, len))
+ error = ENXIO;
+ } else {
+ if (segdev_copyfrom(seg, addr, vaddr, buf, len))
+ error = ENXIO;
+ }
+ } else {
+ if (writing)
+ bcopy(buf, vaddr, len);
+ else
+ bcopy(vaddr, buf, len);
+ }
+ } else {
+ error = EIO;
+ }
+ no_trap();
+
+ /*
+ * If we're writing to an executable page, we may need to sychronize
+ * the I$ with the modifications we made through the D$.
+ */
+ if (writing && (prot & PROT_EXEC))
+ sync_icache(vaddr, (uint_t)len);
+
+ mapout(as, addr, vaddr, writing);
+
+ if (rw == S_READ_NOCOW)
+ rw = S_READ;
+
+ (void) SEGOP_FAULT(as->a_hat, seg, page, PAGESIZE, F_SOFTUNLOCK, rw);
+
+ if (protchanged)
+ (void) SEGOP_SETPROT(seg, page, PAGESIZE, prot);
+
+ AS_LOCK_EXIT(as, &as->a_lock);
+
+ return (error);
+}
+
+int
+uread(proc_t *p, void *buf, size_t len, uintptr_t a)
+{
+ return (urw(p, 0, buf, len, a));
+}
+
+int
+uwrite(proc_t *p, void *buf, size_t len, uintptr_t a)
+{
+ return (urw(p, 1, buf, len, a));
+}