summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/arch/sparc32/switch.S
blob: d5b1b659d23f2eb9dfa136581a9c814039ad7cbc (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
#define __ASSEMBLY
#include "psr.h"
#include "asm/asi.h"
#define ASI_BP ASI_M_BYPASS
#define REGWIN_SZ   0x40

	.globl	__switch_context, __switch_context_nosave, __exit_context, halt

	.text
	.align	4

#define STACKFRAME_SZ     0x60

/* These are just handy. */
#define _SV	save	%sp, -STACKFRAME_SZ, %sp
#define _RS     restore

#define FLUSH_ALL_KERNEL_WINDOWS \
	_SV; _SV; _SV; _SV; _SV; _SV; _SV; \
	_RS; _RS; _RS; _RS; _RS; _RS; _RS;

/*
 * Switch execution context
 * This saves registers in the stack, then
 * switches the stack, and restores everything from the new stack.
 * This function takes no argument. New stack pointer is
 * taken from global variable __context, and old stack pointer
 * is also saved to __context. This way we can just jump to
 * this routine to get back to the original context.
 */

__switch_context:
        FLUSH_ALL_KERNEL_WINDOWS
	/* Save everything in stack */
        st      %fp, [%fp + 120 -144]
        add     %fp, -144, %fp
        st      %g1, [%fp + 4]
        st      %g2, [%fp + 8]
        st      %g3, [%fp + 12]
        st      %g4, [%fp + 16]
        st      %g5, [%fp + 20]
        st      %g6, [%fp + 24]
        st      %g7, [%fp + 28]

        st      %o0, [%fp + 32]
        st      %o1, [%fp + 36]
        st      %o2, [%fp + 40]
        st      %o3, [%fp + 44]
        st      %o4, [%fp + 48]
        st      %o5, [%fp + 52]
        st      %sp, [%fp + 56]
        st      %o7, [%fp + 60]

        st      %l0, [%fp + 64]
        st      %l1, [%fp + 68]
        st      %l2, [%fp + 72]
        st      %l3, [%fp + 76]
        st      %l4, [%fp + 80]
        st      %l5, [%fp + 84]
        st      %l6, [%fp + 88]
        st      %l7, [%fp + 92]

        st      %i0, [%fp + 96]
        st      %i1, [%fp + 100]
        st      %i2, [%fp + 104]
        st      %i3, [%fp + 108]
        st      %i4, [%fp + 112]
        st      %i5, [%fp + 116]
        st      %i7, [%fp + 124]

        /* ctx->return_address:  Return to caller */
        st      %o7, [%fp + 128]

	/* Interrupts are not allowed... */

	/* Turn on Supervisor, EnableFloating, and all the PIL bits.
	 * Also puts us in register window zero with traps off.
	 */
#if 0
	set	(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
	wr	%g2, 0x0, %psr
#endif
        set     __context, %g1
        /* Swap ctx pointer with %fp and jump*/
        ba     __set_context
         swap      [%g1], %fp
__switch_context_nosave:
        set     __context, %g1
        /* load %fp from ctx pointer */
        ld      [%g1], %fp
__set_context:
	/* Load all registers */
        /* offset 0: %g0, no need to load */
        ld      [%fp + 4], %g1
        ld      [%fp + 8], %g2
        ld      [%fp + 12], %g3
        ld      [%fp + 16], %g4
        ld      [%fp + 20], %g5
        ld      [%fp + 24], %g6
        ld      [%fp + 28], %g7

        /* offset 32: %o0, loaded from ctx->param */
        ld      [%fp + 36], %o1
        ld      [%fp + 40], %o2
        ld      [%fp + 44], %o3
        ld      [%fp + 48], %o4
        ld      [%fp + 52], %o5
        ld      [%fp + 56], %sp
        /* offset 60: %o7, loaded from ctx->return_addr */

        ld      [%fp + 64], %l0
        ld      [%fp + 68], %l1
        ld      [%fp + 72], %l2
        ld      [%fp + 76], %l3
        ld      [%fp + 80], %l4
        ld      [%fp + 84], %l5
        ld      [%fp + 88], %l6
        ld      [%fp + 92], %l7

        ld      [%fp + 96], %i0
        ld      [%fp + 100], %i1
        ld      [%fp + 104], %i2
        ld      [%fp + 108], %i3
        ld      [%fp + 112], %i4
        ld      [%fp + 116], %i5
        ld      [%fp + 124], %i7

        /* ctx->return_addr */
        ld      [%fp + 136], %o7

        /* ctx->param */
        ld      [%fp + 140], %o0

        /* ctx->pc, save %g1 to %y and load to %g1 */
        mov     %g1, %y
        ld      [%fp + 128], %g1
        /* %fp last */
        ld      [%fp + 120], %fp
        /* Finally, get the new %pc from %g1 and restore %g1*/
        jmp     %g1
         mov    %y, %g1

        FLUSH_ALL_KERNEL_WINDOWS
__exit_context:
	/* Get back to the original context */
	call	__switch_context
	 nop

	/* We get here if the other context attempt to switch to this
	 * dead context. This should not happen. */

halt:
	b	halt
	 nop