| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * PAL Firmware support |
| * IA-64 Processor Programmers Reference Vol 2 |
| * |
| * Copyright (C) 1999 Don Dugger <don.dugger@intel.com> |
| * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> |
| * Copyright (C) 1999-2001, 2003 Hewlett-Packard Co |
| * David Mosberger <davidm@hpl.hp.com> |
| * Stephane Eranian <eranian@hpl.hp.com> |
| * |
| * 05/22/2000 eranian Added support for stacked register calls |
| * 05/24/2000 eranian Added support for physical mode static calls |
| */ |
| |
| #include <linux/export.h> |
| #include <asm/asmmacro.h> |
| #include <asm/processor.h> |
| |
| .data |
| pal_entry_point: |
| data8 ia64_pal_default_handler |
| .text |
| |
| /* |
| * Set the PAL entry point address. This could be written in C code, but we |
| * do it here to keep it all in one module (besides, it's so trivial that it's |
| * not a big deal). |
| * |
| * in0 Address of the PAL entry point (text address, NOT a function |
| * descriptor). |
| */ |
| GLOBAL_ENTRY(ia64_pal_handler_init) |
| alloc r3=ar.pfs,1,0,0,0 |
| movl r2=pal_entry_point |
| ;; |
| st8 [r2]=in0 |
| br.ret.sptk.many rp |
| END(ia64_pal_handler_init) |
| |
| /* |
| * Default PAL call handler. This needs to be coded in assembly because it |
| * uses the static calling convention, i.e., the RSE may not be used and |
| * calls are done via "br.cond" (not "br.call"). |
| */ |
| GLOBAL_ENTRY(ia64_pal_default_handler) |
| mov r8=-1 |
| br.cond.sptk.many rp |
| END(ia64_pal_default_handler) |
| |
| /* |
| * Make a PAL call using the static calling convention. |
| * |
| * in0 Index of PAL service |
| * in1 - in3 Remaining PAL arguments |
| */ |
| GLOBAL_ENTRY(ia64_pal_call_static) |
| .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(4) |
| alloc loc1 = ar.pfs,4,5,0,0 |
| movl loc2 = pal_entry_point |
| 1: { |
| mov r28 = in0 |
| mov r29 = in1 |
| mov r8 = ip |
| } |
| ;; |
| ld8 loc2 = [loc2] // loc2 <- entry point |
| adds r8 = 1f-1b,r8 |
| mov loc4=ar.rsc // save RSE configuration |
| ;; |
| mov ar.rsc=0 // put RSE in enforced lazy, LE mode |
| mov loc3 = psr |
| mov loc0 = rp |
| .body |
| mov r30 = in2 |
| |
| mov r31 = in3 |
| mov b7 = loc2 |
| |
| rsm psr.i |
| ;; |
| mov rp = r8 |
| br.cond.sptk.many b7 |
| 1: mov psr.l = loc3 |
| mov ar.rsc = loc4 // restore RSE configuration |
| mov ar.pfs = loc1 |
| mov rp = loc0 |
| ;; |
| srlz.d // serialize restoration of psr.l |
| br.ret.sptk.many b0 |
| END(ia64_pal_call_static) |
| EXPORT_SYMBOL(ia64_pal_call_static) |
| |
| /* |
| * Make a PAL call using the stacked registers calling convention. |
| * |
| * Inputs: |
| * in0 Index of PAL service |
| * in2 - in3 Remaining PAL arguments |
| */ |
| GLOBAL_ENTRY(ia64_pal_call_stacked) |
| .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(4) |
| alloc loc1 = ar.pfs,4,4,4,0 |
| movl loc2 = pal_entry_point |
| |
| mov r28 = in0 // Index MUST be copied to r28 |
| mov out0 = in0 // AND in0 of PAL function |
| mov loc0 = rp |
| .body |
| ;; |
| ld8 loc2 = [loc2] // loc2 <- entry point |
| mov out1 = in1 |
| mov out2 = in2 |
| mov out3 = in3 |
| mov loc3 = psr |
| ;; |
| rsm psr.i |
| mov b7 = loc2 |
| ;; |
| br.call.sptk.many rp=b7 // now make the call |
| .ret0: mov psr.l = loc3 |
| mov ar.pfs = loc1 |
| mov rp = loc0 |
| ;; |
| srlz.d // serialize restoration of psr.l |
| br.ret.sptk.many b0 |
| END(ia64_pal_call_stacked) |
| EXPORT_SYMBOL(ia64_pal_call_stacked) |
| |
| /* |
| * Make a physical mode PAL call using the static registers calling convention. |
| * |
| * Inputs: |
| * in0 Index of PAL service |
| * in2 - in3 Remaining PAL arguments |
| * |
| * PSR_LP, PSR_TB, PSR_ID, PSR_DA are never set by the kernel. |
| * So we don't need to clear them. |
| */ |
| #define PAL_PSR_BITS_TO_CLEAR \ |
| (IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_DB | IA64_PSR_RT |\ |
| IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED | \ |
| IA64_PSR_DFL | IA64_PSR_DFH) |
| |
| #define PAL_PSR_BITS_TO_SET \ |
| (IA64_PSR_BN) |
| |
| |
| GLOBAL_ENTRY(ia64_pal_call_phys_static) |
| .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(4) |
| alloc loc1 = ar.pfs,4,7,0,0 |
| movl loc2 = pal_entry_point |
| 1: { |
| mov r28 = in0 // copy procedure index |
| mov r8 = ip // save ip to compute branch |
| mov loc0 = rp // save rp |
| } |
| .body |
| ;; |
| ld8 loc2 = [loc2] // loc2 <- entry point |
| mov r29 = in1 // first argument |
| mov r30 = in2 // copy arg2 |
| mov r31 = in3 // copy arg3 |
| ;; |
| mov loc3 = psr // save psr |
| adds r8 = 1f-1b,r8 // calculate return address for call |
| ;; |
| mov loc4=ar.rsc // save RSE configuration |
| dep.z loc2=loc2,0,61 // convert pal entry point to physical |
| tpa r8=r8 // convert rp to physical |
| ;; |
| mov b7 = loc2 // install target to branch reg |
| mov ar.rsc=0 // put RSE in enforced lazy, LE mode |
| movl r16=PAL_PSR_BITS_TO_CLEAR |
| movl r17=PAL_PSR_BITS_TO_SET |
| ;; |
| or loc3=loc3,r17 // add in psr the bits to set |
| ;; |
| andcm r16=loc3,r16 // removes bits to clear from psr |
| br.call.sptk.many rp=ia64_switch_mode_phys |
| mov rp = r8 // install return address (physical) |
| mov loc5 = r19 |
| mov loc6 = r20 |
| br.cond.sptk.many b7 |
| 1: |
| mov ar.rsc=0 // put RSE in enforced lazy, LE mode |
| mov r16=loc3 // r16= original psr |
| mov r19=loc5 |
| mov r20=loc6 |
| br.call.sptk.many rp=ia64_switch_mode_virt // return to virtual mode |
| mov psr.l = loc3 // restore init PSR |
| |
| mov ar.pfs = loc1 |
| mov rp = loc0 |
| ;; |
| mov ar.rsc=loc4 // restore RSE configuration |
| srlz.d // serialize restoration of psr.l |
| br.ret.sptk.many b0 |
| END(ia64_pal_call_phys_static) |
| EXPORT_SYMBOL(ia64_pal_call_phys_static) |
| |
| /* |
| * Make a PAL call using the stacked registers in physical mode. |
| * |
| * Inputs: |
| * in0 Index of PAL service |
| * in2 - in3 Remaining PAL arguments |
| */ |
| GLOBAL_ENTRY(ia64_pal_call_phys_stacked) |
| .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(5) |
| alloc loc1 = ar.pfs,5,7,4,0 |
| movl loc2 = pal_entry_point |
| 1: { |
| mov r28 = in0 // copy procedure index |
| mov loc0 = rp // save rp |
| } |
| .body |
| ;; |
| ld8 loc2 = [loc2] // loc2 <- entry point |
| mov loc3 = psr // save psr |
| ;; |
| mov loc4=ar.rsc // save RSE configuration |
| dep.z loc2=loc2,0,61 // convert pal entry point to physical |
| ;; |
| mov ar.rsc=0 // put RSE in enforced lazy, LE mode |
| movl r16=PAL_PSR_BITS_TO_CLEAR |
| movl r17=PAL_PSR_BITS_TO_SET |
| ;; |
| or loc3=loc3,r17 // add in psr the bits to set |
| mov b7 = loc2 // install target to branch reg |
| ;; |
| andcm r16=loc3,r16 // removes bits to clear from psr |
| br.call.sptk.many rp=ia64_switch_mode_phys |
| |
| mov out0 = in0 // first argument |
| mov out1 = in1 // copy arg2 |
| mov out2 = in2 // copy arg3 |
| mov out3 = in3 // copy arg3 |
| mov loc5 = r19 |
| mov loc6 = r20 |
| |
| br.call.sptk.many rp=b7 // now make the call |
| |
| mov ar.rsc=0 // put RSE in enforced lazy, LE mode |
| mov r16=loc3 // r16= original psr |
| mov r19=loc5 |
| mov r20=loc6 |
| br.call.sptk.many rp=ia64_switch_mode_virt // return to virtual mode |
| |
| mov psr.l = loc3 // restore init PSR |
| mov ar.pfs = loc1 |
| mov rp = loc0 |
| ;; |
| mov ar.rsc=loc4 // restore RSE configuration |
| srlz.d // serialize restoration of psr.l |
| br.ret.sptk.many b0 |
| END(ia64_pal_call_phys_stacked) |
| EXPORT_SYMBOL(ia64_pal_call_phys_stacked) |
| |
| /* |
| * Save scratch fp scratch regs which aren't saved in pt_regs already |
| * (fp10-fp15). |
| * |
| * NOTE: We need to do this since firmware (SAL and PAL) may use any of the |
| * scratch regs fp-low partition. |
| * |
| * Inputs: |
| * in0 Address of stack storage for fp regs |
| */ |
| GLOBAL_ENTRY(ia64_save_scratch_fpregs) |
| alloc r3=ar.pfs,1,0,0,0 |
| add r2=16,in0 |
| ;; |
| stf.spill [in0] = f10,32 |
| stf.spill [r2] = f11,32 |
| ;; |
| stf.spill [in0] = f12,32 |
| stf.spill [r2] = f13,32 |
| ;; |
| stf.spill [in0] = f14,32 |
| stf.spill [r2] = f15,32 |
| br.ret.sptk.many rp |
| END(ia64_save_scratch_fpregs) |
| EXPORT_SYMBOL(ia64_save_scratch_fpregs) |
| |
| /* |
| * Load scratch fp scratch regs (fp10-fp15) |
| * |
| * Inputs: |
| * in0 Address of stack storage for fp regs |
| */ |
| GLOBAL_ENTRY(ia64_load_scratch_fpregs) |
| alloc r3=ar.pfs,1,0,0,0 |
| add r2=16,in0 |
| ;; |
| ldf.fill f10 = [in0],32 |
| ldf.fill f11 = [r2],32 |
| ;; |
| ldf.fill f12 = [in0],32 |
| ldf.fill f13 = [r2],32 |
| ;; |
| ldf.fill f14 = [in0],32 |
| ldf.fill f15 = [r2],32 |
| br.ret.sptk.many rp |
| END(ia64_load_scratch_fpregs) |
| EXPORT_SYMBOL(ia64_load_scratch_fpregs) |