File : s-bbcppr-ppc.adb
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
5 -- S Y S T E M . B B . C P U _ P R I M I T I V E 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 -- GNAT 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. GNAT 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 -- GNAT was originally developed by the GNAT team at New York 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 -- This package implements PowerPC architecture specific support for the GNAT
38 -- Ravenscar run time.
39
40 with System.Machine_Code; use System.Machine_Code;
41 with System.Multiprocessors;
42 with System.BB.Interrupts;
43 with System.BB.Threads.Queues;
44 with System.BB.Protection;
45 with System.BB.Board_Support;
46 with System.BB.CPU_Primitives.Multiprocessors;
47
48 package body System.BB.CPU_Primitives is
49
50 package SSE renames System.Storage_Elements;
51 use type SSE.Integer_Address;
52 use type SSE.Storage_Offset;
53
54 type MSR_Type is mod 2 ** 32;
55 for MSR_Type'Size use 32;
56
57 MSR_EE : constant MSR_Type := 2 ** 15;
58
59 function Get_MSR return MSR_Type;
60 pragma Inline (Get_MSR);
61 -- Read the MSR
62
63 procedure Set_MSR (MSR : MSR_Type);
64 pragma Inline (Set_MSR);
65 -- Write the MSR
66
67 type Context_Switch_Params is record
68 Running_Thread_Address : Address;
69 -- Address of the running thread entry for the current cpu
70
71 First_Thread_Address : Address;
72 -- Address of the first read thread for the current cpu
73 end record;
74 pragma Convention (C, Context_Switch_Params);
75 pragma Suppress_Initialization (Context_Switch_Params);
76 -- This record describe data that are passed from Pre_Context_Switch
77 -- to Context_Switch. In the assembly code we take advantage of the ABI
78 -- so that the data returned are in the registers of the incoming call.
79 -- So there is no need to copy or to move the data between both calls.
80
81 function Pre_Context_Switch return Context_Switch_Params;
82 pragma Export (Asm, Pre_Context_Switch, "__gnat_pre_context_switch");
83 -- The full context switch is split in 2 stages:
84 -- - Pre_Context_Switch: adjust the current priority (but don't modify
85 -- the MSR.EE bit), and return the running and first thread queue
86 -- addresses.
87 -- - The assembly routine (context_switch) which does the real context
88 -- switch.
89 -- When called from interrupt handler, the stack pointer is saved before
90 -- and restore after the context switch. Therefore the context switch
91 -- cannot allocate a frame but only assembly code can guarantee that. We
92 -- also take advantage of this two stage call to extract queue pointers
93 -- in the Ada code.
94
95 ------------------------
96 -- Pre_Context_Switch --
97 ------------------------
98
99 function Pre_Context_Switch return Context_Switch_Params is
100 use System.BB.Threads.Queues;
101 use System.BB.Threads;
102
103 CPU_Id : constant System.Multiprocessors.CPU :=
104 Multiprocessors.Current_CPU;
105
106 New_Priority : constant Integer :=
107 First_Thread_Table (CPU_Id).Active_Priority;
108 begin
109 -- Called with interrupts disabled
110
111 -- Set interrupt priority. Unlike the SPARC implementation, the
112 -- interrupt priority is not part of the context (not in a register).
113 -- However full interrupt disabling is part of the context switch.
114
115 if New_Priority < Interrupt_Priority'Last then
116 Board_Support.Set_Current_Priority (New_Priority);
117 end if;
118
119 return (Running_Thread_Table (CPU_Id)'Address,
120 First_Thread_Table (CPU_Id)'Address);
121 end Pre_Context_Switch;
122
123 --------------------
124 -- Context_Switch --
125 --------------------
126
127 procedure Context_Switch is
128
129 procedure Context_Switch_Asm
130 (Running_Thread_Table_Element_Address : System.Address;
131 Ready_Thread_Table_Element_Address : System.Address);
132 pragma Import (Asm, Context_Switch_Asm, "__gnat_context_switch");
133 -- Real context switch in assembly code
134
135 Params : Context_Switch_Params;
136 begin
137 -- First set priority and get pointers
138
139 Params := Pre_Context_Switch;
140
141 -- Then the real context switch
142
143 Context_Switch_Asm (Params.Running_Thread_Address,
144 Params.First_Thread_Address);
145 end Context_Switch;
146
147 ------------------------
148 -- Disable_Interrupts --
149 ------------------------
150
151 procedure Disable_Interrupts is
152 begin
153 Set_MSR (Get_MSR and not MSR_EE);
154 end Disable_Interrupts;
155
156 -----------------------
157 -- Enable_Interrupts --
158 -----------------------
159
160 procedure Enable_Interrupts (Level : Integer) is
161 begin
162 if Level /= System.Interrupt_Priority'Last then
163 Board_Support.Set_Current_Priority (Level);
164
165 -- Really enable interrupts
166
167 Set_MSR (Get_MSR or MSR_EE);
168 end if;
169 end Enable_Interrupts;
170
171 -------------
172 -- Get_MSR --
173 -------------
174
175 function Get_MSR return MSR_Type is
176 Res : MSR_Type;
177 begin
178 Asm ("mfmsr %0",
179 Outputs => MSR_Type'Asm_Output ("=r", Res),
180 Volatile => True);
181 return Res;
182 end Get_MSR;
183
184 ----------------------
185 -- Initialize_Stack --
186 ----------------------
187
188 procedure Initialize_Stack
189 (Base : Address;
190 Size : Storage_Elements.Storage_Offset;
191 Stack_Pointer : out Address)
192 is
193 use System.Storage_Elements;
194
195 Minimum_Stack_Size_In_Bytes : constant Integer_Address :=
196 CPU_Specific.Stack_Alignment;
197
198 Initial_SP : constant System.Address :=
199 To_Address
200 (To_Integer (Base + Size) -
201 Minimum_Stack_Size_In_Bytes);
202
203 LR_Save_Word : System.Address;
204 for LR_Save_Word'Address use Initial_SP + Storage_Offset'(4);
205
206 Back_Chain_Word : System.Address;
207 for Back_Chain_Word'Address use Initial_SP;
208
209 begin
210 -- Put Null to these two values to clear the stack.
211
212 LR_Save_Word := Null_Address;
213 Back_Chain_Word := Null_Address;
214
215 Stack_Pointer := Initial_SP;
216 end Initialize_Stack;
217
218 ------------------------
219 -- Initialize_Context --
220 ------------------------
221
222 procedure Initialize_Context
223 (Buffer : not null access Context_Buffer;
224 Program_Counter : System.Address;
225 Argument : System.Address;
226 Stack_Pointer : System.Address)
227 is
228 procedure Start_Thread_Asm;
229 pragma Import (Asm, Start_Thread_Asm, "__gnat_start_thread");
230
231 Initial_SP : Address;
232
233 begin
234 -- No need to initialize the context of the environment task
235
236 if Program_Counter = Null_Address then
237 return;
238 end if;
239
240 -- We cheat as we don't know the stack size nor the stack base
241
242 Initialize_Stack (Stack_Pointer, 0, Initial_SP);
243
244 -- Overwrite Stack Pointer and Program Counter with values that have
245 -- been passed as arguments. The Stack Pointer of the task is 2 words
246 -- below Stack_Pointer. These two words correspond to the header of the
247 -- new stack. This header contains the LR_Save_Word and Back_Chain_Word.
248 -- Program_Counter points to the task_wrapper procedure.
249
250 -- We create a new stack pointer with a size of at least 8 which are
251 -- reserved for the header, but we also have to make sure that the stack
252 -- is aligned with Standard'Maximum_Alignment
253
254 Buffer.R1 := Initial_SP;
255 Buffer.LR := Start_Thread_Asm'Address;
256
257 Buffer.R14 := Program_Counter;
258 Buffer.R15 := Argument;
259
260 CPU_Specific.Finish_Initialize_Context (Buffer);
261 end Initialize_Context;
262
263 --------------------
264 -- Initialize_CPU --
265 --------------------
266
267 procedure Initialize_CPU is
268 begin
269 CPU_Specific.Initialize_CPU;
270 end Initialize_CPU;
271
272 -------------
273 -- Set_MSR --
274 -------------
275
276 -- Note: there is no context synchronization. If required, this must be
277 -- done explicitly by the caller.
278
279 procedure Set_MSR (MSR : MSR_Type) is
280 begin
281 Asm ("mtmsr %0",
282 Inputs => MSR_Type'Asm_Input ("r", MSR),
283 Volatile => True);
284 end Set_MSR;
285
286 end System.BB.CPU_Primitives;