File: splitmode.S

package info (click to toggle)
mol 0.9.61-6
  • links: PTS
  • area: contrib
  • in suites: woody
  • size: 6,140 kB
  • ctags: 8,491
  • sloc: ansic: 50,560; asm: 2,826; sh: 458; makefile: 373; perl: 165; lex: 135; yacc: 131
file content (260 lines) | stat: -rw-r--r-- 6,666 bytes parent folder | download
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
/* 
 *   Creation Date: <2000/07/11 03:38:32 samuel>
 *   Time-stamp: <2001/04/12 16:02:34 samuel>
 *   
 *	<splitmode.S>
 *	
 *	Handles splitmode (MSR_IR != MSR_DR)
 *   
 *   Copyright (C) 2000, 2001 Samuel Rydh (samuel@ibrium.se)
 *   
 *   This program 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
 *   
 */


	//////////////////////////////////////////////////////////////
	// Performance Measurements:
	//
	//	Load/store to NIP segment in splitmode:
	//
	//	OLD:	0.81 MHz	(on a 350 MHz G3)
	//	NEW:	 1.4 MHz

	/////////////////////////////////////////////////////////////
	// prepare_splitmode
	//
	//	srr0:	mac-nip
	//
	// Fill in splitmode segment register table. The segment
	// register containing xNIP is set up for instruction access
	// (if xNIP does not hold nip, an extra ISI exception will
	// occur). The instruction segment is protected from
	// data access through the use of a DBAT register. 
	//
	// It MUST be safe to call this function even if we
	// are *not* in splitmode.
	//
	// M: r0,r3-r5

prepare_splitmode:
	li	r3,0
	stw	r3,K_PREPARE_SPLITMODE(r1)

	lwz	r4,K_IN_SPLITMODE(r1)
	cmpwi	r4,kSplitAlgorithmFast
	bnelr

	// fill split mode table with data segment registers
	lwz	r3,K_SR_DATA(r1)		// physical addr
	addi	r4,r1,K_SPLIT_SR_BASE-4
	li	r5,16
	mfctr	r0				// save ctr in r0
	mtctr	r5
	addi	r3,r3,-4
1:	lwzu	r5,4(r3)
	oris	r5,r5,0x1000			// no-execute segment bit
	stwu	r5,4(r4)
	bdnz	1b
	mtctr	r0				// restore ctr
	
	// insert instruction mode segment
	mfsrr0	r0				// NIP
	rlwinm	r3,r0,0,0,3
	stw	r3,K_SPLIT_NIP_SEGMENT(r1)
	rlwinm	r3,r0,4+2,26,29			// r3 = offset, ((sr & 0xf000000) >> 28 ) * 4
	lwz	r5,K_SR_INST(r1)
	lwzx	r5,r3,r5			// segment register for instructions
	addi	r4,r1,K_SPLIT_SR_BASE
	stwx	r5,r3,r4
	
	// and protect it with DBAT0. 
	//
	// The supervisor valid bit must be cleared 
	// - we don't want to block get_opcode.

	rlwinm	r3,r0,0,0,3			// segment base
	ori	r3,r3,0x1ffd			// user valid bit | 256MB mask
	stw	r3,K_DBAT0U(r1)
	li	r4,0
	stw	r4,K_DBAT0L(r1)			// pp=0, wimg=0
	blr
	

	///////////////////////////////////////////////////////////////////
	// split_sr_no_execute
	//
	//	srr0:	mac-nip
	//
	// An instruction is to be fetched from one of the no-execute
	// segments. This function reinitializes the segment registers.
	//
	// M: r0, r3-r5

split_sr_no_execute:	
	mfsrr1	r4				// Guarded access or no-execute?
	rlwinm.	r0,r4,0,3,3
	beqlr+

	lwz	r3,K_IN_SPLITMODE(r1)		// in splitmode?
	cmpwi	r3,kSplitAlgorithmFast
	bnelr					// might be a guarded PTE

	mfsrr0	r5
	rlwinm	r3,r5,0,0,3			// segment
	lwz	r4,K_SPLIT_NIP_SEGMENT(r1)
	cmpw	r3,r4
	beqlr					// guarded PTE/mac-guarded segment

	bl	prepare_splitmode		// r0,r3-r5 (srr0=NIP)
	bl	reload_sr			// r3-r5
	b	exception_return


	//////////////////////////////////////////////////////////////////
	// splitmode_dsi
	//
	//	srr0:	NIP
	//
	// An DSI exception occured (DBAT protection violation).
	// That is, a load/store instruction targeted the segment
	// instructions was fetched from.
	//
	// Safe to modify: r0,r2-r5, (lr)

splitmode_dsi:
	lwz	r4,K_IN_SPLITMODE(r1)		// In splitmode?
	cmpwi	r4,kSplitAlgorithmFast
	bnelr+

	mfdsisr	r3				// DBAT/page protection violation?
	rlwinm.	r0,r3,0,4,4
	beqlr-					// If not, it does not concern us

	mfdar	r3				// Normal page protected exception?
	rlwinm	r3,r3,0,0,3			// data segment
	lwz	r4,K_SPLIT_NIP_SEGMENT(r1)	// the instruction segment?
	cmpw	r4,r3
	bnelr					// exit - not in the instruction segment

	//////////////////////////////////
	// Handle splitmode write, r4=seg#
	//////////////////////////////////

	// Store splitmode instruction 

	LI_PHYS( r2,split_store_patch )		// r2 = addr of split_store_patch
	lwz	r3,xINST_OPCODE(r1)
	stw     r3,0(r2)			// store instruction

	// Set segment registers to data only and start flushing the cache
	
	mfsrin	r0,r4				// r0 = old segment register
	dcbst   0,r2
	rlwinm	r3,r4,4+2,26,29			// offset = ((sr & 0xf000000) >> 28 ) * 4
	lwz	r5,K_SR_DATA(r1)
	lwzx	r3,r3,r5
	oris	r3,r3,0x4000			// Set supervisor key bit (Ks)
	mtsrin	r3,r4
	stw	r0,K_TMP_SCRATCH0(r1)		// save old segment register

	bl	secint_splitmode		// Setup secondary exception handler
	
	mfsrr0	r0				// r0 = NIP

	sync					// Finish flushing the cache
	icbi    0,r2
	sync					// an explicit isync should not be needed due to the rfi

	mtsrr0	r2				// The simplest thing is to do an RFI
	LOADI	r3,MSR_EE | MSR_PR | MSR_IR | MSR_SE
	lwz	r4,x_MSR(r1)
	stw	r0,xNIP(r1)
	andc	r4,r4,r3
	mtsrr1	r4
	
	xGPR_LOAD_RANGE	r2,r5			// Restore registers (except r1)
	xGPR_LOAD	r0,r1
	rfi

split_store_patch:
	nop	

	mtsprg0	r1				// restore MSR
	li	r1,MSR_ME
	mtmsr	r1
	isync
	mfsprg3	r1				// and stack pointer

	xGPR_SAVE_RANGE r2,r5
	mfsprg0	r2				// r1 - to be saved
	lwz	r3,xNIP(r1)			// restore srr0, srr1 and segment register
	stw	r2,xGPR1(r1)
	mtsrr0	r3
	lwz	r4,x_MSR(r1)
	stw	r0,xGPR0(r1)
	mtsrr1	r4
	lwz	r2,K_TMP_SCRATCH0(r1)
	mtsrin	r2,r3
	b	emulation_done


	//////////////////////////////////////////////////////////////////////
	// secint_splitmode:
	//	r1:		stack (sprg1 = old r1)
	//	r3:		vector index (sprg0 = old r3)
	//	srr0/srr1:	kernel nip/msr
	//
	// xGPR(0-5) are valid (unless this is a trace exception)

secint_splitmode:
	blrl

	lwz	r5,xNIP(r1)			// r5 = NIP, Restore NIP & MSR
	mtsrr0	r5
	lwz	r0,x_MSR(r1)
	mtsrr1	r0

	cmpwi	r3,0x300			// ** DSI **
	bne-	1f
	mfsrin	r2,r5				// r5 = NIP
	rlwinm	r2,r2,0,2,0			// Clear Ks [bit1] (supervisor key bit)
	mtsrin	r2,r5
	bl	save_middle_regs		// Note: If dsi_cont ever returns immediately,
	bl	check_io_page			// we will need to fix the segment registers before
	b	dsi_cont			// the last dsi_cont branch.

1:	lwz	r2,K_TMP_SCRATCH0(r1)		// We might return immediately...
	mtsrin	r2,r5
	
	cmpwi	r3,0x600			// ** Alignment **
	bne	2f
	bl	save_middle_regs
	b	alignment_cont
	
2:	cmpwi	r3,0x800			// ** FPU Unavailable **
	beq	fpu_cont
	cmpwi	r3,0xf20			// ** AltiVec Unavailable **
	beq	altivec_cont
	
	DEBUGGER_SAVE( 0x5918 )			// ERROR...

				
	////////////////////////////////////////////////////////////////////////
	// invalidate_splitmode( kernel_vars_t *kv )
	//
	// This function must be called whenever the segment registers are 
	// modified. A flag is set which will force a refresh of the slitmode 
	// segment registers (at mac context switch in). We could rewrite this 
	// in C but it might be better to keep things centralized.

GLOBAL_SYMBOL(r__invalidate_splitmode_sr):
	lwz	r4,K_IN_SPLITMODE(r3)
	cmpwi	r4,kSplitAlgorithmFast
	bnelr
1:	li	r5,1
	stw	r5,K_PREPARE_SPLITMODE(r3)
	blr