File : s-bbinte.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                  GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS                --
   4 --                                                                          --
   5 --                   S Y S T E M . B B . I N T E R R U P T S                --
   6 --                                                                          --
   7 --                                  B o d y                                 --
   8 --                                                                          --
   9 --        Copyright (C) 1999-2002 Universidad Politecnica de Madrid         --
  10 --             Copyright (C) 2003-2005 The European Space Agency            --
  11 --                     Copyright (C) 2003-2016, AdaCore                     --
  12 --                                                                          --
  13 -- GNARL is free software; you can  redistribute it  and/or modify it under --
  14 -- terms of the  GNU General Public License as published  by the Free Soft- --
  15 -- ware  Foundation;  either version 3,  or (at your option) any later ver- --
  16 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
  17 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  18 -- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
  19 --                                                                          --
  20 --                                                                          --
  21 --                                                                          --
  22 --                                                                          --
  23 --                                                                          --
  24 -- You should have received a copy of the GNU General Public License and    --
  25 -- a copy of the GCC Runtime Library Exception along with this program;     --
  26 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
  27 -- <http://www.gnu.org/licenses/>.                                          --
  28 --                                                                          --
  29 -- GNARL was developed by the GNARL team at Florida State University.       --
  30 -- Extensive contributions were provided by Ada Core Technologies, Inc.     --
  31 --                                                                          --
  32 -- The port of GNARL to bare board targets was initially developed by the   --
  33 -- Real-Time Systems Group at the Technical University of Madrid.           --
  34 --                                                                          --
  35 ------------------------------------------------------------------------------
  36 
  37 pragma Restrictions (No_Elaboration_Code);
  38 
  39 with System.Storage_Elements;
  40 with System.BB.CPU_Primitives;
  41 with System.BB.CPU_Primitives.Multiprocessors;
  42 with System.BB.Threads;
  43 with System.BB.Threads.Queues;
  44 with System.BB.Board_Support;
  45 with System.BB.Time;
  46 
  47 package body System.BB.Interrupts is
  48 
  49    use System.Multiprocessors;
  50    use System.BB.CPU_Primitives.Multiprocessors;
  51    use System.BB.Threads;
  52    use System.BB.Time;
  53 
  54    ----------------
  55    -- Local data --
  56    ----------------
  57 
  58    type Stack_Space is new Storage_Elements.Storage_Array
  59      (1 .. Storage_Elements.Storage_Offset (Parameters.Interrupt_Stack_Size));
  60    for Stack_Space'Alignment use 8;
  61    --  Type used to represent the stack area for each interrupt. The stack must
  62    --  be aligned to 8 bytes to allow double word data movements.
  63 
  64    Interrupt_Stacks : array (CPU) of Stack_Space;
  65    pragma Linker_Section (Interrupt_Stacks, ".interrupt_stacks");
  66    --  Array that contains the stack used for each interrupt priority on each
  67    --  CPU. Note that multiple interrupts with the same priority will share
  68    --  the same stack on a given CPU, as they never can both be executing at
  69    --  the same time. The interrupt stacks are assigned a special section,
  70    --  so the linker script can put them at a specific place and avoid useless
  71    --  initialization.
  72 
  73    Interrupt_Stack_Table : array (CPU) of System.Address;
  74    pragma Export (Asm, Interrupt_Stack_Table, "interrupt_stack_table");
  75    --  Table that contains a pointer to the top of the stack for each interrupt
  76    --  level on each CPU.
  77 
  78    type Handlers_Table is array (Interrupt_ID) of Interrupt_Handler;
  79    pragma Suppress_Initialization (Handlers_Table);
  80    --  Type used to represent the procedures used as interrupt handlers.
  81    --  We need to prevent initialization to avoid elaboration code, so we rely
  82    --  on default initialization to zero of uninitialized data.
  83 
  84    Interrupt_Handlers_Table : Handlers_Table;
  85    --  Table containing handlers attached to the different external interrupts
  86 
  87    Interrupt_Being_Handled : array (CPU) of Interrupt_ID :=
  88                                (others => No_Interrupt);
  89    pragma Volatile (Interrupt_Being_Handled);
  90    --  Interrupt_Being_Handled contains the interrupt currently being handled
  91    --  by each CPU in the system, if any. It is equal to No_Interrupt when no
  92    --  interrupt is handled. Its value is updated by the trap handler.
  93 
  94    -----------------------
  95    -- Local subprograms --
  96    -----------------------
  97 
  98    procedure Interrupt_Wrapper (Vector : CPU_Primitives.Vector_Id);
  99    --  This wrapper procedure is in charge of setting the appropriate
 100    --  software priorities before calling the user-defined handler.
 101 
 102    --------------------
 103    -- Attach_Handler --
 104    --------------------
 105 
 106    procedure Attach_Handler
 107      (Handler : not null Interrupt_Handler;
 108       Id      : Interrupt_ID;
 109       Prio    : Interrupt_Priority)
 110    is
 111    begin
 112       --  Check that we are attaching to a real interrupt
 113 
 114       pragma Assert (Id /= No_Interrupt);
 115 
 116       --  Copy the user's handler to the appropriate place within the table
 117 
 118       Interrupt_Handlers_Table (Id) := Handler;
 119 
 120       --  The BSP determines the vector that will be called when the given
 121       --  interrupt occurs, and then installs the handler there. This may
 122       --  include programming the interrupt controller.
 123 
 124       Board_Support.Install_Interrupt_Handler
 125         (Interrupt_Wrapper'Address, Id, Prio);
 126    end Attach_Handler;
 127 
 128    -----------------------
 129    -- Current_Interrupt --
 130    -----------------------
 131 
 132    function Current_Interrupt return Interrupt_ID is
 133       Result : constant Interrupt_ID := Interrupt_Being_Handled (Current_CPU);
 134 
 135    begin
 136       if Threads.Thread_Self.In_Interrupt then
 137 
 138          pragma Assert (Result /= No_Interrupt);
 139          return Result;
 140 
 141       else
 142          return No_Interrupt;
 143       end if;
 144    end Current_Interrupt;
 145 
 146    -----------------------
 147    -- Interrupt_Wrapper --
 148    -----------------------
 149 
 150    procedure Interrupt_Wrapper (Vector : CPU_Primitives.Vector_Id) is
 151       Self_Id         : constant Threads.Thread_Id := Threads.Thread_Self;
 152       Caller_Priority : constant Integer :=
 153                          Threads.Get_Priority (Self_Id);
 154       Interrupt       : constant Interrupt_ID :=
 155                           Board_Support.Get_Interrupt_Request (Vector);
 156       Int_Priority    : constant Interrupt_Priority :=
 157                           Board_Support.Priority_Of_Interrupt (Interrupt);
 158       CPU_Id          : constant CPU          := Current_CPU;
 159       Previous_Int    : constant Interrupt_ID :=
 160                           Interrupt_Being_Handled (CPU_Id);
 161       Prev_In_Interr  : constant Boolean := Self_Id.In_Interrupt;
 162 
 163    begin
 164       --  Handle spurious interrupts, reported as No_Interrupt. If they must be
 165       --  cleared, this should be done in Get_Interrupt_Request.
 166 
 167       if Interrupt = No_Interrupt then
 168          return;
 169       end if;
 170 
 171       --  Update execution time for the interrupted task
 172 
 173       if Scheduling_Event_Hook /= null then
 174          Scheduling_Event_Hook.all;
 175       end if;
 176 
 177       --  Store the interrupt being handled
 178 
 179       Interrupt_Being_Handled (CPU_Id) := Interrupt;
 180 
 181       --  Then, we must set the appropriate software priority corresponding
 182       --  to the interrupt being handled. It also deals with the appropriate
 183       --  interrupt masking.
 184 
 185       --  When this wrapper is called all interrupts are masked, and the active
 186       --  priority of the interrupted task must be lower than the priority of
 187       --  the interrupt (otherwise the interrupt would have been masked). The
 188       --  only exception to this is when a task is temporarily inserted in the
 189       --  ready queue because there is not a single task ready to execute; this
 190       --  temporarily inserted task may have a priority in the range of the
 191       --  interrupt priorities (it may be waiting in an entry for a protected
 192       --  handler), but interrupts would not be masked.
 193 
 194       pragma Assert
 195         (Caller_Priority <= Int_Priority or else Self_Id.State /= Runnable);
 196 
 197       Self_Id.In_Interrupt := True;
 198       Threads.Queues.Change_Priority (Self_Id, Int_Priority);
 199 
 200       CPU_Primitives.Enable_Interrupts (Int_Priority);
 201 
 202       --  Call the user handler
 203 
 204       Interrupt_Handlers_Table (Interrupt).all (Interrupt);
 205 
 206       CPU_Primitives.Disable_Interrupts;
 207 
 208       --  Update execution time for the interrupt. This must be done before
 209       --  changing priority (Scheduling_Event use priority to determine which
 210       --  task/interrupt will get the elapsed time).
 211 
 212       if Scheduling_Event_Hook /= null then
 213          Scheduling_Event_Hook.all;
 214       end if;
 215 
 216       --  Restore the software priority to the state before the interrupt
 217       --  happened. Interrupt unmasking is not done here (it will be done
 218       --  later by the interrupt epilogue).
 219 
 220       Threads.Queues.Change_Priority (Self_Id, Caller_Priority);
 221 
 222       --  Restore the interrupt that was being handled previously (if any)
 223 
 224       Interrupt_Being_Handled (CPU_Id) := Previous_Int;
 225 
 226       Self_Id.In_Interrupt := Prev_In_Interr;
 227 
 228    end Interrupt_Wrapper;
 229 
 230    ----------------------------
 231    -- Within_Interrupt_Stack --
 232    ----------------------------
 233 
 234    function Within_Interrupt_Stack
 235      (Stack_Address : System.Address) return Boolean
 236    is
 237       (Current_Interrupt /= No_Interrupt and then Stack_Address in
 238           Interrupt_Stacks (CPU'First)(Stack_Space'First)'Address
 239              ..
 240           Interrupt_Stacks (CPU'Last)(Stack_Space'Last)'Address);
 241 
 242    ---------------------------
 243    -- Initialize_Interrupts --
 244    ---------------------------
 245 
 246    procedure Initialize_Interrupts is
 247       use type System.Storage_Elements.Storage_Offset;
 248    begin
 249       for Proc in CPU'Range loop
 250 
 251          --  Store the pointer in the last double word
 252 
 253          Interrupt_Stack_Table (Proc) :=
 254               Interrupt_Stacks (Proc)(Stack_Space'Last - 7)'Address;
 255       end loop;
 256    end Initialize_Interrupts;
 257 end System.BB.Interrupts;