[PATCH] powerpc: Merge align.c (#2)
Paul Mackerras
paulus at samba.org
Thu Nov 17 09:14:16 EST 2005
Benjamin Herrenschmidt writes:
> Since it's likely that I won't be able to test all scenario, code
> inspection is much welcome.
I think you need this patch on top...
Paul.
diff -urN powerpc/arch/powerpc/kernel/align.c merge-hack/arch/powerpc/kernel/align.c
--- powerpc/arch/powerpc/kernel/align.c 2005-11-17 09:05:04.000000000 +1100
+++ merge-hack/arch/powerpc/kernel/align.c 2005-11-17 09:06:29.000000000 +1100
@@ -198,21 +198,20 @@
/* bits 6:15 --> 22:31 */
dsisr = (instr & 0x03ff0000) >> 16;
- if ( IS_XFORM(instr) ) {
+ if (IS_XFORM(instr)) {
/* bits 29:30 --> 15:16 */
dsisr |= (instr & 0x00000006) << 14;
/* bit 25 --> 17 */
dsisr |= (instr & 0x00000040) << 8;
/* bits 21:24 --> 18:21 */
dsisr |= (instr & 0x00000780) << 3;
- }
- else {
+ } else {
/* bit 5 --> 17 */
dsisr |= (instr & 0x04000000) >> 12;
/* bits 1: 4 --> 18:21 */
dsisr |= (instr & 0x78000000) >> 17;
/* bits 30:31 --> 12:13 */
- if ( IS_DSFORM(instr) )
+ if (IS_DSFORM(instr))
dsisr |= (instr & 0x00000003) << 18;
}
@@ -247,13 +246,22 @@
/*
* Emulate load & store multiple instructions
+ * On 64-bit machines, these instructions only affect/use the
+ * bottom 4 bytes of each register, and the loads clear the
+ * top 4 bytes of the affected register.
*/
+#ifdef CONFIG_PPC64
+#define REG_BYTE(rp, i) *((u8 *)((rp) + ((i) >> 2)) + ((i) & 3) + 4)
+#else
+#define REG_BYTE(rp, i) *((u8 *)(rp) + (i))
+#endif
+
static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
unsigned int reg, unsigned int nb,
unsigned int flags, unsigned int instr)
{
- unsigned char *rptr;
- int nb0, i;
+ unsigned long *rptr;
+ unsigned int nb0, i;
/*
* We do not try to emulate 8 bytes multiple as they aren't really
@@ -291,29 +299,38 @@
if (!access_ok((flags & ST ? VERIFY_WRITE: VERIFY_READ), addr, nb+nb0))
return -EFAULT; /* bad address */
- rptr = (unsigned char *) ®s->gpr[reg];
+ rptr = ®s->gpr[reg];
if (flags & LD) {
+ /*
+ * This zeroes the top 4 bytes of the affected registers
+ * in 64-bit mode, and also zeroes out any remaining
+ * bytes of the last register for lsw*.
+ */
+ memset(rptr, 0, ((nb + 3) / 4) * sizeof(unsigned long));
+ if (nb0 > 0)
+ memset(®s->gpr[0], 0,
+ ((nb0 + 3) / 4) * sizeof(unsigned long));
+
for (i = 0; i < nb; ++i)
- if (__get_user(rptr[i], addr + i))
+ if (__get_user(REG_BYTE(rptr, i), addr + i))
return -EFAULT;
if (nb0 > 0) {
- rptr = (unsigned char *) ®s->gpr[0];
+ rptr = ®s->gpr[0];
addr += nb;
for (i = 0; i < nb0; ++i)
- if (__get_user(rptr[i], addr + i))
+ if (__get_user(REG_BYTE(rptr, i), addr + i))
return -EFAULT;
}
- for (; (i & 3) != 0; ++i)
- rptr[i] = 0;
+
} else {
for (i = 0; i < nb; ++i)
- if (__put_user(rptr[i], addr + i))
+ if (__put_user(REG_BYTE(rptr, i), addr + i))
return -EFAULT;
if (nb0 > 0) {
- rptr = (unsigned char *) ®s->gpr[0];
+ rptr = ®s->gpr[0];
addr += nb;
for (i = 0; i < nb0; ++i)
- if (__put_user(rptr[i], addr + i))
+ if (__put_user(REG_BYTE(rptr, i), addr + i))
return -EFAULT;
}
}
@@ -338,7 +355,7 @@
unsigned char __user *p;
int ret, t;
union {
- long ll;
+ u64 ll;
double dd;
unsigned char v[8];
struct {
More information about the Linuxppc64-dev
mailing list