source: mainline/kernel/generic/src/ddi/ddi.c@ 5df1963

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5df1963 was 5df1963, checked in by Martin Decky <martin@…>, 12 years ago

bitmap frame allocator does not keep track of the size of the allocated frame blocks
to avoid memory leaks the number of allocated frames needs to be passed explicitly during deallocation

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup genericddi
30 * @{
31 */
32
33/**
34 * @file
35 * @brief Device Driver Interface functions.
36 *
37 * This file contains functions that comprise the Device Driver Interface.
38 * These are the functions for mapping physical memory and enabling I/O
39 * space to tasks.
40 */
41
42#include <ddi/ddi.h>
43#include <proc/task.h>
44#include <security/cap.h>
45#include <mm/frame.h>
46#include <mm/as.h>
47#include <mm/page.h>
48#include <synch/mutex.h>
49#include <syscall/copy.h>
50#include <adt/btree.h>
51#include <arch.h>
52#include <align.h>
53#include <errno.h>
54#include <trace.h>
55#include <bitops.h>
56
57/** This lock protects the parea_btree. */
58static mutex_t parea_lock;
59
60/** B+tree with enabled physical memory areas. */
61static btree_t parea_btree;
62
63/** Initialize DDI.
64 *
65 */
66void ddi_init(void)
67{
68 btree_create(&parea_btree);
69 mutex_initialize(&parea_lock, MUTEX_PASSIVE);
70}
71
72/** Enable piece of physical memory for mapping by physmem_map().
73 *
74 * @param parea Pointer to physical area structure.
75 *
76 */
77void ddi_parea_register(parea_t *parea)
78{
79 mutex_lock(&parea_lock);
80
81 /*
82 * We don't check for overlaps here as the kernel is pretty sane.
83 */
84 btree_insert(&parea_btree, (btree_key_t) parea->pbase, parea, NULL);
85
86 mutex_unlock(&parea_lock);
87}
88
89/** Map piece of physical memory into virtual address space of current task.
90 *
91 * @param phys Physical address of the starting frame.
92 * @param pages Number of pages to map.
93 * @param flags Address space area flags for the mapping.
94 * @param virt Virtual address of the starting page.
95 * @param bound Lowest virtual address bound.
96 *
97 * @return EOK on success.
98 * @return EPERM if the caller lacks capabilities to use this syscall.
99 * @return EBADMEM if phys is not page aligned.
100 * @return ENOENT if there is no task matching the specified ID or
101 * the physical address space is not enabled for mapping.
102 * @return ENOMEM if there was a problem in creating address space area.
103 *
104 */
105NO_TRACE static int physmem_map(uintptr_t phys, size_t pages,
106 unsigned int flags, uintptr_t *virt, uintptr_t bound)
107{
108 ASSERT(TASK);
109
110 if ((phys % FRAME_SIZE) != 0)
111 return EBADMEM;
112
113 /*
114 * Unprivileged tasks are only allowed to map pareas
115 * which are explicitly marked as such.
116 */
117 bool priv =
118 ((cap_get(TASK) & CAP_MEM_MANAGER) == CAP_MEM_MANAGER);
119
120 mem_backend_data_t backend_data;
121 backend_data.base = phys;
122 backend_data.frames = pages;
123
124 /*
125 * Check if the memory region is explicitly enabled
126 * for mapping by any parea structure.
127 */
128
129 mutex_lock(&parea_lock);
130 btree_node_t *nodep;
131 parea_t *parea = (parea_t *) btree_search(&parea_btree,
132 (btree_key_t) phys, &nodep);
133
134 if ((parea != NULL) && (parea->frames >= pages)) {
135 if ((!priv) && (!parea->unpriv)) {
136 mutex_unlock(&parea_lock);
137 return EPERM;
138 }
139
140 goto map;
141 }
142
143 parea = NULL;
144 mutex_unlock(&parea_lock);
145
146 /*
147 * Check if the memory region is part of physical
148 * memory generally enabled for mapping.
149 */
150
151 irq_spinlock_lock(&zones.lock, true);
152 size_t znum = find_zone(ADDR2PFN(phys), pages, 0);
153
154 if (znum == (size_t) -1) {
155 /*
156 * Frames not found in any zone
157 * -> assume it is a hardware device and allow mapping
158 * for privileged tasks.
159 */
160 irq_spinlock_unlock(&zones.lock, true);
161
162 if (!priv)
163 return EPERM;
164
165 goto map;
166 }
167
168 if (zones.info[znum].flags & (ZONE_FIRMWARE | ZONE_RESERVED)) {
169 /*
170 * Frames are part of firmware or reserved zone
171 * -> allow mapping for privileged tasks.
172 */
173 irq_spinlock_unlock(&zones.lock, true);
174
175 if (!priv)
176 return EPERM;
177
178 goto map;
179 }
180
181 irq_spinlock_unlock(&zones.lock, true);
182 return ENOENT;
183
184map:
185 if (!as_area_create(TASK->as, flags, FRAMES2SIZE(pages),
186 AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
187 /*
188 * The address space area was not created.
189 * We report it using ENOMEM.
190 */
191
192 if (parea != NULL)
193 mutex_unlock(&parea_lock);
194
195 return ENOMEM;
196 }
197
198 /*
199 * Mapping is created on-demand during page fault.
200 */
201
202 if (parea != NULL) {
203 parea->mapped = true;
204 mutex_unlock(&parea_lock);
205 }
206
207 return EOK;
208}
209
210NO_TRACE static int physmem_unmap(uintptr_t virt)
211{
212 // TODO: implement unmap
213 return EOK;
214}
215
216/** Wrapper for SYS_PHYSMEM_MAP syscall.
217 *
218 * @param phys Physical base address to map
219 * @param pages Number of pages
220 * @param flags Flags of newly mapped pages
221 * @param virt_ptr Destination virtual address
222 * @param bound Lowest virtual address bound.
223 *
224 * @return 0 on success, otherwise it returns error code found in errno.h
225 *
226 */
227sysarg_t sys_physmem_map(uintptr_t phys, size_t pages, unsigned int flags,
228 void *virt_ptr, uintptr_t bound)
229{
230 uintptr_t virt = (uintptr_t) -1;
231 int rc = physmem_map(ALIGN_DOWN(phys, FRAME_SIZE), pages, flags,
232 &virt, bound);
233 if (rc != EOK)
234 return rc;
235
236 rc = copy_to_uspace(virt_ptr, &virt, sizeof(virt));
237 if (rc != EOK) {
238 physmem_unmap((uintptr_t) virt);
239 return rc;
240 }
241
242 return EOK;
243}
244
245sysarg_t sys_physmem_unmap(uintptr_t virt)
246{
247 return physmem_unmap(virt);
248}
249
250/** Enable range of I/O space for task.
251 *
252 * @param id Task ID of the destination task.
253 * @param ioaddr Starting I/O address.
254 * @param size Size of the enabled I/O space..
255 *
256 * @return 0 on success, EPERM if the caller lacks capabilities to use this
257 * syscall, ENOENT if there is no task matching the specified ID.
258 *
259 */
260NO_TRACE static int iospace_enable(task_id_t id, uintptr_t ioaddr, size_t size)
261{
262 /*
263 * Make sure the caller is authorised to make this syscall.
264 */
265 cap_t caps = cap_get(TASK);
266 if (!(caps & CAP_IO_MANAGER))
267 return EPERM;
268
269 irq_spinlock_lock(&tasks_lock, true);
270
271 task_t *task = task_find_by_id(id);
272
273 if ((!task) || (!container_check(CONTAINER, task->container))) {
274 /*
275 * There is no task with the specified ID
276 * or the task belongs to a different security
277 * context.
278 */
279 irq_spinlock_unlock(&tasks_lock, true);
280 return ENOENT;
281 }
282
283 /* Lock the task and release the lock protecting tasks_btree. */
284 irq_spinlock_exchange(&tasks_lock, &task->lock);
285 int rc = ddi_iospace_enable_arch(task, ioaddr, size);
286 irq_spinlock_unlock(&task->lock, true);
287
288 return rc;
289}
290
291/** Wrapper for SYS_ENABLE_IOSPACE syscall.
292 *
293 * @param uspace_io_arg User space address of DDI argument structure.
294 *
295 * @return 0 on success, otherwise it returns error code found in errno.h
296 *
297 */
298sysarg_t sys_iospace_enable(ddi_ioarg_t *uspace_io_arg)
299{
300 ddi_ioarg_t arg;
301 int rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
302 if (rc != 0)
303 return (sysarg_t) rc;
304
305 return (sysarg_t) iospace_enable((task_id_t) arg.task_id,
306 (uintptr_t) arg.ioaddr, (size_t) arg.size);
307}
308
309sysarg_t sys_iospace_disable(ddi_ioarg_t *uspace_io_arg)
310{
311 // TODO: implement
312 return ENOTSUP;
313}
314
315NO_TRACE static int dmamem_map(uintptr_t virt, size_t size, unsigned int map_flags,
316 unsigned int flags, uintptr_t *phys)
317{
318 ASSERT(TASK);
319
320 // TODO: implement locking of non-anonymous mapping
321 return page_find_mapping(virt, phys);
322}
323
324NO_TRACE static int dmamem_map_anonymous(size_t size, uintptr_t constraint,
325 unsigned int map_flags, unsigned int flags, uintptr_t *phys,
326 uintptr_t *virt, uintptr_t bound)
327{
328 ASSERT(TASK);
329
330 size_t frames = SIZE2FRAMES(size);
331 *phys = frame_alloc_noreserve(frames, 0, constraint);
332 if (*phys == 0)
333 return ENOMEM;
334
335 mem_backend_data_t backend_data;
336 backend_data.base = *phys;
337 backend_data.frames = frames;
338
339 if (!as_area_create(TASK->as, map_flags, size,
340 AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
341 frame_free_noreserve(*phys, frames);
342 return ENOMEM;
343 }
344
345 return EOK;
346}
347
348NO_TRACE static int dmamem_unmap(uintptr_t virt, size_t size)
349{
350 // TODO: implement unlocking & unmap
351 return EOK;
352}
353
354NO_TRACE static int dmamem_unmap_anonymous(uintptr_t virt)
355{
356 // TODO: implement unlocking & unmap
357 return EOK;
358}
359
360sysarg_t sys_dmamem_map(size_t size, unsigned int map_flags, unsigned int flags,
361 void *phys_ptr, void *virt_ptr, uintptr_t bound)
362{
363 if ((flags & DMAMEM_FLAGS_ANONYMOUS) == 0) {
364 /*
365 * Non-anonymous DMA mapping
366 */
367
368 uintptr_t phys;
369 int rc = dmamem_map((uintptr_t) virt_ptr, size, map_flags,
370 flags, &phys);
371
372 if (rc != EOK)
373 return rc;
374
375 rc = copy_to_uspace(phys_ptr, &phys, sizeof(phys));
376 if (rc != EOK) {
377 dmamem_unmap((uintptr_t) virt_ptr, size);
378 return rc;
379 }
380 } else {
381 /*
382 * Anonymous DMA mapping
383 */
384
385 uintptr_t constraint;
386 int rc = copy_from_uspace(&constraint, phys_ptr,
387 sizeof(constraint));
388 if (rc != EOK)
389 return rc;
390
391 uintptr_t phys;
392 uintptr_t virt = (uintptr_t) -1;
393 rc = dmamem_map_anonymous(size, constraint, map_flags, flags,
394 &phys, &virt, bound);
395 if (rc != EOK)
396 return rc;
397
398 rc = copy_to_uspace(phys_ptr, &phys, sizeof(phys));
399 if (rc != EOK) {
400 dmamem_unmap_anonymous((uintptr_t) virt);
401 return rc;
402 }
403
404 rc = copy_to_uspace(virt_ptr, &virt, sizeof(virt));
405 if (rc != EOK) {
406 dmamem_unmap_anonymous((uintptr_t) virt);
407 return rc;
408 }
409 }
410
411 return EOK;
412}
413
414sysarg_t sys_dmamem_unmap(uintptr_t virt, size_t size, unsigned int flags)
415{
416 if ((flags & DMAMEM_FLAGS_ANONYMOUS) == 0)
417 return dmamem_unmap(virt, size);
418 else
419 return dmamem_unmap_anonymous(virt);
420}
421
422/** @}
423 */
Note: See TracBrowser for help on using the repository browser.