align.c
PATCH 1/1 align.c
This is an (updated) diff to arch/ppc/kernel/align.c of the 2.6.8-rc1 kernel.
The patch adds support for the handling of alignment exceptions of multiple (lmw/stmw) and string (lswi/lswx/stswi/stswx) instructions.
Stef
arch/ppc/kernel/align.c +95 -54
@@ -17,8 +17,8 @@#include <asm/cache.h> struct aligninfo { -unsigned char len; -unsigned char flags; +unsigned int len; +unsigned int flags; };#if defined(CONFIG_4xx) || defined(CONFIG_POWER4 ) || defined(CONFIG_BOOKE )
@@ -30,14 +30,16 @@#define INVALID { 0, 0 } -#define LD 1/* load */ -#define ST 2/* store */ -#define SE 4/* sign-extend value */ -#define F 8/* to/from fp regs */ -#define U 0x10/* update index register */ -#define M 0x20/* multiple load/store */ -#define S 0x40/* single-precision fp, or byte-swap value */ -#define HARD 0x80/* string, stwcx. */ +#define LD 0x001/* load */ +#define ST 0x002/* store */ +#define SE 0x004/* sign-extend value */ +#define F 0x008/* to/from fp regs */ +#define U 0x010/* update index register */ +#define M 0x020/* multiple load/store */ +#define S 0x040/* single-precision fp, or byte-swap value */ +#define STR_OP 0x080/* string, length stored in instruction */ +#define STR_XER 0x100/* string, length stored in XER[25-31] */ +#define HARD 0x200/* too hard: stwcx. */ #define DCBZ 0x5f/* 8xx/82xx dcbz faults when cache not enabled */
@@ -88,10 +90,10 @@INVALID ,/* 01 0 0101: lwax */ INVALID ,/* 01 0 0110 */ INVALID ,/* 01 0 0111 */ - { 0, LD+HARD }, /* 01 0 1000: lswx */ - { 0, LD+HARD }, /* 01 0 1001: lswi */ - { 0, ST+HARD }, /* 01 0 1010: stswx */ - { 0, ST+HARD }, /* 01 0 1011: stswi */ + { 4, LD+STR_XER }, /* 01 0 1000: lswx */ + { 4, LD+STR_OP }, /* 01 0 1001: lswi */ + { 4, ST+STR_XER }, /* 01 0 1010: stswx */ + { 4, ST+STR_OP }, /* 01 0 1011: stswi */INVALID ,/* 01 0 1100 */ INVALID ,/* 01 0 1101 */ INVALID, /* 01 0 1110 */
@@ -183,11 +185,11 @@int fix_alignment (struct pt_regs *regs) { -int instr, nb, flags; +int instr, nb, mb, mr, flags;#if defined(CONFIG_4xx) || defined(CONFIG_POWER4 ) || defined(CONFIG_BOOKE )int opcode, f1, f2, f3;#endif -int i, t; +int i, j, t;int reg, areg;unsigned char __user *addr;union {
@@ -195,7 +197,7 @@float f;double d;unsigned char v[8 ]; - } data; + } data[32 ];CHECK_FULL_REGS (regs);
@@ -256,9 +259,34 @@ addr = (unsigned char __user *)regs->dar; +/* Get mb (total number of bytes to load) +and mr (number of registers needed) if we're dealing with strings */ +if (flags &M ) { + mr =32 - reg; + mb = nb * mr;/* nb = bytes per register */ + }else if (flags &STR_OP ) { + instr = *((unsigned int *)regs->nip); + mb = (instr >>11 ) &0x1f ; +if (mb ==0 ) + mb =32 ;/* mb = 32 if bits are 0 */ + mr = mb /4 ; +if ((mb %4 ) >0 ) + mr++; + }else if (flags &STR_XER ) { + mb = regs->xer &0x7f ; +/* This shouldn't be 128 if XER[25-31] is 0, +in that case it's actually a no-op */ + mr = mb /4 ; +if ((mb %4 ) >0 ) + mr++; + }else { + mb = nb; + mr =1 ; + } +/* Verify the address of the operand */ if (user_mode (regs)) { -if (verify_area ((flags &ST ?VERIFY_WRITE :VERIFY_READ ), addr, nb)) +if (verify_area ((flags &ST ?VERIFY_WRITE :VERIFY_READ ), addr, mb))return -EFAULT ;/* bad address */ }
@@ -268,57 +296,69 @@giveup_fpu (current);preempt_enable (); } -if (flags &M ) -return 0 ;/* too hard for now */ /* If we read the operand, copy it in */ if (flags &LD ) { -if (nb ==2 ) { - data.v[0 ] = data.v[1 ] =0 ; -if (__get_user (data.v[2 ], addr) - ||__get_user (data.v[3 ], addr+1 )) -return -EFAULT ; - }else { -for (i =0 ; i < nb; ++i) -if (__get_user (data.v[i], addr+i)) +for (j =0 ; j < mr; ++j) { +if (nb ==2 ) { + data[j].v[0 ] = data[j].v[1 ] =0 ; +if (__get_user (data[j].v[2 ], addr) + ||__get_user (data[j].v[3 ], addr+1 ))return -EFAULT ; + }else { +for (i =0 ; i < nb; ++i) { +if ((j*4 )+i < mb) { +if (__get_user (data[j].v[i], addr+(j*4 )+i)) +return -EFAULT ; + }else { + data[j].v[i] =0 ; + } + } + } } } -switch (flags & ~U ) { +/* We already did the main work for the multiple register cases +(M, STR_OP and STR_XER), so they get filtered out. As the only +possible combinations with multiples are with LD and ST, there +is only a loop there. data[0] is used when there is only one +register involved. */ +switch (flags & ~(U |M |STR_OP |STR_XER )) {case LD +SE : -if (data.v[2 ] >=0x80 ) - data.v[0 ] = data.v[1 ] =-1 ; +if (data[0 ].v[2 ] >=0x80 ) + data[0 ].v[0 ] = data[0 ].v[1 ] =-1 ;/* fall through */ case LD : - regs->gpr[reg] = data.l; +for (i =0 ; i < mr; ++i) + regs->gpr[reg+i] = data[i].l;break ;case LD +S :if (nb ==2 ) { -SWAP (data.v[2 ], data.v[3 ]); +SWAP (data[0 ].v[2 ], data[0 ].v[3 ]); }else { -SWAP (data.v[0 ], data.v[3 ]); -SWAP (data.v[1 ], data.v[2 ]); +SWAP (data[0 ].v[0 ], data[0 ].v[3 ]); +SWAP (data[0 ].v[1 ], data[0 ].v[2 ]); } - regs->gpr[reg] = data.l; + regs->gpr[reg] = data[0 ].l;break ;case ST : - data.l = regs->gpr[reg]; +for (i =0 ; i < mr; ++i) + data[i].l = regs->gpr[reg+i];break ;case ST +S : - data.l = regs->gpr[reg]; + data[0 ].l = regs->gpr[reg];if (nb ==2 ) { -SWAP (data.v[2 ], data.v[3 ]); +SWAP (data[0 ].v[2 ], data[0 ].v[3 ]); }else { -SWAP (data.v[0 ], data.v[3 ]); -SWAP (data.v[1 ], data.v[2 ]); +SWAP (data[0 ].v[0 ], data[0 ].v[3 ]); +SWAP (data[0 ].v[1 ], data[0 ].v[2 ]); }break ;case LD +F : - current->thread.fpr[reg] = data.d; + current->thread.fpr[reg] = data[0 ].d;break ;case ST +F : - data.d = current->thread.fpr[reg]; + data[0 ].d = current->thread.fpr[reg];break ;/* these require some floating point conversions... */ /* we'd like to use the assignment, but we have to compile
@@ -327,15 +367,15 @@case LD +F +S :preempt_disable ();enable_kernel_fp (); -cvt_fd (&data.f, ¤t->thread.fpr[reg], ¤t->thread.fpscr); -/* current->thread.fpr[reg] = data.f; */ +cvt_fd (&data[0 ].f, ¤t->thread.fpr[reg], ¤t->thread.fpscr); +/* current->thread.fpr[reg] = data[0].f; */ preempt_enable ();break ;case ST +F +S :preempt_disable ();enable_kernel_fp (); -cvt_df (¤t->thread.fpr[reg], &data.f, ¤t->thread.fpscr); -/* data.f = current->thread.fpr[reg]; */ +cvt_df (¤t->thread.fpr[reg], &data[0 ].f, ¤t->thread.fpscr); +/* data[0].f = current->thread.fpr[reg]; */ preempt_enable ();break ;default :
@@ -344,14 +384,16 @@ }if (flags &ST ) { -if (nb ==2 ) { -if (__put_user (data.v[2 ], addr) - ||__put_user (data.v[3 ], addr+1 )) -return -EFAULT ; - }else { -for (i =0 ; i < nb; ++i) -if (__put_user (data.v[i], addr+i)) +for (j =0 ; j < mr; ++j) { +if (nb ==2 ) { +if (__put_user (data[j].v[2 ], addr) + ||__put_user (data[j].v[3 ], addr+1 ))return -EFAULT ; + }else { +for (i =0 ; (i < nb) && (((j*4 )+i) < mb); ++i) +if (__put_user (data[j].v[i], addr+(j*4 )+i)) +return -EFAULT ; + } } }
Do you really see string ops and alignment exceptions? I was under the impression they did byte accesses.
- kumar
Quoted 241 lines · Stef Simoens
This is an (updated) diff to
arch/ppc/kernel/align.cof the 2.6.8-rc1 kernel.
The patch adds support for the handling of alignment exceptions of multiple (lmw/stmw) and string (lswi/lswx/stswi/stswx) instructions. Stefdiff -ur
linux-2.6.8-rc1/arch/ppc/kernel/align.clinux-2.6.8-rc1-mq/arch/ppc/kernel/align.c
---linux-2.6.8-rc1/arch/ppc/kernel/align.c2004-07-12
17:46:42.525695737 +0200
+++linux-2.6.8-rc1-mq/arch/ppc/kernel/align.c2004-07-12
20:25:24.390989048 +0200arch/ppc/kernel/align.c +18
-16@@ -17,8 +17,8 @@#include <asm/cache.h> struct aligninfo { -unsigned char len; -unsigned char flags; +unsigned int len; +unsigned int flags; };#if defined(CONFIG_4xx) || defined(CONFIG_POWER4 ) || defined(CONFIG_BOOKE )@@ -30,14 +30,16 @@#define INVALID { 0, 0 } -#define LD 1/* load */ -#define ST 2/* store */ -#define SE 4/* sign-extend value */ -#define F 8/* to/from fp regs */ -#define U 0x10/* update index register */ -#define M 0x20/* multiple load/store */ -#define S 0x40/* single-precision fp, or byte-swap value */ -#define HARD 0x80/* string, stwcx. */ +#define LD 0x001/* load */ +#define ST 0x002/* store */ +#define SE 0x004/* sign-extend value */ +#define F 0x008/* to/from fp regs */ +#define U 0x010/* update index register */ +#define M 0x020/* multiple load/store */ +#define S 0x040/* single-precision fp, or byte-swap value */ +#define STR_OP 0x080/* string, length stored in instruction */ +#define STR_XER 0x100/* string, length stored in XER[25-31] */ +#define HARD 0x200/* too hard: stwcx. */ #define DCBZ 0x5f/* 8xx/82xx dcbz faults when cache not enabled */ @@ -88,10 +90,10 @@INVALID ,/* 01 0 0101: lwax */ INVALID ,/* 01 0 0110 */ INVALID ,/* 01 0 0111 */ - { 0, LD+HARD }, /* 01 0 1000: lswx */ - { 0, LD+HARD }, /* 01 0 1001: lswi */ - { 0, ST+HARD }, /* 01 0 1010: stswx */ - { 0, ST+HARD }, /* 01 0 1011: stswi */ + { 4, LD+STR_XER }, /* 01 0 1000: lswx */ + { 4, LD+STR_OP }, /* 01 0 1001: lswi */ + { 4, ST+STR_XER }, /* 01 0 1010: stswx */ + { 4, ST+STR_OP }, /* 01 0 1011: stswi */INVALID ,/* 01 0 1100 */ INVALID ,/* 01 0 1101 */ INVALID, /* 01 0 1110 */@@ -183,11 +185,11 @@int fix_alignment (struct pt_regs *regs) { -int instr, nb, flags; +int instr, nb, mb, mr, flags;#if defined(CONFIG_4xx) || defined(CONFIG_POWER4 ) || defined(CONFIG_BOOKE )int opcode, f1, f2, f3;#endif -int i, t; +int i, j, t;int reg, areg;unsigned char __user *addr;arch/ppc/kernel/align.c +68
-31union {@@ -195,7 +197,7 @@float f;double d;unsigned char v[8 ]; - } data; + } data[32 ];CHECK_FULL_REGS (regs);@@ -256,9 +259,34 @@ addr = (unsigned char __user *)regs->dar; +/* Get mb (total number of bytes to load) +and mr (number of registers needed) if we're dealing with strings */ +if (flags &M ) { + mr =32 - reg; + mb = nb * mr;/* nb = bytes per register */ + }else if (flags &STR_OP ) { + instr = *((unsigned int *)regs->nip); + mb = (instr >>11 ) &0x1f ; +if (mb ==0 ) + mb =32 ;/* mb = 32 if bits are 0 */ + mr = mb /4 ; +if ((mb %4 ) >0 ) + mr++; + }else if (flags &STR_XER ) { + mb = regs->xer &0x7f ; +/* This shouldn't be 128 if XER[25-31] is 0, +in that case it's actually a no-op */ + mr = mb /4 ; +if ((mb %4 ) >0 ) + mr++; + }else { + mb = nb; + mr =1 ; + } +/* Verify the address of the operand */ if (user_mode (regs)) { -if (verify_area ((flags &ST ?VERIFY_WRITE :VERIFY_READ ), addr, nb)) +if (verify_area ((flags &ST ?VERIFY_WRITE :VERIFY_READ ), addr, mb))return -EFAULT ;/* bad address */ }@@ -268,57 +296,69 @@giveup_fpu (current);preempt_enable (); } -if (flags &M ) -return 0 ;/* too hard for now */ /* If we read the operand, copy it in */ if (flags &LD ) { -if (nb ==2 ) { - data.v[0 ] = data.v[1 ] =0 ; -if (__get_user (data.v[2 ], addr) - ||__get_user (data.v[3 ], addr+1 )) -return -EFAULT ; - }else { -for (i =0 ; i < nb; ++i) -if (__get_user (data.v[i], addr+i)) +for (j =0 ; j < mr; ++j) { +if (nb ==2 ) { + data[j].v[0 ] = data[j].v[1 ] =0 ; +if (__get_user (data[j].v[2 ], addr) + ||__get_user (data[j].v[3 ], addr+1 ))return -EFAULT ; + }else { +for (i =0 ; i < nb; ++i) { +if ((j*4 )+i < mb) { +if (__get_user (data[j].v[i], addr+(j*4 )+i)) +return -EFAULT ; + }else { + data[j].v[i] =0 ; + } + } + } } } -switch (flags & ~U ) { +/* We already did the main work for the multiple register cases +(M, STR_OP and STR_XER), so they get filtered out. As the only +possible combinations with multiples are with LD and ST, there +is only a loop there. data[0] is used when there is only one +register involved. */ +switch (flags & ~(U |M |STR_OP |STR_XER )) {case LD +SE : -if (data.v[2 ] >=0x80 ) - data.v[0 ] = data.v[1 ] =-1 ; +if (data[0 ].v[2 ] >=0x80 ) + data[0 ].v[0 ] = data[0 ].v[1 ] =-1 ;/* fall through */ case LD : - regs->gpr[reg] = data.l; +for (i =0 ; i < mr; ++i) + regs->gpr[reg+i] = data[i].l;break ;case LD +S :if (nb ==2 ) { -SWAP (data.v[2 ], data.v[3 ]); +SWAP (data[0 ].v[2 ], data[0 ].v[3 ]); }else { -SWAP (data.v[0 ], data.v[3 ]); -SWAP (data.v[1 ], data.v[2 ]); +SWAP (data[0 ].v[0 ], data[0 ].v[3 ]); +SWAP (data[0 ].v[1 ], data[0 ].v[2 ]); } - regs->gpr[reg] = data.l; + regs->gpr[reg] = data[0 ].l;break ;case ST : - data.l = regs->gpr[reg]; +for (i =0 ; i < mr; ++i) + data[i].l = regs->gpr[reg+i];break ;case ST +S : - data.l = regs->gpr[reg]; + data[0 ].l = regs->gpr[reg];if (nb ==2 ) { -SWAP (data.v[2 ], data.v[3 ]); +SWAP (data[0 ].v[2 ], data[0 ].v[3 ]); }else { -SWAP (data.v[0 ], data.v[3 ]); -SWAP (data.v[1 ], data.v[2 ]); +SWAP (data[0 ].v[0 ], data[0 ].v[3 ]); +SWAP (data[0 ].v[1 ], data[0 ].v[2 ]); }break ;case LD +F : - current->thread.fpr[reg] = data.d; + current->thread.fpr[reg] = data[0 ].d;break ;case ST +F : - data.d = current->thread.fpr[reg]; + data[0 ].d = current->thread.fpr[reg];break ;/* these require some floating point conversions... */ /* we'd like to use the assignment, but we have to compile @@ -327,15 +367,15 @@case LD +F +S :preempt_disable ();enable_kernel_fp (); -cvt_fd (&data.f, ¤t->thread.fpr[reg], ¤t->thread.fpscr); -/* current->thread.fpr[reg] = data.f; */ +cvt_fd (&data[0 ].f, ¤t->thread.fpr[reg], ¤t->thread.fpscr); +/* current->thread.fpr[reg] = data[0].f; */ preempt_enable ();break ;case ST +F +S :preempt_disable ();enable_kernel_fp (); -cvt_df (¤t->thread.fpr[reg], &data.f, ¤t->thread.fpscr); -/* data.f = current->thread.fpr[reg]; */ +cvt_df (¤t->thread.fpr[reg], &data[0 ].f, ¤t->thread.fpscr); +/* data[0].f = current->thread.fpr[reg]; */ preempt_enable ();arch/ppc/kernel/align.c +2
-7break ;default :@@ -344,14 +384,16 @@ }if (flags &ST ) { -if (nb ==2 ) { -if (__put_user (data.v[2 ], addr) - ||__put_user (data.v[3 ], addr+1 )) -return -EFAULT ; - }else { -for (i =0 ; i < nb; ++i) -if (__put_user (data.v[i], addr+i)) +for (j =0 ; j < mr; ++j) { +if (nb ==2 ) {
arch/ppc/kernel/align.c line 397
Quoted 10 lines
arch/ppc/kernel/align.c +7
-0+if (__put_user (data[j].v[2 ], addr) + ||__put_user (data[j].v[3 ], addr+1 ))return -EFAULT ; + }else { +for (i =0 ; (i < nb) && (((j*4 )+i) < mb); ++i) +if (__put_user (data[j].v[i], addr+(j*4 )+i)) +return -EFAULT ; + } } }
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
Quoted 2 lines · Kumar Gala
Do you really see string ops and alignment exceptions? I was under the impression they did byte accesses.
Yes, but i've only seen alignment exceptions with stswi/lswi instructions though.
(On the MPC601, stswi/lswi has better timings than a series of stw/lwz; that's why I'm probably the only one seeing them...)
While I was at it, Paulus asked me to handle stmw/lmw and stswx/lswx as well...
Stef
Quoted 8 lines · Stef Simoens
Quoted 7 lines
This is an (updated) diff to
arch/ppc/kernel/align.cof the 2.6.8-rc1 kernel.
The patch adds support for the handling of alignment exceptions of multiple (lmw/stmw) and string (lswi/lswx/stswi/stswx) instructions. Stef
Quoted 6 lines
Quoted 6 lines
diff -ur
linux-2.6.8-rc1/arch/ppc/kernel/align.clinux-2.6.8-rc1-mq/arch/ppc/kernel/align.c
---linux-2.6.8-rc1/arch/ppc/kernel/align.c2004-07-12
17:46:42.525695737 +0200
+++linux-2.6.8-rc1-mq/arch/ppc/kernel/align.c2004-07-12
20:25:24.390989048 +0200
(snip)
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
Paul,
Can you explain to me why/when stswi/lswi would get alignment exceptions. I think I'm missing something :) The reason I ask is we are working on some emulation handlers but are not doing anything with regards to alignment for e500.
- kumar
Quoted 22 lines · Stef Simoens
Quoted 2 lines · Kumar Gala
Do you really see string ops and alignment exceptions? I was under the impression they did byte accesses.
Yes, but i've only seen alignment exceptions with stswi/lswi instructions though.
(On the MPC601, stswi/lswi has better timings than a series of stw/lwz; that's why I'm probably the only one seeing them...)
While I was at it, Paulus asked me to handle stmw/lmw and stswx/lswx as well...
Stef
Quoted 7 lines · Stef Simoens
Quoted 6 lines
This is an (updated) diff to
arch/ppc/kernel/align.cof the 2.6.8-rc1 kernel.
The patch adds support for the handling of alignment exceptions of multiple (lmw/stmw) and string (lswi/lswx/stswi/stswx) instructions. Stef
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
diff -ur
linux-2.6.8-rc1/arch/ppc/kernel/align.clinux-2.6.8-rc1-mq/arch/ppc/kernel/align.c
---linux-2.6.8-rc1/arch/ppc/kernel/align.c2004-07-12
17:46:42.525695737 +0200
+++linux-2.6.8-rc1-mq/arch/ppc/kernel/align.c2004-07-12
20:25:24.390989048 +0200(snip)
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
Quoted 5 lines
Can you explain to me why/when stswi/lswi would get alignment exceptions. I think I'm missing something :) The reason I ask is we are working on some emulation handlers but are not doing anything with regards to alignment for e500.
CPU dependent; one example is stswi to cache-inhibited memory, on some CPUs; another is trying to execute any string insn while in little-endian mode, on some other CPUs.
On the 601, which is what Stef is using, a word-aligned string insn that crosses a 256MB boundary causes an alignement exception (as does *any* insn that does this); and a not-word-aligned string insn that crosses a 4kB boundary, does, too.
Segher
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
Quoted 5 lines · Stef Simoens
Quoted 2 lines · Kumar Gala
Do you really see string ops and alignment exceptions? I was under the impression they did byte accesses.
Quoted 8 lines
Yes, but i've only seen alignment exceptions with stswi/lswi instructions though.
(On the MPC601, stswi/lswi has better timings than a series of stw/lwz; that's why I'm probably the only one seeing them...)
While I was at it, Paulus asked me to handle stmw/lmw and stswx/lswx as well...
Personnaly I disagree for stmw/lmw, they are specified to work only for word-aligned addresses.
Regards,
Gabriel (back form holidays, so the delay)
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
Quoted 7 lines
Quoted 4 lines
While I was at it, Paulus asked me to handle stmw/lmw and stswx/lswx as
well...Personnaly I disagree for stmw/lmw, they are specified to work only for word-aligned addresses.
That's what the PEM says, but the architecture Books disagree. Don't trust the PEM :-)
Also, in some implementations, lmw/stmw can fail for any number of reasons (e.g., using them while in wrong-endian mode, etc.) so it's nice to be able to emulate them.
Segher
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/