sh: use opcode_t and enable unaligned code for sh2a
This patch converts the unaligned access handling code to use opcode_t instead of u16. While at it, enable unaligned access handling for sh2a. Signed-off-by: Magnus Damm <damm@igel.co.jp> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
@@ -179,7 +179,7 @@ static inline void sign_extend(unsigned int count, unsigned char *dst)
|
|||||||
* (if that instruction is in a branch delay slot)
|
* (if that instruction is in a branch delay slot)
|
||||||
* - return 0 if emulation okay, -EFAULT on existential error
|
* - return 0 if emulation okay, -EFAULT on existential error
|
||||||
*/
|
*/
|
||||||
static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
|
static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
int ret, index, count;
|
int ret, index, count;
|
||||||
unsigned long *rm, *rn;
|
unsigned long *rm, *rn;
|
||||||
@@ -320,11 +320,13 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
|
|||||||
* emulate the instruction in the delay slot
|
* emulate the instruction in the delay slot
|
||||||
* - fetches the instruction from PC+2
|
* - fetches the instruction from PC+2
|
||||||
*/
|
*/
|
||||||
static inline int handle_unaligned_delayslot(struct pt_regs *regs)
|
static inline int handle_unaligned_delayslot(struct pt_regs *regs,
|
||||||
|
opcode_t old_instruction)
|
||||||
{
|
{
|
||||||
u16 instruction;
|
opcode_t instruction;
|
||||||
|
void *addr = (void *)(regs->pc + instruction_size(old_instruction));
|
||||||
|
|
||||||
if (copy_from_user(&instruction, (u16 *)(regs->pc+2), 2)) {
|
if (copy_from_user(&instruction, addr, sizeof(instruction))) {
|
||||||
/* the instruction-fetch faulted */
|
/* the instruction-fetch faulted */
|
||||||
if (user_mode(regs))
|
if (user_mode(regs))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -357,10 +359,10 @@ static inline int handle_unaligned_delayslot(struct pt_regs *regs)
|
|||||||
* XXX: SH-2A needs this too, but it needs an overhaul thanks to mixed 32-bit
|
* XXX: SH-2A needs this too, but it needs an overhaul thanks to mixed 32-bit
|
||||||
* opcodes..
|
* opcodes..
|
||||||
*/
|
*/
|
||||||
#ifndef CONFIG_CPU_SH2A
|
|
||||||
static int handle_unaligned_notify_count = 10;
|
static int handle_unaligned_notify_count = 10;
|
||||||
|
|
||||||
static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
|
static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
u_int rm;
|
u_int rm;
|
||||||
int ret, index;
|
int ret, index;
|
||||||
@@ -375,7 +377,7 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
|
|||||||
printk(KERN_NOTICE "Fixing up unaligned userspace access "
|
printk(KERN_NOTICE "Fixing up unaligned userspace access "
|
||||||
"in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
|
"in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
|
||||||
current->comm, task_pid_nr(current),
|
current->comm, task_pid_nr(current),
|
||||||
(u16 *)regs->pc, instruction);
|
(void *)regs->pc, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
@@ -383,19 +385,19 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
|
|||||||
case 0x0000:
|
case 0x0000:
|
||||||
if (instruction==0x000B) {
|
if (instruction==0x000B) {
|
||||||
/* rts */
|
/* rts */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0)
|
if (ret==0)
|
||||||
regs->pc = regs->pr;
|
regs->pc = regs->pr;
|
||||||
}
|
}
|
||||||
else if ((instruction&0x00FF)==0x0023) {
|
else if ((instruction&0x00FF)==0x0023) {
|
||||||
/* braf @Rm */
|
/* braf @Rm */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0)
|
if (ret==0)
|
||||||
regs->pc += rm + 4;
|
regs->pc += rm + 4;
|
||||||
}
|
}
|
||||||
else if ((instruction&0x00FF)==0x0003) {
|
else if ((instruction&0x00FF)==0x0003) {
|
||||||
/* bsrf @Rm */
|
/* bsrf @Rm */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0) {
|
if (ret==0) {
|
||||||
regs->pr = regs->pc + 4;
|
regs->pr = regs->pc + 4;
|
||||||
regs->pc += rm + 4;
|
regs->pc += rm + 4;
|
||||||
@@ -416,13 +418,13 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
|
|||||||
case 0x4000:
|
case 0x4000:
|
||||||
if ((instruction&0x00FF)==0x002B) {
|
if ((instruction&0x00FF)==0x002B) {
|
||||||
/* jmp @Rm */
|
/* jmp @Rm */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0)
|
if (ret==0)
|
||||||
regs->pc = rm;
|
regs->pc = rm;
|
||||||
}
|
}
|
||||||
else if ((instruction&0x00FF)==0x000B) {
|
else if ((instruction&0x00FF)==0x000B) {
|
||||||
/* jsr @Rm */
|
/* jsr @Rm */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0) {
|
if (ret==0) {
|
||||||
regs->pr = regs->pc + 4;
|
regs->pr = regs->pc + 4;
|
||||||
regs->pc = rm;
|
regs->pc = rm;
|
||||||
@@ -449,7 +451,7 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
|
|||||||
case 0x0B00: /* bf lab - no delayslot*/
|
case 0x0B00: /* bf lab - no delayslot*/
|
||||||
break;
|
break;
|
||||||
case 0x0F00: /* bf/s lab */
|
case 0x0F00: /* bf/s lab */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0) {
|
if (ret==0) {
|
||||||
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
|
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
|
||||||
if ((regs->sr & 0x00000001) != 0)
|
if ((regs->sr & 0x00000001) != 0)
|
||||||
@@ -462,7 +464,7 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
|
|||||||
case 0x0900: /* bt lab - no delayslot */
|
case 0x0900: /* bt lab - no delayslot */
|
||||||
break;
|
break;
|
||||||
case 0x0D00: /* bt/s lab */
|
case 0x0D00: /* bt/s lab */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0) {
|
if (ret==0) {
|
||||||
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
|
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
|
||||||
if ((regs->sr & 0x00000001) == 0)
|
if ((regs->sr & 0x00000001) == 0)
|
||||||
@@ -476,13 +478,13 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xA000: /* bra label */
|
case 0xA000: /* bra label */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0)
|
if (ret==0)
|
||||||
regs->pc += SH_PC_12BIT_OFFSET(instruction);
|
regs->pc += SH_PC_12BIT_OFFSET(instruction);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xB000: /* bsr label */
|
case 0xB000: /* bsr label */
|
||||||
ret = handle_unaligned_delayslot(regs);
|
ret = handle_unaligned_delayslot(regs, instruction);
|
||||||
if (ret==0) {
|
if (ret==0) {
|
||||||
regs->pr = regs->pc + 4;
|
regs->pr = regs->pc + 4;
|
||||||
regs->pc += SH_PC_12BIT_OFFSET(instruction);
|
regs->pc += SH_PC_12BIT_OFFSET(instruction);
|
||||||
@@ -498,7 +500,6 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
|
|||||||
regs->pc += instruction_size(instruction);
|
regs->pc += instruction_size(instruction);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_CPU_SH2A */
|
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_HAS_SR_RB
|
#ifdef CONFIG_CPU_HAS_SR_RB
|
||||||
#define lookup_exception_vector(x) \
|
#define lookup_exception_vector(x) \
|
||||||
@@ -526,10 +527,8 @@ asmlinkage void do_address_error(struct pt_regs *regs,
|
|||||||
unsigned long error_code = 0;
|
unsigned long error_code = 0;
|
||||||
mm_segment_t oldfs;
|
mm_segment_t oldfs;
|
||||||
siginfo_t info;
|
siginfo_t info;
|
||||||
#ifndef CONFIG_CPU_SH2A
|
opcode_t instruction;
|
||||||
u16 instruction;
|
|
||||||
int tmp;
|
int tmp;
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Intentional ifdef */
|
/* Intentional ifdef */
|
||||||
#ifdef CONFIG_CPU_HAS_SR_RB
|
#ifdef CONFIG_CPU_HAS_SR_RB
|
||||||
@@ -549,9 +548,9 @@ asmlinkage void do_address_error(struct pt_regs *regs,
|
|||||||
goto uspace_segv;
|
goto uspace_segv;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_CPU_SH2A
|
|
||||||
set_fs(USER_DS);
|
set_fs(USER_DS);
|
||||||
if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
|
if (copy_from_user(&instruction, (void *)(regs->pc),
|
||||||
|
sizeof(instruction))) {
|
||||||
/* Argh. Fault on the instruction itself.
|
/* Argh. Fault on the instruction itself.
|
||||||
This should never happen non-SMP
|
This should never happen non-SMP
|
||||||
*/
|
*/
|
||||||
@@ -564,8 +563,6 @@ asmlinkage void do_address_error(struct pt_regs *regs,
|
|||||||
|
|
||||||
if (tmp==0)
|
if (tmp==0)
|
||||||
return; /* sorted */
|
return; /* sorted */
|
||||||
#endif
|
|
||||||
|
|
||||||
uspace_segv:
|
uspace_segv:
|
||||||
printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned "
|
printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned "
|
||||||
"access (PC %lx PR %lx)\n", current->comm, regs->pc,
|
"access (PC %lx PR %lx)\n", current->comm, regs->pc,
|
||||||
@@ -580,9 +577,9 @@ uspace_segv:
|
|||||||
if (regs->pc & 1)
|
if (regs->pc & 1)
|
||||||
die("unaligned program counter", regs, error_code);
|
die("unaligned program counter", regs, error_code);
|
||||||
|
|
||||||
#ifndef CONFIG_CPU_SH2A
|
|
||||||
set_fs(KERNEL_DS);
|
set_fs(KERNEL_DS);
|
||||||
if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
|
if (copy_from_user(&instruction, (void *)(regs->pc),
|
||||||
|
sizeof(instruction))) {
|
||||||
/* Argh. Fault on the instruction itself.
|
/* Argh. Fault on the instruction itself.
|
||||||
This should never happen non-SMP
|
This should never happen non-SMP
|
||||||
*/
|
*/
|
||||||
@@ -592,12 +589,6 @@ uspace_segv:
|
|||||||
|
|
||||||
handle_unaligned_access(instruction, regs);
|
handle_unaligned_access(instruction, regs);
|
||||||
set_fs(oldfs);
|
set_fs(oldfs);
|
||||||
#else
|
|
||||||
printk(KERN_NOTICE "Killing process \"%s\" due to unaligned "
|
|
||||||
"access\n", current->comm);
|
|
||||||
|
|
||||||
force_sig(SIGSEGV, current);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user