mm.c revision 1.13.16.1 1 /* $NetBSD: mm.c,v 1.13.16.1 2010/03/18 04:36:54 rmind Exp $ */
2
3 /*-
4 * Copyright (c) 2002, 2008, 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas and Joerg Sonnenberger.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: mm.c,v 1.13.16.1 2010/03/18 04:36:54 rmind Exp $");
34
35 #include "opt_compat_netbsd.h"
36
37 #include <sys/param.h>
38 #include <sys/conf.h>
39 #include <sys/ioctl.h>
40 #include <sys/mman.h>
41 #include <sys/uio.h>
42 #include <sys/termios.h>
43
44 #include <dev/mm.h>
45
46 #include <uvm/uvm_extern.h>
47
48 static void * dev_zero_page;
49 static kmutex_t dev_mem_lock;
50 static vaddr_t dev_mem_addr;
51
52 static dev_type_read(mm_readwrite);
53 static dev_type_ioctl(mm_ioctl);
54 static dev_type_mmap(mm_mmap);
55 static dev_type_ioctl(mm_ioctl);
56
57 const struct cdevsw mem_cdevsw = {
58 #ifdef __HAVE_MM_MD_OPEN
59 mm_md_open,
60 #else
61 nullopen,
62 #endif
63 nullclose, mm_readwrite, mm_readwrite,
64 mm_ioctl, nostop, notty, nopoll, mm_mmap, nokqfilter,
65 D_MPSAFE
66 };
67
68 void
69 mm_init(void)
70 {
71 vaddr_t pg;
72
73 mutex_init(&dev_mem_lock, MUTEX_DEFAULT, IPL_NONE);
74
75 pg = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_WIRED|UVM_KMF_ZERO);
76 KASSERT(pg != 0);
77 pmap_protect(pmap_kernel(), pg, pg + PAGE_SIZE, VM_PROT_READ);
78 dev_zero_page = (void *)pg;
79
80 dev_mem_addr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_VAONLY);
81 KASSERT(dev_mem_addr != 0);
82 }
83
84 static int
85 dev_mem_readwrite(struct uio *uio, struct iovec *iov)
86 {
87 bool have_direct;
88 paddr_t paddr;
89 vaddr_t vaddr;
90 vm_prot_t prot;
91 size_t len, offset;
92 int error;
93
94 if ((intptr_t)uio->uio_offset != uio->uio_offset) {
95 return EFAULT;
96 }
97 paddr = uio->uio_offset & ~PAGE_MASK;
98 offset = uio->uio_offset & PAGE_MASK;
99 len = min(uio->uio_resid, PAGE_SIZE - offset);
100 prot = uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ;
101
102 error = mm_md_physacc(paddr, prot);
103 if (error) {
104 return error;
105 }
106
107 #ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS
108 have_direct = mm_md_direct_mapped_phys(paddr, &vaddr);
109 #else
110 have_direct = false;
111 #endif
112 if (!have_direct) {
113 mutex_enter(&dev_mem_lock);
114 pmap_enter(pmap_kernel(), dev_mem_addr, paddr, prot,
115 prot | PMAP_WIRED);
116 pmap_update(pmap_kernel());
117
118 vaddr = dev_mem_addr + offset;
119 error = uiomove((void *)vaddr, len, uio);
120
121 pmap_remove(pmap_kernel(), dev_mem_addr, PAGE_SIZE);
122 mutex_exit(&dev_mem_lock);
123 } else {
124 error = uiomove((void *)vaddr, len, uio);
125 }
126 return error;
127 }
128
129 static int
130 dev_kmem_readwrite(struct uio *uio, struct iovec *iov)
131 {
132 void *addr;
133 size_t len, offset;
134 vm_prot_t prot;
135 int error;
136 bool md_kva;
137
138 addr = (void *)(intptr_t)uio->uio_offset;
139 if ((uintptr_t)addr != uio->uio_offset) {
140 return EFAULT;
141 }
142 offset = uio->uio_offset & PAGE_MASK;
143 len = min(uio->uio_resid, PAGE_SIZE - offset);
144 prot = uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ;
145
146 md_kva = false;
147
148 #ifdef __HAVE_MM_MD_DIRECT_MAPPED_IO
149 paddr_t paddr;
150 if (mm_md_direct_mapped_io(addr, &paddr)) {
151 error = mm_md_physacc(paddr, prot);
152 if (error)
153 return error;
154 md_kva = true;
155 }
156 #endif
157
158 #ifdef __HAVE_MM_MD_KERNACC
159 if (!md_kva && (error = mm_md_kernacc(addr, prot, &md_kva)) != 0) {
160 return error;
161 }
162 #endif
163 if (!md_kva && !uvm_kernacc(addr, prot)) {
164 return EFAULT;
165 }
166 error = uiomove(addr, len, uio);
167 return error;
168 }
169
170 static int
171 dev_zero_readwrite(struct uio *uio, struct iovec *iov)
172 {
173 size_t len;
174
175 if (uio->uio_rw == UIO_WRITE) {
176 uio->uio_resid = 0;
177 return 0;
178 }
179 len = min(iov->iov_len, PAGE_SIZE);
180 return uiomove(dev_zero_page, len, uio);
181 }
182
183 static int
184 mm_readwrite(dev_t dev, struct uio *uio, int flags)
185 {
186 struct iovec *iov;
187 int error;
188
189 #ifdef __HAVE_MM_MD_READWRITE
190 switch (minor(dev)) {
191 case DEV_MEM:
192 case DEV_KMEM:
193 case DEV_NULL:
194 case DEV_ZERO:
195 #if defined(COMPAT_16) && defined(__arm)
196 case _DEV_ZERO_oARM:
197 #endif
198 break;
199 default:
200 return mm_md_readwrite(dev, uio);
201 }
202 #endif
203 error = 0;
204 while (uio->uio_resid > 0 && error == 0) {
205 iov = uio->uio_iov;
206 if (iov->iov_len == 0) {
207 ++uio->uio_iov;
208 --uio->uio_iovcnt;
209 KASSERT(uio->uio_iovcnt >= 0);
210 continue;
211 }
212 switch (minor(dev)) {
213 case DEV_MEM:
214 error = dev_mem_readwrite(uio, iov);
215 break;
216 case DEV_KMEM:
217 error = dev_kmem_readwrite(uio, iov);
218 break;
219 case DEV_NULL:
220 if (uio->uio_rw == UIO_WRITE) {
221 uio->uio_resid = 0;
222 }
223 /* Break directly out of the loop. */
224 return 0;
225 #if defined(COMPAT_16) && defined(__arm)
226 case _DEV_ZERO_oARM:
227 #endif
228 case DEV_ZERO:
229 error = dev_zero_readwrite(uio, iov);
230 break;
231 default:
232 error = ENXIO;
233 break;
234 }
235 }
236 return error;
237 }
238
239 static paddr_t
240 mm_mmap(dev_t dev, off_t off, int acc)
241 {
242 vm_prot_t prot;
243
244 #ifdef __HAVE_MM_MD_MMAP
245 switch (minor(dev)) {
246 case DEV_MEM:
247 case DEV_KMEM:
248 case DEV_NULL:
249 #if defined(COMPAT_16) && defined(__arm)
250 case _DEV_ZERO_oARM:
251 #endif
252 case DEV_ZERO:
253 break;
254 default:
255 return mm_md_mmap(dev, off, acc);
256 }
257 #endif
258 /*
259 * /dev/null does not make sense, /dev/kmem is volatile and
260 * /dev/zero is handled in mmap already.
261 */
262 if (minor(dev) != DEV_MEM) {
263 return -1;
264 }
265
266 prot = 0;
267 if (acc & PROT_EXEC)
268 prot |= VM_PROT_EXECUTE;
269 if (acc & PROT_READ)
270 prot |= VM_PROT_READ;
271 if (acc & PROT_WRITE)
272 prot |= VM_PROT_WRITE;
273
274 if (mm_md_physacc(off, prot) != 0) {
275 return -1;
276 }
277 return off >> PGSHIFT;
278 }
279
280 static int
281 mm_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
282 {
283
284 switch (cmd) {
285 case FIONBIO:
286 /* We never block anyway. */
287 return 0;
288
289 case FIOSETOWN:
290 case FIOGETOWN:
291 case TIOCGPGRP:
292 case TIOCSPGRP:
293 case TIOCGETA:
294 return ENOTTY;
295
296 case FIOASYNC:
297 if ((*(int *)data) == 0) {
298 return 0;
299 }
300 /* FALLTHROUGH */
301 default:
302 return EOPNOTSUPP;
303 }
304 }
305