File : s-bbinte-ppc.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_Specific;
  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    use System.Multiprocessors;
  49    use System.BB.Time;
  50    use System.BB.Threads;
  51 
  52    ----------------
  53    -- Local data --
  54    ----------------
  55 
  56    type Stack_Space is new Storage_Elements.Storage_Array
  57      (1 .. Storage_Elements.Storage_Offset (Parameters.Interrupt_Stack_Size));
  58    for Stack_Space'Alignment use CPU_Specific.Stack_Alignment;
  59    pragma Suppress_Initialization (Stack_Space);
  60    --  Type used to represent the stack area for each interrupt. The stack must
  61    --  be aligned to 8 bytes to allow double word data movements.
  62 
  63    Interrupt_Stacks : array (CPU) of Stack_Space;
  64    pragma Linker_Section (Interrupt_Stacks, ".interrupt_stacks");
  65    --  Array that contains the stack used for each processor. The stacks are
  66    --  in a dedicated section so that assembly code can easily know its bounds.
  67    --
  68    --  Having a separate interrupt stask (from user tasks stack) helps to
  69    --  reduce the memory footprint as there is no need to reserve space for
  70    --  interrupts in user stacks.
  71    --
  72    --  Because several interrupts can share the same priority and because there
  73    --  can be many priorities we prefered not to have one stack per priority.
  74    --  Instead we have one interrupt stack per CPU.
  75    --
  76    --  An interrupt handler doesn't need to save non-volatile registers,
  77    --  because the interrupt is always completed before the interrupted task is
  78    --  resumed. This is obvious for non interrupt priority tasks and for
  79    --  active task at interrupt priority. The idle task (the one actived in
  80    --  System.BB.Protection.Leave_Kernel) cannot be at interrupt priority as
  81    --  there is always one task not in the interrupt priority range (the
  82    --  environmental task), and this one must be active or idle when an higher
  83    --  priority task is resumed.
  84 
  85    Interrupt_Stack_Table : array (CPU) of System.Address;
  86    pragma Export (Asm, Interrupt_Stack_Table, "interrupt_stack_table");
  87    --  Table that contains a pointer to the top of the stack for each processor
  88 
  89    type Handlers_Table is array (Interrupt_ID) of Interrupt_Handler;
  90    pragma Suppress_Initialization (Handlers_Table);
  91    --  Type used to represent the procedures used as interrupt handlers
  92 
  93    Interrupt_Handlers_Table : Handlers_Table;
  94    --  Table containing handlers attached to the different external interrupts
  95 
  96    Interrupt_Being_Handled : Interrupt_ID := No_Interrupt;
  97    pragma Volatile (Interrupt_Being_Handled);
  98    --  Interrupt_Being_Handled contains the interrupt currently being
  99    --  handled if any. It is equal to No_Interrupt when no interrupt
 100    --  is handled. Its value is updated by the trap handler.
 101 
 102    -----------------------
 103    -- Local subprograms --
 104    -----------------------
 105 
 106    procedure Interrupt_Wrapper;
 107    --  This wrapper procedure is in charge of setting the appropriate
 108    --  software priorities before calling the user-defined handler.
 109 
 110    --------------------
 111    -- Attach_Handler --
 112    --------------------
 113 
 114    procedure Attach_Handler
 115      (Handler : not null Interrupt_Handler;
 116       Id      : Interrupt_ID;
 117       Prio    : Interrupt_Priority)
 118    is
 119    begin
 120       --  Check that we are attaching to a real interrupt
 121 
 122       pragma Assert (Id /= No_Interrupt);
 123 
 124       --  Copy the user's handler to the appropriate place within the table
 125 
 126       Interrupt_Handlers_Table (Id) := Handler;
 127 
 128       --  Transform the interrupt level to the place in the interrupt vector
 129       --  table. Then insert the wrapper for the interrupt handlers in the
 130       --  underlying vector table.
 131 
 132       Board_Support.Install_Interrupt_Handler
 133         (Interrupt_Wrapper'Address, Id, Prio);
 134    end Attach_Handler;
 135 
 136    -----------------------
 137    -- Current_Interrupt --
 138    -----------------------
 139 
 140    function Current_Interrupt return Interrupt_ID is
 141       Result : constant Interrupt_ID := Interrupt_Being_Handled;
 142    begin
 143       if Threads.Thread_Self.In_Interrupt then
 144          pragma Assert (Result /= No_Interrupt);
 145          return Result;
 146       else
 147          return No_Interrupt;
 148       end if;
 149    end Current_Interrupt;
 150 
 151    -----------------------
 152    -- Interrupt_Wrapper --
 153    -----------------------
 154 
 155    procedure Interrupt_Wrapper
 156    is
 157       Self_Id         : constant Threads.Thread_Id := Threads.Thread_Self;
 158       Caller_Priority : constant Integer :=
 159                           Threads.Get_Priority (Self_Id);
 160       Interrupt       : constant Interrupt_ID :=
 161                           Board_Support.Get_Interrupt_Request (0);
 162       Int_Priority    : constant Any_Priority :=
 163                           Board_Support.Priority_Of_Interrupt (Interrupt);
 164       Previous_Int    : constant Interrupt_ID := Interrupt_Being_Handled;
 165       Prev_In_Interr  : constant Boolean := Self_Id.In_Interrupt;
 166 
 167    begin
 168       --  Handle spurious interrupts, reported as No_Interrupt. If they must be
 169       --  cleared, this should be done in Get_Interrupt_Request.
 170 
 171       if Interrupt = No_Interrupt then
 172          return;
 173       end if;
 174 
 175       --  Update execution time for the interrupted task
 176 
 177       if Scheduling_Event_Hook /= null then
 178          Scheduling_Event_Hook.all;
 179       end if;
 180 
 181       --  Store the interrupt being handled
 182 
 183       Interrupt_Being_Handled := Interrupt;
 184 
 185       --  Then, we must set the appropriate software priority corresponding
 186       --  to the interrupt being handled. It  also deals with the appropriate
 187       --  interrupt masking.
 188 
 189       --  When this wrapper is called all interrupts are masked, and the active
 190       --  priority of the interrupted task must be lower than the priority of
 191       --  the interrupt (otherwise the interrupt would have been masked). The
 192       --  only exception to this is when a task is temporarily inserted in the
 193       --  ready queue because there is not a single task ready to execute; this
 194       --  temporarily inserted task may have a priority in the range of the
 195       --  interrupt priorities (it may be waiting in an entry for a protected
 196       --  handler), but interrupts would not be masked.
 197 
 198       pragma Assert
 199         (Caller_Priority <= Int_Priority or else Self_Id.State /= Runnable);
 200 
 201       Self_Id.In_Interrupt := True;
 202       Threads.Queues.Change_Priority (Self_Id, Int_Priority);
 203 
 204       CPU_Primitives.Enable_Interrupts (Int_Priority);
 205 
 206       --  Call the user handler
 207 
 208       Interrupt_Handlers_Table (Interrupt).all (Interrupt);
 209 
 210       CPU_Primitives.Disable_Interrupts;
 211 
 212       --  Update execution time for the interrupt. This must be done before
 213       --  changing priority (Scheduling_Event use priority to determine which
 214       --  task/interrupt will get the elapsed time).
 215 
 216       if Scheduling_Event_Hook /= null then
 217          Scheduling_Event_Hook.all;
 218       end if;
 219 
 220       --  Restore the software priority to the state before the interrupt
 221       --  happened. Interrupt unmasking is not done here (it will be done
 222       --  later by the interrupt epilogue).
 223 
 224       Threads.Queues.Change_Priority (Self_Id, Caller_Priority);
 225 
 226       --  Restore the interrupt that was being handled previously (if any)
 227 
 228       Interrupt_Being_Handled := Previous_Int;
 229 
 230       Board_Support.Clear_Interrupt_Request (Interrupt);
 231 
 232       Self_Id.In_Interrupt := Prev_In_Interr;
 233 
 234       --  Unlike on SPARC, the interrupt priority is not part of the context,
 235       --  and therefore won't be adjusted by the context switch. Do it now.
 236 
 237       --  ??? Possibly do that in the asm code.
 238 
 239       --  The priority used (Caller_Priority) may not be correct if a task has
 240       --  been unblocked. But in that case, the task was blocked inside the
 241       --  kernel (so with interrupt disabled), and the correct priority will
 242       --  be set by Leave_Kernel.
 243 
 244       Board_Support.Set_Current_Priority (Caller_Priority);
 245    end Interrupt_Wrapper;
 246 
 247    ----------------------------
 248    -- Within_Interrupt_Stack --
 249    ----------------------------
 250 
 251    function Within_Interrupt_Stack
 252      (Stack_Address : System.Address) return Boolean
 253    is
 254    begin
 255       return Stack_Address >=
 256         Interrupt_Stacks (CPU'First)(Stack_Space'First)'Address
 257           and then Stack_Address <=
 258                      Interrupt_Stacks (CPU'Last)(Stack_Space'Last)'Address;
 259    end Within_Interrupt_Stack;
 260 
 261    ---------------------------
 262    -- Initialize_Interrupts --
 263    ---------------------------
 264 
 265    procedure Initialize_Interrupts is
 266       use type System.Storage_Elements.Storage_Offset;
 267    begin
 268       for Index in CPU loop
 269          CPU_Primitives.Initialize_Stack
 270            (Interrupt_Stacks (Index)'Address,
 271             Stack_Space'Length,
 272             Interrupt_Stack_Table (Index));
 273       end loop;
 274    end Initialize_Interrupts;
 275 
 276 end System.BB.Interrupts;