~galfy/helenos/bird-port-mainline

« back to all changes in this revision

Viewing changes to kernel/generic/src/ddi/ddi.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <ddi/ddi_arg.h>
 
44
#include <proc/task.h>
 
45
#include <security/cap.h>
 
46
#include <mm/frame.h>
 
47
#include <mm/as.h>
 
48
#include <synch/spinlock.h>
 
49
#include <syscall/copy.h>
 
50
#include <adt/btree.h>
 
51
#include <arch.h>
 
52
#include <align.h>
 
53
#include <errno.h>
 
54
 
 
55
/** This lock protects the parea_btree. */
 
56
SPINLOCK_INITIALIZE(parea_lock);
 
57
 
 
58
/** B+tree with enabled physical memory areas. */
 
59
static btree_t parea_btree;
 
60
 
 
61
/** Initialize DDI. */
 
62
void ddi_init(void)
 
63
{
 
64
        btree_create(&parea_btree);
 
65
}
 
66
 
 
67
/** Enable piece of physical memory for mapping by physmem_map().
 
68
 *
 
69
 * @param parea Pointer to physical area structure.
 
70
 *
 
71
 */
 
72
void ddi_parea_register(parea_t *parea)
 
73
{
 
74
        ipl_t ipl = interrupts_disable();
 
75
        spinlock_lock(&parea_lock);
 
76
        
 
77
        /*
 
78
         * We don't check for overlaps here as the kernel is pretty sane.
 
79
         */
 
80
        btree_insert(&parea_btree, (btree_key_t) parea->pbase, parea, NULL);
 
81
        
 
82
        spinlock_unlock(&parea_lock);
 
83
        interrupts_restore(ipl);
 
84
}
 
85
 
 
86
/** Map piece of physical memory into virtual address space of current task.
 
87
 *
 
88
 * @param pf    Physical address of the starting frame.
 
89
 * @param vp    Virtual address of the starting page.
 
90
 * @param pages Number of pages to map.
 
91
 * @param flags Address space area flags for the mapping.
 
92
 *
 
93
 * @return 0 on success, EPERM if the caller lacks capabilities to use this
 
94
 *         syscall, EBADMEM if pf or vf is not page aligned, ENOENT if there
 
95
 *         is no task matching the specified ID or the physical address space
 
96
 *         is not enabled for mapping and ENOMEM if there was a problem in
 
97
 *         creating address space area.
 
98
 *
 
99
 */
 
100
static int ddi_physmem_map(uintptr_t pf, uintptr_t vp, size_t pages, int flags)
 
101
{
 
102
        ASSERT(TASK);
 
103
        ASSERT((pf % FRAME_SIZE) == 0);
 
104
        ASSERT((vp % PAGE_SIZE) == 0);
 
105
        
 
106
        /*
 
107
         * Make sure the caller is authorised to make this syscall.
 
108
         */
 
109
        cap_t caps = cap_get(TASK);
 
110
        if (!(caps & CAP_MEM_MANAGER))
 
111
                return EPERM;
 
112
        
 
113
        mem_backend_data_t backend_data;
 
114
        backend_data.base = pf;
 
115
        backend_data.frames = pages;
 
116
        
 
117
        ipl_t ipl = interrupts_disable();
 
118
        
 
119
        /* Find the zone of the physical memory */
 
120
        spinlock_lock(&zones.lock);
 
121
        size_t znum = find_zone(ADDR2PFN(pf), pages, 0);
 
122
        
 
123
        if (znum == (size_t) -1) {
 
124
                /* Frames not found in any zones
 
125
                 * -> assume it is hardware device and allow mapping
 
126
                 */
 
127
                spinlock_unlock(&zones.lock);
 
128
                goto map;
 
129
        }
 
130
        
 
131
        if (zones.info[znum].flags & ZONE_FIRMWARE) {
 
132
                /* Frames are part of firmware */
 
133
                spinlock_unlock(&zones.lock);
 
134
                goto map;
 
135
        }
 
136
        
 
137
        if (zone_flags_available(zones.info[znum].flags)) {
 
138
                /* Frames are part of physical memory, check if the memory
 
139
                 * region is enabled for mapping.
 
140
                 */
 
141
                spinlock_unlock(&zones.lock);
 
142
                
 
143
                spinlock_lock(&parea_lock);
 
144
                btree_node_t *nodep;
 
145
                parea_t *parea = (parea_t *) btree_search(&parea_btree,
 
146
                    (btree_key_t) pf, &nodep);
 
147
                
 
148
                if ((!parea) || (parea->frames < pages))
 
149
                        goto err;
 
150
                
 
151
                spinlock_unlock(&parea_lock);
 
152
                goto map;
 
153
        }
 
154
        
 
155
err:
 
156
        spinlock_unlock(&zones.lock);
 
157
        interrupts_restore(ipl);
 
158
        return ENOENT;
 
159
        
 
160
map:
 
161
        spinlock_lock(&TASK->lock);
 
162
        
 
163
        if (!as_area_create(TASK->as, flags, pages * PAGE_SIZE, vp,
 
164
            AS_AREA_ATTR_NONE, &phys_backend, &backend_data)) {
 
165
                /*
 
166
                 * The address space area could not have been created.
 
167
                 * We report it using ENOMEM.
 
168
                 */
 
169
                spinlock_unlock(&TASK->lock);
 
170
                interrupts_restore(ipl);
 
171
                return ENOMEM;
 
172
        }
 
173
        
 
174
        /*
 
175
         * Mapping is created on-demand during page fault.
 
176
         */
 
177
        
 
178
        spinlock_unlock(&TASK->lock);
 
179
        interrupts_restore(ipl);
 
180
        return 0;
 
181
}
 
182
 
 
183
/** Enable range of I/O space for task.
 
184
 *
 
185
 * @param id Task ID of the destination task.
 
186
 * @param ioaddr Starting I/O address.
 
187
 * @param size Size of the enabled I/O space..
 
188
 *
 
189
 * @return 0 on success, EPERM if the caller lacks capabilities to use this
 
190
 *           syscall, ENOENT if there is no task matching the specified ID.
 
191
 *
 
192
 */
 
193
static int ddi_iospace_enable(task_id_t id, uintptr_t ioaddr, size_t size)
 
194
{
 
195
        /*
 
196
         * Make sure the caller is authorised to make this syscall.
 
197
         */
 
198
        cap_t caps = cap_get(TASK);
 
199
        if (!(caps & CAP_IO_MANAGER))
 
200
                return EPERM;
 
201
        
 
202
        ipl_t ipl = interrupts_disable();
 
203
        spinlock_lock(&tasks_lock);
 
204
        
 
205
        task_t *task = task_find_by_id(id);
 
206
        
 
207
        if ((!task) || (!context_check(CONTEXT, task->context))) {
 
208
                /*
 
209
                 * There is no task with the specified ID
 
210
                 * or the task belongs to a different security
 
211
                 * context.
 
212
                 */
 
213
                spinlock_unlock(&tasks_lock);
 
214
                interrupts_restore(ipl);
 
215
                return ENOENT;
 
216
        }
 
217
        
 
218
        /* Lock the task and release the lock protecting tasks_btree. */
 
219
        spinlock_lock(&task->lock);
 
220
        spinlock_unlock(&tasks_lock);
 
221
        
 
222
        int rc = ddi_iospace_enable_arch(task, ioaddr, size);
 
223
        
 
224
        spinlock_unlock(&task->lock);
 
225
        interrupts_restore(ipl);
 
226
        
 
227
        return rc;
 
228
}
 
229
 
 
230
/** Wrapper for SYS_PHYSMEM_MAP syscall.
 
231
 *
 
232
 * @param phys_base Physical base address to map
 
233
 * @param virt_base Destination virtual address
 
234
 * @param pages Number of pages
 
235
 * @param flags Flags of newly mapped pages
 
236
 *
 
237
 * @return 0 on success, otherwise it returns error code found in errno.h
 
238
 *
 
239
 */
 
240
unative_t sys_physmem_map(unative_t phys_base, unative_t virt_base,
 
241
    unative_t pages, unative_t flags)
 
242
{
 
243
        return (unative_t) ddi_physmem_map(ALIGN_DOWN((uintptr_t) phys_base,
 
244
            FRAME_SIZE), ALIGN_DOWN((uintptr_t) virt_base, PAGE_SIZE),
 
245
            (size_t) pages, (int) flags);
 
246
}
 
247
 
 
248
/** Wrapper for SYS_ENABLE_IOSPACE syscall.
 
249
 *
 
250
 * @param uspace_io_arg User space address of DDI argument structure.
 
251
 *
 
252
 * @return 0 on success, otherwise it returns error code found in errno.h
 
253
 *
 
254
 */
 
255
unative_t sys_iospace_enable(ddi_ioarg_t *uspace_io_arg)
 
256
{
 
257
        ddi_ioarg_t arg;
 
258
        int rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
 
259
        if (rc != 0)
 
260
                return (unative_t) rc;
 
261
        
 
262
        return (unative_t) ddi_iospace_enable((task_id_t) arg.task_id,
 
263
            (uintptr_t) arg.ioaddr, (size_t) arg.size);
 
264
}
 
265
 
 
266
/** Disable or enable preemption.
 
267
 *
 
268
 * @param enable If non-zero, the preemption counter will be decremented,
 
269
 *               leading to potential enabling of preemption. Otherwise
 
270
 *               the preemption counter will be incremented, preventing
 
271
 *               preemption from occurring.
 
272
 *
 
273
 * @return Zero on success or EPERM if callers capabilities are not sufficient.
 
274
 *
 
275
 */
 
276
unative_t sys_preempt_control(int enable)
 
277
{
 
278
        if (!cap_get(TASK) & CAP_PREEMPT_CONTROL)
 
279
                return EPERM;
 
280
        
 
281
        if (enable)
 
282
                preemption_enable();
 
283
        else
 
284
                preemption_disable();
 
285
        
 
286
        return 0;
 
287
}
 
288
 
 
289
/** @}
 
290
 */