xref: /linux/arch/alpha/kernel/entry.S (revision 278de0f04816c97f701456b72af4f59d36440249)
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * arch/alpha/kernel/entry.S
4 *
5 * Kernel entry-points.
6 */
7
8#include <asm/asm-offsets.h>
9#include <asm/thread_info.h>
10#include <asm/pal.h>
11#include <asm/errno.h>
12#include <asm/unistd.h>
13#include <linux/errno.h>
14
15	.text
16	.set noat
17	.cfi_sections	.debug_frame
18
19.macro	CFI_START_OSF_FRAME	func
20	.align	4
21	.globl	\func
22	.type	\func,@function
23\func:
24	.cfi_startproc simple
25	.cfi_return_column 64
26	.cfi_def_cfa	$sp, 48
27	.cfi_rel_offset	64, 8
28	.cfi_rel_offset	$gp, 16
29	.cfi_rel_offset	$16, 24
30	.cfi_rel_offset	$17, 32
31	.cfi_rel_offset	$18, 40
32.endm
33
34.macro	CFI_END_OSF_FRAME	func
35	.cfi_endproc
36	.size	\func, . - \func
37.endm
38
39.macro LOCKDEP_HARDIRQS_ON_RESTORE psreg
40#ifdef CONFIG_PROVE_LOCKING
41	/* a0 = saved PS */
42	ldq	$16, \psreg
43
44	/* a1 = callsite IP for lockdep */
45	lda	$17, 1f
46
47	jsr	$26, lockdep_on_restore
48	ldgp	$gp, 0($26)
491:
50#endif
51.endm
52
53/*
54 * This defines the normal kernel pt-regs layout.
55 *
56 * regs 9-15 preserved by C code
57 * regs 16-18 saved by PAL-code
58 * regs 29-30 saved and set up by PAL-code
59 * JRP - Save regs 16-18 in a special area of the stack, so that
60 * the palcode-provided values are available to the signal handler.
61 */
62
63.macro	SAVE_ALL
64	subq	$sp, SP_OFF, $sp
65	.cfi_adjust_cfa_offset	SP_OFF
66	stq	$0, 0($sp)
67	stq	$1, 8($sp)
68	stq	$2, 16($sp)
69	stq	$3, 24($sp)
70	stq	$4, 32($sp)
71	stq	$28, 144($sp)
72	.cfi_rel_offset	$0, 0
73	.cfi_rel_offset $1, 8
74	.cfi_rel_offset	$2, 16
75	.cfi_rel_offset	$3, 24
76	.cfi_rel_offset	$4, 32
77	.cfi_rel_offset	$28, 144
78	lda	$2, alpha_mv
79	stq	$5, 40($sp)
80	stq	$6, 48($sp)
81	stq	$7, 56($sp)
82	stq	$8, 64($sp)
83	stq	$19, 72($sp)
84	stq	$20, 80($sp)
85	stq	$21, 88($sp)
86	ldq	$2, HAE_CACHE($2)
87	stq	$22, 96($sp)
88	stq	$23, 104($sp)
89	stq	$24, 112($sp)
90	stq	$25, 120($sp)
91	stq	$26, 128($sp)
92	stq	$27, 136($sp)
93	stq	$2, 152($sp)
94	stq	$16, 160($sp)
95	stq	$17, 168($sp)
96	stq	$18, 176($sp)
97	.cfi_rel_offset	$5, 40
98	.cfi_rel_offset	$6, 48
99	.cfi_rel_offset	$7, 56
100	.cfi_rel_offset	$8, 64
101	.cfi_rel_offset $19, 72
102	.cfi_rel_offset	$20, 80
103	.cfi_rel_offset	$21, 88
104	.cfi_rel_offset $22, 96
105	.cfi_rel_offset	$23, 104
106	.cfi_rel_offset	$24, 112
107	.cfi_rel_offset	$25, 120
108	.cfi_rel_offset	$26, 128
109	.cfi_rel_offset	$27, 136
110.endm
111
112.macro	RESTORE_ALL
113	lda	$19, alpha_mv
114	ldq	$0, 0($sp)
115	ldq	$1, 8($sp)
116	ldq	$2, 16($sp)
117	ldq	$3, 24($sp)
118	ldq	$21, 152($sp)
119	ldq	$20, HAE_CACHE($19)
120	ldq	$4, 32($sp)
121	ldq	$5, 40($sp)
122	ldq	$6, 48($sp)
123	ldq	$7, 56($sp)
124	subq	$20, $21, $20
125	ldq	$8, 64($sp)
126	beq	$20, 99f
127	ldq	$20, HAE_REG($19)
128	stq	$21, HAE_CACHE($19)
129	stq	$21, 0($20)
13099:	ldq	$19, 72($sp)
131	ldq	$20, 80($sp)
132	ldq	$21, 88($sp)
133	ldq	$22, 96($sp)
134	ldq	$23, 104($sp)
135	ldq	$24, 112($sp)
136	ldq	$25, 120($sp)
137	ldq	$26, 128($sp)
138	ldq	$27, 136($sp)
139	ldq	$28, 144($sp)
140	addq	$sp, SP_OFF, $sp
141	.cfi_restore	$0
142	.cfi_restore	$1
143	.cfi_restore	$2
144	.cfi_restore	$3
145	.cfi_restore	$4
146	.cfi_restore	$5
147	.cfi_restore	$6
148	.cfi_restore	$7
149	.cfi_restore	$8
150	.cfi_restore	$19
151	.cfi_restore	$20
152	.cfi_restore	$21
153	.cfi_restore	$22
154	.cfi_restore	$23
155	.cfi_restore	$24
156	.cfi_restore	$25
157	.cfi_restore	$26
158	.cfi_restore	$27
159	.cfi_restore	$28
160	.cfi_adjust_cfa_offset	-SP_OFF
161.endm
162
163.macro	DO_SWITCH_STACK
164	bsr	$1, do_switch_stack
165	.cfi_adjust_cfa_offset	SWITCH_STACK_SIZE
166	.cfi_rel_offset	$9, 0
167	.cfi_rel_offset	$10, 8
168	.cfi_rel_offset	$11, 16
169	.cfi_rel_offset	$12, 24
170	.cfi_rel_offset	$13, 32
171	.cfi_rel_offset	$14, 40
172	.cfi_rel_offset	$15, 48
173.endm
174
175.macro	UNDO_SWITCH_STACK
176	bsr	$1, undo_switch_stack
177	.cfi_restore	$9
178	.cfi_restore	$10
179	.cfi_restore	$11
180	.cfi_restore	$12
181	.cfi_restore	$13
182	.cfi_restore	$14
183	.cfi_restore	$15
184	.cfi_adjust_cfa_offset	-SWITCH_STACK_SIZE
185.endm
186
187/*
188 * Non-syscall kernel entry points.
189 */
190
191CFI_START_OSF_FRAME entInt
192	SAVE_ALL
193	lda	$8, 0x3fff
194	lda	$26, ret_from_exception
195	bic	$sp, $8, $8
196	mov	$sp, $19
197	jsr	$31, do_entInt
198CFI_END_OSF_FRAME entInt
199
200CFI_START_OSF_FRAME entArith
201	SAVE_ALL
202	lda	$8, 0x3fff
203	lda	$26, ret_from_exception
204	bic	$sp, $8, $8
205	mov	$sp, $18
206	jsr	$31, do_entArith
207CFI_END_OSF_FRAME entArith
208
209CFI_START_OSF_FRAME entMM
210	SAVE_ALL
211/* save $9 - $15 so the inline exception code can manipulate them.  */
212	subq	$sp, 64, $sp
213	.cfi_adjust_cfa_offset	64
214	stq	$9, 0($sp)
215	stq	$10, 8($sp)
216	stq	$11, 16($sp)
217	stq	$12, 24($sp)
218	stq	$13, 32($sp)
219	stq	$14, 40($sp)
220	stq	$15, 48($sp)
221	.cfi_rel_offset	$9, 0
222	.cfi_rel_offset	$10, 8
223	.cfi_rel_offset	$11, 16
224	.cfi_rel_offset	$12, 24
225	.cfi_rel_offset	$13, 32
226	.cfi_rel_offset	$14, 40
227	.cfi_rel_offset	$15, 48
228	addq	$sp, 64, $19
229/* handle the fault */
230	lda	$8, 0x3fff
231	bic	$sp, $8, $8
232	jsr	$26, do_page_fault
233/* reload the registers after the exception code played.  */
234	ldq	$9, 0($sp)
235	ldq	$10, 8($sp)
236	ldq	$11, 16($sp)
237	ldq	$12, 24($sp)
238	ldq	$13, 32($sp)
239	ldq	$14, 40($sp)
240	ldq	$15, 48($sp)
241	addq	$sp, 64, $sp
242	.cfi_restore	$9
243	.cfi_restore	$10
244	.cfi_restore	$11
245	.cfi_restore	$12
246	.cfi_restore	$13
247	.cfi_restore	$14
248	.cfi_restore	$15
249	.cfi_adjust_cfa_offset	-64
250/* finish up the syscall as normal.  */
251	br	ret_from_exception
252CFI_END_OSF_FRAME entMM
253
254CFI_START_OSF_FRAME entIF
255	SAVE_ALL
256	lda	$8, 0x3fff
257	lda	$26, ret_from_exception
258	bic	$sp, $8, $8
259	mov	$sp, $17
260	jsr	$31, do_entIF
261CFI_END_OSF_FRAME entIF
262
263CFI_START_OSF_FRAME entUna
264	lda	$sp, -256($sp)
265	.cfi_adjust_cfa_offset	256
266	stq	$0, 0($sp)
267	.cfi_rel_offset	$0, 0
268	.cfi_remember_state
269	ldq	$0, 256($sp)	/* get PS */
270	stq	$1, 8($sp)
271	stq	$2, 16($sp)
272	stq	$3, 24($sp)
273	and	$0, 8, $0		/* user mode? */
274	stq	$4, 32($sp)
275	bne	$0, entUnaUser	/* yup -> do user-level unaligned fault */
276	stq	$5, 40($sp)
277	stq	$6, 48($sp)
278	stq	$7, 56($sp)
279	stq	$8, 64($sp)
280	stq	$9, 72($sp)
281	stq	$10, 80($sp)
282	stq	$11, 88($sp)
283	stq	$12, 96($sp)
284	stq	$13, 104($sp)
285	stq	$14, 112($sp)
286	stq	$15, 120($sp)
287	/* 16-18 PAL-saved */
288	stq	$19, 152($sp)
289	stq	$20, 160($sp)
290	stq	$21, 168($sp)
291	stq	$22, 176($sp)
292	stq	$23, 184($sp)
293	stq	$24, 192($sp)
294	stq	$25, 200($sp)
295	stq	$26, 208($sp)
296	stq	$27, 216($sp)
297	stq	$28, 224($sp)
298	mov	$sp, $19
299	stq	$gp, 232($sp)
300	.cfi_rel_offset	$1, 1*8
301	.cfi_rel_offset	$2, 2*8
302	.cfi_rel_offset	$3, 3*8
303	.cfi_rel_offset	$4, 4*8
304	.cfi_rel_offset	$5, 5*8
305	.cfi_rel_offset	$6, 6*8
306	.cfi_rel_offset	$7, 7*8
307	.cfi_rel_offset	$8, 8*8
308	.cfi_rel_offset	$9, 9*8
309	.cfi_rel_offset	$10, 10*8
310	.cfi_rel_offset	$11, 11*8
311	.cfi_rel_offset	$12, 12*8
312	.cfi_rel_offset	$13, 13*8
313	.cfi_rel_offset	$14, 14*8
314	.cfi_rel_offset	$15, 15*8
315	.cfi_rel_offset	$19, 19*8
316	.cfi_rel_offset	$20, 20*8
317	.cfi_rel_offset	$21, 21*8
318	.cfi_rel_offset	$22, 22*8
319	.cfi_rel_offset	$23, 23*8
320	.cfi_rel_offset	$24, 24*8
321	.cfi_rel_offset	$25, 25*8
322	.cfi_rel_offset	$26, 26*8
323	.cfi_rel_offset	$27, 27*8
324	.cfi_rel_offset	$28, 28*8
325	.cfi_rel_offset	$29, 29*8
326	lda	$8, 0x3fff
327	stq	$31, 248($sp)
328	bic	$sp, $8, $8
329	jsr	$26, do_entUna
330	ldq	$0, 0($sp)
331	ldq	$1, 8($sp)
332	ldq	$2, 16($sp)
333	ldq	$3, 24($sp)
334	ldq	$4, 32($sp)
335	ldq	$5, 40($sp)
336	ldq	$6, 48($sp)
337	ldq	$7, 56($sp)
338	ldq	$8, 64($sp)
339	ldq	$9, 72($sp)
340	ldq	$10, 80($sp)
341	ldq	$11, 88($sp)
342	ldq	$12, 96($sp)
343	ldq	$13, 104($sp)
344	ldq	$14, 112($sp)
345	ldq	$15, 120($sp)
346	/* 16-18 PAL-saved */
347	ldq	$19, 152($sp)
348	ldq	$20, 160($sp)
349	ldq	$21, 168($sp)
350	ldq	$22, 176($sp)
351	ldq	$23, 184($sp)
352	ldq	$24, 192($sp)
353	ldq	$25, 200($sp)
354	ldq	$26, 208($sp)
355	ldq	$27, 216($sp)
356	ldq	$28, 224($sp)
357	ldq	$gp, 232($sp)
358	lda	$sp, 256($sp)
359	.cfi_restore	$1
360	.cfi_restore	$2
361	.cfi_restore	$3
362	.cfi_restore	$4
363	.cfi_restore	$5
364	.cfi_restore	$6
365	.cfi_restore	$7
366	.cfi_restore	$8
367	.cfi_restore	$9
368	.cfi_restore	$10
369	.cfi_restore	$11
370	.cfi_restore	$12
371	.cfi_restore	$13
372	.cfi_restore	$14
373	.cfi_restore	$15
374	.cfi_restore	$19
375	.cfi_restore	$20
376	.cfi_restore	$21
377	.cfi_restore	$22
378	.cfi_restore	$23
379	.cfi_restore	$24
380	.cfi_restore	$25
381	.cfi_restore	$26
382	.cfi_restore	$27
383	.cfi_restore	$28
384	.cfi_restore	$29
385	.cfi_adjust_cfa_offset	-256
386	LOCKDEP_HARDIRQS_ON_RESTORE SP_OFF($sp)
387	call_pal PAL_rti
388
389	.align	4
390entUnaUser:
391	.cfi_restore_state
392	ldq	$0, 0($sp)	/* restore original $0 */
393	lda	$sp, 256($sp)	/* pop entUna's stack frame */
394	.cfi_restore	$0
395	.cfi_adjust_cfa_offset	-256
396	SAVE_ALL		/* setup normal kernel stack */
397	lda	$sp, -64($sp)
398	.cfi_adjust_cfa_offset	64
399	stq	$9, 0($sp)
400	stq	$10, 8($sp)
401	stq	$11, 16($sp)
402	stq	$12, 24($sp)
403	stq	$13, 32($sp)
404	stq	$14, 40($sp)
405	stq	$15, 48($sp)
406	.cfi_rel_offset	$9, 0
407	.cfi_rel_offset	$10, 8
408	.cfi_rel_offset	$11, 16
409	.cfi_rel_offset	$12, 24
410	.cfi_rel_offset	$13, 32
411	.cfi_rel_offset	$14, 40
412	.cfi_rel_offset	$15, 48
413	lda	$8, 0x3fff
414	addq	$sp, 64, $19
415	bic	$sp, $8, $8
416	jsr	$26, do_entUnaUser
417	ldq	$9, 0($sp)
418	ldq	$10, 8($sp)
419	ldq	$11, 16($sp)
420	ldq	$12, 24($sp)
421	ldq	$13, 32($sp)
422	ldq	$14, 40($sp)
423	ldq	$15, 48($sp)
424	lda	$sp, 64($sp)
425	.cfi_restore	$9
426	.cfi_restore	$10
427	.cfi_restore	$11
428	.cfi_restore	$12
429	.cfi_restore	$13
430	.cfi_restore	$14
431	.cfi_restore	$15
432	.cfi_adjust_cfa_offset	-64
433	br	ret_from_exception
434CFI_END_OSF_FRAME entUna
435
436CFI_START_OSF_FRAME entDbg
437	SAVE_ALL
438	lda	$8, 0x3fff
439	lda	$26, ret_from_exception
440	bic	$sp, $8, $8
441	mov	$sp, $16
442	jsr	$31, do_entDbg
443CFI_END_OSF_FRAME entDbg
444
445
446/*
447 * The system call entry point is special.  Most importantly, it looks
448 * like a function call to userspace as far as clobbered registers.  We
449 * do preserve the argument registers (for syscall restarts) and $26
450 * (for leaf syscall functions).
451 *
452 * So much for theory.  We don't take advantage of this yet.
453 *
454 * Note that a0-a2 are not saved by PALcode as with the other entry points.
455 *
456 * Alpha syscall ABI uses:
457 *   - r0 for return value
458 *   - r19 ("a3") as error indicator (0=success, 1=error; r0 holds errno)
459 *
460 * For seccomp/ptrace/generic syscall helpers we track the syscall
461 * number separately:
462 *   - regs->r1: current (mutable) syscall number (may be changed or set to -1)
463 *
464 */
465
466	.align	4
467	.globl	entSys
468	.type	entSys, @function
469	.cfi_startproc simple
470	.cfi_return_column 64
471	.cfi_def_cfa	$sp, 48
472	.cfi_rel_offset	64, 8
473	.cfi_rel_offset	$gp, 16
474entSys:
475	SAVE_ALL
476	ldq	$1, 0($sp)	/* syscall nr from saved r0 */
477	stq	$1, 8($sp)	/* regs->r1 = shadow syscall nr */
478
479	mov	$0, $8
480	call_pal PAL_rdusp
481	stq	$0, PT_REGS_USP($sp)
482	mov	$8, $0
483
484	lda	$8, 0x3fff
485	bic	$sp, $8, $8
486
487	stq	$16, SP_OFF+24($sp)
488	stq	$17, SP_OFF+32($sp)
489	stq	$18, SP_OFF+40($sp)
490
491	mov	$0, $1
492	lda	$16, 7
493	call_pal PAL_swpipl
494	mov	$1, $0
495	mov	$sp, $16
496	mov	$0, $17	 /* pv = selected syscall function */
497
498	DO_SWITCH_STACK
499	jsr	$26, alpha_syscall_enter_select
500	ldgp	$gp, 0($26)
501	UNDO_SWITCH_STACK
502
503	/*
504	 * C returned syscall function pointer in $0.
505	 * It also stored SKIP in TI_SYSCALL_META if dispatch is skipped.
506	 */
507	lda	$8, 0x3fff
508	bic	$sp, $8, $8
509	ldq	$3, TI_SYSCALL_META($8)
510	lda	$6, ALPHA_SYSCALL_META_SKIP
511	and	$3, $6, $6
512	bne	$6, skip_dispatch
513
514
515	mov	$0, $27
516	ldq	$16, SP_OFF+24($sp)
517	ldq	$17, SP_OFF+32($sp)
518	ldq	$18, SP_OFF+40($sp)
519	ldq	$19, 72($sp)
520	ldq	$20, 80($sp)
521	ldq	$21, 88($sp)
522
523	jsr	$26, ($27), sys_ni_syscall
524	ldgp	$gp, 0($26)
525
526skip_dispatch:
527	mov	$0, $17		/* raw ret; ignored if SKIP is set */
528	mov	$sp, $16	/* regs */
529
530	DO_SWITCH_STACK
531	jsr	$26, alpha_finish_syscall_to_user_mode
532	ldgp	$gp, 0($26)
533	UNDO_SWITCH_STACK
534
535restore_all:
536	lda	$8, 0x3fff
537	bic	$sp, $8, $8
538	ldl	$2, TI_STATUS($8)
539	and	$2, TS_SAVED_FP | TS_RESTORE_FP, $3
540	bne	$3, restore_fpu
541restore_other:
542	.cfi_remember_state
543	RESTORE_ALL
544	call_pal PAL_rti
545
546ret_to_user_from_syscall:
547	lda	$8, 0x3fff
548	bic	$sp, $8, $8
549
550	mov	$sp, $16
551	DO_SWITCH_STACK
552	jsr	$26, alpha_syscall_exit_to_user_mode
553	ldgp	$gp, 0($26)
554	UNDO_SWITCH_STACK
555	br	restore_all
556
557	.align	4
558	.globl	ret_from_sys_call
559ret_from_sys_call:
560	ldq	$0, SP_OFF($sp)
561	and	$0, 8, $0
562	beq	$0, ret_to_kernel
563	br	ret_to_user_from_syscall
564
565ret_from_exception:
566	ldq	$0, SP_OFF($sp)
567	and	$0, 8, $0
568	beq	$0, ret_to_kernel
569	br	ret_to_user_from_exception
570
571ret_to_user_from_exception:
572
573	mov	$sp, $16
574	DO_SWITCH_STACK
575	jsr	$26, alpha_exit_to_user_mode
576	ldgp	$gp, 0($26)
577	UNDO_SWITCH_STACK
578	br	restore_all
579
580ret_to_kernel:
581	.cfi_restore_state
582	lda	$16, 7
583	call_pal PAL_swpipl
584	LOCKDEP_HARDIRQS_ON_RESTORE SP_OFF($sp)
585	br restore_other
586
587CFI_END_OSF_FRAME entSys
588
589/*
590 * Save and restore the switch stack -- aka the balance of the user context.
591 */
592
593	.align	4
594	.type	do_switch_stack, @function
595	.cfi_startproc simple
596	.cfi_return_column 64
597	.cfi_def_cfa $sp, 0
598	.cfi_register 64, $1
599do_switch_stack:
600	lda	$sp, -SWITCH_STACK_SIZE($sp)
601	.cfi_adjust_cfa_offset	SWITCH_STACK_SIZE
602	stq	$9, 0($sp)
603	stq	$10, 8($sp)
604	stq	$11, 16($sp)
605	stq	$12, 24($sp)
606	stq	$13, 32($sp)
607	stq	$14, 40($sp)
608	stq	$15, 48($sp)
609	stq	$26, 56($sp)
610	ret	$31, ($1), 1
611	.cfi_endproc
612	.size	do_switch_stack, .-do_switch_stack
613
614	.align	4
615	.type	undo_switch_stack, @function
616	.cfi_startproc simple
617	.cfi_def_cfa $sp, 0
618	.cfi_register 64, $1
619undo_switch_stack:
620	ldq	$9, 0($sp)
621	ldq	$10, 8($sp)
622	ldq	$11, 16($sp)
623	ldq	$12, 24($sp)
624	ldq	$13, 32($sp)
625	ldq	$14, 40($sp)
626	ldq	$15, 48($sp)
627	ldq	$26, 56($sp)
628	lda	$sp, SWITCH_STACK_SIZE($sp)
629	ret	$31, ($1), 1
630	.cfi_endproc
631	.size	undo_switch_stack, .-undo_switch_stack
632
633#define FR(n) n * 8 + TI_FP($8)
634	.align	4
635	.globl	__save_fpu
636	.type	__save_fpu, @function
637__save_fpu:
638#define V(n) stt	$f##n, FR(n)
639	V( 0); V( 1); V( 2); V( 3)
640	V( 4); V( 5); V( 6); V( 7)
641	V( 8); V( 9); V(10); V(11)
642	V(12); V(13); V(14); V(15)
643	V(16); V(17); V(18); V(19)
644	V(20); V(21); V(22); V(23)
645	V(24); V(25); V(26); V(27)
646	mf_fpcr	$f0		# get fpcr
647	V(28); V(29); V(30)
648	stt	$f0, FR(31)	# save fpcr in slot of $f31
649	ldt	$f0, FR(0)	# don't let "__save_fpu" change fp state.
650	ret
651#undef V
652	.size	__save_fpu, .-__save_fpu
653
654	.align	4
655restore_fpu:
656	and	$3, TS_RESTORE_FP, $3
657	bic	$2, TS_SAVED_FP | TS_RESTORE_FP, $2
658	beq	$3, 1f
659#define V(n) ldt	$f##n, FR(n)
660	ldt	$f30, FR(31)	# get saved fpcr
661	V( 0); V( 1); V( 2); V( 3)
662	mt_fpcr	$f30		# install saved fpcr
663	V( 4); V( 5); V( 6); V( 7)
664	V( 8); V( 9); V(10); V(11)
665	V(12); V(13); V(14); V(15)
666	V(16); V(17); V(18); V(19)
667	V(20); V(21); V(22); V(23)
668	V(24); V(25); V(26); V(27)
669	V(28); V(29); V(30)
6701:	stl $2, TI_STATUS($8)
671	br restore_other
672#undef V
673
674/*
675 * The meat of the context switch code.
676 */
677	.align	4
678	.globl	alpha_switch_to
679	.type	alpha_switch_to, @function
680	.cfi_startproc
681alpha_switch_to:
682	DO_SWITCH_STACK
683	ldl	$1, TI_STATUS($8)
684	and	$1, TS_RESTORE_FP, $3
685	bne	$3, 1f
686	or	$1, TS_RESTORE_FP | TS_SAVED_FP, $2
687	and	$1, TS_SAVED_FP, $3
688	stl	$2, TI_STATUS($8)
689	bne	$3, 1f
690	bsr	$26, __save_fpu
6911:
692	call_pal PAL_swpctx
693	lda	$8, 0x3fff
694	UNDO_SWITCH_STACK
695	bic	$sp, $8, $8
696	mov	$17, $0
697	ret
698	.cfi_endproc
699	.size	alpha_switch_to, .-alpha_switch_to
700
701/*
702 * New processes begin life here.
703 */
704
705	.globl	ret_from_fork
706	.align	4
707	.ent	ret_from_fork
708ret_from_fork:
709	lda	$26, ret_to_user_from_exception
710	mov	$17, $16
711	jmp	$31, schedule_tail
712.end ret_from_fork
713
714/*
715 * ... and new kernel threads - here
716 */
717	.align 4
718	.globl	ret_from_kernel_thread
719	.ent	ret_from_kernel_thread
720ret_from_kernel_thread:
721	mov	$17, $16
722	jsr	$26, schedule_tail
723	mov	$9, $27
724	mov	$10, $16
725	jsr	$26, ($9)
726	br	$31, ret_to_user_from_exception
727.end ret_from_kernel_thread
728
729
730/*
731 * Special system calls.  Most of these are special in that they either
732 * have to play switch_stack games.
733 */
734
735.macro	fork_like name
736	.align	4
737	.globl	alpha_\name
738	.ent	alpha_\name
739alpha_\name:
740	.prologue 0
741	bsr	$1, do_switch_stack
742	// NB: if anyone adds preemption, this block will need to be protected
743	ldl	$1, TI_STATUS($8)
744	and	$1, TS_SAVED_FP, $3
745	or	$1, TS_SAVED_FP, $2
746	bne	$3, 1f
747	stl	$2, TI_STATUS($8)
748	bsr	$26, __save_fpu
7491:
750	jsr	$26, sys_\name
751	ldq	$26, 56($sp)
752	lda	$sp, SWITCH_STACK_SIZE($sp)
753	ret
754.end	alpha_\name
755.endm
756
757fork_like fork
758fork_like vfork
759fork_like clone
760fork_like clone3
761
762.macro	sigreturn_like name
763	.align	4
764	.globl	sys_\name
765	.ent	sys_\name
766sys_\name:
767	.prologue 0
768	mov	$sp, $10
769	lda	$sp, -SWITCH_STACK_SIZE($sp)
770	jsr	$26, do_\name
7711:	br	$1, undo_switch_stack
772	br	ret_from_sys_call
773.end sys_\name
774.endm
775
776sigreturn_like sigreturn
777sigreturn_like rt_sigreturn
778
779	.align	4
780	.globl	alpha_syscall_zero
781	.ent	alpha_syscall_zero
782alpha_syscall_zero:
783	.prologue 0
784	/* Special because it needs to do something opposite to
785	   force_successful_syscall_return().  We use the saved
786	   syscall number for that, zero meaning "not an error".
787	   That works nicely, but for real syscall 0 we need to
788	   make sure that this logics doesn't get confused.
789	   Store a non-zero there - -ENOSYS we need in register
790	   for our return value will do just fine.
791	  */
792	lda	$0, -ENOSYS
793	unop
794	stq	$0, 0($sp)
795	ret
796.end alpha_syscall_zero
797