Commit 28b3026e authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Russell King

[ARM PATCH] 1866/4: kernel support for iWMMXt present on some XScale cores

Patch from Nicolas Pitre

This is required for a system with user space binaries using iWMMXt 
instructions to even boot (revised again).

It also moves defines for thread_info offsets from  magic static
values to the auto generated asm/constants.h in order to better
cope with changing structure offsets and avoid user errors.
parent 2ac7b327
...@@ -19,6 +19,9 @@ obj-$(CONFIG_ARTHUR) += arthur.o ...@@ -19,6 +19,9 @@ obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o obj-$(CONFIG_PCI) += bios32.o
obj-$(CONFIG_IWMMXT) += iwmmxt.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
ifneq ($(CONFIG_ARCH_EBSA110),y) ifneq ($(CONFIG_ARCH_EBSA110),y)
obj-y += io.o obj-y += io.o
endif endif
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/thread_info.h>
/* /*
* Make sure that the compiler and target are compatible. * Make sure that the compiler and target are compatible.
...@@ -48,10 +49,23 @@ int main(void) ...@@ -48,10 +49,23 @@ int main(void)
{ {
DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
BLANK(); BLANK();
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
DEFINE(TI_TASK, offsetof(struct thread_info, task));
DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain));
DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context));
DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp));
DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate));
DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate));
DEFINE(TI_IWMMXT_STATE, (offsetof(struct thread_info, fpstate)+4)&~7);
BLANK();
#if __LINUX_ARM_ARCH__ >= 6 #if __LINUX_ARM_ARCH__ >= 6
DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id)); DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id));
#endif
BLANK(); BLANK();
#endif
DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm)); DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags)); DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags));
BLANK(); BLANK();
......
...@@ -1174,7 +1174,7 @@ __und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go ...@@ -1174,7 +1174,7 @@ __und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
* r0 - instruction opcode. * r0 - instruction opcode.
* r10 - this threads thread_info structure. * r10 - this threads thread_info structure.
*/ */
call_fpe: enable_irq r10 @ Enable interrupts call_fpe:
tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27 tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27
#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710) #if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710)
and r8, r0, #0x0f000000 @ mask out op-code bits and r8, r0, #0x0f000000 @ mask out op-code bits
...@@ -1186,6 +1186,14 @@ call_fpe: enable_irq r10 @ Enable interrupts ...@@ -1186,6 +1186,14 @@ call_fpe: enable_irq r10 @ Enable interrupts
mov r7, #1 mov r7, #1
add r6, r10, #TI_USED_CP add r6, r10, #TI_USED_CP
strb r7, [r6, r8, lsr #8] @ set appropriate used_cp[] strb r7, [r6, r8, lsr #8] @ set appropriate used_cp[]
#ifdef CONFIG_IWMMXT
@ Test if we need to give access to iWMMXt coprocessors
ldr r5, [r10, #TI_FLAGS]
rsbs r7, r8, #(1 << 8) @ CP 0 or 1 only
movcss r7, r5, lsr #(TIF_USING_IWMMXT + 1)
bcs iwmmxt_task_enable
#endif
enable_irq r7
add pc, pc, r8, lsr #6 add pc, pc, r8, lsr #6
mov r0, r0 mov r0, r0
...@@ -1264,7 +1272,11 @@ ENTRY(ret_from_exception) ...@@ -1264,7 +1272,11 @@ ENTRY(ret_from_exception)
ENTRY(__switch_to) ENTRY(__switch_to)
add ip, r1, #TI_CPU_SAVE add ip, r1, #TI_CPU_SAVE
ldr r3, [r2, #TI_CPU_DOMAIN]! ldr r3, [r2, #TI_CPU_DOMAIN]!
stmia ip, {r4 - sl, fp, sp, lr} @ Store most regs on stack stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack
#if defined(CONFIG_CPU_XSCALE) && !defined(CONFIG_IWMMXT)
mra r4, r5, acc0
stmia ip, {r4, r5}
#endif
mcr p15, 0, r3, c3, c0, 0 @ Set domain register mcr p15, 0, r3, c3, c0, 0 @ Set domain register
#ifdef CONFIG_VFP #ifdef CONFIG_VFP
@ Always disable VFP so we can lazily save/restore the old @ Always disable VFP so we can lazily save/restore the old
...@@ -1272,6 +1284,13 @@ ENTRY(__switch_to) ...@@ -1272,6 +1284,13 @@ ENTRY(__switch_to)
VFPFMRX r4, FPEXC VFPFMRX r4, FPEXC
bic r4, r4, #FPEXC_ENABLE bic r4, r4, #FPEXC_ENABLE
VFPFMXR FPEXC, r4 VFPFMXR FPEXC, r4
#endif
#if defined(CONFIG_IWMMXT)
bl iwmmxt_task_switch
#elif defined(CONFIG_CPU_XSCALE)
add r4, r2, #40 @ cpu_context_save->extra
ldmib r4, {r4, r5}
mar acc0, r4, r5
#endif #endif
ldmib r2, {r4 - sl, fp, sp, pc} @ Load all regs saved previously ldmib r2, {r4 - sl, fp, sp, pc} @ Load all regs saved previously
......
/*
* linux/arch/arm/kernel/iwmmxt.S
*
* XScale iWMMXt (Concan) context switching and handling
*
* Initial code:
* Copyright (c) 2003, Intel Corporation
*
* Full lazy switching support, optimizations and more, by Nicolas Pitre
* Copyright (c) 2003-2004, MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/linkage.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#include <asm/constants.h>
#define MMX_WR0 (0x00)
#define MMX_WR1 (0x08)
#define MMX_WR2 (0x10)
#define MMX_WR3 (0x18)
#define MMX_WR4 (0x20)
#define MMX_WR5 (0x28)
#define MMX_WR6 (0x30)
#define MMX_WR7 (0x38)
#define MMX_WR8 (0x40)
#define MMX_WR9 (0x48)
#define MMX_WR10 (0x50)
#define MMX_WR11 (0x58)
#define MMX_WR12 (0x60)
#define MMX_WR13 (0x68)
#define MMX_WR14 (0x70)
#define MMX_WR15 (0x78)
#define MMX_WCSSF (0x80)
#define MMX_WCASF (0x84)
#define MMX_WCGR0 (0x88)
#define MMX_WCGR1 (0x8C)
#define MMX_WCGR2 (0x90)
#define MMX_WCGR3 (0x94)
#define MMX_SIZE (0x98)
.text
/*
* Lazy switching of Concan coprocessor context
*
* r10 = struct thread_info pointer
* r9 = ret_from_exception
* lr = undefined instr exit
*
* called from prefetch exception handler with interrupts disabled
*/
ENTRY(iwmmxt_task_enable)
mrc p15, 0, r2, c15, c1, 0
tst r2, #0x3 @ CP0 and CP1 accessible?
movne pc, lr @ if so no business here
orr r2, r2, #0x3 @ enable access to CP0 and CP1
mcr p15, 0, r2, c15, c1, 0
ldr r3, =concan_owner
add r0, r10, #TI_IWMMXT_STATE @ get task Concan save area
ldr r2, [sp, #60] @ current task pc value
ldr r1, [r3] @ get current Concan owner
str r0, [r3] @ this task now owns Concan regs
sub r2, r2, #4 @ adjust pc back
str r2, [sp, #60]
mrc p15, 0, r2, c2, c0, 0
mov r2, r2 @ cpwait
teq r1, #0 @ test for last ownership
mov lr, r9 @ normal exit from exception
beq concan_load @ no owner, skip save
concan_save:
tmrc r2, wCon
@ CUP? wCx
tst r2, #0x1
beq 1f
concan_dump:
wstrw wCSSF, [r1, #MMX_WCSSF]
wstrw wCASF, [r1, #MMX_WCASF]
wstrw wCGR0, [r1, #MMX_WCGR0]
wstrw wCGR1, [r1, #MMX_WCGR1]
wstrw wCGR2, [r1, #MMX_WCGR2]
wstrw wCGR3, [r1, #MMX_WCGR3]
1: @ MUP? wRn
tst r2, #0x2
beq 2f
wstrd wR0, [r1, #MMX_WR0]
wstrd wR1, [r1, #MMX_WR1]
wstrd wR2, [r1, #MMX_WR2]
wstrd wR3, [r1, #MMX_WR3]
wstrd wR4, [r1, #MMX_WR4]
wstrd wR5, [r1, #MMX_WR5]
wstrd wR6, [r1, #MMX_WR6]
wstrd wR7, [r1, #MMX_WR7]
wstrd wR8, [r1, #MMX_WR8]
wstrd wR9, [r1, #MMX_WR9]
wstrd wR10, [r1, #MMX_WR10]
wstrd wR11, [r1, #MMX_WR11]
wstrd wR12, [r1, #MMX_WR12]
wstrd wR13, [r1, #MMX_WR13]
wstrd wR14, [r1, #MMX_WR14]
wstrd wR15, [r1, #MMX_WR15]
2: teq r0, #0 @ anything to load?
moveq pc, lr
concan_load:
@ Load wRn
wldrd wR0, [r0, #MMX_WR0]
wldrd wR1, [r0, #MMX_WR1]
wldrd wR2, [r0, #MMX_WR2]
wldrd wR3, [r0, #MMX_WR3]
wldrd wR4, [r0, #MMX_WR4]
wldrd wR5, [r0, #MMX_WR5]
wldrd wR6, [r0, #MMX_WR6]
wldrd wR7, [r0, #MMX_WR7]
wldrd wR8, [r0, #MMX_WR8]
wldrd wR9, [r0, #MMX_WR9]
wldrd wR10, [r0, #MMX_WR10]
wldrd wR11, [r0, #MMX_WR11]
wldrd wR12, [r0, #MMX_WR12]
wldrd wR13, [r0, #MMX_WR13]
wldrd wR14, [r0, #MMX_WR14]
wldrd wR15, [r0, #MMX_WR15]
@ Load wCx
wldrw wCSSF, [r0, #MMX_WCSSF]
wldrw wCASF, [r0, #MMX_WCASF]
wldrw wCGR0, [r0, #MMX_WCGR0]
wldrw wCGR1, [r0, #MMX_WCGR1]
wldrw wCGR2, [r0, #MMX_WCGR2]
wldrw wCGR3, [r0, #MMX_WCGR3]
@ clear CUP/MUP (only if r1 != 0)
teq r1, #0
mov r2, #0
moveq pc, lr
tmcr wCon, r2
mov pc, lr
/*
* Back up Concan regs to save area and disable access to them
* (mainly for gdb or sleep mode usage)
*
* r0 = struct thread_info pointer of target task or NULL for any
*/
ENTRY(iwmmxt_task_disable)
stmfd sp!, {r4, lr}
mrs ip, cpsr
orr r2, ip, #PSR_I_BIT @ disable interrupts
msr cpsr_c, r2
ldr r3, =concan_owner
add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
ldr r1, [r3] @ get current Concan owner
teq r1, #0 @ any current owner?
beq 1f @ no: quit
teq r0, #0 @ any owner?
teqne r1, r2 @ or specified one?
bne 1f @ no: quit
mrc p15, 0, r4, c15, c1, 0
orr r4, r4, #0x3 @ enable access to CP0 and CP1
mcr p15, 0, r4, c15, c1, 0
mov r0, #0 @ nothing to load
str r0, [r3] @ no more current owner
mrc p15, 0, r2, c2, c0, 0
mov r2, r2 @ cpwait
bl concan_save
bic r4, r4, #0x3 @ disable access to CP0 and CP1
mcr p15, 0, r4, c15, c1, 0
mrc p15, 0, r2, c2, c0, 0
mov r2, r2 @ cpwait
1: msr cpsr_c, ip @ restore interrupt mode
ldmfd sp!, {r4, pc}
/*
* Copy Concan state to given memory address
*
* r0 = struct thread_info pointer of target task
* r1 = memory address where to store Concan state
*
* this is called mainly in the creation of signal stack frames
*/
ENTRY(iwmmxt_task_copy)
mrs ip, cpsr
orr r2, ip, #PSR_I_BIT @ disable interrupts
msr cpsr_c, r2
ldr r3, =concan_owner
add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
ldr r3, [r3] @ get current Concan owner
teq r2, r3 @ does this task own it...
beq 1f
@ current Concan values are in the task save area
msr cpsr_c, ip @ restore interrupt mode
mov r0, r1
mov r1, r2
mov r2, #MMX_SIZE
b memcpy
1: @ this task owns Concan regs -- grab a copy from there
mov r0, #0 @ nothing to load
mov r2, #3 @ save all regs
mov r3, lr @ preserve return address
bl concan_dump
msr cpsr_c, ip @ restore interrupt mode
mov pc, r3
/*
* Restore Concan state from given memory address
*
* r0 = struct thread_info pointer of target task
* r1 = memory address where to get Concan state from
*
* this is used to restore Concan state when unwinding a signal stack frame
*/
ENTRY(iwmmxt_task_restore)
mrs ip, cpsr
orr r2, ip, #PSR_I_BIT @ disable interrupts
msr cpsr_c, r2
ldr r3, =concan_owner
add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
ldr r3, [r3] @ get current Concan owner
bic r2, r2, #0x7 @ 64-bit alignment
teq r2, r3 @ does this task own it...
beq 1f
@ this task doesn't own Concan regs -- use its save area
msr cpsr_c, ip @ restore interrupt mode
mov r0, r2
mov r2, #MMX_SIZE
b memcpy
1: @ this task owns Concan regs -- load them directly
mov r0, r1
mov r1, #0 @ don't clear CUP/MUP
mov r3, lr @ preserve return address
bl concan_load
msr cpsr_c, ip @ restore interrupt mode
mov pc, r3
/*
* Concan handling on task switch
*
* r0 = previous task_struct pointer (must be preserved)
* r1 = previous thread_info pointer
* r2 = next thread_info.cpu_domain pointer (must be preserved)
*
* Called only from __switch_to with task preemption disabled.
* No need to care about preserving r4 and above.
*/
ENTRY(iwmmxt_task_switch)
mrc p15, 0, r4, c15, c1, 0
tst r4, #0x3 @ CP0 and CP1 accessible?
bne 1f @ yes: block them for next task
ldr r5, =concan_owner
add r6, r2, #(TI_IWMMXT_STATE - TI_CPU_DOMAIN) @ get next task Concan save area
ldr r5, [r5] @ get current Concan owner
teq r5, r6 @ next task owns it?
movne pc, lr @ no: leave Concan disabled
1: eor r4, r4, #3 @ flip Concan access
mcr p15, 0, r4, c15, c1, 0
mrc p15, 0, r4, c2, c0, 0
sub pc, lr, r4, lsr #32 @ cpwait and return
/*
* Remove Concan ownership of given task
*
* r0 = struct thread_info pointer
*/
ENTRY(iwmmxt_task_release)
mrs r2, cpsr
orr ip, r2, #PSR_I_BIT @ disable interrupts
msr cpsr_c, ip
ldr r3, =concan_owner
add r0, r0, #TI_IWMMXT_STATE @ get task Concan save area
ldr r1, [r3] @ get current Concan owner
eors r0, r0, r1 @ if equal...
streq r0, [r3] @ then clear ownership
msr cpsr_c, r2 @ restore interrupts
mov pc, lr
.data
concan_owner:
.word 0
...@@ -313,6 +313,9 @@ void flush_thread(void) ...@@ -313,6 +313,9 @@ void flush_thread(void)
memset(thread->used_cp, 0, sizeof(thread->used_cp)); memset(thread->used_cp, 0, sizeof(thread->used_cp));
memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
#if defined(CONFIG_IWMMXT)
iwmmxt_task_release(thread);
#endif
fp_init(&thread->fpstate); fp_init(&thread->fpstate);
#if defined(CONFIG_VFP) #if defined(CONFIG_VFP)
vfp_flush_thread(&thread->vfpstate); vfp_flush_thread(&thread->vfpstate);
...@@ -324,6 +327,9 @@ void release_thread(struct task_struct *dead_task) ...@@ -324,6 +327,9 @@ void release_thread(struct task_struct *dead_task)
#if defined(CONFIG_VFP) #if defined(CONFIG_VFP)
vfp_release_thread(&dead_task->thread_info->vfpstate); vfp_release_thread(&dead_task->thread_info->vfpstate);
#endif #endif
#if defined(CONFIG_IWMMXT)
iwmmxt_task_release(dead_task->thread_info);
#endif
} }
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
......
...@@ -145,6 +145,100 @@ struct rt_sigframe ...@@ -145,6 +145,100 @@ struct rt_sigframe
unsigned long retcode; unsigned long retcode;
}; };
#ifdef CONFIG_IWMMXT
/* iwmmxt_area is 0x98 bytes long, preceeded by 8 bytes of signature */
#define IWMMXT_STORAGE_SIZE (0x98 + 8)
#define IWMMXT_MAGIC0 0x12ef842a
#define IWMMXT_MAGIC1 0x1c07ca71
static int page_present(struct mm_struct *mm, unsigned long addr, int wr)
{
pgd_t *pgd = pgd_offset(mm, addr);
if (pgd_present(*pgd)) {
pmd_t *pmd = pmd_offset(pgd, addr);
if (pmd_present(*pmd)) {
pte_t *pte = pte_offset_map(pmd, addr);
return (pte_present(*pte) && (!wr || pte_write(*pte)));
}
}
return 0;
}
static int
preserve_iwmmxt_context(void *iwmmxt_save_area)
{
int err = 0;
/* the iWMMXt context must be 64 bit aligned */
long *iwmmxt_storage = (long *)(((long)iwmmxt_save_area + 4) & ~7);
again:
__put_user_error(IWMMXT_MAGIC0, iwmmxt_storage+0, err);
__put_user_error(IWMMXT_MAGIC1, iwmmxt_storage+1, err);
/*
* iwmmxt_task_copy() doesn't check user permissions.
* Let's do a dummy write on the upper boundary to ensure
* access to user mem is OK all way up.
*/
__put_user_error(0, iwmmxt_storage+IWMMXT_STORAGE_SIZE/4-1, err);
if (!err) {
/* Let's make sure the user mapping won't disappear under us */
struct mm_struct *mm = current->mm;
unsigned long addr = (unsigned long)iwmmxt_storage;
spin_lock(&mm->page_table_lock);
if ( !page_present(mm, addr, 1) ||
!page_present(mm, addr+IWMMXT_STORAGE_SIZE-1, 1) ) {
/* our user area has gone before grabbing the lock */
spin_unlock(&mm->page_table_lock);
goto again;
}
iwmmxt_task_copy(current_thread_info(), iwmmxt_storage+2);
spin_unlock(&mm->page_table_lock);
return 0;
}
return err;
}
static int
restore_iwmmxt_context(void *iwmmxt_save_area)
{
int err = 0;
long *iwmmxt_storage, magic0, magic1, dummy;
/* the iWMMXt context is 64 bit aligned */
iwmmxt_storage = (long *)(((long)iwmmxt_save_area + 4) & ~7);
/*
* Validate iWMMXt context signature.
* Also, iwmmxt_task_restore() doesn't check user permissions.
* Let's do a dummy write on the upper boundary to ensure
* access to user mem is OK all way up.
*/
again:
__get_user_error(magic0, iwmmxt_storage+0, err);
__get_user_error(magic1, iwmmxt_storage+1, err);
if (!err && magic0 == IWMMXT_MAGIC0 && magic1 == IWMMXT_MAGIC1 &&
!__get_user(dummy, iwmmxt_storage+IWMMXT_STORAGE_SIZE/4-1)) {
/* Let's make sure the user mapping won't disappear under us */
struct mm_struct *mm = current->mm;
unsigned long addr = (unsigned long)iwmmxt_storage;
spin_lock(&mm->page_table_lock);
if ( !page_present(mm, addr, 0) ||
!page_present(mm, addr+IWMMXT_STORAGE_SIZE-1, 0) ) {
/* our user area has gone before grabbing the lock */
spin_unlock(&mm->page_table_lock);
goto again;
}
iwmmxt_task_restore(current_thread_info(), iwmmxt_storage+2);
spin_unlock(&mm->page_table_lock);
return 0;
}
return -1;
}
#endif
static int static int
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{ {
...@@ -208,6 +302,11 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) ...@@ -208,6 +302,11 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs)
if (restore_sigcontext(regs, &frame->sc)) if (restore_sigcontext(regs, &frame->sc))
goto badframe; goto badframe;
#ifdef CONFIG_IWMMXT
if (test_thread_flag(TIF_USING_IWMMXT) && restore_iwmmxt_context(frame+1))
goto badframe;
#endif
/* Send SIGTRAP if we're single-stepping */ /* Send SIGTRAP if we're single-stepping */
if (current->ptrace & PT_SINGLESTEP) { if (current->ptrace & PT_SINGLESTEP) {
ptrace_cancel_bpt(current); ptrace_cancel_bpt(current);
...@@ -256,6 +355,11 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) ...@@ -256,6 +355,11 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->ARM_sp) == -EFAULT) if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->ARM_sp) == -EFAULT)
goto badframe; goto badframe;
#ifdef CONFIG_IWMMXT
if (test_thread_flag(TIF_USING_IWMMXT) && restore_iwmmxt_context(frame+1))
goto badframe;
#endif
/* Send SIGTRAP if we're single-stepping */ /* Send SIGTRAP if we're single-stepping */
if (current->ptrace & PT_SINGLESTEP) { if (current->ptrace & PT_SINGLESTEP) {
ptrace_cancel_bpt(current); ptrace_cancel_bpt(current);
...@@ -306,6 +410,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize) ...@@ -306,6 +410,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
{ {
unsigned long sp = regs->ARM_sp; unsigned long sp = regs->ARM_sp;
#ifdef CONFIG_IWMMXT
if (test_thread_flag(TIF_USING_IWMMXT))
framesize = (framesize + 4 + IWMMXT_STORAGE_SIZE) & ~7;
#endif
/* /*
* This is the X/Open sanctioned signal stack switching. * This is the X/Open sanctioned signal stack switching.
*/ */
...@@ -394,6 +503,11 @@ setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *reg ...@@ -394,6 +503,11 @@ setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *reg
sizeof(frame->extramask)); sizeof(frame->extramask));
} }
#ifdef CONFIG_IWMMXT
if (test_thread_flag(TIF_USING_IWMMXT))
err |= preserve_iwmmxt_context(frame+1);
#endif
if (err == 0) if (err == 0)
err = setup_return(regs, ka, &frame->retcode, frame, usig); err = setup_return(regs, ka, &frame->retcode, frame, usig);
...@@ -428,6 +542,11 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, ...@@ -428,6 +542,11 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
regs, set->sig[0]); regs, set->sig[0]);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
#ifdef CONFIG_IWMMXT
if (test_thread_flag(TIF_USING_IWMMXT))
err |= preserve_iwmmxt_context(frame+1);
#endif
if (err == 0) if (err == 0)
err = setup_return(regs, ka, &frame->retcode, frame, usig); err = setup_return(regs, ka, &frame->retcode, frame, usig);
......
...@@ -12,7 +12,7 @@ config ARCH_LUBBOCK ...@@ -12,7 +12,7 @@ config ARCH_LUBBOCK
config MACH_MAINSTONE config MACH_MAINSTONE
bool "Intel HCDDBBVA0 Development Platform" bool "Intel HCDDBBVA0 Development Platform"
select PXA27x select PXA27x
#select IWMMXT select IWMMXT
config ARCH_PXA_IDP config ARCH_PXA_IDP
bool "Accelent Xscale IDP" bool "Accelent Xscale IDP"
...@@ -32,4 +32,9 @@ config PXA27x ...@@ -32,4 +32,9 @@ config PXA27x
help help
Select code specific to PXA27x variants Select code specific to PXA27x variants
config IWMMXT
bool
help
Enable support for iWMMXt
endif endif
...@@ -585,7 +585,12 @@ __xscale_setup: ...@@ -585,7 +585,12 @@ __xscale_setup:
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
mov r0, #0x1f @ Domains 0, 1 = client mov r0, #0x1f @ Domains 0, 1 = client
mcr p15, 0, r0, c3, c0, 0 @ load domain access register mcr p15, 0, r0, c3, c0, 0 @ load domain access register
mov r0, #1 @ Allow access to CP0 and CP13 #ifdef CONFIG_IWMMXT
mov r0, #0 @ initially disallow access to CP0/CP1
#else
mov r0, #1 @ Allow access to CP0
#endif
orr r0, r0, #1 << 6 @ cp6 for IOP3xx and Bulverde
orr r0, r0, #1 << 13 @ Its undefined whether this orr r0, r0, #1 << 13 @ Its undefined whether this
mcr p15, 0, r0, c15, c1, 0 @ affects USR or SVC modes mcr p15, 0, r0, c15, c1, 0 @ affects USR or SVC modes
mrc p15, 0, r0, c1, c0, 0 @ get control register mrc p15, 0, r0, c1, c0, 0 @ get control register
......
#ifndef __ASMARM_ELF_H #ifndef __ASMARM_ELF_H
#define __ASMARM_ELF_H #define __ASMARM_ELF_H
#include <linux/config.h>
/* /*
* ELF register definitions.. * ELF register definitions..
*/ */
...@@ -14,6 +16,7 @@ typedef unsigned long elf_freg_t[3]; ...@@ -14,6 +16,7 @@ typedef unsigned long elf_freg_t[3];
#define EM_ARM 40 #define EM_ARM 40
#define EF_ARM_APCS26 0x08 #define EF_ARM_APCS26 0x08
#define EF_ARM_SOFT_FLOAT 0x200
#define R_ARM_NONE 0 #define R_ARM_NONE 0
#define R_ARM_PC24 1 #define R_ARM_PC24 1
...@@ -91,6 +94,8 @@ extern char elf_platform[]; ...@@ -91,6 +94,8 @@ extern char elf_platform[];
(( (elf_hwcap & HWCAP_26BIT) && (x)->e_flags & EF_ARM_APCS26) || \ (( (elf_hwcap & HWCAP_26BIT) && (x)->e_flags & EF_ARM_APCS26) || \
((x)->e_flags & EF_ARM_APCS26) == 0) ((x)->e_flags & EF_ARM_APCS26) == 0)
#ifndef CONFIG_IWMMXT
/* Old NetWinder binaries were compiled in such a way that the iBCS /* Old NetWinder binaries were compiled in such a way that the iBCS
heuristic always trips on them. Until these binaries become uncommon heuristic always trips on them. Until these binaries become uncommon
enough not to care, don't trust the `ibcs' flag here. In any case enough not to care, don't trust the `ibcs' flag here. In any case
...@@ -99,6 +104,28 @@ extern char elf_platform[]; ...@@ -99,6 +104,28 @@ extern char elf_platform[];
#define SET_PERSONALITY(ex,ibcs2) \ #define SET_PERSONALITY(ex,ibcs2) \
set_personality(((ex).e_flags&EF_ARM_APCS26 ?PER_LINUX :PER_LINUX_32BIT)) set_personality(((ex).e_flags&EF_ARM_APCS26 ?PER_LINUX :PER_LINUX_32BIT))
#else
/*
* All iWMMXt capable CPUs don't support 26-bit mode. Yet they can run
* legacy binaries which used to contain FPA11 floating point instructions
* that have always been emulated by the kernel. PFA11 and iWMMXt overlap
* on coprocessor 1 space though. We therefore must decide if given task
* is allowed to use CP 0 and 1 for iWMMXt, or if they should be blocked
* at all times for the prefetch exception handler to catch FPA11 opcodes
* and emulate them. The best indication to discriminate those two cases
* is the SOFT_FLOAT flag in the ELF header.
*/
#define SET_PERSONALITY(ex,ibcs2) \
do { \
set_personality(PER_LINUX_32BIT); \
if ((ex).e_flags & EF_ARM_SOFT_FLOAT) \
set_thread_flag(TIF_USING_IWMMXT); \
} while (0)
#endif
#endif #endif
#endif #endif
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#ifndef __ASM_ARM_FPSTATE_H #ifndef __ASM_ARM_FPSTATE_H
#define __ASM_ARM_FPSTATE_H #define __ASM_ARM_FPSTATE_H
#define FP_SIZE 35 #include <linux/config.h>
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
...@@ -43,19 +43,32 @@ union vfp_state { ...@@ -43,19 +43,32 @@ union vfp_state {
extern void vfp_flush_thread(union vfp_state *); extern void vfp_flush_thread(union vfp_state *);
extern void vfp_release_thread(union vfp_state *); extern void vfp_release_thread(union vfp_state *);
#define FP_HARD_SIZE 35
struct fp_hard_struct { struct fp_hard_struct {
unsigned int save[FP_SIZE]; /* as yet undefined */ unsigned int save[FP_HARD_SIZE]; /* as yet undefined */
}; };
#define FP_SOFT_SIZE 35
struct fp_soft_struct { struct fp_soft_struct {
unsigned int save[FP_SIZE]; /* undefined information */ unsigned int save[FP_SOFT_SIZE]; /* undefined information */
};
struct iwmmxt_struct {
unsigned int save[0x98/sizeof(int) + 1];
}; };
union fp_state { union fp_state {
struct fp_hard_struct hard; struct fp_hard_struct hard;
struct fp_soft_struct soft; struct fp_soft_struct soft;
#ifdef CONFIG_IWMMXT
struct iwmmxt_struct iwmmxt;
#endif
}; };
#define FP_SIZE (sizeof(union fp_state) / sizeof(int))
#endif #endif
#endif #endif
...@@ -100,19 +100,10 @@ extern void free_thread_info(struct thread_info *); ...@@ -100,19 +100,10 @@ extern void free_thread_info(struct thread_info *);
#define thread_saved_fp(tsk) \ #define thread_saved_fp(tsk) \
((unsigned long)((tsk)->thread_info->cpu_context.fp)) ((unsigned long)((tsk)->thread_info->cpu_context.fp))
#else /* !__ASSEMBLY__ */ extern void iwmmxt_task_disable(struct thread_info *);
extern void iwmmxt_task_copy(struct thread_info *, void *);
#define TI_FLAGS 0 extern void iwmmxt_task_restore(struct thread_info *, void *);
#define TI_PREEMPT 4 extern void iwmmxt_task_release(struct thread_info *);
#define TI_ADDR_LIMIT 8
#define TI_TASK 12
#define TI_EXEC_DOMAIN 16
#define TI_CPU 20
#define TI_CPU_DOMAIN 24
#define TI_CPU_SAVE 28
#define TI_USED_CP 76
#define TI_FPSTATE (TI_USED_CP+16)
#define TI_VFPSTATE (TI_FPSTATE+FP_SIZE*4)
#endif #endif
...@@ -133,6 +124,7 @@ extern void free_thread_info(struct thread_info *); ...@@ -133,6 +124,7 @@ extern void free_thread_info(struct thread_info *);
#define TIF_SYSCALL_TRACE 8 #define TIF_SYSCALL_TRACE 8
#define TIF_USED_FPU 16 #define TIF_USED_FPU 16
#define TIF_POLLING_NRFLAG 17 #define TIF_POLLING_NRFLAG 17
#define TIF_USING_IWMMXT 18
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
...@@ -140,6 +132,7 @@ extern void free_thread_info(struct thread_info *); ...@@ -140,6 +132,7 @@ extern void free_thread_info(struct thread_info *);
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_USED_FPU (1 << TIF_USED_FPU) #define _TIF_USED_FPU (1 << TIF_USED_FPU)
#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
/* /*
* Change these and you break ASM code in entry-common.S * Change these and you break ASM code in entry-common.S
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment