source: mainline/kernel/generic/src/ddi/ddi.c@ 8df5f20

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8df5f20 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
RevLine 
[9a8d91b]1/*
[df4ed85]2 * Copyright (c) 2006 Jakub Jermar
[9a8d91b]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 */
[b45c443]28
[174156fd]29/** @addtogroup kernel_generic_ddi
[b45c443]30 * @{
31 */
[e49e234]32
[9179d0a]33/**
[b45c443]34 * @file
[e49e234]35 * @brief Device Driver Interface functions.
[9179d0a]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 */
[9a8d91b]41
[63e27ef]42#include <assert.h>
[9a8d91b]43#include <ddi/ddi.h>
44#include <proc/task.h>
[719a208]45#include <security/perm.h>
[9a8d91b]46#include <mm/frame.h>
47#include <mm/as.h>
[c6ae4c2]48#include <mm/page.h>
[373acb4]49#include <synch/mutex.h>
[e3c762cd]50#include <syscall/copy.h>
[6f7071b]51#include <adt/odict.h>
[9a8d91b]52#include <arch.h>
53#include <align.h>
54#include <errno.h>
[6f7071b]55#include <mem.h>
[7a0359b]56#include <trace.h>
[c6ae4c2]57#include <bitops.h>
[9a8d91b]58
[6f7071b]59/** This lock protects the @c pareas ordered dictionary. */
60static mutex_t pareas_lock;
[f8ddd17]61
[6f7071b]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 *);
[ae318d3]67
[da1bafb]68/** Initialize DDI.
69 *
70 */
[f8ddd17]71void ddi_init(void)
72{
[6f7071b]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));
[f8ddd17]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{
[6f7071b]97 mutex_lock(&pareas_lock);
[a35b458]98
[f8ddd17]99 /*
[e49e234]100 * We don't check for overlaps here as the kernel is pretty sane.
[f8ddd17]101 */
[6f7071b]102 odict_insert(&parea->lpareas, &pareas, NULL);
[a35b458]103
[6f7071b]104 mutex_unlock(&pareas_lock);
[f8ddd17]105}
106
[8da51ad]107/** Map piece of physical memory into virtual address space of current task.
[9a8d91b]108 *
[c6ae4c2]109 * @param phys Physical address of the starting frame.
[9a8d91b]110 * @param pages Number of pages to map.
[6212095]111 * @param flags Address space area flags for the mapping.
[fbcdeb8]112 * @param virt Virtual address of the starting page.
113 * @param bound Lowest virtual address bound.
[9a8d91b]114 *
[c6ae4c2]115 * @return EOK on success.
[719a208]116 * @return EPERM if the caller lacks permissions to use this syscall.
[fbcdeb8]117 * @return EBADMEM if phys is not page aligned.
[c6ae4c2]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.
[e49e234]121 *
[9a8d91b]122 */
[8df5f20]123_NO_TRACE static errno_t physmem_map(uintptr_t phys, size_t pages,
[fbcdeb8]124 unsigned int flags, uintptr_t *virt, uintptr_t bound)
[9a8d91b]125{
[63e27ef]126 assert(TASK);
[a35b458]127
[c6ae4c2]128 if ((phys % FRAME_SIZE) != 0)
[d7533c7]129 return EBADMEM;
[a35b458]130
[9a8d91b]131 /*
[d7533c7]132 * Unprivileged tasks are only allowed to map pareas
133 * which are explicitly marked as such.
[9a8d91b]134 */
[d7533c7]135 bool priv =
[719a208]136 ((perm_get(TASK) & PERM_MEM_MANAGER) == PERM_MEM_MANAGER);
[a35b458]137
[e49e234]138 mem_backend_data_t backend_data;
[c6ae4c2]139 backend_data.base = phys;
[e49e234]140 backend_data.frames = pages;
[c101dc0]141 backend_data.anonymous = false;
[a35b458]142
[b366a6f4]143 /*
144 * Check if the memory region is explicitly enabled
145 * for mapping by any parea structure.
146 */
[a35b458]147
[6f7071b]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;
[a35b458]152
[b366a6f4]153 if ((parea != NULL) && (parea->frames >= pages)) {
154 if ((!priv) && (!parea->unpriv)) {
[6f7071b]155 mutex_unlock(&pareas_lock);
[b366a6f4]156 return EPERM;
157 }
[a35b458]158
[b366a6f4]159 goto map;
160 }
[a35b458]161
[b366a6f4]162 parea = NULL;
[6f7071b]163 mutex_unlock(&pareas_lock);
[a35b458]164
[b366a6f4]165 /*
166 * Check if the memory region is part of physical
167 * memory generally enabled for mapping.
168 */
[a35b458]169
[da1bafb]170 irq_spinlock_lock(&zones.lock, true);
[c6ae4c2]171 size_t znum = find_zone(ADDR2PFN(phys), pages, 0);
[a35b458]172
[98000fb]173 if (znum == (size_t) -1) {
[d7533c7]174 /*
175 * Frames not found in any zone
176 * -> assume it is a hardware device and allow mapping
177 * for privileged tasks.
[e49e234]178 */
[da1bafb]179 irq_spinlock_unlock(&zones.lock, true);
[a35b458]180
[d7533c7]181 if (!priv)
182 return EPERM;
[a35b458]183
[e49e234]184 goto map;
[ae318d3]185 }
[a35b458]186
[3164e3b]187 if (zones.info[znum].flags & (ZONE_FIRMWARE | ZONE_RESERVED)) {
[d7533c7]188 /*
[3164e3b]189 * Frames are part of firmware or reserved zone
[d7533c7]190 * -> allow mapping for privileged tasks.
191 */
[da1bafb]192 irq_spinlock_unlock(&zones.lock, true);
[a35b458]193
[d7533c7]194 if (!priv)
195 return EPERM;
[a35b458]196
[e49e234]197 goto map;
198 }
[a35b458]199
[da1bafb]200 irq_spinlock_unlock(&zones.lock, true);
[e49e234]201 return ENOENT;
[a35b458]202
[e49e234]203map:
[fbcdeb8]204 if (!as_area_create(TASK->as, flags, FRAMES2SIZE(pages),
205 AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
[9a8d91b]206 /*
[b366a6f4]207 * The address space area was not created.
[9a8d91b]208 * We report it using ENOMEM.
209 */
[a35b458]210
[b366a6f4]211 if (parea != NULL)
[6f7071b]212 mutex_unlock(&pareas_lock);
[a35b458]213
[9a8d91b]214 return ENOMEM;
215 }
[a35b458]216
[0ee077ee]217 /*
218 * Mapping is created on-demand during page fault.
219 */
[a35b458]220
[b366a6f4]221 if (parea != NULL) {
222 parea->mapped = true;
[6f7071b]223 mutex_unlock(&pareas_lock);
[b366a6f4]224 }
[a35b458]225
[b366a6f4]226 return EOK;
[9a8d91b]227}
228
[8df5f20]229_NO_TRACE static errno_t physmem_unmap(uintptr_t virt)
[fbcdeb8]230{
[63e27ef]231 assert(TASK);
[8cd680c]232
233 return as_area_destroy(TASK->as, virt);
[fbcdeb8]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 */
[b7fd2a0]247sys_errno_t sys_physmem_map(uintptr_t phys, size_t pages, unsigned int flags,
[fbcdeb8]248 void *virt_ptr, uintptr_t bound)
249{
[bf9cb2f]250 uintptr_t virt;
[b7fd2a0]251 errno_t rc = copy_from_uspace(&virt, virt_ptr, sizeof(virt));
[bf9cb2f]252 if (rc != EOK)
253 return rc;
[a35b458]254
[bf9cb2f]255 rc = physmem_map(ALIGN_DOWN(phys, FRAME_SIZE), pages, flags, &virt,
256 bound);
[fbcdeb8]257 if (rc != EOK)
258 return rc;
[a35b458]259
[fbcdeb8]260 rc = copy_to_uspace(virt_ptr, &virt, sizeof(virt));
261 if (rc != EOK) {
262 physmem_unmap((uintptr_t) virt);
263 return rc;
264 }
[a35b458]265
[fbcdeb8]266 return EOK;
267}
268
[b7fd2a0]269sys_errno_t sys_physmem_unmap(uintptr_t virt)
[fbcdeb8]270{
271 return physmem_unmap(virt);
272}
273
[6f7071b]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
[f52e54da]304/** Enable range of I/O space for task.
305 *
[8cd680c]306 * @param id Task ID of the destination task.
[f52e54da]307 * @param ioaddr Starting I/O address.
[8cd680c]308 * @param size Size of the enabled I/O space.
[f52e54da]309 *
[719a208]310 * @return 0 on success, EPERM if the caller lacks permissions to use this
[e49e234]311 * syscall, ENOENT if there is no task matching the specified ID.
312 *
[f52e54da]313 */
[8df5f20]314_NO_TRACE static errno_t iospace_enable(task_id_t id, uintptr_t ioaddr, size_t size)
[f52e54da]315{
316 /*
317 * Make sure the caller is authorised to make this syscall.
318 */
[719a208]319 perm_t perms = perm_get(TASK);
320 if (!(perms & PERM_IO_MANAGER))
[f52e54da]321 return EPERM;
[a35b458]322
[da1bafb]323 irq_spinlock_lock(&tasks_lock, true);
[a35b458]324
[e49e234]325 task_t *task = task_find_by_id(id);
[a35b458]326
[473d5d2]327 if ((!task) || (!container_check(CONTAINER, task->container))) {
[f52e54da]328 /*
[cfffb290]329 * There is no task with the specified ID
330 * or the task belongs to a different security
331 * context.
[f52e54da]332 */
[da1bafb]333 irq_spinlock_unlock(&tasks_lock, true);
[f52e54da]334 return ENOENT;
335 }
[a35b458]336
[6f7071b]337 /* Lock the task and release the lock protecting tasks dictionary. */
[da1bafb]338 irq_spinlock_exchange(&tasks_lock, &task->lock);
[b7fd2a0]339 errno_t rc = ddi_iospace_enable_arch(task, ioaddr, size);
[da1bafb]340 irq_spinlock_unlock(&task->lock, true);
[8cd680c]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 *
[719a208]351 * @return 0 on success, EPERM if the caller lacks permissions to use this
[8cd680c]352 * syscall, ENOENT if there is no task matching the specified ID.
353 *
354 */
[8df5f20]355_NO_TRACE static errno_t iospace_disable(task_id_t id, uintptr_t ioaddr, size_t size)
[8cd680c]356{
357 /*
358 * Make sure the caller is authorised to make this syscall.
359 */
[719a208]360 perm_t perms = perm_get(TASK);
361 if (!(perms & PERM_IO_MANAGER))
[8cd680c]362 return EPERM;
[a35b458]363
[8cd680c]364 irq_spinlock_lock(&tasks_lock, true);
[a35b458]365
[8cd680c]366 task_t *task = task_find_by_id(id);
[a35b458]367
[8cd680c]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 }
[a35b458]377
[6f7071b]378 /* Lock the task and release the lock protecting tasks dictionary. */
[8cd680c]379 irq_spinlock_exchange(&tasks_lock, &task->lock);
[b7fd2a0]380 errno_t rc = ddi_iospace_disable_arch(task, ioaddr, size);
[8cd680c]381 irq_spinlock_unlock(&task->lock, true);
[a35b458]382
[f52e54da]383 return rc;
384}
385
386/** Wrapper for SYS_ENABLE_IOSPACE syscall.
387 *
[abbc16e]388 * @param uspace_io_arg User space address of DDI argument structure.
[f52e54da]389 *
390 * @return 0 on success, otherwise it returns error code found in errno.h
[e49e234]391 *
392 */
[b7fd2a0]393sys_errno_t sys_iospace_enable(ddi_ioarg_t *uspace_io_arg)
[f52e54da]394{
395 ddi_ioarg_t arg;
[b7fd2a0]396 errno_t rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
[a53ed3a]397 if (rc != EOK)
[b7fd2a0]398 return (sys_errno_t) rc;
[a35b458]399
[b7fd2a0]400 return (sys_errno_t) iospace_enable((task_id_t) arg.task_id,
[f619ec11]401 (uintptr_t) arg.ioaddr, (size_t) arg.size);
[f52e54da]402}
[2bb8648]403
[b7fd2a0]404sys_errno_t sys_iospace_disable(ddi_ioarg_t *uspace_io_arg)
[fbcdeb8]405{
[8cd680c]406 ddi_ioarg_t arg;
[b7fd2a0]407 errno_t rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
[a53ed3a]408 if (rc != EOK)
[b7fd2a0]409 return (sys_errno_t) rc;
[8cd680c]410
[b7fd2a0]411 return (sys_errno_t) iospace_disable((task_id_t) arg.task_id,
[8cd680c]412 (uintptr_t) arg.ioaddr, (size_t) arg.size);
[fbcdeb8]413}
414
[8df5f20]415_NO_TRACE static errno_t dmamem_map(uintptr_t virt, size_t size, unsigned int map_flags,
[8cbf1c3]416 unsigned int flags, uintptr_t *phys)
[c6ae4c2]417{
[63e27ef]418 assert(TASK);
[a35b458]419
[fbcdeb8]420 // TODO: implement locking of non-anonymous mapping
421 return page_find_mapping(virt, phys);
422}
423
[8df5f20]424_NO_TRACE static errno_t dmamem_map_anonymous(size_t size, uintptr_t constraint,
[b0c2075]425 unsigned int map_flags, unsigned int flags, uintptr_t *phys,
426 uintptr_t *virt, uintptr_t bound)
[fbcdeb8]427{
[63e27ef]428 assert(TASK);
[a35b458]429
[e2a0d76]430 size_t frames = SIZE2FRAMES(size);
[14741a0]431 if (frames == 0)
432 return EINVAL;
433
[482f968]434 // FIXME: probably need to ensure that the memory is suitable for DMA
[a17cced]435 *phys = frame_alloc(frames, FRAME_ATOMIC, constraint);
[8cbf1c3]436 if (*phys == 0)
[fbcdeb8]437 return ENOMEM;
[a35b458]438
[fbcdeb8]439 mem_backend_data_t backend_data;
[8cbf1c3]440 backend_data.base = *phys;
[e2a0d76]441 backend_data.frames = frames;
[c101dc0]442 backend_data.anonymous = true;
[a35b458]443
[fbcdeb8]444 if (!as_area_create(TASK->as, map_flags, size,
445 AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
[a17cced]446 frame_free(*phys, frames);
[fbcdeb8]447 return ENOMEM;
[c6ae4c2]448 }
[a35b458]449
[fbcdeb8]450 return EOK;
[c6ae4c2]451}
452
[8df5f20]453_NO_TRACE static errno_t dmamem_unmap(uintptr_t virt, size_t size)
[c6ae4c2]454{
455 // TODO: implement unlocking & unmap
456 return EOK;
457}
458
[8df5f20]459_NO_TRACE static errno_t dmamem_unmap_anonymous(uintptr_t virt)
[c6ae4c2]460{
[c101dc0]461 return as_area_destroy(TASK->as, virt);
[fbcdeb8]462}
463
[b7fd2a0]464sys_errno_t sys_dmamem_map(size_t size, unsigned int map_flags, unsigned int flags,
[fbcdeb8]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 */
[a35b458]471
[8cbf1c3]472 uintptr_t phys;
[b7fd2a0]473 errno_t rc = dmamem_map((uintptr_t) virt_ptr, size, map_flags,
[fbcdeb8]474 flags, &phys);
[a35b458]475
[fbcdeb8]476 if (rc != EOK)
477 return rc;
[a35b458]478
[fbcdeb8]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 */
[a35b458]488
[b0c2075]489 uintptr_t constraint;
[b7fd2a0]490 errno_t rc = copy_from_uspace(&constraint, phys_ptr,
[b0c2075]491 sizeof(constraint));
492 if (rc != EOK)
493 return rc;
[a35b458]494
[bf9cb2f]495 uintptr_t virt;
496 rc = copy_from_uspace(&virt, virt_ptr, sizeof(virt));
497 if (rc != EOK)
498 return rc;
[a35b458]499
[8cbf1c3]500 uintptr_t phys;
[b0c2075]501 rc = dmamem_map_anonymous(size, constraint, map_flags, flags,
[fbcdeb8]502 &phys, &virt, bound);
503 if (rc != EOK)
504 return rc;
[a35b458]505
[fbcdeb8]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 }
[a35b458]511
[fbcdeb8]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 }
[c6ae4c2]517 }
[a35b458]518
[c6ae4c2]519 return EOK;
520}
521
[b7fd2a0]522sys_errno_t sys_dmamem_unmap(uintptr_t virt, size_t size, unsigned int flags)
[c6ae4c2]523{
[fbcdeb8]524 if ((flags & DMAMEM_FLAGS_ANONYMOUS) == 0)
525 return dmamem_unmap(virt, size);
526 else
527 return dmamem_unmap_anonymous(virt);
[c6ae4c2]528}
529
[06e1e95]530/** @}
[b45c443]531 */
Note: See TracBrowser for help on using the repository browser.