source: mainline/kernel/generic/src/ddi/ddi.c@ 69c31abc

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 69c31abc was 8df5f20, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Rename NO_TRACE to _NO_TRACE

<trace.h> may end up transitively included from standard headers,
so it needs to be a reserved identifier.

  • Property mode set to 100644
File size: 13.2 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 kernel_generic_ddi
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 <assert.h>
43#include <ddi/ddi.h>
44#include <proc/task.h>
45#include <security/perm.h>
46#include <mm/frame.h>
47#include <mm/as.h>
48#include <mm/page.h>
49#include <synch/mutex.h>
50#include <syscall/copy.h>
51#include <adt/odict.h>
52#include <arch.h>
53#include <align.h>
54#include <errno.h>
55#include <mem.h>
56#include <trace.h>
57#include <bitops.h>
58
59/** This lock protects the @c pareas ordered dictionary. */
60static mutex_t pareas_lock;
61
62/** Ordered dictionary of enabled physical memory areas by base address. */
63static odict_t pareas;
64
65static void *pareas_getkey(odlink_t *);
66static int pareas_cmp(void *, void *);
67
68/** Initialize DDI.
69 *
70 */
71void ddi_init(void)
72{
73 odict_initialize(&pareas, pareas_getkey, pareas_cmp);
74 mutex_initialize(&pareas_lock, MUTEX_PASSIVE);
75}
76
77/** Initialize physical area structure.
78 *
79 * This should always be called first on the parea structure before
80 * filling in fields and calling ddi_parea_register.
81 *
82 * @param parea Pointer to physical area structure.
83 *
84 */
85void ddi_parea_init(parea_t *parea)
86{
87 memset(parea, 0, sizeof(parea_t));
88}
89
90/** Enable piece of physical memory for mapping by physmem_map().
91 *
92 * @param parea Pointer to physical area structure.
93 *
94 */
95void ddi_parea_register(parea_t *parea)
96{
97 mutex_lock(&pareas_lock);
98
99 /*
100 * We don't check for overlaps here as the kernel is pretty sane.
101 */
102 odict_insert(&parea->lpareas, &pareas, NULL);
103
104 mutex_unlock(&pareas_lock);
105}
106
107/** Map piece of physical memory into virtual address space of current task.
108 *
109 * @param phys Physical address of the starting frame.
110 * @param pages Number of pages to map.
111 * @param flags Address space area flags for the mapping.
112 * @param virt Virtual address of the starting page.
113 * @param bound Lowest virtual address bound.
114 *
115 * @return EOK on success.
116 * @return EPERM if the caller lacks permissions to use this syscall.
117 * @return EBADMEM if phys is not page aligned.
118 * @return ENOENT if there is no task matching the specified ID or
119 * the physical address space is not enabled for mapping.
120 * @return ENOMEM if there was a problem in creating address space area.
121 *
122 */
123_NO_TRACE static errno_t physmem_map(uintptr_t phys, size_t pages,
124 unsigned int flags, uintptr_t *virt, uintptr_t bound)
125{
126 assert(TASK);
127
128 if ((phys % FRAME_SIZE) != 0)
129 return EBADMEM;
130
131 /*
132 * Unprivileged tasks are only allowed to map pareas
133 * which are explicitly marked as such.
134 */
135 bool priv =
136 ((perm_get(TASK) & PERM_MEM_MANAGER) == PERM_MEM_MANAGER);
137
138 mem_backend_data_t backend_data;
139 backend_data.base = phys;
140 backend_data.frames = pages;
141 backend_data.anonymous = false;
142
143 /*
144 * Check if the memory region is explicitly enabled
145 * for mapping by any parea structure.
146 */
147
148 mutex_lock(&pareas_lock);
149 odlink_t *odlink = odict_find_eq(&pareas, &phys, NULL);
150 parea_t *parea = odlink != NULL ?
151 odict_get_instance(odlink, parea_t, lpareas) : NULL;
152
153 if ((parea != NULL) && (parea->frames >= pages)) {
154 if ((!priv) && (!parea->unpriv)) {
155 mutex_unlock(&pareas_lock);
156 return EPERM;
157 }
158
159 goto map;
160 }
161
162 parea = NULL;
163 mutex_unlock(&pareas_lock);
164
165 /*
166 * Check if the memory region is part of physical
167 * memory generally enabled for mapping.
168 */
169
170 irq_spinlock_lock(&zones.lock, true);
171 size_t znum = find_zone(ADDR2PFN(phys), pages, 0);
172
173 if (znum == (size_t) -1) {
174 /*
175 * Frames not found in any zone
176 * -> assume it is a hardware device and allow mapping
177 * for privileged tasks.
178 */
179 irq_spinlock_unlock(&zones.lock, true);
180
181 if (!priv)
182 return EPERM;
183
184 goto map;
185 }
186
187 if (zones.info[znum].flags & (ZONE_FIRMWARE | ZONE_RESERVED)) {
188 /*
189 * Frames are part of firmware or reserved zone
190 * -> allow mapping for privileged tasks.
191 */
192 irq_spinlock_unlock(&zones.lock, true);
193
194 if (!priv)
195 return EPERM;
196
197 goto map;
198 }
199
200 irq_spinlock_unlock(&zones.lock, true);
201 return ENOENT;
202
203map:
204 if (!as_area_create(TASK->as, flags, FRAMES2SIZE(pages),
205 AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
206 /*
207 * The address space area was not created.
208 * We report it using ENOMEM.
209 */
210
211 if (parea != NULL)
212 mutex_unlock(&pareas_lock);
213
214 return ENOMEM;
215 }
216
217 /*
218 * Mapping is created on-demand during page fault.
219 */
220
221 if (parea != NULL) {
222 parea->mapped = true;
223 mutex_unlock(&pareas_lock);
224 }
225
226 return EOK;
227}
228
229_NO_TRACE static errno_t physmem_unmap(uintptr_t virt)
230{
231 assert(TASK);
232
233 return as_area_destroy(TASK->as, virt);
234}
235
236/** Wrapper for SYS_PHYSMEM_MAP syscall.
237 *
238 * @param phys Physical base address to map
239 * @param pages Number of pages
240 * @param flags Flags of newly mapped pages
241 * @param virt_ptr Destination virtual address
242 * @param bound Lowest virtual address bound.
243 *
244 * @return 0 on success, otherwise it returns error code found in errno.h
245 *
246 */
247sys_errno_t sys_physmem_map(uintptr_t phys, size_t pages, unsigned int flags,
248 void *virt_ptr, uintptr_t bound)
249{
250 uintptr_t virt;
251 errno_t rc = copy_from_uspace(&virt, virt_ptr, sizeof(virt));
252 if (rc != EOK)
253 return rc;
254
255 rc = physmem_map(ALIGN_DOWN(phys, FRAME_SIZE), pages, flags, &virt,
256 bound);
257 if (rc != EOK)
258 return rc;
259
260 rc = copy_to_uspace(virt_ptr, &virt, sizeof(virt));
261 if (rc != EOK) {
262 physmem_unmap((uintptr_t) virt);
263 return rc;
264 }
265
266 return EOK;
267}
268
269sys_errno_t sys_physmem_unmap(uintptr_t virt)
270{
271 return physmem_unmap(virt);
272}
273
274/** Get key function for the @c pareas ordered dictionary.
275 *
276 * @param odlink Link
277 * @return Pointer to base address cast as 'void *'
278 */
279static void *pareas_getkey(odlink_t *odlink)
280{
281 parea_t *parea = odict_get_instance(odlink, parea_t, lpareas);
282 return (void *) &parea->pbase;
283}
284
285/** Key comparison function for the @c pareas ordered dictionary.
286 *
287 * @param a Pointer to parea A base
288 * @param b Pointer to parea B base
289 * @return -1, 0, 1 iff base of A is less than, equal to, greater than B
290 */
291static int pareas_cmp(void *a, void *b)
292{
293 uintptr_t pa = *(uintptr_t *)a;
294 uintptr_t pb = *(uintptr_t *)b;
295
296 if (pa < pb)
297 return -1;
298 else if (pa == pb)
299 return 0;
300 else
301 return +1;
302}
303
304/** Enable range of I/O space for task.
305 *
306 * @param id Task ID of the destination task.
307 * @param ioaddr Starting I/O address.
308 * @param size Size of the enabled I/O space.
309 *
310 * @return 0 on success, EPERM if the caller lacks permissions to use this
311 * syscall, ENOENT if there is no task matching the specified ID.
312 *
313 */
314_NO_TRACE static errno_t iospace_enable(task_id_t id, uintptr_t ioaddr, size_t size)
315{
316 /*
317 * Make sure the caller is authorised to make this syscall.
318 */
319 perm_t perms = perm_get(TASK);
320 if (!(perms & PERM_IO_MANAGER))
321 return EPERM;
322
323 irq_spinlock_lock(&tasks_lock, true);
324
325 task_t *task = task_find_by_id(id);
326
327 if ((!task) || (!container_check(CONTAINER, task->container))) {
328 /*
329 * There is no task with the specified ID
330 * or the task belongs to a different security
331 * context.
332 */
333 irq_spinlock_unlock(&tasks_lock, true);
334 return ENOENT;
335 }
336
337 /* Lock the task and release the lock protecting tasks dictionary. */
338 irq_spinlock_exchange(&tasks_lock, &task->lock);
339 errno_t rc = ddi_iospace_enable_arch(task, ioaddr, size);
340 irq_spinlock_unlock(&task->lock, true);
341
342 return rc;
343}
344
345/** Disable range of I/O space for task.
346 *
347 * @param id Task ID of the destination task.
348 * @param ioaddr Starting I/O address.
349 * @param size Size of the enabled I/O space.
350 *
351 * @return 0 on success, EPERM if the caller lacks permissions to use this
352 * syscall, ENOENT if there is no task matching the specified ID.
353 *
354 */
355_NO_TRACE static errno_t iospace_disable(task_id_t id, uintptr_t ioaddr, size_t size)
356{
357 /*
358 * Make sure the caller is authorised to make this syscall.
359 */
360 perm_t perms = perm_get(TASK);
361 if (!(perms & PERM_IO_MANAGER))
362 return EPERM;
363
364 irq_spinlock_lock(&tasks_lock, true);
365
366 task_t *task = task_find_by_id(id);
367
368 if ((!task) || (!container_check(CONTAINER, task->container))) {
369 /*
370 * There is no task with the specified ID
371 * or the task belongs to a different security
372 * context.
373 */
374 irq_spinlock_unlock(&tasks_lock, true);
375 return ENOENT;
376 }
377
378 /* Lock the task and release the lock protecting tasks dictionary. */
379 irq_spinlock_exchange(&tasks_lock, &task->lock);
380 errno_t rc = ddi_iospace_disable_arch(task, ioaddr, size);
381 irq_spinlock_unlock(&task->lock, true);
382
383 return rc;
384}
385
386/** Wrapper for SYS_ENABLE_IOSPACE syscall.
387 *
388 * @param uspace_io_arg User space address of DDI argument structure.
389 *
390 * @return 0 on success, otherwise it returns error code found in errno.h
391 *
392 */
393sys_errno_t sys_iospace_enable(ddi_ioarg_t *uspace_io_arg)
394{
395 ddi_ioarg_t arg;
396 errno_t rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
397 if (rc != EOK)
398 return (sys_errno_t) rc;
399
400 return (sys_errno_t) iospace_enable((task_id_t) arg.task_id,
401 (uintptr_t) arg.ioaddr, (size_t) arg.size);
402}
403
404sys_errno_t sys_iospace_disable(ddi_ioarg_t *uspace_io_arg)
405{
406 ddi_ioarg_t arg;
407 errno_t rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
408 if (rc != EOK)
409 return (sys_errno_t) rc;
410
411 return (sys_errno_t) iospace_disable((task_id_t) arg.task_id,
412 (uintptr_t) arg.ioaddr, (size_t) arg.size);
413}
414
415_NO_TRACE static errno_t dmamem_map(uintptr_t virt, size_t size, unsigned int map_flags,
416 unsigned int flags, uintptr_t *phys)
417{
418 assert(TASK);
419
420 // TODO: implement locking of non-anonymous mapping
421 return page_find_mapping(virt, phys);
422}
423
424_NO_TRACE static errno_t dmamem_map_anonymous(size_t size, uintptr_t constraint,
425 unsigned int map_flags, unsigned int flags, uintptr_t *phys,
426 uintptr_t *virt, uintptr_t bound)
427{
428 assert(TASK);
429
430 size_t frames = SIZE2FRAMES(size);
431 if (frames == 0)
432 return EINVAL;
433
434 // FIXME: probably need to ensure that the memory is suitable for DMA
435 *phys = frame_alloc(frames, FRAME_ATOMIC, constraint);
436 if (*phys == 0)
437 return ENOMEM;
438
439 mem_backend_data_t backend_data;
440 backend_data.base = *phys;
441 backend_data.frames = frames;
442 backend_data.anonymous = true;
443
444 if (!as_area_create(TASK->as, map_flags, size,
445 AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
446 frame_free(*phys, frames);
447 return ENOMEM;
448 }
449
450 return EOK;
451}
452
453_NO_TRACE static errno_t dmamem_unmap(uintptr_t virt, size_t size)
454{
455 // TODO: implement unlocking & unmap
456 return EOK;
457}
458
459_NO_TRACE static errno_t dmamem_unmap_anonymous(uintptr_t virt)
460{
461 return as_area_destroy(TASK->as, virt);
462}
463
464sys_errno_t sys_dmamem_map(size_t size, unsigned int map_flags, unsigned int flags,
465 void *phys_ptr, void *virt_ptr, uintptr_t bound)
466{
467 if ((flags & DMAMEM_FLAGS_ANONYMOUS) == 0) {
468 /*
469 * Non-anonymous DMA mapping
470 */
471
472 uintptr_t phys;
473 errno_t rc = dmamem_map((uintptr_t) virt_ptr, size, map_flags,
474 flags, &phys);
475
476 if (rc != EOK)
477 return rc;
478
479 rc = copy_to_uspace(phys_ptr, &phys, sizeof(phys));
480 if (rc != EOK) {
481 dmamem_unmap((uintptr_t) virt_ptr, size);
482 return rc;
483 }
484 } else {
485 /*
486 * Anonymous DMA mapping
487 */
488
489 uintptr_t constraint;
490 errno_t rc = copy_from_uspace(&constraint, phys_ptr,
491 sizeof(constraint));
492 if (rc != EOK)
493 return rc;
494
495 uintptr_t virt;
496 rc = copy_from_uspace(&virt, virt_ptr, sizeof(virt));
497 if (rc != EOK)
498 return rc;
499
500 uintptr_t phys;
501 rc = dmamem_map_anonymous(size, constraint, map_flags, flags,
502 &phys, &virt, bound);
503 if (rc != EOK)
504 return rc;
505
506 rc = copy_to_uspace(phys_ptr, &phys, sizeof(phys));
507 if (rc != EOK) {
508 dmamem_unmap_anonymous((uintptr_t) virt);
509 return rc;
510 }
511
512 rc = copy_to_uspace(virt_ptr, &virt, sizeof(virt));
513 if (rc != EOK) {
514 dmamem_unmap_anonymous((uintptr_t) virt);
515 return rc;
516 }
517 }
518
519 return EOK;
520}
521
522sys_errno_t sys_dmamem_unmap(uintptr_t virt, size_t size, unsigned int flags)
523{
524 if ((flags & DMAMEM_FLAGS_ANONYMOUS) == 0)
525 return dmamem_unmap(virt, size);
526 else
527 return dmamem_unmap_anonymous(virt);
528}
529
530/** @}
531 */
Note: See TracBrowser for help on using the repository browser.