summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/arch/sparc64/cpustate.h
blob: 0c276bfd111a10eb051c9502cbd835aaf5e19167 (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
/*
 *   Save/restore CPU state macros
 *
 *   Copyright (C) 2015 Mark Cave-Ayland (mark.cave-ayland@ilande.co.uk>)
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   version 2
 *
 */

/* State size for context (see below) */
#define CONTEXT_STATE_SIZE 0x510

/* Stack size for context (allocated inline of the context stack) */
#define CONTEXT_STACK_SIZE 0x2000

/*
 * SAVE_CPU_STATE and RESTORE_CPU_STATE are macros used to enable a context switch
 * to C to occur within the MMU I/D TLB miss handlers.
 *
 * Because these handlers are called on a TLB miss, we cannot use flushw to store
 * processor window state on the stack, as the memory areas used by each window's
 * stack pointer may not be in the TLB, causing recursive TLB miss traps.
 *
 * For this reason, we save window state by manually rotating the window registers
 * and saving their contents (along with other vital registers) into a special
 * tlb_handler_stack defined above which is guaranteed to be locked in the TLB, and
 * so won't cause issues with trap recursion.
 *
 * Once this process is complete, we remain in a TL=0, CWP=0 state (with IE=1 to allow
 * window fill/spill traps if required), switch to our safe tlb_handler_stack and 
 * invoke the miss handler.
 */

#define SAVE_CPU_WINDOW_STATE(type) \
	/* Save window state into context at %g1 */ \
	rdpr	%cwp, %g7; \
	stx	%g7, [%g1]; \
	rdpr	%cansave, %g7; \
	stx	%g7, [%g1 + 0x8]; \
	rdpr	%canrestore, %g7; \
	stx	%g7, [%g1 + 0x10]; \
	rdpr	%otherwin, %g7; \
	stx	%g7, [%g1 + 0x18]; \
	rdpr	%wstate, %g7; \
	stx	%g7, [%g1 + 0x20]; \
	rdpr	%cleanwin, %g7; \
	stx	%g7, [%g1 + 0x28]; \
	\
	stx	%o0, [%g1 + 0x30]; \
	stx	%o1, [%g1 + 0x38]; \
	stx	%o2, [%g1 + 0x40]; \
	stx	%o3, [%g1 + 0x48]; \
	stx	%o4, [%g1 + 0x50]; \
	stx	%o5, [%g1 + 0x58]; \
	stx	%o6, [%g1 + 0x60]; \
	stx	%o7, [%g1 + 0x68]; \
	\
	rdpr	%pstate, %g7; \
	stx	%g7, [%g1 + 0x70]; \
	rd	%y, %g7; \
	stx	%g7, [%g1 + 0x78]; \
	rd	%fprs, %g7; \
	stx	%g7, [%g1 + 0x80]; \
	rdpr    %tl, %g7; \
	stx     %g7, [%g1 + 0x88]; \
	\
	/* Now iterate through all of the windows saving all l and i registers */ \
	add	%g1, 0x90, %g5; \
	\
	/* Get the number of windows in %g6 */ \
	rdpr	%ver, %g6; \
	and	%g6, 0xf, %g6; \
	\
	mov     %g6, %g4; \
	inc     %g4; \
	\
	/* Starting cwp in g7 */ \
	rdpr    %cwp, %g7; \
	\
save_cpu_window_##type: \
	wrpr    %g7, %cwp; \
	stx	%l0, [%g5]; \
	stx	%l1, [%g5 + 0x8]; \
	stx	%l2, [%g5 + 0x10]; \
	stx	%l3, [%g5 + 0x18]; \
	stx	%l4, [%g5 + 0x20]; \
	stx	%l5, [%g5 + 0x28]; \
	stx	%l6, [%g5 + 0x30]; \
	stx	%l7, [%g5 + 0x38]; \
	stx	%i0, [%g5 + 0x40]; \
	stx	%i1, [%g5 + 0x48]; \
	stx	%i2, [%g5 + 0x50]; \
	stx	%i3, [%g5 + 0x58]; \
	stx	%i4, [%g5 + 0x60]; \
	stx	%i5, [%g5 + 0x68]; \
	stx	%i6, [%g5 + 0x70]; \
	stx	%i7, [%g5 + 0x78]; \
	dec	%g7; \
	and	%g7, %g6, %g7; \
	subcc	%g4, 1, %g4; \
	bne	save_cpu_window_##type; \
	 add	%g5, 0x80, %g5; \
	\
	/* For 8 windows with 16 registers to save in the window, memory required \
	is 16*8*8 = 0x400 bytes */ \
	\
	/* Now we should be in window 0 so update the other window registers */ \
	rdpr	%ver, %g6; \
	and	%g6, 0xf, %g6; \
	dec	%g6; \
	wrpr	%g6, %cansave; \
	\
	wrpr	%g0, %cleanwin; \
	wrpr	%g0, %canrestore; \
	wrpr	%g0, %otherwin; \

	
#define SAVE_CPU_TRAP_STATE(type) \
	/* Save trap state into context at %g1 */ \
	add	%g1, 0x490, %g5; \
	mov	4, %g6; \
	\
save_trap_state_##type: \
	deccc	%g6; \
	wrpr	%g6, %tl; \
	rdpr	%tpc, %g7; \
	stx	%g7, [%g5]; \
	rdpr	%tnpc, %g7; \
	stx	%g7, [%g5 + 0x8]; \
	rdpr	%tstate, %g7; \
	stx	%g7, [%g5 + 0x10]; \
	rdpr	%tt, %g7; \
	stx	%g7, [%g5 + 0x18]; \
	bne	save_trap_state_##type; \
	 add	%g5, 0x20, %g5; \
	\
	/* For 4 trap levels with 4 registers, memory required is \
	4*8*4 = 0x80 bytes */

/* Save all state into context at %g1 */
#define SAVE_CPU_STATE(type) \
	SAVE_CPU_WINDOW_STATE(type); \
	SAVE_CPU_TRAP_STATE(type);


#define RESTORE_CPU_WINDOW_STATE(type) \
	/* Restore window state from context at %g1 */ \
	\
	/* Get the number of windows in %g6 */ \
	rdpr	%ver, %g6; \
	and	%g6, 0xf, %g6; \
	\
	mov	%g6, %g4; \
	inc	%g4; \
	\
	/* Set starting window */ \
	ldx	[%g1], %g7; \
	\
	/* Now iterate through all of the windows restoring all l and i registers */ \
	add	%g1, 0x90, %g5; \
	\
restore_cpu_window_##type: \
	wrpr	%g7, %cwp; \
	ldx	[%g5], %l0; \
	ldx	[%g5 + 0x8], %l1; \
	ldx	[%g5 + 0x10], %l2; \
	ldx	[%g5 + 0x18], %l3; \
	ldx	[%g5 + 0x20], %l4; \
	ldx	[%g5 + 0x28], %l5; \
	ldx	[%g5 + 0x30], %l6; \
	ldx	[%g5 + 0x38], %l7; \
	ldx	[%g5 + 0x40], %i0; \
	ldx	[%g5 + 0x48], %i1; \
	ldx	[%g5 + 0x50], %i2; \
	ldx	[%g5 + 0x58], %i3; \
	ldx	[%g5 + 0x60], %i4; \
	ldx	[%g5 + 0x68], %i5; \
	ldx	[%g5 + 0x70], %i6; \
	ldx	[%g5 + 0x78], %i7; \
	dec	%g7; \
	and	%g7, %g6, %g7; \
	subcc	%g4, 1, %g4; \
	bne	restore_cpu_window_##type; \
	 add	%g5, 0x80, %g5; \
	\
	/* Restore the window registers to their original value */ \
	ldx	[%g1], %g7; \
	wrpr	%g7, %cwp; \
	ldx	[%g1 + 0x8], %g7; \
	wrpr	%g7, %cansave; \
	ldx	[%g1 + 0x10], %g7; \
	wrpr	%g7, %canrestore; \
	ldx	[%g1 + 0x18], %g7; \
	wrpr	%g7, %otherwin; \
	ldx	[%g1 + 0x20], %g7; \
	wrpr	%g7, %wstate; \
	ldx	[%g1 + 0x28], %g7; \
	wrpr	%g7, %cleanwin; \
	\
	ldx	[%g1 + 0x30], %o0; \
	ldx	[%g1 + 0x38], %o1; \
	ldx	[%g1 + 0x40], %o2; \
	ldx	[%g1 + 0x48], %o3; \
	ldx	[%g1 + 0x50], %o4; \
	ldx	[%g1 + 0x58], %o5; \
	ldx	[%g1 + 0x60], %o6; \
	ldx	[%g1 + 0x68], %o7; \
	\
	ldx	[%g1 + 0x70], %g7; \
	wrpr	%g7, %pstate; \
	ldx	[%g1 + 0x78], %g7; \
	wr	%g7, 0, %y; \
	ldx	[%g1 + 0x80], %g7; \
	wr	%g7, 0, %fprs; \


#define RESTORE_CPU_TRAP_STATE(type) \
	/* Restore trap state from context at %g1 */ \
	add	%g1, 0x490, %g5; \
	mov	4, %g6; \
	\
restore_trap_state_##type: \
	deccc	%g6; \
	wrpr	%g6, %tl; \
	ldx	[%g5], %g7; \
	wrpr	%g7, %tpc; \
	ldx	[%g5 + 0x8], %g7; \
	wrpr	%g7, %tnpc; \
	ldx	[%g5 + 0x10], %g7; \
	wrpr	%g7, %tstate; \
	ldx	[%g5 + 0x18], %g7; \
	wrpr	%g7, %tt; \
	bne	restore_trap_state_##type; \
	 add	%g5, 0x20, %g5; \
	\
	ldx	[%g1 + 0x88], %g7; \
	wrpr	%g7, %tl

/* Restore all state from context at %g1 */
#define RESTORE_CPU_STATE(type) \
	RESTORE_CPU_WINDOW_STATE(type); \
	RESTORE_CPU_TRAP_STATE(type);