source: rtems/bsps/shared/irq/irq-generic.c @ dea4bbe3

5
Last change on this file since dea4bbe3 was dea4bbe3, checked in by Sebastian Huber <sebastian.huber@…>, on 06/01/18 at 05:00:37

bsps: Avoid malloc() in generic IRQ support

Use rtems_heap_allocate_aligned_with_boundary() instead of malloc() to
avoid a dependency on errno.

  • Property mode set to 100644
File size: 16.0 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup bsp_interrupt
5 *
6 * @brief Generic BSP interrupt support implementation.
7 */
8
9/*
10 * Based on concepts of Pavel Pisa, Till Straumann and Eric Valette.
11 *
12 * Copyright (c) 2008, 2018 embedded brains GmbH.
13 *
14 *  embedded brains GmbH
15 *  Dornierstr. 4
16 *  82178 Puchheim
17 *  Germany
18 *  <rtems@embedded-brains.de>
19 *
20 * The license and distribution terms for this file may be
21 * found in the file LICENSE in this distribution or at
22 * http://www.rtems.org/license/LICENSE.
23 */
24
25#include <bsp/irq-generic.h>
26#include <bsp/fatal.h>
27
28#include <stdlib.h>
29
30#include <rtems/score/apimutex.h>
31#include <rtems/score/processormask.h>
32#include <rtems/score/sysstate.h>
33#include <rtems/malloc.h>
34
35#ifdef BSP_INTERRUPT_USE_INDEX_TABLE
36  bsp_interrupt_handler_index_type bsp_interrupt_handler_index_table
37    [BSP_INTERRUPT_VECTOR_NUMBER];
38#endif
39
40bsp_interrupt_handler_entry bsp_interrupt_handler_table
41  [BSP_INTERRUPT_HANDLER_TABLE_SIZE];
42
43/* The last entry indicates if everything is initialized */
44static uint8_t bsp_interrupt_handler_unique_table
45  [(BSP_INTERRUPT_HANDLER_TABLE_SIZE + 7 + 1) / 8];
46
47static void bsp_interrupt_handler_empty(void *arg)
48{
49  rtems_vector_number vector = (rtems_vector_number) (uintptr_t) arg;
50
51  bsp_interrupt_handler_default(vector);
52}
53
54#ifdef RTEMS_SMP
55  static void bsp_interrupt_handler_do_nothing(void *arg)
56  {
57    (void) arg;
58  }
59#endif
60
61static inline bool bsp_interrupt_is_handler_unique(rtems_vector_number index)
62{
63  rtems_vector_number i = index / 8;
64  rtems_vector_number s = index % 8;
65  return (bsp_interrupt_handler_unique_table [i] >> s) & 0x1;
66}
67
68static inline void bsp_interrupt_set_handler_unique(
69  rtems_vector_number index,
70  bool unique
71)
72{
73  rtems_vector_number i = index / 8;
74  rtems_vector_number s = index % 8;
75  if (unique) {
76    bsp_interrupt_handler_unique_table [i] |= (uint8_t) (0x1U << s);
77  } else {
78    bsp_interrupt_handler_unique_table [i] &= (uint8_t) ~(0x1U << s);
79  }
80}
81
82static inline bool bsp_interrupt_is_initialized(void)
83{
84  return bsp_interrupt_is_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE);
85}
86
87static inline void bsp_interrupt_set_initialized(void)
88{
89  bsp_interrupt_set_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE, true);
90}
91
92static inline bool bsp_interrupt_is_empty_handler_entry(
93  const bsp_interrupt_handler_entry *e
94)
95{
96  return e->handler == bsp_interrupt_handler_empty;
97}
98
99static inline void bsp_interrupt_clear_handler_entry(
100  bsp_interrupt_handler_entry *e,
101  rtems_vector_number vector
102)
103{
104  e->handler = bsp_interrupt_handler_empty;
105  bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
106  e->arg = (void *) (uintptr_t) vector;
107  e->info = NULL;
108  e->next = NULL;
109}
110
111static inline bool bsp_interrupt_allocate_handler_index(
112  rtems_vector_number vector,
113  rtems_vector_number *index
114)
115{
116  #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
117    rtems_vector_number i = 0;
118
119    /* The first entry will remain empty */
120    for (i = 1; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
121      const bsp_interrupt_handler_entry *e = &bsp_interrupt_handler_table [i];
122      if (bsp_interrupt_is_empty_handler_entry(e)) {
123        *index = i;
124        return true;
125      }
126    }
127
128    return false;
129  #else
130    *index = bsp_interrupt_handler_index(vector);
131    return true;
132  #endif
133}
134
135static bsp_interrupt_handler_entry *bsp_interrupt_allocate_handler_entry(void)
136{
137  #ifdef BSP_INTERRUPT_NO_HEAP_USAGE
138    rtems_vector_number index = 0;
139    if (bsp_interrupt_allocate_handler_index(0, &index)) {
140      return &bsp_interrupt_handler_table [index];
141    } else {
142      return NULL;
143    }
144  #else
145    return rtems_heap_allocate_aligned_with_boundary(sizeof(bsp_interrupt_handler_entry), 0, 0);
146  #endif
147}
148
149static void bsp_interrupt_free_handler_entry(bsp_interrupt_handler_entry *e)
150{
151  #ifdef BSP_INTERRUPT_NO_HEAP_USAGE
152    bsp_interrupt_clear_handler_entry(e, 0);
153  #else
154    free(e);
155  #endif
156}
157
158void bsp_interrupt_lock(void)
159{
160  if (_System_state_Is_up(_System_state_Get())) {
161    _RTEMS_Lock_allocator();
162  }
163}
164
165void bsp_interrupt_unlock(void)
166{
167  if (_System_state_Is_up(_System_state_Get())) {
168    _RTEMS_Unlock_allocator();
169  }
170}
171
172void bsp_interrupt_initialize(void)
173{
174  rtems_status_code sc = RTEMS_SUCCESSFUL;
175  size_t i = 0;
176
177  /* Initialize handler table */
178  for (i = 0; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
179    bsp_interrupt_handler_table [i].handler = bsp_interrupt_handler_empty;
180    bsp_interrupt_handler_table [i].arg = (void *) i;
181  }
182
183  sc = bsp_interrupt_facility_initialize();
184  if (sc != RTEMS_SUCCESSFUL) {
185    bsp_fatal(BSP_FATAL_INTERRUPT_INITIALIZATION);
186  }
187
188  bsp_interrupt_set_initialized();
189}
190
191/**
192 * @brief Installs an interrupt handler.
193 *
194 * @ingroup bsp_interrupt
195 *
196 * @return In addition to the standard status codes this function returns:
197 * - If the BSP interrupt support is not initialized RTEMS_INTERNAL_ERROR will
198 * be returned.
199 * - If not enough memory for a new handler is available RTEMS_NO_MEMORY will
200 * be returned
201 *
202 * @see rtems_interrupt_handler_install()
203 */
204static rtems_status_code bsp_interrupt_handler_install(
205  rtems_vector_number vector,
206  const char *info,
207  rtems_option options,
208  rtems_interrupt_handler handler,
209  void *arg
210)
211{
212  rtems_interrupt_level level;
213  rtems_vector_number index = 0;
214  bsp_interrupt_handler_entry *head = NULL;
215  bool enable_vector = false;
216  bool replace = RTEMS_INTERRUPT_IS_REPLACE(options);
217
218  /* Check parameters and system state */
219  if (!bsp_interrupt_is_initialized()) {
220    return RTEMS_INTERNAL_ERROR;
221  } else if (!bsp_interrupt_is_valid_vector(vector)) {
222    return RTEMS_INVALID_ID;
223  } else if (handler == NULL) {
224    return RTEMS_INVALID_ADDRESS;
225  } else if (rtems_interrupt_is_in_progress()) {
226    return RTEMS_CALLED_FROM_ISR;
227  }
228
229  /* Lock */
230  bsp_interrupt_lock();
231
232  /* Get handler table index */
233  index = bsp_interrupt_handler_index(vector);
234
235  /* Get head entry of the handler list for current vector */
236  head = &bsp_interrupt_handler_table [index];
237
238  if (bsp_interrupt_is_empty_handler_entry(head)) {
239    if (replace) {
240      /* No handler to replace exists */
241      bsp_interrupt_unlock();
242      return RTEMS_UNSATISFIED;
243    }
244
245    /*
246     * No real handler installed yet.  So allocate a new index in
247     * the handler table and fill the entry with life.
248     */
249    if (bsp_interrupt_allocate_handler_index(vector, &index)) {
250      bsp_interrupt_disable(level);
251      bsp_interrupt_handler_table [index].arg = arg;
252      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
253      bsp_interrupt_handler_table [index].handler = handler;
254      #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
255        bsp_interrupt_handler_index_table [vector] = index;
256      #endif
257      bsp_interrupt_enable(level);
258      bsp_interrupt_handler_table [index].info = info;
259    } else {
260      /* Handler table is full */
261      bsp_interrupt_unlock();
262      return RTEMS_NO_MEMORY;
263    }
264
265    /* This is the first handler so enable the vector later */
266    enable_vector = true;
267  } else {
268    bsp_interrupt_handler_entry *current = head;
269    bsp_interrupt_handler_entry *tail = NULL;
270    bsp_interrupt_handler_entry *match = NULL;
271
272    /* Ensure that a unique handler remains unique */
273    if (
274      !replace
275        && (RTEMS_INTERRUPT_IS_UNIQUE(options)
276          || bsp_interrupt_is_handler_unique(index))
277    ) {
278      /*
279       * Tried to install a unique handler on a not empty
280       * list or there is already a unique handler installed.
281       */
282      bsp_interrupt_unlock();
283      return RTEMS_RESOURCE_IN_USE;
284    }
285
286    /*
287     * Search for the list tail and check if the handler is already
288     * installed.
289     */
290    do {
291      if (
292        match == NULL
293          && (current->handler == handler || replace)
294          && current->arg == arg
295      ) {
296        match = current;
297      }
298      tail = current;
299      current = current->next;
300    } while (current != NULL);
301
302    if (replace) {
303      /* Ensure that a handler to replace exists */
304      if (match == NULL) {
305        bsp_interrupt_unlock();
306        return RTEMS_UNSATISFIED;
307      }
308
309      /* Use existing entry */
310      current = match;
311    } else {
312      /* Ensure the handler is not already installed */
313      if (match != NULL) {
314        /* The handler is already installed */
315        bsp_interrupt_unlock();
316        return RTEMS_TOO_MANY;
317      }
318
319      /* Allocate a new entry */
320      current = bsp_interrupt_allocate_handler_entry();
321      if (current == NULL) {
322        /* Not enough memory */
323        bsp_interrupt_unlock();
324        return RTEMS_NO_MEMORY;
325      }
326    }
327
328    /* Update existing entry or set new entry */
329    current->handler = handler;
330    current->info = info;
331
332    if (!replace) {
333      /* Set new entry */
334      current->arg = arg;
335      current->next = NULL;
336
337      /* Link to list tail */
338      bsp_interrupt_disable(level);
339      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
340      tail->next = current;
341      bsp_interrupt_enable(level);
342    }
343  }
344
345  /* Make the handler unique if necessary */
346  bsp_interrupt_set_handler_unique(index, RTEMS_INTERRUPT_IS_UNIQUE(options));
347
348  /* Enable the vector if necessary */
349  if (enable_vector) {
350    bsp_interrupt_vector_enable(vector);
351  }
352
353  /* Unlock */
354  bsp_interrupt_unlock();
355
356  return RTEMS_SUCCESSFUL;
357}
358
359/**
360 * @brief Removes an interrupt handler.
361 *
362 * @ingroup bsp_interrupt
363 *
364 * @return In addition to the standard status codes this function returns
365 * RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
366 *
367 * @see rtems_interrupt_handler_remove().
368 */
369static rtems_status_code bsp_interrupt_handler_remove(
370  rtems_vector_number vector,
371  rtems_interrupt_handler handler,
372  void *arg
373)
374{
375  rtems_interrupt_level level;
376  rtems_vector_number index = 0;
377  bsp_interrupt_handler_entry *head = NULL;
378  bsp_interrupt_handler_entry *current = NULL;
379  bsp_interrupt_handler_entry *previous = NULL;
380  bsp_interrupt_handler_entry *match = NULL;
381
382  /* Check parameters and system state */
383  if (!bsp_interrupt_is_initialized()) {
384    return RTEMS_INTERNAL_ERROR;
385  } else if (!bsp_interrupt_is_valid_vector(vector)) {
386    return RTEMS_INVALID_ID;
387  } else if (handler == NULL) {
388    return RTEMS_INVALID_ADDRESS;
389  } else if (rtems_interrupt_is_in_progress()) {
390    return RTEMS_CALLED_FROM_ISR;
391  }
392
393  /* Lock */
394  bsp_interrupt_lock();
395
396  /* Get handler table index */
397  index = bsp_interrupt_handler_index(vector);
398
399  /* Get head entry of the handler list for current vector */
400  head = &bsp_interrupt_handler_table [index];
401
402  /* Search for a matching entry */
403  current = head;
404  do {
405    if (current->handler == handler && current->arg == arg) {
406      match = current;
407      break;
408    }
409    previous = current;
410    current = current->next;
411  } while (current != NULL);
412
413  /* Remove the matching entry */
414  if (match != NULL) {
415    if (match->next != NULL) {
416      /*
417       * The match has a successor.  A successor is always
418       * allocated.  So replace the match with its successor
419       * and free the successor entry.
420       */
421      current = match->next;
422
423      bsp_interrupt_disable(level);
424      #ifdef RTEMS_SMP
425        match->handler = bsp_interrupt_handler_do_nothing;
426        bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
427      #endif
428      match->arg = current->arg;
429      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
430      match->handler = current->handler;
431      match->info = current->info;
432      match->next = current->next;
433      bsp_interrupt_enable(level);
434
435      bsp_interrupt_free_handler_entry(current);
436    } else if (match == head) {
437      /*
438       * The match is the list head and has no successor.
439       * The list head is stored in a static table so clear
440       * this entry.  Since now the list is empty disable the
441       * vector.
442       */
443
444      /* Disable the vector */
445      bsp_interrupt_vector_disable(vector);
446
447      /* Clear entry */
448      bsp_interrupt_disable(level);
449      bsp_interrupt_clear_handler_entry(head, vector);
450      #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
451        bsp_interrupt_handler_index_table [vector] = 0;
452      #endif
453      bsp_interrupt_enable(level);
454
455      /* Allow shared handlers */
456      bsp_interrupt_set_handler_unique(index, false);
457    } else {
458      /*
459       * The match is the list tail and has a predecessor.
460       * So terminate the predecessor and free the match.
461       */
462      bsp_interrupt_disable(level);
463      previous->next = NULL;
464      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
465      bsp_interrupt_enable(level);
466
467      bsp_interrupt_free_handler_entry(match);
468    }
469  } else {
470    /* No matching entry found */
471    bsp_interrupt_unlock();
472    return RTEMS_UNSATISFIED;
473  }
474
475  /* Unlock */
476  bsp_interrupt_unlock();
477
478  return RTEMS_SUCCESSFUL;
479}
480
481/**
482 * @brief Iterates over all installed interrupt handler of a vector.
483 *
484 * @ingroup bsp_interrupt
485 *
486 * @return In addition to the standard status codes this function returns
487 * RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
488 *
489 * @see rtems_interrupt_handler_iterate().
490 */
491static rtems_status_code bsp_interrupt_handler_iterate(
492  rtems_vector_number vector,
493  rtems_interrupt_per_handler_routine routine,
494  void *arg
495)
496{
497  bsp_interrupt_handler_entry *current = NULL;
498  rtems_option options = 0;
499  rtems_vector_number index = 0;
500
501  /* Check parameters and system state */
502  if (!bsp_interrupt_is_initialized()) {
503    return RTEMS_INTERNAL_ERROR;
504  } else if (!bsp_interrupt_is_valid_vector(vector)) {
505    return RTEMS_INVALID_ID;
506  } else if (rtems_interrupt_is_in_progress()) {
507    return RTEMS_CALLED_FROM_ISR;
508  }
509
510  /* Lock */
511  bsp_interrupt_lock();
512
513  /* Interate */
514  index = bsp_interrupt_handler_index(vector);
515  current = &bsp_interrupt_handler_table [index];
516  if (!bsp_interrupt_is_empty_handler_entry(current)) {
517    do {
518      options = bsp_interrupt_is_handler_unique(index) ?
519        RTEMS_INTERRUPT_UNIQUE : RTEMS_INTERRUPT_SHARED;
520      routine(arg, current->info, options, current->handler, current->arg);
521      current = current->next;
522    } while (current != NULL);
523  }
524
525  /* Unlock */
526  bsp_interrupt_unlock();
527
528  return RTEMS_SUCCESSFUL;
529}
530
531rtems_status_code rtems_interrupt_handler_install(
532  rtems_vector_number vector,
533  const char *info,
534  rtems_option options,
535  rtems_interrupt_handler handler,
536  void *arg
537)
538{
539  return bsp_interrupt_handler_install(vector, info, options, handler, arg);
540}
541
542rtems_status_code rtems_interrupt_handler_remove(
543  rtems_vector_number vector,
544  rtems_interrupt_handler handler,
545  void *arg
546)
547{
548  return bsp_interrupt_handler_remove(vector, handler, arg);
549}
550
551rtems_status_code rtems_interrupt_handler_iterate(
552  rtems_vector_number vector,
553  rtems_interrupt_per_handler_routine routine,
554  void *arg
555)
556{
557  return bsp_interrupt_handler_iterate(vector, routine, arg);
558}
559
560bool bsp_interrupt_handler_is_empty(rtems_vector_number vector)
561{
562  rtems_vector_number index = 0;
563  bsp_interrupt_handler_entry *head = NULL;
564  bool empty;
565
566  /* For use in interrupts so no lock. */
567
568  /* Get handler table index */
569  index = bsp_interrupt_handler_index(vector);
570
571  /* Get head entry of the handler list for the vector */
572  head = &bsp_interrupt_handler_table [index];
573
574  empty = bsp_interrupt_is_empty_handler_entry(head);
575
576  return empty;
577}
578
579rtems_status_code rtems_interrupt_set_affinity(
580  rtems_vector_number  vector,
581  size_t               affinity_size,
582  const cpu_set_t     *affinity
583)
584{
585  Processor_mask             set;
586  Processor_mask_Copy_status status;
587
588  if (!bsp_interrupt_is_valid_vector(vector)) {
589    return RTEMS_INVALID_ID;
590  }
591
592  status = _Processor_mask_From_cpu_set_t(&set, affinity_size, affinity);
593  if (status != PROCESSOR_MASK_COPY_LOSSLESS) {
594    return RTEMS_INVALID_SIZE;
595  }
596
597#if defined(RTEMS_SMP)
598  bsp_interrupt_set_affinity(vector, &set);
599#endif
600  return RTEMS_SUCCESSFUL;
601}
602
603rtems_status_code rtems_interrupt_get_affinity(
604  rtems_vector_number  vector,
605  size_t               affinity_size,
606  cpu_set_t           *affinity
607)
608{
609  Processor_mask             set;
610  Processor_mask_Copy_status status;
611
612  if (!bsp_interrupt_is_valid_vector(vector)) {
613    return RTEMS_INVALID_ID;
614  }
615
616#if defined(RTEMS_SMP)
617  bsp_interrupt_get_affinity(vector, &set);
618#else
619  _Processor_mask_From_index(&set, 0);
620#endif
621
622  status = _Processor_mask_To_cpu_set_t(&set, affinity_size, affinity);
623  if (status != PROCESSOR_MASK_COPY_LOSSLESS) {
624    return RTEMS_INVALID_SIZE;
625  }
626
627  return RTEMS_SUCCESSFUL;
628}
Note: See TracBrowser for help on using the repository browser.