source: mainline/kernel/generic/src/ddi/ddi.c@ 46e886f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 46e886f was 46e886f, checked in by Jakub Jermar <jakub@…>, 6 years ago

Add port/MM IO agnostic functions to map/unmap PIO

In kernel we have drivers that use port IO on one architecture and
MM IO on another. We therefore need some function which does the right
thing depending on the type of the register address.

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