source: mainline/kernel/generic/src/ipc/irq.c@ 1a4ec93f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1a4ec93f was 5a5269d, checked in by GitHub <noreply@…>, 6 years ago

Change type of uspace pointers in kernel from pointer type to numeric (#170)

From kernel's perspective, userspace addresses are not valid pointers,
and can only be used in calls to copy_to/from_uspace().
Therefore, we change the type of those arguments and variables to
uspace_addr_t which is an alias for sysarg_t.

This allows the compiler to catch accidental direct accesses to
userspace addresses.

Additionally, to avoid losing the type information in code,
a macro uspace_ptr(type) is used that translates to uspace_addr_t.
I makes no functional difference, but allows keeping the type information
in code in case we implement some sort of static checking for it in the future.

However, ccheck doesn't like that, so instead of using uspace_ptr(char),
we use uspace_ptr_char which is defined as
#define uspace_ptr_char uspace_ptr(char).

  • Property mode set to 100644
File size: 14.7 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
3 * Copyright (c) 2006 Jakub Jermar
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup kernel_generic_ipc
31 * @{
32 */
33
34/**
35 * @file
36 * @brief IRQ notification framework.
37 *
38 * This framework allows applications to subscribe to receive a notification
39 * when an interrupt is detected. The application may provide a simple
40 * 'top-half' handler as part of its registration, which can perform simple
41 * operations (read/write port/memory, add information to notification IPC
42 * message).
43 *
44 * The structure of a notification message is as follows:
45 * - IMETHOD: interface and method as set by the SYS_IPC_IRQ_SUBSCRIBE syscall
46 * - ARG1: payload modified by a 'top-half' handler (scratch[1])
47 * - ARG2: payload modified by a 'top-half' handler (scratch[2])
48 * - ARG3: payload modified by a 'top-half' handler (scratch[3])
49 * - ARG4: payload modified by a 'top-half' handler (scratch[4])
50 * - ARG5: payload modified by a 'top-half' handler (scratch[5])
51 * - request_label: interrupt counter (may be needed to assure correct order
52 * in multithreaded drivers)
53 */
54
55#include <arch.h>
56#include <assert.h>
57#include <mm/slab.h>
58#include <mm/page.h>
59#include <mm/km.h>
60#include <errno.h>
61#include <ddi/irq.h>
62#include <ipc/ipc.h>
63#include <ipc/irq.h>
64#include <syscall/copy.h>
65#include <console/console.h>
66#include <macros.h>
67#include <cap/cap.h>
68#include <stdlib.h>
69
70static void ranges_unmap(irq_pio_range_t *ranges, size_t rangecount)
71{
72 for (size_t i = 0; i < rangecount; i++) {
73#ifdef IO_SPACE_BOUNDARY
74 if ((void *) ranges[i].base >= IO_SPACE_BOUNDARY)
75#endif
76 km_unmap(ranges[i].base, ranges[i].size);
77 }
78}
79
80static errno_t ranges_map_and_apply(irq_pio_range_t *ranges, size_t rangecount,
81 irq_cmd_t *cmds, size_t cmdcount)
82{
83 /* Copy the physical base addresses aside. */
84 uintptr_t *pbase = malloc(rangecount * sizeof(uintptr_t));
85 if (!pbase)
86 return ENOMEM;
87 for (size_t i = 0; i < rangecount; i++)
88 pbase[i] = ranges[i].base;
89
90 /* Map the PIO ranges into the kernel virtual address space. */
91 for (size_t i = 0; i < rangecount; i++) {
92#ifdef IO_SPACE_BOUNDARY
93 if ((void *) ranges[i].base < IO_SPACE_BOUNDARY)
94 continue;
95#endif
96 ranges[i].base = km_map(pbase[i], ranges[i].size,
97 KM_NATURAL_ALIGNMENT,
98 PAGE_READ | PAGE_WRITE | PAGE_KERNEL | PAGE_NOT_CACHEABLE);
99 if (!ranges[i].base) {
100 ranges_unmap(ranges, i);
101 free(pbase);
102 return ENOMEM;
103 }
104 }
105
106 /* Rewrite the IRQ code addresses from physical to kernel virtual. */
107 for (size_t i = 0; i < cmdcount; i++) {
108 uintptr_t addr;
109 size_t size;
110
111 /* Process only commands that use an address. */
112 switch (cmds[i].cmd) {
113 case CMD_PIO_READ_8:
114 case CMD_PIO_WRITE_8:
115 case CMD_PIO_WRITE_A_8:
116 size = 1;
117 break;
118 case CMD_PIO_READ_16:
119 case CMD_PIO_WRITE_16:
120 case CMD_PIO_WRITE_A_16:
121 size = 2;
122 break;
123 case CMD_PIO_READ_32:
124 case CMD_PIO_WRITE_32:
125 case CMD_PIO_WRITE_A_32:
126 size = 4;
127 break;
128 default:
129 /* Move onto the next command. */
130 continue;
131 }
132
133 addr = (uintptr_t) cmds[i].addr;
134
135 size_t j;
136 for (j = 0; j < rangecount; j++) {
137 /* Find the matching range. */
138 if (!iswithin(pbase[j], ranges[j].size, addr, size))
139 continue;
140
141 /* Switch the command to a kernel virtual address. */
142 addr -= pbase[j];
143 addr += ranges[j].base;
144
145 cmds[i].addr = (void *) addr;
146 break;
147 }
148
149 if (j == rangecount) {
150 /*
151 * The address used in this command is outside of all
152 * defined ranges.
153 */
154 ranges_unmap(ranges, rangecount);
155 free(pbase);
156 return EINVAL;
157 }
158 }
159
160 free(pbase);
161 return EOK;
162}
163
164/** Statically check the top-half IRQ code
165 *
166 * Check the top-half IRQ code for invalid or unsafe constructs.
167 *
168 */
169static errno_t code_check(irq_cmd_t *cmds, size_t cmdcount)
170{
171 for (size_t i = 0; i < cmdcount; i++) {
172 /*
173 * Check for accepted ranges.
174 */
175 if (cmds[i].cmd >= CMD_LAST)
176 return EINVAL;
177
178 if (cmds[i].srcarg >= IPC_CALL_LEN)
179 return EINVAL;
180
181 if (cmds[i].dstarg >= IPC_CALL_LEN)
182 return EINVAL;
183
184 switch (cmds[i].cmd) {
185 case CMD_PREDICATE:
186 /*
187 * Check for control flow overflow.
188 * Note that jumping just beyond the last
189 * command is a correct behaviour.
190 */
191 if (i + cmds[i].value > cmdcount)
192 return EINVAL;
193
194 break;
195 default:
196 break;
197 }
198 }
199
200 return EOK;
201}
202
203/** Free the top-half IRQ code.
204 *
205 * @param code Pointer to the top-half IRQ code.
206 *
207 */
208static void code_free(irq_code_t *code)
209{
210 if (code) {
211 ranges_unmap(code->ranges, code->rangecount);
212 free(code->ranges);
213 free(code->cmds);
214 free(code);
215 }
216}
217
218/** Copy the top-half IRQ code from userspace into the kernel.
219 *
220 * @param ucode Userspace address of the top-half IRQ code.
221 *
222 * @return Kernel address of the copied IRQ code.
223 *
224 */
225static irq_code_t *code_from_uspace(uspace_ptr_irq_code_t ucode)
226{
227 irq_pio_range_t *ranges = NULL;
228 irq_cmd_t *cmds = NULL;
229
230 irq_code_t *code = malloc(sizeof(*code));
231 if (!code)
232 return NULL;
233 errno_t rc = copy_from_uspace(code, ucode, sizeof(*code));
234 if (rc != EOK)
235 goto error;
236
237 if ((code->rangecount > IRQ_MAX_RANGE_COUNT) ||
238 (code->cmdcount > IRQ_MAX_PROG_SIZE))
239 goto error;
240
241 ranges = malloc(sizeof(code->ranges[0]) * code->rangecount);
242 if (!ranges)
243 goto error;
244 rc = copy_from_uspace(ranges, (uintptr_t) code->ranges,
245 sizeof(code->ranges[0]) * code->rangecount);
246 if (rc != EOK)
247 goto error;
248
249 cmds = malloc(sizeof(code->cmds[0]) * code->cmdcount);
250 if (!cmds)
251 goto error;
252 rc = copy_from_uspace(cmds, (uintptr_t) code->cmds,
253 sizeof(code->cmds[0]) * code->cmdcount);
254 if (rc != EOK)
255 goto error;
256
257 rc = code_check(cmds, code->cmdcount);
258 if (rc != EOK)
259 goto error;
260
261 rc = ranges_map_and_apply(ranges, code->rangecount, cmds,
262 code->cmdcount);
263 if (rc != EOK)
264 goto error;
265
266 code->ranges = ranges;
267 code->cmds = cmds;
268
269 return code;
270
271error:
272 if (cmds)
273 free(cmds);
274
275 if (ranges)
276 free(ranges);
277
278 free(code);
279 return NULL;
280}
281
282static void irq_hash_out(irq_t *irq)
283{
284 irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
285 irq_spinlock_lock(&irq->lock, false);
286
287 if (irq->notif_cfg.hashed_in) {
288 /* Remove the IRQ from the uspace IRQ hash table. */
289 hash_table_remove_item(&irq_uspace_hash_table, &irq->link);
290 irq->notif_cfg.hashed_in = false;
291 }
292
293 irq_spinlock_unlock(&irq->lock, false);
294 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
295}
296
297static void irq_destroy(void *arg)
298{
299 irq_t *irq = (irq_t *) arg;
300
301 irq_hash_out(irq);
302
303 /* Free up the IRQ code and associated structures. */
304 code_free(irq->notif_cfg.code);
305 slab_free(irq_cache, irq);
306}
307
308static kobject_ops_t irq_kobject_ops = {
309 .destroy = irq_destroy
310};
311
312/** Subscribe an answerbox as a receiving end for IRQ notifications.
313 *
314 * @param box Receiving answerbox.
315 * @param inr IRQ number.
316 * @param imethod Interface and method to be associated with the notification.
317 * @param ucode Uspace pointer to top-half IRQ code.
318 *
319 * @param[out] uspace_handle Uspace pointer to IRQ capability handle
320 *
321 * @return Error code.
322 *
323 */
324errno_t ipc_irq_subscribe(answerbox_t *box, inr_t inr, sysarg_t imethod,
325 uspace_ptr_irq_code_t ucode, uspace_ptr_cap_irq_handle_t uspace_handle)
326{
327 if ((inr < 0) || (inr > last_inr))
328 return ELIMIT;
329
330 irq_code_t *code;
331 if (ucode) {
332 code = code_from_uspace(ucode);
333 if (!code)
334 return EBADMEM;
335 } else
336 code = NULL;
337
338 /*
339 * Allocate and populate the IRQ kernel object.
340 */
341 cap_handle_t handle;
342 errno_t rc = cap_alloc(TASK, &handle);
343 if (rc != EOK)
344 return rc;
345
346 rc = copy_to_uspace(uspace_handle, &handle, sizeof(cap_handle_t));
347 if (rc != EOK) {
348 cap_free(TASK, handle);
349 return rc;
350 }
351
352 irq_t *irq = (irq_t *) slab_alloc(irq_cache, FRAME_ATOMIC);
353 if (!irq) {
354 cap_free(TASK, handle);
355 return ENOMEM;
356 }
357
358 kobject_t *kobject = kobject_alloc(FRAME_ATOMIC);
359 if (!kobject) {
360 cap_free(TASK, handle);
361 slab_free(irq_cache, irq);
362 return ENOMEM;
363 }
364
365 irq_initialize(irq);
366 irq->inr = inr;
367 irq->claim = ipc_irq_top_half_claim;
368 irq->handler = ipc_irq_top_half_handler;
369 irq->notif_cfg.notify = true;
370 irq->notif_cfg.answerbox = box;
371 irq->notif_cfg.imethod = imethod;
372 irq->notif_cfg.code = code;
373 irq->notif_cfg.counter = 0;
374
375 /*
376 * Insert the IRQ structure into the uspace IRQ hash table.
377 */
378 irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
379 irq_spinlock_lock(&irq->lock, false);
380
381 irq->notif_cfg.hashed_in = true;
382 hash_table_insert(&irq_uspace_hash_table, &irq->link);
383
384 irq_spinlock_unlock(&irq->lock, false);
385 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
386
387 kobject_initialize(kobject, KOBJECT_TYPE_IRQ, irq, &irq_kobject_ops);
388 cap_publish(TASK, handle, kobject);
389
390 return EOK;
391}
392
393/** Unsubscribe task from IRQ notification.
394 *
395 * @param box Answerbox associated with the notification.
396 * @param handle IRQ capability handle.
397 *
398 * @return EOK on success or an error code.
399 *
400 */
401errno_t ipc_irq_unsubscribe(answerbox_t *box, cap_irq_handle_t handle)
402{
403 kobject_t *kobj = cap_unpublish(TASK, handle, KOBJECT_TYPE_IRQ);
404 if (!kobj)
405 return ENOENT;
406
407 assert(kobj->irq->notif_cfg.answerbox == box);
408
409 irq_hash_out(kobj->irq);
410
411 kobject_put(kobj);
412 cap_free(TASK, handle);
413
414 return EOK;
415}
416
417/** Add a call to the proper answerbox queue.
418 *
419 * Assume irq->lock is locked and interrupts disabled.
420 *
421 * @param irq IRQ structure referencing the target answerbox.
422 * @param call IRQ notification call.
423 *
424 */
425static void send_call(irq_t *irq, call_t *call)
426{
427 irq_spinlock_lock(&irq->notif_cfg.answerbox->irq_lock, false);
428 list_append(&call->ab_link, &irq->notif_cfg.answerbox->irq_notifs);
429 irq_spinlock_unlock(&irq->notif_cfg.answerbox->irq_lock, false);
430
431 waitq_wakeup(&irq->notif_cfg.answerbox->wq, WAKEUP_FIRST);
432}
433
434/** Apply the top-half IRQ code to find out whether to accept the IRQ or not.
435 *
436 * @param irq IRQ structure.
437 *
438 * @return IRQ_ACCEPT if the interrupt is accepted by the IRQ code.
439 * @return IRQ_DECLINE if the interrupt is not accepted byt the IRQ code.
440 *
441 */
442irq_ownership_t ipc_irq_top_half_claim(irq_t *irq)
443{
444 irq_code_t *code = irq->notif_cfg.code;
445 uint32_t *scratch = irq->notif_cfg.scratch;
446
447 if (!irq->notif_cfg.notify)
448 return IRQ_DECLINE;
449
450 if (!code)
451 return IRQ_DECLINE;
452
453 for (size_t i = 0; i < code->cmdcount; i++) {
454 uintptr_t srcarg = code->cmds[i].srcarg;
455 uintptr_t dstarg = code->cmds[i].dstarg;
456
457 switch (code->cmds[i].cmd) {
458 case CMD_PIO_READ_8:
459 scratch[dstarg] =
460 pio_read_8((ioport8_t *) code->cmds[i].addr);
461 break;
462 case CMD_PIO_READ_16:
463 scratch[dstarg] =
464 pio_read_16((ioport16_t *) code->cmds[i].addr);
465 break;
466 case CMD_PIO_READ_32:
467 scratch[dstarg] =
468 pio_read_32((ioport32_t *) code->cmds[i].addr);
469 break;
470 case CMD_PIO_WRITE_8:
471 pio_write_8((ioport8_t *) code->cmds[i].addr,
472 (uint8_t) code->cmds[i].value);
473 break;
474 case CMD_PIO_WRITE_16:
475 pio_write_16((ioport16_t *) code->cmds[i].addr,
476 (uint16_t) code->cmds[i].value);
477 break;
478 case CMD_PIO_WRITE_32:
479 pio_write_32((ioport32_t *) code->cmds[i].addr,
480 (uint32_t) code->cmds[i].value);
481 break;
482 case CMD_PIO_WRITE_A_8:
483 pio_write_8((ioport8_t *) code->cmds[i].addr,
484 (uint8_t) scratch[srcarg]);
485 break;
486 case CMD_PIO_WRITE_A_16:
487 pio_write_16((ioport16_t *) code->cmds[i].addr,
488 (uint16_t) scratch[srcarg]);
489 break;
490 case CMD_PIO_WRITE_A_32:
491 pio_write_32((ioport32_t *) code->cmds[i].addr,
492 (uint32_t) scratch[srcarg]);
493 break;
494 case CMD_LOAD:
495 scratch[dstarg] = code->cmds[i].value;
496 break;
497 case CMD_AND:
498 scratch[dstarg] = scratch[srcarg] &
499 code->cmds[i].value;
500 break;
501 case CMD_PREDICATE:
502 if (scratch[srcarg] == 0)
503 i += code->cmds[i].value;
504
505 break;
506 case CMD_ACCEPT:
507 return IRQ_ACCEPT;
508 case CMD_DECLINE:
509 default:
510 return IRQ_DECLINE;
511 }
512 }
513
514 return IRQ_DECLINE;
515}
516
517/** IRQ top-half handler.
518 *
519 * We expect interrupts to be disabled and the irq->lock already held.
520 *
521 * @param irq IRQ structure.
522 *
523 */
524void ipc_irq_top_half_handler(irq_t *irq)
525{
526 assert(irq);
527
528 assert(interrupts_disabled());
529 assert(irq_spinlock_locked(&irq->lock));
530
531 if (irq->notif_cfg.answerbox) {
532 call_t *call = ipc_call_alloc();
533 if (!call)
534 return;
535
536 call->flags |= IPC_CALL_NOTIF;
537 /* Put a counter to the message */
538 call->priv = ++irq->notif_cfg.counter;
539
540 /* Set up args */
541 ipc_set_imethod(&call->data, irq->notif_cfg.imethod);
542 ipc_set_arg1(&call->data, irq->notif_cfg.scratch[1]);
543 ipc_set_arg2(&call->data, irq->notif_cfg.scratch[2]);
544 ipc_set_arg3(&call->data, irq->notif_cfg.scratch[3]);
545 ipc_set_arg4(&call->data, irq->notif_cfg.scratch[4]);
546 ipc_set_arg5(&call->data, irq->notif_cfg.scratch[5]);
547
548 send_call(irq, call);
549 }
550}
551
552/** Send notification message.
553 *
554 * @param irq IRQ structure.
555 * @param a1 Driver-specific payload argument.
556 * @param a2 Driver-specific payload argument.
557 * @param a3 Driver-specific payload argument.
558 * @param a4 Driver-specific payload argument.
559 * @param a5 Driver-specific payload argument.
560 *
561 */
562void ipc_irq_send_msg(irq_t *irq, sysarg_t a1, sysarg_t a2, sysarg_t a3,
563 sysarg_t a4, sysarg_t a5)
564{
565 irq_spinlock_lock(&irq->lock, true);
566
567 if (irq->notif_cfg.answerbox) {
568 call_t *call = ipc_call_alloc();
569 if (!call) {
570 irq_spinlock_unlock(&irq->lock, true);
571 return;
572 }
573
574 call->flags |= IPC_CALL_NOTIF;
575 /* Put a counter to the message */
576 call->priv = ++irq->notif_cfg.counter;
577
578 ipc_set_imethod(&call->data, irq->notif_cfg.imethod);
579 ipc_set_arg1(&call->data, a1);
580 ipc_set_arg2(&call->data, a2);
581 ipc_set_arg3(&call->data, a3);
582 ipc_set_arg4(&call->data, a4);
583 ipc_set_arg5(&call->data, a5);
584
585 send_call(irq, call);
586 }
587
588 irq_spinlock_unlock(&irq->lock, true);
589}
590
591/** @}
592 */
Note: See TracBrowser for help on using the repository browser.