diff options
author | Paul Mackerras <paulus@samba.org> | 2006-06-07 02:14:40 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-06-09 07:24:15 -0400 |
commit | fab5db97e44f76461f76b24adfa8ccb14d4df498 (patch) | |
tree | 123026a1a6f1702468220189b7410077479ae8a2 /arch/powerpc/kernel/signal_32.c | |
parent | 651d765d0b2c72d33430487c8b6ef64c60cd2134 (diff) |
[PATCH] powerpc: Implement support for setting little-endian mode via prctl
This adds the PowerPC part of the code to allow processes to change
their endian mode via prctl.
This also extends the alignment exception handler to be able to fix up
alignment exceptions that occur in little-endian mode, both for
"PowerPC" little-endian and true little-endian.
We always enter signal handlers in big-endian mode -- the support for
little-endian mode does not amount to the creation of a little-endian
user/kernel ABI. If the signal handler returns, the endian mode is
restored to what it was when the signal was delivered.
We have two new kernel CPU feature bits, one for PPC little-endian and
one for true little-endian. Most of the classic 32-bit processors
support PPC little-endian, and this is reflected in the CPU feature
table. There are two corresponding feature bits reported to userland
in the AT_HWCAP aux vector entry.
This is based on an earlier patch by Anton Blanchard.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/signal_32.c')
-rw-r--r-- | arch/powerpc/kernel/signal_32.c | 15 |
1 files changed, 11 insertions, 4 deletions
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 22f078984845..237faeec2ec2 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c | |||
@@ -419,9 +419,7 @@ static long restore_user_regs(struct pt_regs *regs, | |||
419 | { | 419 | { |
420 | long err; | 420 | long err; |
421 | unsigned int save_r2 = 0; | 421 | unsigned int save_r2 = 0; |
422 | #if defined(CONFIG_ALTIVEC) || defined(CONFIG_SPE) | ||
423 | unsigned long msr; | 422 | unsigned long msr; |
424 | #endif | ||
425 | 423 | ||
426 | /* | 424 | /* |
427 | * restore general registers but not including MSR or SOFTE. Also | 425 | * restore general registers but not including MSR or SOFTE. Also |
@@ -430,11 +428,16 @@ static long restore_user_regs(struct pt_regs *regs, | |||
430 | if (!sig) | 428 | if (!sig) |
431 | save_r2 = (unsigned int)regs->gpr[2]; | 429 | save_r2 = (unsigned int)regs->gpr[2]; |
432 | err = restore_general_regs(regs, sr); | 430 | err = restore_general_regs(regs, sr); |
431 | err |= __get_user(msr, &sr->mc_gregs[PT_MSR]); | ||
433 | if (!sig) | 432 | if (!sig) |
434 | regs->gpr[2] = (unsigned long) save_r2; | 433 | regs->gpr[2] = (unsigned long) save_r2; |
435 | if (err) | 434 | if (err) |
436 | return 1; | 435 | return 1; |
437 | 436 | ||
437 | /* if doing signal return, restore the previous little-endian mode */ | ||
438 | if (sig) | ||
439 | regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE); | ||
440 | |||
438 | /* | 441 | /* |
439 | * Do this before updating the thread state in | 442 | * Do this before updating the thread state in |
440 | * current->thread.fpr/vr/evr. That way, if we get preempted | 443 | * current->thread.fpr/vr/evr. That way, if we get preempted |
@@ -455,7 +458,7 @@ static long restore_user_regs(struct pt_regs *regs, | |||
455 | /* force the process to reload the altivec registers from | 458 | /* force the process to reload the altivec registers from |
456 | current->thread when it next does altivec instructions */ | 459 | current->thread when it next does altivec instructions */ |
457 | regs->msr &= ~MSR_VEC; | 460 | regs->msr &= ~MSR_VEC; |
458 | if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_VEC) != 0) { | 461 | if (msr & MSR_VEC) { |
459 | /* restore altivec registers from the stack */ | 462 | /* restore altivec registers from the stack */ |
460 | if (__copy_from_user(current->thread.vr, &sr->mc_vregs, | 463 | if (__copy_from_user(current->thread.vr, &sr->mc_vregs, |
461 | sizeof(sr->mc_vregs))) | 464 | sizeof(sr->mc_vregs))) |
@@ -472,7 +475,7 @@ static long restore_user_regs(struct pt_regs *regs, | |||
472 | /* force the process to reload the spe registers from | 475 | /* force the process to reload the spe registers from |
473 | current->thread when it next does spe instructions */ | 476 | current->thread when it next does spe instructions */ |
474 | regs->msr &= ~MSR_SPE; | 477 | regs->msr &= ~MSR_SPE; |
475 | if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_SPE) != 0) { | 478 | if (msr & MSR_SPE) { |
476 | /* restore spe registers from the stack */ | 479 | /* restore spe registers from the stack */ |
477 | if (__copy_from_user(current->thread.evr, &sr->mc_vregs, | 480 | if (__copy_from_user(current->thread.evr, &sr->mc_vregs, |
478 | ELF_NEVRREG * sizeof(u32))) | 481 | ELF_NEVRREG * sizeof(u32))) |
@@ -777,6 +780,8 @@ static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka, | |||
777 | regs->gpr[5] = (unsigned long) &rt_sf->uc; | 780 | regs->gpr[5] = (unsigned long) &rt_sf->uc; |
778 | regs->gpr[6] = (unsigned long) rt_sf; | 781 | regs->gpr[6] = (unsigned long) rt_sf; |
779 | regs->nip = (unsigned long) ka->sa.sa_handler; | 782 | regs->nip = (unsigned long) ka->sa.sa_handler; |
783 | /* enter the signal handler in big-endian mode */ | ||
784 | regs->msr &= ~MSR_LE; | ||
780 | regs->trap = 0; | 785 | regs->trap = 0; |
781 | return 1; | 786 | return 1; |
782 | 787 | ||
@@ -1047,6 +1052,8 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
1047 | regs->gpr[3] = sig; | 1052 | regs->gpr[3] = sig; |
1048 | regs->gpr[4] = (unsigned long) sc; | 1053 | regs->gpr[4] = (unsigned long) sc; |
1049 | regs->nip = (unsigned long) ka->sa.sa_handler; | 1054 | regs->nip = (unsigned long) ka->sa.sa_handler; |
1055 | /* enter the signal handler in big-endian mode */ | ||
1056 | regs->msr &= ~MSR_LE; | ||
1050 | regs->trap = 0; | 1057 | regs->trap = 0; |
1051 | 1058 | ||
1052 | return 1; | 1059 | return 1; |