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;