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;