source: mainline/kernel/generic/src/ddi/ddi.c@ deed510

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since deed510 was 7d83c54, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Initialize backend_data.parea in dmamem_map_anonymous()

This was braking USB hid. Intriguing.

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