| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * PAL Firmware support | 
 | 3 |  * IA-64 Processor Programmers Reference Vol 2 | 
 | 4 |  * | 
 | 5 |  * Copyright (C) 1999 Don Dugger <don.dugger@intel.com> | 
 | 6 |  * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> | 
 | 7 |  * Copyright (C) 1999-2001, 2003 Hewlett-Packard Co | 
 | 8 |  *	David Mosberger <davidm@hpl.hp.com> | 
 | 9 |  *	Stephane Eranian <eranian@hpl.hp.com> | 
 | 10 |  * | 
 | 11 |  * 05/22/2000 eranian Added support for stacked register calls | 
 | 12 |  * 05/24/2000 eranian Added support for physical mode static calls | 
 | 13 |  */ | 
 | 14 |  | 
 | 15 | #include <asm/asmmacro.h> | 
 | 16 | #include <asm/processor.h> | 
 | 17 |  | 
 | 18 | 	.data | 
 | 19 | pal_entry_point: | 
 | 20 | 	data8 ia64_pal_default_handler | 
 | 21 | 	.text | 
 | 22 |  | 
 | 23 | /* | 
 | 24 |  * Set the PAL entry point address.  This could be written in C code, but we do it here | 
 | 25 |  * to keep it all in one module (besides, it's so trivial that it's | 
 | 26 |  * not a big deal). | 
 | 27 |  * | 
 | 28 |  * in0		Address of the PAL entry point (text address, NOT a function descriptor). | 
 | 29 |  */ | 
 | 30 | GLOBAL_ENTRY(ia64_pal_handler_init) | 
 | 31 | 	alloc r3=ar.pfs,1,0,0,0 | 
 | 32 | 	movl r2=pal_entry_point | 
 | 33 | 	;; | 
 | 34 | 	st8 [r2]=in0 | 
 | 35 | 	br.ret.sptk.many rp | 
 | 36 | END(ia64_pal_handler_init) | 
 | 37 |  | 
 | 38 | /* | 
 | 39 |  * Default PAL call handler.  This needs to be coded in assembly because it uses | 
 | 40 |  * the static calling convention, i.e., the RSE may not be used and calls are | 
 | 41 |  * done via "br.cond" (not "br.call"). | 
 | 42 |  */ | 
 | 43 | GLOBAL_ENTRY(ia64_pal_default_handler) | 
 | 44 | 	mov r8=-1 | 
 | 45 | 	br.cond.sptk.many rp | 
 | 46 | END(ia64_pal_default_handler) | 
 | 47 |  | 
 | 48 | /* | 
 | 49 |  * Make a PAL call using the static calling convention. | 
 | 50 |  * | 
 | 51 |  * in0         Index of PAL service | 
 | 52 |  * in1 - in3   Remaining PAL arguments | 
 | 53 |  * in4	       1 ==> clear psr.ic,  0 ==> don't clear psr.ic | 
 | 54 |  * | 
 | 55 |  */ | 
 | 56 | GLOBAL_ENTRY(ia64_pal_call_static) | 
 | 57 | 	.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(5) | 
 | 58 | 	alloc loc1 = ar.pfs,5,5,0,0 | 
 | 59 | 	movl loc2 = pal_entry_point | 
 | 60 | 1:	{ | 
 | 61 | 	  mov r28 = in0 | 
 | 62 | 	  mov r29 = in1 | 
 | 63 | 	  mov r8 = ip | 
 | 64 | 	} | 
 | 65 | 	;; | 
 | 66 | 	ld8 loc2 = [loc2]		// loc2 <- entry point | 
 | 67 | 	tbit.nz p6,p7 = in4, 0 | 
 | 68 | 	adds r8 = 1f-1b,r8 | 
 | 69 | 	mov loc4=ar.rsc			// save RSE configuration | 
 | 70 | 	;; | 
 | 71 | 	mov ar.rsc=0			// put RSE in enforced lazy, LE mode | 
 | 72 | 	mov loc3 = psr | 
 | 73 | 	mov loc0 = rp | 
 | 74 | 	.body | 
 | 75 | 	mov r30 = in2 | 
 | 76 |  | 
 | 77 | (p6)	rsm psr.i | psr.ic | 
 | 78 | 	mov r31 = in3 | 
 | 79 | 	mov b7 = loc2 | 
 | 80 |  | 
 | 81 | (p7)	rsm psr.i | 
 | 82 | 	;; | 
 | 83 | (p6)	srlz.i | 
 | 84 | 	mov rp = r8 | 
 | 85 | 	br.cond.sptk.many b7 | 
 | 86 | 1:	mov psr.l = loc3 | 
 | 87 | 	mov ar.rsc = loc4		// restore RSE configuration | 
 | 88 | 	mov ar.pfs = loc1 | 
 | 89 | 	mov rp = loc0 | 
 | 90 | 	;; | 
 | 91 | 	srlz.d				// seralize restoration of psr.l | 
 | 92 | 	br.ret.sptk.many b0 | 
 | 93 | END(ia64_pal_call_static) | 
 | 94 |  | 
 | 95 | /* | 
 | 96 |  * Make a PAL call using the stacked registers calling convention. | 
 | 97 |  * | 
 | 98 |  * Inputs: | 
 | 99 |  * 	in0         Index of PAL service | 
 | 100 |  * 	in2 - in3   Remaning PAL arguments | 
 | 101 |  */ | 
 | 102 | GLOBAL_ENTRY(ia64_pal_call_stacked) | 
 | 103 | 	.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(4) | 
 | 104 | 	alloc loc1 = ar.pfs,4,4,4,0 | 
 | 105 | 	movl loc2 = pal_entry_point | 
 | 106 |  | 
 | 107 | 	mov r28  = in0			// Index MUST be copied to r28 | 
 | 108 | 	mov out0 = in0			// AND in0 of PAL function | 
 | 109 | 	mov loc0 = rp | 
 | 110 | 	.body | 
 | 111 | 	;; | 
 | 112 | 	ld8 loc2 = [loc2]		// loc2 <- entry point | 
 | 113 | 	mov out1 = in1 | 
 | 114 | 	mov out2 = in2 | 
 | 115 | 	mov out3 = in3 | 
 | 116 | 	mov loc3 = psr | 
 | 117 | 	;; | 
 | 118 | 	rsm psr.i | 
 | 119 | 	mov b7 = loc2 | 
 | 120 | 	;; | 
 | 121 | 	br.call.sptk.many rp=b7		// now make the call | 
 | 122 | .ret0:	mov psr.l  = loc3 | 
 | 123 | 	mov ar.pfs = loc1 | 
 | 124 | 	mov rp = loc0 | 
 | 125 | 	;; | 
 | 126 | 	srlz.d				// serialize restoration of psr.l | 
 | 127 | 	br.ret.sptk.many b0 | 
 | 128 | END(ia64_pal_call_stacked) | 
 | 129 |  | 
 | 130 | /* | 
 | 131 |  * Make a physical mode PAL call using the static registers calling convention. | 
 | 132 |  * | 
 | 133 |  * Inputs: | 
 | 134 |  * 	in0         Index of PAL service | 
 | 135 |  * 	in2 - in3   Remaning PAL arguments | 
 | 136 |  * | 
 | 137 |  * PSR_LP, PSR_TB, PSR_ID, PSR_DA are never set by the kernel. | 
 | 138 |  * So we don't need to clear them. | 
 | 139 |  */ | 
 | 140 | #define PAL_PSR_BITS_TO_CLEAR							\ | 
 | 141 | 	(IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT  | IA64_PSR_DB | IA64_PSR_RT |	\ | 
 | 142 | 	 IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED |		\ | 
 | 143 | 	 IA64_PSR_DFL | IA64_PSR_DFH) | 
 | 144 |  | 
 | 145 | #define PAL_PSR_BITS_TO_SET							\ | 
 | 146 | 	(IA64_PSR_BN) | 
 | 147 |  | 
 | 148 |  | 
 | 149 | GLOBAL_ENTRY(ia64_pal_call_phys_static) | 
 | 150 | 	.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(4) | 
 | 151 | 	alloc loc1 = ar.pfs,4,7,0,0 | 
 | 152 | 	movl loc2 = pal_entry_point | 
 | 153 | 1:	{ | 
 | 154 | 	  mov r28  = in0		// copy procedure index | 
 | 155 | 	  mov r8   = ip			// save ip to compute branch | 
 | 156 | 	  mov loc0 = rp			// save rp | 
 | 157 | 	} | 
 | 158 | 	.body | 
 | 159 | 	;; | 
 | 160 | 	ld8 loc2 = [loc2]		// loc2 <- entry point | 
 | 161 | 	mov r29  = in1			// first argument | 
 | 162 | 	mov r30  = in2			// copy arg2 | 
 | 163 | 	mov r31  = in3			// copy arg3 | 
 | 164 | 	;; | 
 | 165 | 	mov loc3 = psr			// save psr | 
 | 166 | 	adds r8  = 1f-1b,r8		// calculate return address for call | 
 | 167 | 	;; | 
 | 168 | 	mov loc4=ar.rsc			// save RSE configuration | 
 | 169 | 	dep.z loc2=loc2,0,61		// convert pal entry point to physical | 
 | 170 | 	tpa r8=r8			// convert rp to physical | 
 | 171 | 	;; | 
 | 172 | 	mov b7 = loc2			// install target to branch reg | 
 | 173 | 	mov ar.rsc=0			// put RSE in enforced lazy, LE mode | 
 | 174 | 	movl r16=PAL_PSR_BITS_TO_CLEAR | 
 | 175 | 	movl r17=PAL_PSR_BITS_TO_SET | 
 | 176 | 	;; | 
 | 177 | 	or loc3=loc3,r17		// add in psr the bits to set | 
 | 178 | 	;; | 
 | 179 | 	andcm r16=loc3,r16		// removes bits to clear from psr | 
 | 180 | 	br.call.sptk.many rp=ia64_switch_mode_phys | 
 | 181 | .ret1:	mov rp = r8			// install return address (physical) | 
 | 182 | 	mov loc5 = r19 | 
 | 183 | 	mov loc6 = r20 | 
 | 184 | 	br.cond.sptk.many b7 | 
 | 185 | 1: | 
 | 186 | 	mov ar.rsc=0			// put RSE in enforced lazy, LE mode | 
 | 187 | 	mov r16=loc3			// r16= original psr | 
 | 188 | 	mov r19=loc5 | 
 | 189 | 	mov r20=loc6 | 
 | 190 | 	br.call.sptk.many rp=ia64_switch_mode_virt // return to virtual mode | 
 | 191 | .ret2: | 
 | 192 | 	mov psr.l = loc3		// restore init PSR | 
 | 193 |  | 
 | 194 | 	mov ar.pfs = loc1 | 
 | 195 | 	mov rp = loc0 | 
 | 196 | 	;; | 
 | 197 | 	mov ar.rsc=loc4			// restore RSE configuration | 
 | 198 | 	srlz.d				// seralize restoration of psr.l | 
 | 199 | 	br.ret.sptk.many b0 | 
 | 200 | END(ia64_pal_call_phys_static) | 
 | 201 |  | 
 | 202 | /* | 
 | 203 |  * Make a PAL call using the stacked registers in physical mode. | 
 | 204 |  * | 
 | 205 |  * Inputs: | 
 | 206 |  * 	in0         Index of PAL service | 
 | 207 |  * 	in2 - in3   Remaning PAL arguments | 
 | 208 |  */ | 
 | 209 | GLOBAL_ENTRY(ia64_pal_call_phys_stacked) | 
 | 210 | 	.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(5) | 
 | 211 | 	alloc	loc1 = ar.pfs,5,7,4,0 | 
 | 212 | 	movl	loc2 = pal_entry_point | 
 | 213 | 1:	{ | 
 | 214 | 	  mov r28  = in0		// copy procedure index | 
 | 215 | 	  mov loc0 = rp		// save rp | 
 | 216 | 	} | 
 | 217 | 	.body | 
 | 218 | 	;; | 
 | 219 | 	ld8 loc2 = [loc2]		// loc2 <- entry point | 
 | 220 | 	mov out0 = in0		// first argument | 
 | 221 | 	mov out1 = in1		// copy arg2 | 
 | 222 | 	mov out2 = in2		// copy arg3 | 
 | 223 | 	mov out3 = in3		// copy arg3 | 
 | 224 | 	;; | 
 | 225 | 	mov loc3 = psr		// save psr | 
 | 226 | 	;; | 
 | 227 | 	mov loc4=ar.rsc			// save RSE configuration | 
 | 228 | 	dep.z loc2=loc2,0,61		// convert pal entry point to physical | 
 | 229 | 	;; | 
 | 230 | 	mov ar.rsc=0			// put RSE in enforced lazy, LE mode | 
 | 231 | 	movl r16=PAL_PSR_BITS_TO_CLEAR | 
 | 232 | 	movl r17=PAL_PSR_BITS_TO_SET | 
 | 233 | 	;; | 
 | 234 | 	or loc3=loc3,r17		// add in psr the bits to set | 
 | 235 | 	mov b7 = loc2			// install target to branch reg | 
 | 236 | 	;; | 
 | 237 | 	andcm r16=loc3,r16		// removes bits to clear from psr | 
 | 238 | 	br.call.sptk.many rp=ia64_switch_mode_phys | 
 | 239 | .ret6: | 
 | 240 | 	mov loc5 = r19 | 
 | 241 | 	mov loc6 = r20 | 
 | 242 | 	br.call.sptk.many rp=b7		// now make the call | 
 | 243 | .ret7: | 
 | 244 | 	mov ar.rsc=0			// put RSE in enforced lazy, LE mode | 
 | 245 | 	mov r16=loc3			// r16= original psr | 
 | 246 | 	mov r19=loc5 | 
 | 247 | 	mov r20=loc6 | 
 | 248 | 	br.call.sptk.many rp=ia64_switch_mode_virt	// return to virtual mode | 
 | 249 |  | 
 | 250 | .ret8:	mov psr.l  = loc3		// restore init PSR | 
 | 251 | 	mov ar.pfs = loc1 | 
 | 252 | 	mov rp = loc0 | 
 | 253 | 	;; | 
 | 254 | 	mov ar.rsc=loc4			// restore RSE configuration | 
 | 255 | 	srlz.d				// seralize restoration of psr.l | 
 | 256 | 	br.ret.sptk.many b0 | 
 | 257 | END(ia64_pal_call_phys_stacked) | 
 | 258 |  | 
 | 259 | /* | 
 | 260 |  * Save scratch fp scratch regs which aren't saved in pt_regs already (fp10-fp15). | 
 | 261 |  * | 
 | 262 |  * NOTE: We need to do this since firmware (SAL and PAL) may use any of the scratch | 
 | 263 |  * regs fp-low partition. | 
 | 264 |  * | 
 | 265 |  * Inputs: | 
 | 266 |  *      in0	Address of stack storage for fp regs | 
 | 267 |  */ | 
 | 268 | GLOBAL_ENTRY(ia64_save_scratch_fpregs) | 
 | 269 | 	alloc r3=ar.pfs,1,0,0,0 | 
 | 270 | 	add r2=16,in0 | 
 | 271 | 	;; | 
 | 272 | 	stf.spill [in0] = f10,32 | 
 | 273 | 	stf.spill [r2]  = f11,32 | 
 | 274 | 	;; | 
 | 275 | 	stf.spill [in0] = f12,32 | 
 | 276 | 	stf.spill [r2]  = f13,32 | 
 | 277 | 	;; | 
 | 278 | 	stf.spill [in0] = f14,32 | 
 | 279 | 	stf.spill [r2]  = f15,32 | 
 | 280 | 	br.ret.sptk.many rp | 
 | 281 | END(ia64_save_scratch_fpregs) | 
 | 282 |  | 
 | 283 | /* | 
 | 284 |  * Load scratch fp scratch regs (fp10-fp15) | 
 | 285 |  * | 
 | 286 |  * Inputs: | 
 | 287 |  *      in0	Address of stack storage for fp regs | 
 | 288 |  */ | 
 | 289 | GLOBAL_ENTRY(ia64_load_scratch_fpregs) | 
 | 290 | 	alloc r3=ar.pfs,1,0,0,0 | 
 | 291 | 	add r2=16,in0 | 
 | 292 | 	;; | 
 | 293 | 	ldf.fill  f10 = [in0],32 | 
 | 294 | 	ldf.fill  f11 = [r2],32 | 
 | 295 | 	;; | 
 | 296 | 	ldf.fill  f12 = [in0],32 | 
 | 297 | 	ldf.fill  f13 = [r2],32 | 
 | 298 | 	;; | 
 | 299 | 	ldf.fill  f14 = [in0],32 | 
 | 300 | 	ldf.fill  f15 = [r2],32 | 
 | 301 | 	br.ret.sptk.many rp | 
 | 302 | END(ia64_load_scratch_fpregs) |