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) |