summaryrefslogtreecommitdiff
path: root/ecos/packages/hal/sparc/arch/current/src/context.S
blob: 09da27c8314e61ffe4f0b8f4932c8c2d0fc909ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/*=============================================================================
//
//	context.S
//
//	SPARC context switch code
//
//=============================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//=============================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): 	nickg, gthomas, hmt
// Contributors:	nickg, gthomas, hmt
// Date:	1998-12-15
// Purpose:	SPARC context switch code
// Description:	This file contains implementations of the thread context 
//		switch routines. It also contains the longjmp() and setjmp()
//              routines.
//
//####DESCRIPTIONEND####
//
//===========================================================================*/

#include <pkgconf/hal.h>

#include <cyg/hal/vectors.h>
	
#define DELAYS_AFTER_WRPSR_SAME_WINDOW
#define DELAYS_AFTER_WRWIM

	.text

! ------------------------------------------------------------------------------
!  hal_thread_switch_context
!  Switch thread contexts
!  %o0 = address of sp of next thread to execute
!  %o1 = address of sp save location of current thread

	.global	hal_thread_switch_context
hal_thread_switch_context:	

	! First take the stack down to make room for the saved register
	! state, including a window save area at the base.  Leave the
	! current window save area undisturbed.  It is unused within the
	! save but will become current again when we continue in this
	! context.  This lets us do this whole piece of work without
	! diabling interrupts for too long, since, for example, we can
	! lower the stack atomically with one instruction:
	sub	%sp, SAVE_REGS_SIZE, %sp
	st	%sp, [ %o1 ]		! return SP for this thread
			
	std	%l0, [%sp + 0 * 4]	! save L & I registers
	std	%l2, [%sp + 2 * 4]
	std	%l4, [%sp + 4 * 4]
	std	%l6, [%sp + 6 * 4]

	std	%i0, [%sp + 8 * 4]
	std	%i2, [%sp + 10 * 4]
	std	%i4, [%sp + 12 * 4]
	std	%i6, [%sp + 14 * 4]
		
#ifdef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
	st	%o7, [%sp + 31 * 4]	! save only the return address
	! and no need to preserve %o0 even though it is restored
#else // save a maximal context
	st	%g1, [%sp + 17 * 4]	! save G & O registers
	std	%g2, [%sp + 18 * 4]
	std	%g4, [%sp + 20 * 4]
	std	%g6, [%sp + 22 * 4]

	std	%o0, [%sp + 24 * 4]
	std	%o2, [%sp + 26 * 4]
	std	%o4, [%sp + 28 * 4]
	std	%o6, [%sp + 30 * 4]
#endif // !CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
	! and save the CWP in %g0 save place
	rd	%psr, %g7
	st	%g7, [%sp + 16 * 4]
	
	! Now load the destination thread by dropping through
	! to hal_thread_load_context
	
! ------------------------------------------------------------------------------
!  hal_thread_load_context
!  Load thread context
!  %o0 = address of sp of next thread to execute
!  Note that this function is also the second half of hal_thread_switch_context
!  and is simply dropped into from it.

	.global hal_thread_load_context
hal_thread_load_context:

	! Here, we are a leaf routine but with slightly odd properties.
	! The stack is still the callers at this point but the register
	! set is up for grabs.  So we can use globals:
	
	ld	[ %o0 ], %g7		! Get the next saved SP
	
	! DISABLE INTERRUPTS *ONLY* NOT TRAPS
	rd	%psr, %g6
	or	%g6, 0xfe0, %g5		! PIL up to 15 leave traps enabled
	wr	%g5, %psr
	nop; nop; nop
	
	! force out all our callers register sets onto the stack
	! if necessary:	the system will handily take care of this for
	! us as follows:
	save	%sp, -16 * 4, %sp	! need all these to preserve
	save	%sp, -16 * 4, %sp	! the linked list property...
	save	%sp, -16 * 4, %sp
	save	%sp, -16 * 4, %sp
#if 6 < __WINSIZE
	save	%sp, -16 * 4, %sp
#if 7 < __WINSIZE
	save	%sp, -16 * 4, %sp
#endif
#endif
	! Fewer saves if fewer register windows.  For 8 register windows,
	! six of these is correct; a seventh would force out the current
	! set that was already saved manually above.  Note that minimal
	! space is allowed on stack for locals and ins in case this
	! sequence itself gets interrupted and recurses too deep.
	
	! now select the new window with traps disabled...

	! get the new PSR and CWP that we will ultimately restore
	! from the %g0 save place...
	ld	[%g7 + 16 * 4], %g6	! %g7 holds the new stack pointer
	andn	%g6, 0x20, %g5		! clear ET into %g5
	and	%g6, __WINBITS, %g4	! CWP bits only in %g4
		
	! calculate a new WIM...
	add	%g4, 1, %g3		! required invalid window number
#if 8 == __WINSIZE
	and	%g3, __WINBITS, %g3	! modulo 8
#else   // expect 5 or 6 or 7 windows
	cmp	%g3, __WINSIZE
	bge,a	567f			! taken: do delay slot, handle overflow
	 mov	0, %g3			! only if .ge. above
567:	
#endif
	mov	1, %g2
	sll	%g2, %g3, %g2		! converted to a mask for the WIM

	! DISABLE INTERRUPTS (TRAPS)
	wr	%g5, %psr		! set CWP to new window, disable traps
	wr	%g2, %wim		! and WIM to new value
	nop
	nop
	nop
		
	! Must do this atomically so that the registers match the stack.
	! After locals and ins are loaded, we are conformant to the PCS
	! so can re-enable interrupts.
	mov	%g7, %sp		! target sp in situ (%sp = %o6)

	ldd	[%sp + 0 * 4], %l0	! restore L & I registers
	ldd	[%sp + 2 * 4], %l2
	ldd	[%sp + 4 * 4], %l4
	ldd	[%sp + 6 * 4], %l6

	ldd	[%sp + 8 * 4], %i0
	ldd	[%sp + 10 * 4], %i2
	ldd	[%sp + 12 * 4], %i4
	ldd	[%sp + 14 * 4], %i6

	! RESTORE INTERRUPTS to saved state
	wr	%g6, %psr		! set new CWP and old ET and PIL
	nop
	nop
	nop
	
	! now load the rest of the context; we can be interrupted here
	! (if the saved context was a voluntary yield or threadstart)
	! but that is OK, other state will be preserved in that case...

#ifdef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
	ld	[%sp + 24 * 4], %o0	! must load the initial argument
#else // restore a maximal context
	ld	[%sp + 17 * 4], %g1
	ldd	[%sp + 18 * 4], %g2
	ldd	[%sp + 20 * 4], %g4
	ldd	[%sp + 22 * 4], %g6

	ldd	[%sp + 24 * 4], %o0
	ldd	[%sp + 26 * 4], %o2
	ldd	[%sp + 28 * 4], %o4
#endif // !CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
					! %o6 = %sp, already set up
	ld	[%sp + 31 * 4], %o7	! "return" address

	retl
	add	%sp, SAVE_REGS_SIZE, %sp ! and set the stack back
					! to its entrant value

! ------------------------------------------------------------------------------
!  HAL longjmp, setjmp implementations

!FUNC_START(hal_setjmp)
	.global	hal_setjmp
hal_setjmp:
	! Treat this as a leaf routine, may as well.
	! %o0 is the address of the buffer.

	std	%l0, [%o0 + 0 * 4]	! save L & I registers
	std	%l2, [%o0 + 2 * 4]
	std	%l4, [%o0 + 4 * 4]
	std	%l6, [%o0 + 6 * 4]

	std	%i0, [%o0 + 8 * 4]
	std	%i2, [%o0 + 10 * 4]
	std	%i4, [%o0 + 12 * 4]
	std	%i6, [%o0 + 14 * 4]
		
#ifdef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
	std	%o6, [%o0 + 30 * 4]	! just save %sp and return address
#else // save a maximal context
	st	%g1, [%o0 + 17 * 4]	! save G & O registers
	std	%g2, [%o0 + 18 * 4]
	std	%g4, [%o0 + 20 * 4]
	std	%g6, [%o0 + 22 * 4]

	std	%o0, [%o0 + 24 * 4]
	std	%o2, [%o0 + 26 * 4]
	std	%o4, [%o0 + 28 * 4]
	std	%o6, [%o0 + 30 * 4]
#endif // !CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
	
	! and save the CWP in %g0 save place
	rd	%psr, %g7
	st	%g7, [%o0 + 16 * 4]

	! DISABLE INTERRUPTS *ONLY* NOT TRAPS
	or	%g7, 0xfe0, %g6		! PIL up to 15 leave traps enabled
	wr	%g6, %psr
	nop; nop; nop
	
	! force out all our callers register sets onto the stack
	! if necessary:	the system will handily take care of this for
	! us as follows:
	save	%sp, -16 * 4, %sp	! need all these to preserve
	save	%sp, -16 * 4, %sp	! the linked list property...
	save	%sp, -16 * 4, %sp
	save	%sp, -16 * 4, %sp
#if 6 < __WINSIZE
	save	%sp, -16 * 4, %sp
#if 7 < __WINSIZE
	save	%sp, -16 * 4, %sp
#endif
#endif
	! Fewer saves if fewer register windows.  For 8 register windows,
	! six of these is correct; a seventh would force out the current
	! set that was already saved manually above.  Note that minimal
	! space is allowed on stack for locals and ins in case this
	! sequence itself gets interrupted and recurses too deep.

	! (after all, we are about to call deeper not shallower, otherwise
	!  using setjmp is inappropriate)

	! ENABLE INTERRUPTS
	wr	%g7, %psr		! set CWP back to as-was
	nop
	nop
	nop
	
#ifndef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
	ldd	[%o0 + 22 * 4], %g6	! preserve %g7 and %g6
#endif
	
	retl				! ret and return zero to indicate
	mov	0, %o0			! not longjumped-to
	
!  hal_longjmp loads state from arg0 and returns arg1
!FUNC_START(hal_longjmp)
	.global	hal_longjmp
hal_longjmp:
	! This is kind of a leaf routine, it returns elsewhere
	! %o0 is the address of the buffer.
	! %o1 is the value to return in %o0 (since we are a leaf)

	mov	%o0, %g7		! keep the pointer handy
	mov	%o1, %g1		! and the return value
	! now select the new window with traps disabled...
	rd	%psr, %g6
					! preserve ET, clear CWP
	andn	%g6, __WINBITS_MAXIMAL, %g6
	andn	%g6, 0x20, %g5		! clear ET also into %g5

	! get new CWP from %g0 save place...
	ld	[%g7 + 16 * 4], %g4	! %g7 holds the new stack pointer
	and	%g4, __WINBITS, %g4	! preserve CWP bits
		
	! calculate a new WIM...
	add	%g4, 1, %g3		! required invalid window number
#if 8 == __WINSIZE
	and	%g3, __WINBITS, %g3	! modulo 8
#else   // expect 5 or 6 or 7 windows
	cmp	%g3, __WINSIZE
	bge,a	567f			! taken: do delay slot, handle overflow
	 mov	0, %g3			! only if .ge. above
567:	
#endif
	mov	1, %g2
	sll	%g2, %g3, %g2		! converted to a mask for the WIM

	! DISABLE INTERRUPTS
	wr	%g5, %g4, %psr		! set CWP to new window, disable traps
	wr	%g2, %wim		! and WIM to new value
	nop
	nop
	nop
		
	! Must do this atomically so that the registers match the stack.
	! After locals and ins are loaded, we are conformant to the PCS
	! so can re-enable interrupts.

	ldd	[%g7 + 0 * 4], %l0	! restore L & I registers
	ldd	[%g7 + 2 * 4], %l2
	ldd	[%g7 + 4 * 4], %l4
	ldd	[%g7 + 6 * 4], %l6

	ldd	[%g7 + 8 * 4], %i0
	ldd	[%g7 + 10 * 4], %i2
	ldd	[%g7 + 12 * 4], %i4
	ldd	[%g7 + 14 * 4], %i6

	ld	[%g7 + 30 * 4], %sp	! %o6 = %sp, set up now so as to conform
					! to PCS and so be interruptible
	! ENABLE INTERRUPTS
	wr	%g6, %g4, %psr		! set new CWP and old ET
	nop
	nop
	nop
	
	! now load the rest of the context; we can be interrupted here, but
	! that is OK, other state will be preserved in that case...
#ifdef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
	! we are not preserving globals...
					! %o6 = %sp, already set up
	ld	[%g7 + 31 * 4], %o7	! "return" address
	retl				! %g1 still holds the return value
	mov	%g1, %o0

#else // load a maximal context
	mov	%g7, %o0		! original pointer was in %o0 anyway

	ldd	[%o0 + 18 * 4], %g2
	ldd	[%o0 + 20 * 4], %g4
	ldd	[%o0 + 22 * 4], %g6

	ld	[%o0 + 25 * 4], %o1	! %o0 = original pointer
	ldd	[%o0 + 26 * 4], %o2
	ldd	[%o0 + 28 * 4], %o4
					! %o6 = %sp, already set up
	ld	[%o0 + 31 * 4], %o7	! "return" address

	! %g1 still holds the return value; want to get this into %o0
	! and restore %g1 from the saved state;  %o0 is the state pointer:
					! g1 = R,   o0 = P
	xor	%o0, %g1, %g1		! g1 = R^P, o0 = P
	xor	%o0, %g1, %o0		! g1 = R^P, o0 = R
	xor	%o0, %g1, %g1		! g1 = P,   o0 = R all done
		
	retl
	ld	[%g1 + 17 * 4], %g1	! and finally restore %g1
#endif // !CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM
	
! ------------------------------------------------------------------------------
! end of context.S