mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
acb15c85de
We found an issue in pal.S. According to the software runtime SPEC, The caller's output registers do not need to be preserved for caller. The callee may reuse input registers for any other purpose within the procedure. in ia64_pal_call_phys_stacked, input registers are copied to output registers before call into ia64_switch_mode_phys, then used to call into PAL. This assumes output registers are preserved in ia64_switch_mode_phys, which may not be true. In this particular case, ia64_switch_mode_phys alloc a null frame , and mask off psr.i. If an interrupt comes at this small window, or an MCA comes inside the procedure, output registers maybe changed, then the pal call may got some staled input registers. This patch moves the copies from input to output after ia64_switch_mode_phys to follow the software runtime convention. It also removed some unused labels in ia64_pal_call_phys_stacked. Signed-off-by: Zou Nan hai <nanhai.zou@intel.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
302 lines
7.5 KiB
ArmAsm
302 lines
7.5 KiB
ArmAsm
/*
|
|
* 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 <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
|
|
* in4 1 ==> clear psr.ic, 0 ==> don't clear psr.ic
|
|
*
|
|
*/
|
|
GLOBAL_ENTRY(ia64_pal_call_static)
|
|
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(5)
|
|
alloc loc1 = ar.pfs,5,5,0,0
|
|
movl loc2 = pal_entry_point
|
|
1: {
|
|
mov r28 = in0
|
|
mov r29 = in1
|
|
mov r8 = ip
|
|
}
|
|
;;
|
|
ld8 loc2 = [loc2] // loc2 <- entry point
|
|
tbit.nz p6,p7 = in4, 0
|
|
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
|
|
|
|
(p6) rsm psr.i | psr.ic
|
|
mov r31 = in3
|
|
mov b7 = loc2
|
|
|
|
(p7) rsm psr.i
|
|
;;
|
|
(p6) srlz.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 // seralize restoration of psr.l
|
|
br.ret.sptk.many b0
|
|
END(ia64_pal_call_static)
|
|
|
|
/*
|
|
* Make a PAL call using the stacked registers calling convention.
|
|
*
|
|
* Inputs:
|
|
* in0 Index of PAL service
|
|
* in2 - in3 Remaning 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)
|
|
|
|
/*
|
|
* Make a physical mode PAL call using the static registers calling convention.
|
|
*
|
|
* Inputs:
|
|
* in0 Index of PAL service
|
|
* in2 - in3 Remaning 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
|
|
.ret1: 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
|
|
.ret2:
|
|
mov psr.l = loc3 // restore init PSR
|
|
|
|
mov ar.pfs = loc1
|
|
mov rp = loc0
|
|
;;
|
|
mov ar.rsc=loc4 // restore RSE configuration
|
|
srlz.d // seralize restoration of psr.l
|
|
br.ret.sptk.many b0
|
|
END(ia64_pal_call_phys_static)
|
|
|
|
/*
|
|
* Make a PAL call using the stacked registers in physical mode.
|
|
*
|
|
* Inputs:
|
|
* in0 Index of PAL service
|
|
* in2 - in3 Remaning 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 // seralize restoration of psr.l
|
|
br.ret.sptk.many b0
|
|
END(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)
|
|
|
|
/*
|
|
* 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)
|