1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/types.h>
28 #include <sys/errno.h>
29 #include <sys/proc.h>
30 #include <sys/procfs.h>
31 #include <sys/sysmacros.h>
32 #include <sys/uio.h>
33 #include <sys/cmn_err.h>
34
35 #if defined(__sparc)
36 #include <sys/stack.h>
37 #endif
38
39 #define STACK_BUF_SIZE 64 /* power of 2 */
40
41 int
prusrio(proc_t * p,enum uio_rw rw,struct uio * uiop,int old)42 prusrio(proc_t *p, enum uio_rw rw, struct uio *uiop, int old)
43 {
44 /* longlong-aligned short buffer */
45 longlong_t buffer[STACK_BUF_SIZE / sizeof (longlong_t)];
46 int error = 0;
47 void *bp;
48 int allocated;
49 ssize_t total = uiop->uio_resid;
50 uintptr_t addr;
51 size_t len;
52
53 /* for short reads/writes, use the on-stack buffer */
54 if (uiop->uio_resid <= STACK_BUF_SIZE) {
55 bp = buffer;
56 allocated = 0;
57 } else {
58 bp = kmem_alloc(PAGESIZE, KM_SLEEP);
59 allocated = 1;
60 }
61
62 #if defined(__sparc)
63 if (p == curproc)
64 (void) flush_user_windows_to_stack(NULL);
65 #endif
66
67 switch (rw) {
68 case UIO_READ:
69 while (uiop->uio_resid != 0) {
70 addr = uiop->uio_offset;
71 len = MIN(uiop->uio_resid,
72 PAGESIZE - (addr & PAGEOFFSET));
73
74 if ((error = uread(p, bp, len, addr)) != 0 ||
75 (error = uiomove(bp, len, UIO_READ, uiop)) != 0)
76 break;
77 }
78
79 /*
80 * ENXIO indicates that a page didn't exist. If the I/O was
81 * truncated, return success; otherwise convert the error into
82 * EIO. When obeying new /proc semantics, we don't return an
83 * error for a read that begins at an invalid address.
84 */
85 if (error == ENXIO) {
86 if (total != uiop->uio_resid || !old)
87 error = 0;
88 else
89 error = EIO;
90 }
91 break;
92 case UIO_WRITE:
93 while (uiop->uio_resid != 0) {
94 addr = uiop->uio_offset;
95 len = MIN(uiop->uio_resid,
96 PAGESIZE - (addr & PAGEOFFSET));
97
98 if ((error = uiomove(bp, len, UIO_WRITE, uiop)) != 0)
99 break;
100 if ((error = uwrite(p, bp, len, addr)) != 0) {
101 uiop->uio_resid += len;
102 uiop->uio_loffset -= len;
103 break;
104 }
105 }
106
107 /*
108 * ENXIO indicates that a page didn't exist. If the I/O was
109 * truncated, return success; otherwise convert the error
110 * into EIO.
111 */
112 if (error == ENXIO) {
113 if (total != uiop->uio_resid)
114 error = 0;
115 else
116 error = EIO;
117 }
118 break;
119 default:
120 panic("prusrio: rw=%d neither UIO_READ not UIO_WRITE", rw);
121 /*NOTREACHED*/
122 }
123
124 if (allocated)
125 kmem_free(bp, PAGESIZE);
126
127 return (error);
128 }
129