mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-28 21:35:13 -04:00
Fix an issue in call_o32() where the upper 32-bit half of incoming n64
stack arguments is fetched and used for outgoing o32 stack arguments on
big-endian platforms.
This code was adapted from arch/mips/dec/prom/call_o32.S which was meant
for a little-endian platform only and therefore using 32-bit loads from
64-bit stack slot locations holding incoming stack arguments resulted in
correct values being retrieved for data that is expected to be 32-bit.
This works on little-endian platforms where the lower 32-bit half of the
64-bit value is located at every 64-bit stack slot location. However on
big-endian platforms the lower 32-bit half is instead located at offset
4 from every 64-bit stack slot location.
So to fix the issue the offset of 4 would have to be used on big-endian
platforms only, or alternatively a 64-bit load from the 64-bit stack
slot location can be used across the board, as the subsequent 32-bit
store to the corresponding outgoing stack argument slot will correctly
truncate the value and cause no unpredictable result. We already take
advantage of this architectural feature for the incoming arguments held
in $a6 and $a7 registers, since the o32 wrapper does not know how many
incoming arguments there are and consequently propagates incoming data
which may not be 32-bit.
Since this code is generally supposed to be used with the stack located
in cached memory there is no extra overhead expected for 64-bit loads as
opposed to 32-bit ones, so pick this variant for code simplicity.
Fixes: 231a35d372 ("[MIPS] RM: Collected changes")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
105 lines
2.8 KiB
ArmAsm
105 lines
2.8 KiB
ArmAsm
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* O32 interface for the 64 (or N32) ABI.
|
|
*
|
|
* Copyright (C) 2002, 2014 Maciej W. Rozycki
|
|
*/
|
|
|
|
#include <asm/asm.h>
|
|
#include <asm/regdef.h>
|
|
|
|
/* O32 register size. */
|
|
#define O32_SZREG 4
|
|
/* Maximum number of arguments supported. Must be even! */
|
|
#define O32_ARGC 32
|
|
/* Number of static registers we save. */
|
|
#define O32_STATC 11
|
|
/* Argument area frame size. */
|
|
#define O32_ARGSZ (O32_SZREG * O32_ARGC)
|
|
/* Static register save area frame size. */
|
|
#define O32_STATSZ (SZREG * O32_STATC)
|
|
/* Stack pointer register save area frame size. */
|
|
#define O32_SPSZ SZREG
|
|
/* Combined area frame size. */
|
|
#define O32_FRAMESZ (O32_ARGSZ + O32_SPSZ + O32_STATSZ)
|
|
/* Switched stack frame size. */
|
|
#define O32_NFRAMESZ (O32_ARGSZ + O32_SPSZ)
|
|
|
|
.text
|
|
|
|
/*
|
|
* O32 function call dispatcher, for interfacing 32-bit ROM routines.
|
|
*
|
|
* The standard 64 (N32) calling sequence is supported, with a0 holding
|
|
* a function pointer, a1 a pointer to the new stack to call the
|
|
* function with or 0 if no stack switching is requested, a2-a7 -- the
|
|
* function call's first six arguments, and the stack -- the remaining
|
|
* arguments (up to O32_ARGC, including a2-a7). Static registers, gp
|
|
* and fp are preserved, v0 holds the result. This code relies on the
|
|
* called o32 function for sp and ra restoration and this dispatcher has
|
|
* to be placed in a KSEGx (or KUSEG) address space. Any pointers
|
|
* passed have to point to addresses within one of these spaces as well.
|
|
*/
|
|
NESTED(call_o32, O32_FRAMESZ, ra)
|
|
REG_SUBU sp,O32_FRAMESZ
|
|
|
|
REG_S ra,O32_FRAMESZ-1*SZREG(sp)
|
|
REG_S fp,O32_FRAMESZ-2*SZREG(sp)
|
|
REG_S gp,O32_FRAMESZ-3*SZREG(sp)
|
|
REG_S s7,O32_FRAMESZ-4*SZREG(sp)
|
|
REG_S s6,O32_FRAMESZ-5*SZREG(sp)
|
|
REG_S s5,O32_FRAMESZ-6*SZREG(sp)
|
|
REG_S s4,O32_FRAMESZ-7*SZREG(sp)
|
|
REG_S s3,O32_FRAMESZ-8*SZREG(sp)
|
|
REG_S s2,O32_FRAMESZ-9*SZREG(sp)
|
|
REG_S s1,O32_FRAMESZ-10*SZREG(sp)
|
|
REG_S s0,O32_FRAMESZ-11*SZREG(sp)
|
|
|
|
move jp,a0
|
|
|
|
move fp,sp
|
|
beqz a1,0f
|
|
REG_SUBU fp,a1,O32_NFRAMESZ
|
|
0:
|
|
REG_S sp,O32_NFRAMESZ-1*SZREG(fp)
|
|
|
|
sll a0,a2,zero
|
|
sll a1,a3,zero
|
|
sll a2,a4,zero
|
|
sll a3,a5,zero
|
|
sw a6,4*O32_SZREG(fp)
|
|
sw a7,5*O32_SZREG(fp)
|
|
|
|
PTR_LA t0,O32_FRAMESZ(sp)
|
|
PTR_LA t1,6*O32_SZREG(fp)
|
|
li t2,O32_ARGC-6
|
|
1:
|
|
ld t3,(t0)
|
|
REG_ADDU t0,SZREG
|
|
sw t3,(t1)
|
|
REG_SUBU t2,1
|
|
REG_ADDU t1,O32_SZREG
|
|
bnez t2,1b
|
|
|
|
move sp,fp
|
|
|
|
jalr jp
|
|
|
|
REG_L sp,O32_NFRAMESZ-1*SZREG(sp)
|
|
|
|
REG_L s0,O32_FRAMESZ-11*SZREG(sp)
|
|
REG_L s1,O32_FRAMESZ-10*SZREG(sp)
|
|
REG_L s2,O32_FRAMESZ-9*SZREG(sp)
|
|
REG_L s3,O32_FRAMESZ-8*SZREG(sp)
|
|
REG_L s4,O32_FRAMESZ-7*SZREG(sp)
|
|
REG_L s5,O32_FRAMESZ-6*SZREG(sp)
|
|
REG_L s6,O32_FRAMESZ-5*SZREG(sp)
|
|
REG_L s7,O32_FRAMESZ-4*SZREG(sp)
|
|
REG_L gp,O32_FRAMESZ-3*SZREG(sp)
|
|
REG_L fp,O32_FRAMESZ-2*SZREG(sp)
|
|
REG_L ra,O32_FRAMESZ-1*SZREG(sp)
|
|
|
|
REG_ADDU sp,O32_FRAMESZ
|
|
jr ra
|
|
END(call_o32)
|