mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
11
arch/score/kernel/Makefile
Normal file
11
arch/score/kernel/Makefile
Normal file
|
@ -0,0 +1,11 @@
|
|||
#
|
||||
# Makefile for the Linux/SCORE kernel.
|
||||
#
|
||||
|
||||
extra-y := head.o vmlinux.lds
|
||||
|
||||
obj-y += entry.o irq.o process.o ptrace.o \
|
||||
setup.o signal.o sys_score.o time.o traps.o \
|
||||
sys_call_table.o
|
||||
|
||||
obj-$(CONFIG_MODULES) += module.o
|
216
arch/score/kernel/asm-offsets.c
Normal file
216
arch/score/kernel/asm-offsets.c
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* arch/score/kernel/asm-offsets.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/kbuild.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm-generic/cmpxchg-local.h>
|
||||
|
||||
void output_ptreg_defines(void)
|
||||
{
|
||||
COMMENT("SCORE pt_regs offsets.");
|
||||
OFFSET(PT_R0, pt_regs, regs[0]);
|
||||
OFFSET(PT_R1, pt_regs, regs[1]);
|
||||
OFFSET(PT_R2, pt_regs, regs[2]);
|
||||
OFFSET(PT_R3, pt_regs, regs[3]);
|
||||
OFFSET(PT_R4, pt_regs, regs[4]);
|
||||
OFFSET(PT_R5, pt_regs, regs[5]);
|
||||
OFFSET(PT_R6, pt_regs, regs[6]);
|
||||
OFFSET(PT_R7, pt_regs, regs[7]);
|
||||
OFFSET(PT_R8, pt_regs, regs[8]);
|
||||
OFFSET(PT_R9, pt_regs, regs[9]);
|
||||
OFFSET(PT_R10, pt_regs, regs[10]);
|
||||
OFFSET(PT_R11, pt_regs, regs[11]);
|
||||
OFFSET(PT_R12, pt_regs, regs[12]);
|
||||
OFFSET(PT_R13, pt_regs, regs[13]);
|
||||
OFFSET(PT_R14, pt_regs, regs[14]);
|
||||
OFFSET(PT_R15, pt_regs, regs[15]);
|
||||
OFFSET(PT_R16, pt_regs, regs[16]);
|
||||
OFFSET(PT_R17, pt_regs, regs[17]);
|
||||
OFFSET(PT_R18, pt_regs, regs[18]);
|
||||
OFFSET(PT_R19, pt_regs, regs[19]);
|
||||
OFFSET(PT_R20, pt_regs, regs[20]);
|
||||
OFFSET(PT_R21, pt_regs, regs[21]);
|
||||
OFFSET(PT_R22, pt_regs, regs[22]);
|
||||
OFFSET(PT_R23, pt_regs, regs[23]);
|
||||
OFFSET(PT_R24, pt_regs, regs[24]);
|
||||
OFFSET(PT_R25, pt_regs, regs[25]);
|
||||
OFFSET(PT_R26, pt_regs, regs[26]);
|
||||
OFFSET(PT_R27, pt_regs, regs[27]);
|
||||
OFFSET(PT_R28, pt_regs, regs[28]);
|
||||
OFFSET(PT_R29, pt_regs, regs[29]);
|
||||
OFFSET(PT_R30, pt_regs, regs[30]);
|
||||
OFFSET(PT_R31, pt_regs, regs[31]);
|
||||
|
||||
OFFSET(PT_ORIG_R4, pt_regs, orig_r4);
|
||||
OFFSET(PT_ORIG_R7, pt_regs, orig_r7);
|
||||
OFFSET(PT_CEL, pt_regs, cel);
|
||||
OFFSET(PT_CEH, pt_regs, ceh);
|
||||
OFFSET(PT_SR0, pt_regs, sr0);
|
||||
OFFSET(PT_SR1, pt_regs, sr1);
|
||||
OFFSET(PT_SR2, pt_regs, sr2);
|
||||
OFFSET(PT_EPC, pt_regs, cp0_epc);
|
||||
OFFSET(PT_EMA, pt_regs, cp0_ema);
|
||||
OFFSET(PT_PSR, pt_regs, cp0_psr);
|
||||
OFFSET(PT_ECR, pt_regs, cp0_ecr);
|
||||
OFFSET(PT_CONDITION, pt_regs, cp0_condition);
|
||||
OFFSET(PT_IS_SYSCALL, pt_regs, is_syscall);
|
||||
|
||||
DEFINE(PT_SIZE, sizeof(struct pt_regs));
|
||||
BLANK();
|
||||
}
|
||||
|
||||
void output_task_defines(void)
|
||||
{
|
||||
COMMENT("SCORE task_struct offsets.");
|
||||
OFFSET(TASK_STATE, task_struct, state);
|
||||
OFFSET(TASK_THREAD_INFO, task_struct, stack);
|
||||
OFFSET(TASK_FLAGS, task_struct, flags);
|
||||
OFFSET(TASK_MM, task_struct, mm);
|
||||
OFFSET(TASK_PID, task_struct, pid);
|
||||
DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct));
|
||||
BLANK();
|
||||
}
|
||||
|
||||
void output_thread_info_defines(void)
|
||||
{
|
||||
COMMENT("SCORE thread_info offsets.");
|
||||
OFFSET(TI_TASK, thread_info, task);
|
||||
OFFSET(TI_EXEC_DOMAIN, thread_info, exec_domain);
|
||||
OFFSET(TI_FLAGS, thread_info, flags);
|
||||
OFFSET(TI_TP_VALUE, thread_info, tp_value);
|
||||
OFFSET(TI_CPU, thread_info, cpu);
|
||||
OFFSET(TI_PRE_COUNT, thread_info, preempt_count);
|
||||
OFFSET(TI_ADDR_LIMIT, thread_info, addr_limit);
|
||||
OFFSET(TI_RESTART_BLOCK, thread_info, restart_block);
|
||||
OFFSET(TI_REGS, thread_info, regs);
|
||||
DEFINE(KERNEL_STACK_SIZE, THREAD_SIZE);
|
||||
DEFINE(KERNEL_STACK_MASK, THREAD_MASK);
|
||||
BLANK();
|
||||
}
|
||||
|
||||
void output_thread_defines(void)
|
||||
{
|
||||
COMMENT("SCORE specific thread_struct offsets.");
|
||||
OFFSET(THREAD_REG0, task_struct, thread.reg0);
|
||||
OFFSET(THREAD_REG2, task_struct, thread.reg2);
|
||||
OFFSET(THREAD_REG3, task_struct, thread.reg3);
|
||||
OFFSET(THREAD_REG12, task_struct, thread.reg12);
|
||||
OFFSET(THREAD_REG13, task_struct, thread.reg13);
|
||||
OFFSET(THREAD_REG14, task_struct, thread.reg14);
|
||||
OFFSET(THREAD_REG15, task_struct, thread.reg15);
|
||||
OFFSET(THREAD_REG16, task_struct, thread.reg16);
|
||||
OFFSET(THREAD_REG17, task_struct, thread.reg17);
|
||||
OFFSET(THREAD_REG18, task_struct, thread.reg18);
|
||||
OFFSET(THREAD_REG19, task_struct, thread.reg19);
|
||||
OFFSET(THREAD_REG20, task_struct, thread.reg20);
|
||||
OFFSET(THREAD_REG21, task_struct, thread.reg21);
|
||||
OFFSET(THREAD_REG29, task_struct, thread.reg29);
|
||||
|
||||
OFFSET(THREAD_PSR, task_struct, thread.cp0_psr);
|
||||
OFFSET(THREAD_EMA, task_struct, thread.cp0_ema);
|
||||
OFFSET(THREAD_BADUADDR, task_struct, thread.cp0_baduaddr);
|
||||
OFFSET(THREAD_ECODE, task_struct, thread.error_code);
|
||||
OFFSET(THREAD_TRAPNO, task_struct, thread.trap_no);
|
||||
BLANK();
|
||||
}
|
||||
|
||||
void output_mm_defines(void)
|
||||
{
|
||||
COMMENT("Size of struct page");
|
||||
DEFINE(STRUCT_PAGE_SIZE, sizeof(struct page));
|
||||
BLANK();
|
||||
COMMENT("Linux mm_struct offsets.");
|
||||
OFFSET(MM_USERS, mm_struct, mm_users);
|
||||
OFFSET(MM_PGD, mm_struct, pgd);
|
||||
OFFSET(MM_CONTEXT, mm_struct, context);
|
||||
BLANK();
|
||||
DEFINE(_PAGE_SIZE, PAGE_SIZE);
|
||||
DEFINE(_PAGE_SHIFT, PAGE_SHIFT);
|
||||
BLANK();
|
||||
DEFINE(_PGD_T_SIZE, sizeof(pgd_t));
|
||||
DEFINE(_PTE_T_SIZE, sizeof(pte_t));
|
||||
BLANK();
|
||||
DEFINE(_PGD_ORDER, PGD_ORDER);
|
||||
DEFINE(_PTE_ORDER, PTE_ORDER);
|
||||
BLANK();
|
||||
DEFINE(_PGDIR_SHIFT, PGDIR_SHIFT);
|
||||
BLANK();
|
||||
DEFINE(_PTRS_PER_PGD, PTRS_PER_PGD);
|
||||
DEFINE(_PTRS_PER_PTE, PTRS_PER_PTE);
|
||||
BLANK();
|
||||
}
|
||||
|
||||
void output_sc_defines(void)
|
||||
{
|
||||
COMMENT("Linux sigcontext offsets.");
|
||||
OFFSET(SC_REGS, sigcontext, sc_regs);
|
||||
OFFSET(SC_MDCEH, sigcontext, sc_mdceh);
|
||||
OFFSET(SC_MDCEL, sigcontext, sc_mdcel);
|
||||
OFFSET(SC_PC, sigcontext, sc_pc);
|
||||
OFFSET(SC_PSR, sigcontext, sc_psr);
|
||||
OFFSET(SC_ECR, sigcontext, sc_ecr);
|
||||
OFFSET(SC_EMA, sigcontext, sc_ema);
|
||||
BLANK();
|
||||
}
|
||||
|
||||
void output_signal_defined(void)
|
||||
{
|
||||
COMMENT("Linux signal numbers.");
|
||||
DEFINE(_SIGHUP, SIGHUP);
|
||||
DEFINE(_SIGINT, SIGINT);
|
||||
DEFINE(_SIGQUIT, SIGQUIT);
|
||||
DEFINE(_SIGILL, SIGILL);
|
||||
DEFINE(_SIGTRAP, SIGTRAP);
|
||||
DEFINE(_SIGIOT, SIGIOT);
|
||||
DEFINE(_SIGABRT, SIGABRT);
|
||||
DEFINE(_SIGFPE, SIGFPE);
|
||||
DEFINE(_SIGKILL, SIGKILL);
|
||||
DEFINE(_SIGBUS, SIGBUS);
|
||||
DEFINE(_SIGSEGV, SIGSEGV);
|
||||
DEFINE(_SIGSYS, SIGSYS);
|
||||
DEFINE(_SIGPIPE, SIGPIPE);
|
||||
DEFINE(_SIGALRM, SIGALRM);
|
||||
DEFINE(_SIGTERM, SIGTERM);
|
||||
DEFINE(_SIGUSR1, SIGUSR1);
|
||||
DEFINE(_SIGUSR2, SIGUSR2);
|
||||
DEFINE(_SIGCHLD, SIGCHLD);
|
||||
DEFINE(_SIGPWR, SIGPWR);
|
||||
DEFINE(_SIGWINCH, SIGWINCH);
|
||||
DEFINE(_SIGURG, SIGURG);
|
||||
DEFINE(_SIGIO, SIGIO);
|
||||
DEFINE(_SIGSTOP, SIGSTOP);
|
||||
DEFINE(_SIGTSTP, SIGTSTP);
|
||||
DEFINE(_SIGCONT, SIGCONT);
|
||||
DEFINE(_SIGTTIN, SIGTTIN);
|
||||
DEFINE(_SIGTTOU, SIGTTOU);
|
||||
DEFINE(_SIGVTALRM, SIGVTALRM);
|
||||
DEFINE(_SIGPROF, SIGPROF);
|
||||
DEFINE(_SIGXCPU, SIGXCPU);
|
||||
DEFINE(_SIGXFSZ, SIGXFSZ);
|
||||
BLANK();
|
||||
}
|
493
arch/score/kernel/entry.S
Normal file
493
arch/score/kernel/entry.S
Normal file
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
* arch/score/kernel/entry.S
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
/*
|
||||
* disable interrupts.
|
||||
*/
|
||||
.macro disable_irq
|
||||
mfcr r8, cr0
|
||||
srli r8, r8, 1
|
||||
slli r8, r8, 1
|
||||
mtcr r8, cr0
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endm
|
||||
|
||||
/*
|
||||
* enable interrupts.
|
||||
*/
|
||||
.macro enable_irq
|
||||
mfcr r8, cr0
|
||||
ori r8, 1
|
||||
mtcr r8, cr0
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endm
|
||||
|
||||
__INIT
|
||||
ENTRY(debug_exception_vector)
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
|
||||
ENTRY(general_exception_vector) # should move to addr 0x200
|
||||
j general_exception
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
|
||||
ENTRY(interrupt_exception_vector) # should move to addr 0x210
|
||||
j interrupt_exception
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
nop!
|
||||
|
||||
.section ".text", "ax"
|
||||
.align 2;
|
||||
general_exception:
|
||||
mfcr r31, cr2
|
||||
nop
|
||||
la r30, exception_handlers
|
||||
andi r31, 0x1f # get ecr.exc_code
|
||||
slli r31, r31, 2
|
||||
add r30, r30, r31
|
||||
lw r30, [r30]
|
||||
br r30
|
||||
|
||||
interrupt_exception:
|
||||
SAVE_ALL
|
||||
mfcr r4, cr2
|
||||
nop
|
||||
lw r16, [r28, TI_REGS]
|
||||
sw r0, [r28, TI_REGS]
|
||||
la r3, ret_from_irq
|
||||
srli r4, r4, 18 # get ecr.ip[7:2], interrupt No.
|
||||
mv r5, r0
|
||||
j do_IRQ
|
||||
|
||||
ENTRY(handle_nmi) # NMI #1
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, nmi_exception_handler
|
||||
brl r8
|
||||
j restore_all
|
||||
|
||||
ENTRY(handle_adelinsn) # AdEL-instruction #2
|
||||
SAVE_ALL
|
||||
mfcr r8, cr6
|
||||
nop
|
||||
nop
|
||||
sw r8, [r0, PT_EMA]
|
||||
mv r4, r0
|
||||
la r8, do_adelinsn
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_ibe) # BusEL-instruction #5
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_be
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_pel) # P-EL #6
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_pel
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_ccu) # CCU #8
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_ccu
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_ri) # RI #9
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_ri
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_tr) # Trap #10
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_tr
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_adedata) # AdES-instruction #12
|
||||
SAVE_ALL
|
||||
mfcr r8, cr6
|
||||
nop
|
||||
nop
|
||||
sw r8, [r0, PT_EMA]
|
||||
mv r4, r0
|
||||
la r8, do_adedata
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_cee) # CeE #16
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_cee
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_cpe) # CpE #17
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_cpe
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_dbe) # BusEL-data #18
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_be
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
ENTRY(handle_reserved) # others
|
||||
SAVE_ALL
|
||||
mv r4, r0
|
||||
la r8, do_reserved
|
||||
brl r8
|
||||
mv r4, r0
|
||||
j ret_from_exception
|
||||
nop
|
||||
|
||||
#ifndef CONFIG_PREEMPT
|
||||
#define resume_kernel restore_all
|
||||
#else
|
||||
#define __ret_from_irq ret_from_exception
|
||||
#endif
|
||||
|
||||
.align 2
|
||||
#ifndef CONFIG_PREEMPT
|
||||
ENTRY(ret_from_exception)
|
||||
disable_irq # preempt stop
|
||||
nop
|
||||
j __ret_from_irq
|
||||
nop
|
||||
#endif
|
||||
|
||||
ENTRY(ret_from_irq)
|
||||
sw r16, [r28, TI_REGS]
|
||||
|
||||
ENTRY(__ret_from_irq)
|
||||
lw r8, [r0, PT_PSR] # returning to kernel mode?
|
||||
andri.c r8, r8, KU_USER
|
||||
beq resume_kernel
|
||||
|
||||
resume_userspace:
|
||||
disable_irq
|
||||
lw r6, [r28, TI_FLAGS] # current->work
|
||||
li r8, _TIF_WORK_MASK
|
||||
and.c r8, r8, r6 # ignoring syscall_trace
|
||||
bne work_pending
|
||||
nop
|
||||
j restore_all
|
||||
nop
|
||||
|
||||
#ifdef CONFIG_PREEMPT
|
||||
resume_kernel:
|
||||
disable_irq
|
||||
lw r8, [r28, TI_PRE_COUNT]
|
||||
cmpz.c r8
|
||||
bne restore_all
|
||||
need_resched:
|
||||
lw r8, [r28, TI_FLAGS]
|
||||
andri.c r9, r8, _TIF_NEED_RESCHED
|
||||
beq restore_all
|
||||
lw r8, [r28, PT_PSR] # Interrupts off?
|
||||
andri.c r8, r8, 1
|
||||
beq restore_all
|
||||
bl preempt_schedule_irq
|
||||
nop
|
||||
j need_resched
|
||||
nop
|
||||
#endif
|
||||
|
||||
ENTRY(ret_from_kernel_thread)
|
||||
bl schedule_tail # r4=struct task_struct *prev
|
||||
nop
|
||||
mv r4, r13
|
||||
brl r12
|
||||
j syscall_exit
|
||||
|
||||
ENTRY(ret_from_fork)
|
||||
bl schedule_tail # r4=struct task_struct *prev
|
||||
|
||||
ENTRY(syscall_exit)
|
||||
nop
|
||||
disable_irq
|
||||
lw r6, [r28, TI_FLAGS] # current->work
|
||||
li r8, _TIF_WORK_MASK
|
||||
and.c r8, r6, r8
|
||||
bne syscall_exit_work
|
||||
|
||||
ENTRY(restore_all) # restore full frame
|
||||
RESTORE_ALL_AND_RET
|
||||
|
||||
work_pending:
|
||||
andri.c r8, r6, _TIF_NEED_RESCHED # r6 is preloaded with TI_FLAGS
|
||||
beq work_notifysig
|
||||
work_resched:
|
||||
bl schedule
|
||||
nop
|
||||
disable_irq
|
||||
lw r6, [r28, TI_FLAGS]
|
||||
li r8, _TIF_WORK_MASK
|
||||
and.c r8, r6, r8 # is there any work to be done
|
||||
# other than syscall tracing?
|
||||
beq restore_all
|
||||
andri.c r8, r6, _TIF_NEED_RESCHED
|
||||
bne work_resched
|
||||
|
||||
work_notifysig:
|
||||
mv r4, r0
|
||||
li r5, 0
|
||||
bl do_notify_resume # r6 already loaded
|
||||
nop
|
||||
j resume_userspace
|
||||
nop
|
||||
|
||||
ENTRY(syscall_exit_work)
|
||||
li r8, _TIF_SYSCALL_TRACE
|
||||
and.c r8, r8, r6 # r6 is preloaded with TI_FLAGS
|
||||
beq work_pending # trace bit set?
|
||||
nop
|
||||
enable_irq
|
||||
mv r4, r0
|
||||
li r5, 1
|
||||
bl do_syscall_trace
|
||||
nop
|
||||
b resume_userspace
|
||||
nop
|
||||
|
||||
.macro save_context reg
|
||||
sw r12, [\reg, THREAD_REG12];
|
||||
sw r13, [\reg, THREAD_REG13];
|
||||
sw r14, [\reg, THREAD_REG14];
|
||||
sw r15, [\reg, THREAD_REG15];
|
||||
sw r16, [\reg, THREAD_REG16];
|
||||
sw r17, [\reg, THREAD_REG17];
|
||||
sw r18, [\reg, THREAD_REG18];
|
||||
sw r19, [\reg, THREAD_REG19];
|
||||
sw r20, [\reg, THREAD_REG20];
|
||||
sw r21, [\reg, THREAD_REG21];
|
||||
sw r29, [\reg, THREAD_REG29];
|
||||
sw r2, [\reg, THREAD_REG2];
|
||||
sw r0, [\reg, THREAD_REG0]
|
||||
.endm
|
||||
|
||||
.macro restore_context reg
|
||||
lw r12, [\reg, THREAD_REG12];
|
||||
lw r13, [\reg, THREAD_REG13];
|
||||
lw r14, [\reg, THREAD_REG14];
|
||||
lw r15, [\reg, THREAD_REG15];
|
||||
lw r16, [\reg, THREAD_REG16];
|
||||
lw r17, [\reg, THREAD_REG17];
|
||||
lw r18, [\reg, THREAD_REG18];
|
||||
lw r19, [\reg, THREAD_REG19];
|
||||
lw r20, [\reg, THREAD_REG20];
|
||||
lw r21, [\reg, THREAD_REG21];
|
||||
lw r29, [\reg, THREAD_REG29];
|
||||
lw r0, [\reg, THREAD_REG0];
|
||||
lw r2, [\reg, THREAD_REG2];
|
||||
lw r3, [\reg, THREAD_REG3]
|
||||
.endm
|
||||
|
||||
/*
|
||||
* task_struct *resume(task_struct *prev, task_struct *next,
|
||||
* struct thread_info *next_ti)
|
||||
*/
|
||||
ENTRY(resume)
|
||||
mfcr r9, cr0
|
||||
nop
|
||||
nop
|
||||
sw r9, [r4, THREAD_PSR]
|
||||
save_context r4
|
||||
sw r3, [r4, THREAD_REG3]
|
||||
|
||||
mv r28, r6
|
||||
restore_context r5
|
||||
mv r8, r6
|
||||
addi r8, KERNEL_STACK_SIZE
|
||||
subi r8, 32
|
||||
la r9, kernelsp;
|
||||
sw r8, [r9];
|
||||
|
||||
mfcr r9, cr0
|
||||
ldis r7, 0x00ff
|
||||
nop
|
||||
and r9, r9, r7
|
||||
lw r6, [r5, THREAD_PSR]
|
||||
not r7, r7
|
||||
and r6, r6, r7
|
||||
or r6, r6, r9
|
||||
mtcr r6, cr0
|
||||
nop; nop; nop; nop; nop
|
||||
br r3
|
||||
|
||||
ENTRY(handle_sys)
|
||||
SAVE_ALL
|
||||
sw r8, [r0, 16] # argument 5 from user r8
|
||||
sw r9, [r0, 20] # argument 6 from user r9
|
||||
enable_irq
|
||||
|
||||
sw r4, [r0, PT_ORIG_R4] #for restart syscall
|
||||
sw r7, [r0, PT_ORIG_R7] #for restart syscall
|
||||
sw r27, [r0, PT_IS_SYSCALL] # it from syscall
|
||||
|
||||
lw r9, [r0, PT_EPC] # skip syscall on return
|
||||
addi r9, 4
|
||||
sw r9, [r0, PT_EPC]
|
||||
|
||||
cmpi.c r27, __NR_syscalls # check syscall number
|
||||
bcs illegal_syscall
|
||||
|
||||
slli r8, r27, 2 # get syscall routine
|
||||
la r11, sys_call_table
|
||||
add r11, r11, r8
|
||||
lw r10, [r11] # get syscall entry
|
||||
|
||||
cmpz.c r10
|
||||
beq illegal_syscall
|
||||
|
||||
lw r8, [r28, TI_FLAGS]
|
||||
li r9, _TIF_SYSCALL_TRACE
|
||||
and.c r8, r8, r9
|
||||
bne syscall_trace_entry
|
||||
|
||||
brl r10 # Do The Real system call
|
||||
|
||||
cmpi.c r4, 0
|
||||
blt 1f
|
||||
ldi r8, 0
|
||||
sw r8, [r0, PT_R7]
|
||||
b 2f
|
||||
1:
|
||||
cmpi.c r4, -MAX_ERRNO - 1
|
||||
ble 2f
|
||||
ldi r8, 0x1;
|
||||
sw r8, [r0, PT_R7]
|
||||
neg r4, r4
|
||||
2:
|
||||
sw r4, [r0, PT_R4] # save result
|
||||
|
||||
syscall_return:
|
||||
disable_irq
|
||||
lw r6, [r28, TI_FLAGS] # current->work
|
||||
li r8, _TIF_WORK_MASK
|
||||
and.c r8, r6, r8
|
||||
bne syscall_return_work
|
||||
j restore_all
|
||||
|
||||
syscall_return_work:
|
||||
j syscall_exit_work
|
||||
|
||||
syscall_trace_entry:
|
||||
mv r16, r10
|
||||
mv r4, r0
|
||||
li r5, 0
|
||||
bl do_syscall_trace
|
||||
|
||||
mv r8, r16
|
||||
lw r4, [r0, PT_R4] # Restore argument registers
|
||||
lw r5, [r0, PT_R5]
|
||||
lw r6, [r0, PT_R6]
|
||||
lw r7, [r0, PT_R7]
|
||||
brl r8
|
||||
|
||||
li r8, -MAX_ERRNO - 1
|
||||
sw r8, [r0, PT_R7] # set error flag
|
||||
|
||||
neg r4, r4 # error
|
||||
sw r4, [r0, PT_R0] # set flag for syscall
|
||||
# restarting
|
||||
1: sw r4, [r0, PT_R2] # result
|
||||
j syscall_exit
|
||||
|
||||
illegal_syscall:
|
||||
ldi r4, -ENOSYS # error
|
||||
sw r4, [r0, PT_ORIG_R4]
|
||||
sw r4, [r0, PT_R4]
|
||||
ldi r9, 1 # set error flag
|
||||
sw r9, [r0, PT_R7]
|
||||
j syscall_return
|
||||
|
||||
ENTRY(sys_rt_sigreturn)
|
||||
mv r4, r0
|
||||
la r8, score_rt_sigreturn
|
||||
br r8
|
70
arch/score/kernel/head.S
Normal file
70
arch/score/kernel/head.S
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* arch/score/kernel/head.S
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/asm-offsets.h>
|
||||
|
||||
.extern start_kernel
|
||||
.global init_thread_union
|
||||
.global kernelsp
|
||||
|
||||
__INIT
|
||||
ENTRY(_stext)
|
||||
la r30, __bss_start /* initialize BSS segment. */
|
||||
la r31, _end
|
||||
xor r8, r8, r8
|
||||
|
||||
1: cmp.c r31, r30
|
||||
beq 2f
|
||||
|
||||
sw r8, [r30] /* clean memory. */
|
||||
addi r30, 4
|
||||
b 1b
|
||||
|
||||
2: la r28, init_thread_union /* set kernel stack. */
|
||||
mv r0, r28
|
||||
addi r0, KERNEL_STACK_SIZE - 32
|
||||
la r30, kernelsp
|
||||
sw r0, [r30]
|
||||
subi r0, 4*4
|
||||
xor r30, r30, r30
|
||||
ori r30, 0x02 /* enable MMU. */
|
||||
mtcr r30, cr4
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
/* there is no parameter */
|
||||
xor r4, r4, r4
|
||||
xor r5, r5, r5
|
||||
xor r6, r6, r6
|
||||
xor r7, r7, r7
|
||||
la r30, start_kernel /* jump to init_arch */
|
||||
br r30
|
111
arch/score/kernel/irq.c
Normal file
111
arch/score/kernel/irq.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* arch/score/kernel/irq.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/* the interrupt controller is hardcoded at this address */
|
||||
#define SCORE_PIC ((u32 __iomem __force *)0x95F50000)
|
||||
|
||||
#define INT_PNDL 0
|
||||
#define INT_PNDH 1
|
||||
#define INT_PRIORITY_M 2
|
||||
#define INT_PRIORITY_SG0 4
|
||||
#define INT_PRIORITY_SG1 5
|
||||
#define INT_PRIORITY_SG2 6
|
||||
#define INT_PRIORITY_SG3 7
|
||||
#define INT_MASKL 8
|
||||
#define INT_MASKH 9
|
||||
|
||||
/*
|
||||
* handles all normal device IRQs
|
||||
*/
|
||||
asmlinkage void do_IRQ(int irq)
|
||||
{
|
||||
irq_enter();
|
||||
generic_handle_irq(irq);
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
static void score_mask(struct irq_data *d)
|
||||
{
|
||||
unsigned int irq_source = 63 - d->irq;
|
||||
|
||||
if (irq_source < 32)
|
||||
__raw_writel((__raw_readl(SCORE_PIC + INT_MASKL) | \
|
||||
(1 << irq_source)), SCORE_PIC + INT_MASKL);
|
||||
else
|
||||
__raw_writel((__raw_readl(SCORE_PIC + INT_MASKH) | \
|
||||
(1 << (irq_source - 32))), SCORE_PIC + INT_MASKH);
|
||||
}
|
||||
|
||||
static void score_unmask(struct irq_data *d)
|
||||
{
|
||||
unsigned int irq_source = 63 - d->irq;
|
||||
|
||||
if (irq_source < 32)
|
||||
__raw_writel((__raw_readl(SCORE_PIC + INT_MASKL) & \
|
||||
~(1 << irq_source)), SCORE_PIC + INT_MASKL);
|
||||
else
|
||||
__raw_writel((__raw_readl(SCORE_PIC + INT_MASKH) & \
|
||||
~(1 << (irq_source - 32))), SCORE_PIC + INT_MASKH);
|
||||
}
|
||||
|
||||
struct irq_chip score_irq_chip = {
|
||||
.name = "Score7-level",
|
||||
.irq_mask = score_mask,
|
||||
.irq_mask_ack = score_mask,
|
||||
.irq_unmask = score_unmask,
|
||||
};
|
||||
|
||||
/*
|
||||
* initialise the interrupt system
|
||||
*/
|
||||
void __init init_IRQ(void)
|
||||
{
|
||||
int index;
|
||||
unsigned long target_addr;
|
||||
|
||||
for (index = 0; index < NR_IRQS; ++index)
|
||||
irq_set_chip_and_handler(index, &score_irq_chip,
|
||||
handle_level_irq);
|
||||
|
||||
for (target_addr = IRQ_VECTOR_BASE_ADDR;
|
||||
target_addr <= IRQ_VECTOR_END_ADDR;
|
||||
target_addr += IRQ_VECTOR_SIZE)
|
||||
memcpy((void *)target_addr, \
|
||||
interrupt_exception_vector, IRQ_VECTOR_SIZE);
|
||||
|
||||
__raw_writel(0xffffffff, SCORE_PIC + INT_MASKL);
|
||||
__raw_writel(0xffffffff, SCORE_PIC + INT_MASKH);
|
||||
|
||||
__asm__ __volatile__(
|
||||
"mtcr %0, cr3\n\t"
|
||||
: : "r" (EXCEPTION_VECTOR_BASE_ADDR | \
|
||||
VECTOR_ADDRESS_OFFSET_MODE16));
|
||||
}
|
132
arch/score/kernel/module.c
Normal file
132
arch/score/kernel/module.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* arch/score/kernel/module.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
int apply_relocate(Elf_Shdr *sechdrs, const char *strtab,
|
||||
unsigned int symindex, unsigned int relindex,
|
||||
struct module *me)
|
||||
{
|
||||
Elf32_Shdr *symsec = sechdrs + symindex;
|
||||
Elf32_Shdr *relsec = sechdrs + relindex;
|
||||
Elf32_Shdr *dstsec = sechdrs + relsec->sh_info;
|
||||
Elf32_Rel *rel = (void *)relsec->sh_addr;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) {
|
||||
unsigned long loc;
|
||||
Elf32_Sym *sym;
|
||||
s32 r_offset;
|
||||
|
||||
r_offset = ELF32_R_SYM(rel->r_info);
|
||||
if ((r_offset < 0) ||
|
||||
(r_offset > (symsec->sh_size / sizeof(Elf32_Sym)))) {
|
||||
printk(KERN_ERR "%s: bad relocation, section %d reloc %d\n",
|
||||
me->name, relindex, i);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
sym = ((Elf32_Sym *)symsec->sh_addr) + r_offset;
|
||||
|
||||
if ((rel->r_offset < 0) ||
|
||||
(rel->r_offset > dstsec->sh_size - sizeof(u32))) {
|
||||
printk(KERN_ERR "%s: out of bounds relocation, "
|
||||
"section %d reloc %d offset %d size %d\n",
|
||||
me->name, relindex, i, rel->r_offset,
|
||||
dstsec->sh_size);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
loc = dstsec->sh_addr + rel->r_offset;
|
||||
switch (ELF32_R_TYPE(rel->r_info)) {
|
||||
case R_SCORE_NONE:
|
||||
break;
|
||||
case R_SCORE_ABS32:
|
||||
*(unsigned long *)loc += sym->st_value;
|
||||
break;
|
||||
case R_SCORE_HI16:
|
||||
break;
|
||||
case R_SCORE_LO16: {
|
||||
unsigned long hi16_offset, offset;
|
||||
unsigned long uvalue;
|
||||
unsigned long temp, temp_hi;
|
||||
temp_hi = *((unsigned long *)loc - 1);
|
||||
temp = *(unsigned long *)loc;
|
||||
|
||||
hi16_offset = (((((temp_hi) >> 16) & 0x3) << 15) |
|
||||
((temp_hi) & 0x7fff)) >> 1;
|
||||
offset = ((temp >> 16 & 0x03) << 15) |
|
||||
((temp & 0x7fff) >> 1);
|
||||
offset = (hi16_offset << 16) | (offset & 0xffff);
|
||||
uvalue = sym->st_value + offset;
|
||||
hi16_offset = (uvalue >> 16) << 1;
|
||||
|
||||
temp_hi = ((temp_hi) & (~(0x37fff))) |
|
||||
(hi16_offset & 0x7fff) |
|
||||
((hi16_offset << 1) & 0x30000);
|
||||
*((unsigned long *)loc - 1) = temp_hi;
|
||||
|
||||
offset = (uvalue & 0xffff) << 1;
|
||||
temp = (temp & (~(0x37fff))) | (offset & 0x7fff) |
|
||||
((offset << 1) & 0x30000);
|
||||
*(unsigned long *)loc = temp;
|
||||
break;
|
||||
}
|
||||
case R_SCORE_24: {
|
||||
unsigned long hi16_offset, offset;
|
||||
unsigned long uvalue;
|
||||
unsigned long temp;
|
||||
|
||||
temp = *(unsigned long *)loc;
|
||||
offset = (temp & 0x03FF7FFE);
|
||||
hi16_offset = (offset & 0xFFFF0000);
|
||||
offset = (hi16_offset | ((offset & 0xFFFF) << 1)) >> 2;
|
||||
|
||||
uvalue = (sym->st_value + offset) >> 1;
|
||||
uvalue = uvalue & 0x00ffffff;
|
||||
|
||||
temp = (temp & 0xfc008001) |
|
||||
((uvalue << 2) & 0x3ff0000) |
|
||||
((uvalue & 0x3fff) << 1);
|
||||
*(unsigned long *)loc = temp;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printk(KERN_ERR "%s: unknown relocation: %u\n",
|
||||
me->name, ELF32_R_TYPE(rel->r_info));
|
||||
return -ENOEXEC;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Given an address, look for it in the module exception tables. */
|
||||
const struct exception_table_entry *search_module_dbetables(unsigned long addr)
|
||||
{
|
||||
return NULL;
|
||||
}
|
123
arch/score/kernel/process.c
Normal file
123
arch/score/kernel/process.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* arch/score/kernel/process.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/elfcore.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
void (*pm_power_off)(void);
|
||||
EXPORT_SYMBOL(pm_power_off);
|
||||
|
||||
/* If or when software machine-restart is implemented, add code here. */
|
||||
void machine_restart(char *command) {}
|
||||
|
||||
/* If or when software machine-halt is implemented, add code here. */
|
||||
void machine_halt(void) {}
|
||||
|
||||
/* If or when software machine-power-off is implemented, add code here. */
|
||||
void machine_power_off(void) {}
|
||||
|
||||
void ret_from_fork(void);
|
||||
void ret_from_kernel_thread(void);
|
||||
|
||||
void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp)
|
||||
{
|
||||
unsigned long status;
|
||||
|
||||
/* New thread loses kernel privileges. */
|
||||
status = regs->cp0_psr & ~(KU_MASK);
|
||||
status |= KU_USER;
|
||||
regs->cp0_psr = status;
|
||||
regs->cp0_epc = pc;
|
||||
regs->regs[0] = sp;
|
||||
}
|
||||
|
||||
void exit_thread(void) {}
|
||||
|
||||
/*
|
||||
* When a process does an "exec", machine state like FPU and debug
|
||||
* registers need to be reset. This is a hook function for that.
|
||||
* Currently we don't have any such state to reset, so this is empty.
|
||||
*/
|
||||
void flush_thread(void) {}
|
||||
|
||||
/*
|
||||
* set up the kernel stack and exception frames for a new process
|
||||
*/
|
||||
int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long arg, struct task_struct *p)
|
||||
{
|
||||
struct thread_info *ti = task_thread_info(p);
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
struct pt_regs *regs = current_pt_regs();
|
||||
|
||||
p->thread.reg0 = (unsigned long) childregs;
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
p->thread.reg12 = usp;
|
||||
p->thread.reg13 = arg;
|
||||
p->thread.reg3 = (unsigned long) ret_from_kernel_thread;
|
||||
} else {
|
||||
*childregs = *current_pt_regs();
|
||||
childregs->regs[7] = 0; /* Clear error flag */
|
||||
childregs->regs[4] = 0; /* Child gets zero as return value */
|
||||
if (usp)
|
||||
childregs->regs[0] = usp; /* user fork */
|
||||
p->thread.reg3 = (unsigned long) ret_from_fork;
|
||||
}
|
||||
|
||||
p->thread.cp0_psr = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fill in the fpu structure for a core dump. */
|
||||
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned long thread_saved_pc(struct task_struct *tsk)
|
||||
{
|
||||
return task_pt_regs(tsk)->cp0_epc;
|
||||
}
|
||||
|
||||
unsigned long get_wchan(struct task_struct *task)
|
||||
{
|
||||
if (!task || task == current || task->state == TASK_RUNNING)
|
||||
return 0;
|
||||
|
||||
if (!task_stack_page(task))
|
||||
return 0;
|
||||
|
||||
return task_pt_regs(task)->cp0_epc;
|
||||
}
|
||||
|
||||
unsigned long arch_align_stack(unsigned long sp)
|
||||
{
|
||||
return sp;
|
||||
}
|
383
arch/score/kernel/ptrace.c
Normal file
383
arch/score/kernel/ptrace.c
Normal file
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* arch/score/kernel/ptrace.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/elf.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/regset.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* retrieve the contents of SCORE userspace general registers
|
||||
*/
|
||||
static int genregs_get(struct task_struct *target,
|
||||
const struct user_regset *regset,
|
||||
unsigned int pos, unsigned int count,
|
||||
void *kbuf, void __user *ubuf)
|
||||
{
|
||||
const struct pt_regs *regs = task_pt_regs(target);
|
||||
int ret;
|
||||
|
||||
/* skip 9 * sizeof(unsigned long) not use for pt_regs */
|
||||
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
|
||||
0, offsetof(struct pt_regs, regs));
|
||||
|
||||
/* r0 - r31, cel, ceh, sr0, sr1, sr2, epc, ema, psr, ecr, condition */
|
||||
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
||||
regs->regs,
|
||||
offsetof(struct pt_regs, regs),
|
||||
offsetof(struct pt_regs, cp0_condition));
|
||||
|
||||
if (!ret)
|
||||
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
|
||||
sizeof(struct pt_regs), -1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* update the contents of the SCORE userspace general registers
|
||||
*/
|
||||
static int genregs_set(struct task_struct *target,
|
||||
const struct user_regset *regset,
|
||||
unsigned int pos, unsigned int count,
|
||||
const void *kbuf, const void __user *ubuf)
|
||||
{
|
||||
struct pt_regs *regs = task_pt_regs(target);
|
||||
int ret;
|
||||
|
||||
/* skip 9 * sizeof(unsigned long) */
|
||||
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
|
||||
0, offsetof(struct pt_regs, regs));
|
||||
|
||||
/* r0 - r31, cel, ceh, sr0, sr1, sr2, epc, ema, psr, ecr, condition */
|
||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||
regs->regs,
|
||||
offsetof(struct pt_regs, regs),
|
||||
offsetof(struct pt_regs, cp0_condition));
|
||||
|
||||
if (!ret)
|
||||
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
|
||||
sizeof(struct pt_regs), -1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the register sets available on the score7 under Linux
|
||||
*/
|
||||
enum score7_regset {
|
||||
REGSET_GENERAL,
|
||||
};
|
||||
|
||||
static const struct user_regset score7_regsets[] = {
|
||||
[REGSET_GENERAL] = {
|
||||
.core_note_type = NT_PRSTATUS,
|
||||
.n = ELF_NGREG,
|
||||
.size = sizeof(long),
|
||||
.align = sizeof(long),
|
||||
.get = genregs_get,
|
||||
.set = genregs_set,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct user_regset_view user_score_native_view = {
|
||||
.name = "score7",
|
||||
.e_machine = EM_SCORE7,
|
||||
.regsets = score7_regsets,
|
||||
.n = ARRAY_SIZE(score7_regsets),
|
||||
};
|
||||
|
||||
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
|
||||
{
|
||||
return &user_score_native_view;
|
||||
}
|
||||
|
||||
static int is_16bitinsn(unsigned long insn)
|
||||
{
|
||||
if ((insn & INSN32_MASK) == INSN32_MASK)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
read_tsk_long(struct task_struct *child,
|
||||
unsigned long addr, unsigned long *res)
|
||||
{
|
||||
int copied;
|
||||
|
||||
copied = access_process_vm(child, addr, res, sizeof(*res), 0);
|
||||
|
||||
return copied != sizeof(*res) ? -EIO : 0;
|
||||
}
|
||||
|
||||
int
|
||||
read_tsk_short(struct task_struct *child,
|
||||
unsigned long addr, unsigned short *res)
|
||||
{
|
||||
int copied;
|
||||
|
||||
copied = access_process_vm(child, addr, res, sizeof(*res), 0);
|
||||
|
||||
return copied != sizeof(*res) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
write_tsk_short(struct task_struct *child,
|
||||
unsigned long addr, unsigned short val)
|
||||
{
|
||||
int copied;
|
||||
|
||||
copied = access_process_vm(child, addr, &val, sizeof(val), 1);
|
||||
|
||||
return copied != sizeof(val) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
write_tsk_long(struct task_struct *child,
|
||||
unsigned long addr, unsigned long val)
|
||||
{
|
||||
int copied;
|
||||
|
||||
copied = access_process_vm(child, addr, &val, sizeof(val), 1);
|
||||
|
||||
return copied != sizeof(val) ? -EIO : 0;
|
||||
}
|
||||
|
||||
void user_enable_single_step(struct task_struct *child)
|
||||
{
|
||||
/* far_epc is the target of branch */
|
||||
unsigned int epc, far_epc = 0;
|
||||
unsigned long epc_insn, far_epc_insn;
|
||||
int ninsn_type; /* next insn type 0=16b, 1=32b */
|
||||
unsigned int tmp, tmp2;
|
||||
struct pt_regs *regs = task_pt_regs(child);
|
||||
child->thread.single_step = 1;
|
||||
child->thread.ss_nextcnt = 1;
|
||||
epc = regs->cp0_epc;
|
||||
|
||||
read_tsk_long(child, epc, &epc_insn);
|
||||
|
||||
if (is_16bitinsn(epc_insn)) {
|
||||
if ((epc_insn & J16M) == J16) {
|
||||
tmp = epc_insn & 0xFFE;
|
||||
epc = (epc & 0xFFFFF000) | tmp;
|
||||
} else if ((epc_insn & B16M) == B16) {
|
||||
child->thread.ss_nextcnt = 2;
|
||||
tmp = (epc_insn & 0xFF) << 1;
|
||||
tmp = tmp << 23;
|
||||
tmp = (unsigned int)((int) tmp >> 23);
|
||||
far_epc = epc + tmp;
|
||||
epc += 2;
|
||||
} else if ((epc_insn & BR16M) == BR16) {
|
||||
child->thread.ss_nextcnt = 2;
|
||||
tmp = (epc_insn >> 4) & 0xF;
|
||||
far_epc = regs->regs[tmp];
|
||||
epc += 2;
|
||||
} else
|
||||
epc += 2;
|
||||
} else {
|
||||
if ((epc_insn & J32M) == J32) {
|
||||
tmp = epc_insn & 0x03FFFFFE;
|
||||
tmp2 = tmp & 0x7FFF;
|
||||
tmp = (((tmp >> 16) & 0x3FF) << 15) | tmp2;
|
||||
epc = (epc & 0xFFC00000) | tmp;
|
||||
} else if ((epc_insn & B32M) == B32) {
|
||||
child->thread.ss_nextcnt = 2;
|
||||
tmp = epc_insn & 0x03FFFFFE; /* discard LK bit */
|
||||
tmp2 = tmp & 0x3FF;
|
||||
tmp = (((tmp >> 16) & 0x3FF) << 10) | tmp2; /* 20bit */
|
||||
tmp = tmp << 12;
|
||||
tmp = (unsigned int)((int) tmp >> 12);
|
||||
far_epc = epc + tmp;
|
||||
epc += 4;
|
||||
} else if ((epc_insn & BR32M) == BR32) {
|
||||
child->thread.ss_nextcnt = 2;
|
||||
tmp = (epc_insn >> 16) & 0x1F;
|
||||
far_epc = regs->regs[tmp];
|
||||
epc += 4;
|
||||
} else
|
||||
epc += 4;
|
||||
}
|
||||
|
||||
if (child->thread.ss_nextcnt == 1) {
|
||||
read_tsk_long(child, epc, &epc_insn);
|
||||
|
||||
if (is_16bitinsn(epc_insn)) {
|
||||
write_tsk_short(child, epc, SINGLESTEP16_INSN);
|
||||
ninsn_type = 0;
|
||||
} else {
|
||||
write_tsk_long(child, epc, SINGLESTEP32_INSN);
|
||||
ninsn_type = 1;
|
||||
}
|
||||
|
||||
if (ninsn_type == 0) { /* 16bits */
|
||||
child->thread.insn1_type = 0;
|
||||
child->thread.addr1 = epc;
|
||||
/* the insn may have 32bit data */
|
||||
child->thread.insn1 = (short)epc_insn;
|
||||
} else {
|
||||
child->thread.insn1_type = 1;
|
||||
child->thread.addr1 = epc;
|
||||
child->thread.insn1 = epc_insn;
|
||||
}
|
||||
} else {
|
||||
/* branch! have two target child->thread.ss_nextcnt=2 */
|
||||
read_tsk_long(child, epc, &epc_insn);
|
||||
read_tsk_long(child, far_epc, &far_epc_insn);
|
||||
if (is_16bitinsn(epc_insn)) {
|
||||
write_tsk_short(child, epc, SINGLESTEP16_INSN);
|
||||
ninsn_type = 0;
|
||||
} else {
|
||||
write_tsk_long(child, epc, SINGLESTEP32_INSN);
|
||||
ninsn_type = 1;
|
||||
}
|
||||
|
||||
if (ninsn_type == 0) { /* 16bits */
|
||||
child->thread.insn1_type = 0;
|
||||
child->thread.addr1 = epc;
|
||||
/* the insn may have 32bit data */
|
||||
child->thread.insn1 = (short)epc_insn;
|
||||
} else {
|
||||
child->thread.insn1_type = 1;
|
||||
child->thread.addr1 = epc;
|
||||
child->thread.insn1 = epc_insn;
|
||||
}
|
||||
|
||||
if (is_16bitinsn(far_epc_insn)) {
|
||||
write_tsk_short(child, far_epc, SINGLESTEP16_INSN);
|
||||
ninsn_type = 0;
|
||||
} else {
|
||||
write_tsk_long(child, far_epc, SINGLESTEP32_INSN);
|
||||
ninsn_type = 1;
|
||||
}
|
||||
|
||||
if (ninsn_type == 0) { /* 16bits */
|
||||
child->thread.insn2_type = 0;
|
||||
child->thread.addr2 = far_epc;
|
||||
/* the insn may have 32bit data */
|
||||
child->thread.insn2 = (short)far_epc_insn;
|
||||
} else {
|
||||
child->thread.insn2_type = 1;
|
||||
child->thread.addr2 = far_epc;
|
||||
child->thread.insn2 = far_epc_insn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void user_disable_single_step(struct task_struct *child)
|
||||
{
|
||||
if (child->thread.insn1_type == 0)
|
||||
write_tsk_short(child, child->thread.addr1,
|
||||
child->thread.insn1);
|
||||
|
||||
if (child->thread.insn1_type == 1)
|
||||
write_tsk_long(child, child->thread.addr1,
|
||||
child->thread.insn1);
|
||||
|
||||
if (child->thread.ss_nextcnt == 2) { /* branch */
|
||||
if (child->thread.insn1_type == 0)
|
||||
write_tsk_short(child, child->thread.addr1,
|
||||
child->thread.insn1);
|
||||
if (child->thread.insn1_type == 1)
|
||||
write_tsk_long(child, child->thread.addr1,
|
||||
child->thread.insn1);
|
||||
if (child->thread.insn2_type == 0)
|
||||
write_tsk_short(child, child->thread.addr2,
|
||||
child->thread.insn2);
|
||||
if (child->thread.insn2_type == 1)
|
||||
write_tsk_long(child, child->thread.addr2,
|
||||
child->thread.insn2);
|
||||
}
|
||||
|
||||
child->thread.single_step = 0;
|
||||
child->thread.ss_nextcnt = 0;
|
||||
}
|
||||
|
||||
void ptrace_disable(struct task_struct *child)
|
||||
{
|
||||
user_disable_single_step(child);
|
||||
}
|
||||
|
||||
long
|
||||
arch_ptrace(struct task_struct *child, long request,
|
||||
unsigned long addr, unsigned long data)
|
||||
{
|
||||
int ret;
|
||||
unsigned long __user *datap = (void __user *)data;
|
||||
|
||||
switch (request) {
|
||||
case PTRACE_GETREGS:
|
||||
ret = copy_regset_to_user(child, &user_score_native_view,
|
||||
REGSET_GENERAL,
|
||||
0, sizeof(struct pt_regs),
|
||||
datap);
|
||||
break;
|
||||
|
||||
case PTRACE_SETREGS:
|
||||
ret = copy_regset_from_user(child, &user_score_native_view,
|
||||
REGSET_GENERAL,
|
||||
0, sizeof(struct pt_regs),
|
||||
datap);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notification of system call entry/exit
|
||||
* - triggered by current->work.syscall_trace
|
||||
*/
|
||||
asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
|
||||
{
|
||||
if (!(current->ptrace & PT_PTRACED))
|
||||
return;
|
||||
|
||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return;
|
||||
|
||||
/* The 0x80 provides a way for the tracing parent to distinguish
|
||||
between a syscall stop and SIGTRAP delivery. */
|
||||
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ?
|
||||
0x80 : 0));
|
||||
|
||||
/*
|
||||
* this isn't the same as continuing with a signal, but it will do
|
||||
* for normal use. strace only continues with a signal if the
|
||||
* stopping signal is not SIGTRAP. -brl
|
||||
*/
|
||||
if (current->exit_code) {
|
||||
send_sig(current->exit_code, current, 1);
|
||||
current->exit_code = 0;
|
||||
}
|
||||
}
|
162
arch/score/kernel/setup.c
Normal file
162
arch/score/kernel/setup.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* arch/score/kernel/setup.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/initrd.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/screen_info.h>
|
||||
|
||||
#include <asm-generic/sections.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
struct screen_info screen_info;
|
||||
unsigned long kernelsp;
|
||||
|
||||
static char command_line[COMMAND_LINE_SIZE];
|
||||
static struct resource code_resource = { .name = "Kernel code",};
|
||||
static struct resource data_resource = { .name = "Kernel data",};
|
||||
|
||||
static void __init bootmem_init(void)
|
||||
{
|
||||
unsigned long start_pfn, bootmap_size;
|
||||
unsigned long size = initrd_end - initrd_start;
|
||||
|
||||
start_pfn = PFN_UP(__pa(&_end));
|
||||
|
||||
min_low_pfn = PFN_UP(MEMORY_START);
|
||||
max_low_pfn = PFN_UP(MEMORY_START + MEMORY_SIZE);
|
||||
max_mapnr = max_low_pfn - min_low_pfn;
|
||||
|
||||
/* Initialize the boot-time allocator with low memory only. */
|
||||
bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn,
|
||||
min_low_pfn, max_low_pfn);
|
||||
memblock_add_node(PFN_PHYS(min_low_pfn),
|
||||
PFN_PHYS(max_low_pfn - min_low_pfn), 0);
|
||||
|
||||
free_bootmem(PFN_PHYS(start_pfn),
|
||||
(max_low_pfn - start_pfn) << PAGE_SHIFT);
|
||||
memory_present(0, start_pfn, max_low_pfn);
|
||||
|
||||
/* Reserve space for the bootmem bitmap. */
|
||||
reserve_bootmem(PFN_PHYS(start_pfn), bootmap_size, BOOTMEM_DEFAULT);
|
||||
|
||||
if (size == 0) {
|
||||
printk(KERN_INFO "Initrd not found or empty");
|
||||
goto disable;
|
||||
}
|
||||
|
||||
if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) {
|
||||
printk(KERN_ERR "Initrd extends beyond end of memory");
|
||||
goto disable;
|
||||
}
|
||||
|
||||
/* Reserve space for the initrd bitmap. */
|
||||
reserve_bootmem(__pa(initrd_start), size, BOOTMEM_DEFAULT);
|
||||
initrd_below_start_ok = 1;
|
||||
|
||||
pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n",
|
||||
initrd_start, size);
|
||||
return;
|
||||
disable:
|
||||
printk(KERN_CONT " - disabling initrd\n");
|
||||
initrd_start = 0;
|
||||
initrd_end = 0;
|
||||
}
|
||||
|
||||
static void __init resource_init(void)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
code_resource.start = __pa(&_text);
|
||||
code_resource.end = __pa(&_etext) - 1;
|
||||
data_resource.start = __pa(&_etext);
|
||||
data_resource.end = __pa(&_edata) - 1;
|
||||
|
||||
res = alloc_bootmem(sizeof(struct resource));
|
||||
res->name = "System RAM";
|
||||
res->start = MEMORY_START;
|
||||
res->end = MEMORY_START + MEMORY_SIZE - 1;
|
||||
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
||||
request_resource(&iomem_resource, res);
|
||||
|
||||
request_resource(res, &code_resource);
|
||||
request_resource(res, &data_resource);
|
||||
}
|
||||
|
||||
void __init setup_arch(char **cmdline_p)
|
||||
{
|
||||
randomize_va_space = 0;
|
||||
*cmdline_p = command_line;
|
||||
|
||||
cpu_cache_init();
|
||||
tlb_init();
|
||||
bootmem_init();
|
||||
paging_init();
|
||||
resource_init();
|
||||
}
|
||||
|
||||
static int show_cpuinfo(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned long n = (unsigned long) v - 1;
|
||||
|
||||
seq_printf(m, "processor\t\t: %ld\n", n);
|
||||
seq_printf(m, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *c_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
unsigned long i = *pos;
|
||||
|
||||
return i < 1 ? (void *) (i + 1) : NULL;
|
||||
}
|
||||
|
||||
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
return c_start(m, pos);
|
||||
}
|
||||
|
||||
static void c_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
const struct seq_operations cpuinfo_op = {
|
||||
.start = c_start,
|
||||
.next = c_next,
|
||||
.stop = c_stop,
|
||||
.show = show_cpuinfo,
|
||||
};
|
||||
|
||||
static int __init topology_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
subsys_initcall(topology_init);
|
308
arch/score/kernel/signal.c
Normal file
308
arch/score/kernel/signal.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* arch/score/kernel/signal.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/tracehook.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/syscalls.h>
|
||||
#include <asm/ucontext.h>
|
||||
|
||||
struct rt_sigframe {
|
||||
u32 rs_ass[4]; /* argument save space */
|
||||
u32 rs_code[2]; /* signal trampoline */
|
||||
struct siginfo rs_info;
|
||||
struct ucontext rs_uc;
|
||||
};
|
||||
|
||||
static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned long reg;
|
||||
|
||||
reg = regs->cp0_epc; err |= __put_user(reg, &sc->sc_pc);
|
||||
err |= __put_user(regs->cp0_psr, &sc->sc_psr);
|
||||
err |= __put_user(regs->cp0_condition, &sc->sc_condition);
|
||||
|
||||
|
||||
#define save_gp_reg(i) { \
|
||||
reg = regs->regs[i]; \
|
||||
err |= __put_user(reg, &sc->sc_regs[i]); \
|
||||
} while (0)
|
||||
save_gp_reg(0); save_gp_reg(1); save_gp_reg(2);
|
||||
save_gp_reg(3); save_gp_reg(4); save_gp_reg(5);
|
||||
save_gp_reg(6); save_gp_reg(7); save_gp_reg(8);
|
||||
save_gp_reg(9); save_gp_reg(10); save_gp_reg(11);
|
||||
save_gp_reg(12); save_gp_reg(13); save_gp_reg(14);
|
||||
save_gp_reg(15); save_gp_reg(16); save_gp_reg(17);
|
||||
save_gp_reg(18); save_gp_reg(19); save_gp_reg(20);
|
||||
save_gp_reg(21); save_gp_reg(22); save_gp_reg(23);
|
||||
save_gp_reg(24); save_gp_reg(25); save_gp_reg(26);
|
||||
save_gp_reg(27); save_gp_reg(28); save_gp_reg(29);
|
||||
#undef save_gp_reg
|
||||
|
||||
reg = regs->ceh; err |= __put_user(reg, &sc->sc_mdceh);
|
||||
reg = regs->cel; err |= __put_user(reg, &sc->sc_mdcel);
|
||||
err |= __put_user(regs->cp0_ecr, &sc->sc_ecr);
|
||||
err |= __put_user(regs->cp0_ema, &sc->sc_ema);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
|
||||
{
|
||||
int err = 0;
|
||||
u32 reg;
|
||||
|
||||
err |= __get_user(regs->cp0_epc, &sc->sc_pc);
|
||||
err |= __get_user(regs->cp0_condition, &sc->sc_condition);
|
||||
|
||||
err |= __get_user(reg, &sc->sc_mdceh);
|
||||
regs->ceh = (int) reg;
|
||||
err |= __get_user(reg, &sc->sc_mdcel);
|
||||
regs->cel = (int) reg;
|
||||
|
||||
err |= __get_user(reg, &sc->sc_psr);
|
||||
regs->cp0_psr = (int) reg;
|
||||
err |= __get_user(reg, &sc->sc_ecr);
|
||||
regs->cp0_ecr = (int) reg;
|
||||
err |= __get_user(reg, &sc->sc_ema);
|
||||
regs->cp0_ema = (int) reg;
|
||||
|
||||
#define restore_gp_reg(i) do { \
|
||||
err |= __get_user(reg, &sc->sc_regs[i]); \
|
||||
regs->regs[i] = reg; \
|
||||
} while (0)
|
||||
restore_gp_reg(0); restore_gp_reg(1); restore_gp_reg(2);
|
||||
restore_gp_reg(3); restore_gp_reg(4); restore_gp_reg(5);
|
||||
restore_gp_reg(6); restore_gp_reg(7); restore_gp_reg(8);
|
||||
restore_gp_reg(9); restore_gp_reg(10); restore_gp_reg(11);
|
||||
restore_gp_reg(12); restore_gp_reg(13); restore_gp_reg(14);
|
||||
restore_gp_reg(15); restore_gp_reg(16); restore_gp_reg(17);
|
||||
restore_gp_reg(18); restore_gp_reg(19); restore_gp_reg(20);
|
||||
restore_gp_reg(21); restore_gp_reg(22); restore_gp_reg(23);
|
||||
restore_gp_reg(24); restore_gp_reg(25); restore_gp_reg(26);
|
||||
restore_gp_reg(27); restore_gp_reg(28); restore_gp_reg(29);
|
||||
#undef restore_gp_reg
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine which stack to use..
|
||||
*/
|
||||
static void __user *get_sigframe(struct k_sigaction *ka,
|
||||
struct pt_regs *regs, size_t frame_size)
|
||||
{
|
||||
unsigned long sp;
|
||||
|
||||
/* Default to using normal stack */
|
||||
sp = regs->regs[0];
|
||||
sp -= 32;
|
||||
|
||||
/* This is the X/Open sanctioned signal stack switching. */
|
||||
if ((ka->sa.sa_flags & SA_ONSTACK) && (!on_sig_stack(sp)))
|
||||
sp = current->sas_ss_sp + current->sas_ss_size;
|
||||
|
||||
return (void __user*)((sp - frame_size) & ~7);
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
score_rt_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
struct rt_sigframe __user *frame;
|
||||
sigset_t set;
|
||||
int sig;
|
||||
|
||||
/* Always make any pending restarted system calls return -EINTR */
|
||||
current_thread_info()->restart_block.fn = do_no_restart_syscall;
|
||||
|
||||
frame = (struct rt_sigframe __user *) regs->regs[0];
|
||||
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
|
||||
goto badframe;
|
||||
if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
|
||||
goto badframe;
|
||||
|
||||
set_current_blocked(&set);
|
||||
|
||||
sig = restore_sigcontext(regs, &frame->rs_uc.uc_mcontext);
|
||||
if (sig < 0)
|
||||
goto badframe;
|
||||
else if (sig)
|
||||
force_sig(sig, current);
|
||||
|
||||
if (restore_altstack(&frame->rs_uc.uc_stack))
|
||||
goto badframe;
|
||||
regs->is_syscall = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"mv\tr0, %0\n\t"
|
||||
"la\tr8, syscall_exit\n\t"
|
||||
"br\tr8\n\t"
|
||||
: : "r" (regs) : "r8");
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs,
|
||||
sigset_t *set)
|
||||
{
|
||||
struct rt_sigframe __user *frame;
|
||||
int err = 0;
|
||||
|
||||
frame = get_sigframe(&ksig->ka, regs, sizeof(*frame));
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Set up the return code ...
|
||||
*
|
||||
* li v0, __NR_rt_sigreturn
|
||||
* syscall
|
||||
*/
|
||||
err |= __put_user(0x87788000 + __NR_rt_sigreturn*2,
|
||||
frame->rs_code + 0);
|
||||
err |= __put_user(0x80008002, frame->rs_code + 1);
|
||||
flush_cache_sigtramp((unsigned long) frame->rs_code);
|
||||
|
||||
err |= copy_siginfo_to_user(&frame->rs_info, &ksig->info);
|
||||
err |= __put_user(0, &frame->rs_uc.uc_flags);
|
||||
err |= __put_user(NULL, &frame->rs_uc.uc_link);
|
||||
err |= __save_altstack(&frame->rs_uc.uc_stack, regs->regs[0]);
|
||||
err |= setup_sigcontext(regs, &frame->rs_uc.uc_mcontext);
|
||||
err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set));
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
regs->regs[0] = (unsigned long) frame;
|
||||
regs->regs[3] = (unsigned long) frame->rs_code;
|
||||
regs->regs[4] = ksig->sig;
|
||||
regs->regs[5] = (unsigned long) &frame->rs_info;
|
||||
regs->regs[6] = (unsigned long) &frame->rs_uc;
|
||||
regs->regs[29] = (unsigned long) ksig->ka.sa.sa_handler;
|
||||
regs->cp0_epc = (unsigned long) ksig->ka.sa.sa_handler;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (regs->is_syscall) {
|
||||
switch (regs->regs[4]) {
|
||||
case ERESTART_RESTARTBLOCK:
|
||||
case ERESTARTNOHAND:
|
||||
regs->regs[4] = EINTR;
|
||||
break;
|
||||
case ERESTARTSYS:
|
||||
if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
|
||||
regs->regs[4] = EINTR;
|
||||
break;
|
||||
}
|
||||
case ERESTARTNOINTR:
|
||||
regs->regs[4] = regs->orig_r4;
|
||||
regs->regs[7] = regs->orig_r7;
|
||||
regs->cp0_epc -= 8;
|
||||
}
|
||||
|
||||
regs->is_syscall = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the stack frame
|
||||
*/
|
||||
ret = setup_rt_frame(ksig, regs, sigmask_to_save());
|
||||
|
||||
signal_setup_done(ret, ksig, 0);
|
||||
}
|
||||
|
||||
static void do_signal(struct pt_regs *regs)
|
||||
{
|
||||
struct ksignal ksig;
|
||||
|
||||
/*
|
||||
* We want the common case to go fast, which is why we may in certain
|
||||
* cases get here from kernel mode. Just return without doing anything
|
||||
* if so.
|
||||
*/
|
||||
if (!user_mode(regs))
|
||||
return;
|
||||
|
||||
if (get_signal(&ksig)) {
|
||||
/* Actually deliver the signal. */
|
||||
handle_signal(&ksig, regs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (regs->is_syscall) {
|
||||
if (regs->regs[4] == ERESTARTNOHAND ||
|
||||
regs->regs[4] == ERESTARTSYS ||
|
||||
regs->regs[4] == ERESTARTNOINTR) {
|
||||
regs->regs[4] = regs->orig_r4;
|
||||
regs->regs[7] = regs->orig_r7;
|
||||
regs->cp0_epc -= 8;
|
||||
}
|
||||
|
||||
if (regs->regs[4] == ERESTART_RESTARTBLOCK) {
|
||||
regs->regs[27] = __NR_restart_syscall;
|
||||
regs->regs[4] = regs->orig_r4;
|
||||
regs->regs[7] = regs->orig_r7;
|
||||
regs->cp0_epc -= 8;
|
||||
}
|
||||
|
||||
regs->is_syscall = 0; /* Don't deal with this again. */
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's no signal to deliver, we just put the saved sigmask
|
||||
* back
|
||||
*/
|
||||
restore_saved_sigmask();
|
||||
}
|
||||
|
||||
/*
|
||||
* notification of userspace execution resumption
|
||||
* - triggered by the TIF_WORK_MASK flags
|
||||
*/
|
||||
asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
|
||||
__u32 thread_info_flags)
|
||||
{
|
||||
/* deal with pending signal delivery */
|
||||
if (thread_info_flags & _TIF_SIGPENDING)
|
||||
do_signal(regs);
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
}
|
||||
}
|
12
arch/score/kernel/sys_call_table.c
Normal file
12
arch/score/kernel/sys_call_table.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <linux/syscalls.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <asm/syscalls.h>
|
||||
|
||||
#undef __SYSCALL
|
||||
#define __SYSCALL(nr, call) [nr] = (call),
|
||||
|
||||
void *sys_call_table[__NR_syscalls] = {
|
||||
#include <asm/unistd.h>
|
||||
};
|
50
arch/score/kernel/sys_score.c
Normal file
50
arch/score/kernel/sys_score.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* arch/score/kernel/syscall.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <asm/syscalls.h>
|
||||
|
||||
asmlinkage long
|
||||
sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
|
||||
unsigned long flags, unsigned long fd, unsigned long pgoff)
|
||||
{
|
||||
return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_mmap(unsigned long addr, unsigned long len, unsigned long prot,
|
||||
unsigned long flags, unsigned long fd, off_t offset)
|
||||
{
|
||||
if (unlikely(offset & ~PAGE_MASK))
|
||||
return -EINVAL;
|
||||
return sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
|
||||
}
|
99
arch/score/kernel/time.c
Normal file
99
arch/score/kernel/time.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* arch/score/kernel/time.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <asm/scoreregs.h>
|
||||
|
||||
static irqreturn_t timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *evdev = dev_id;
|
||||
|
||||
/* clear timer interrupt flag */
|
||||
outl(1, P_TIMER0_CPP_REG);
|
||||
evdev->event_handler(evdev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irqaction timer_irq = {
|
||||
.handler = timer_interrupt,
|
||||
.flags = IRQF_TIMER,
|
||||
.name = "timer",
|
||||
};
|
||||
|
||||
static int score_timer_set_next_event(unsigned long delta,
|
||||
struct clock_event_device *evdev)
|
||||
{
|
||||
outl((TMR_M_PERIODIC | TMR_IE_ENABLE), P_TIMER0_CTRL);
|
||||
outl(delta, P_TIMER0_PRELOAD);
|
||||
outl(inl(P_TIMER0_CTRL) | TMR_ENABLE, P_TIMER0_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void score_timer_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *evdev)
|
||||
{
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
outl((TMR_M_PERIODIC | TMR_IE_ENABLE), P_TIMER0_CTRL);
|
||||
outl(SYSTEM_CLOCK/HZ, P_TIMER0_PRELOAD);
|
||||
outl(inl(P_TIMER0_CTRL) | TMR_ENABLE, P_TIMER0_CTRL);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static struct clock_event_device score_clockevent = {
|
||||
.name = "score_clockevent",
|
||||
.features = CLOCK_EVT_FEAT_PERIODIC,
|
||||
.shift = 16,
|
||||
.set_next_event = score_timer_set_next_event,
|
||||
.set_mode = score_timer_set_mode,
|
||||
};
|
||||
|
||||
void __init time_init(void)
|
||||
{
|
||||
timer_irq.dev_id = &score_clockevent;
|
||||
setup_irq(IRQ_TIMER , &timer_irq);
|
||||
|
||||
/* setup COMPARE clockevent */
|
||||
score_clockevent.mult = div_sc(SYSTEM_CLOCK, NSEC_PER_SEC,
|
||||
score_clockevent.shift);
|
||||
score_clockevent.max_delta_ns = clockevent_delta2ns((u32)~0,
|
||||
&score_clockevent);
|
||||
score_clockevent.min_delta_ns = clockevent_delta2ns(50,
|
||||
&score_clockevent) + 1;
|
||||
score_clockevent.cpumask = cpumask_of(0);
|
||||
clockevents_register_device(&score_clockevent);
|
||||
}
|
341
arch/score/kernel/traps.c
Normal file
341
arch/score/kernel/traps.c
Normal file
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* arch/score/kernel/traps.c
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
unsigned long exception_handlers[32];
|
||||
|
||||
/*
|
||||
* The architecture-independent show_stack generator
|
||||
*/
|
||||
void show_stack(struct task_struct *task, unsigned long *sp)
|
||||
{
|
||||
int i;
|
||||
long stackdata;
|
||||
|
||||
sp = sp ? sp : (unsigned long *)&sp;
|
||||
|
||||
printk(KERN_NOTICE "Stack: ");
|
||||
i = 1;
|
||||
while ((long) sp & (PAGE_SIZE - 1)) {
|
||||
if (i && ((i % 8) == 0))
|
||||
printk(KERN_NOTICE "\n");
|
||||
if (i > 40) {
|
||||
printk(KERN_NOTICE " ...");
|
||||
break;
|
||||
}
|
||||
|
||||
if (__get_user(stackdata, sp++)) {
|
||||
printk(KERN_NOTICE " (Bad stack address)");
|
||||
break;
|
||||
}
|
||||
|
||||
printk(KERN_NOTICE " %08lx", stackdata);
|
||||
i++;
|
||||
}
|
||||
printk(KERN_NOTICE "\n");
|
||||
}
|
||||
|
||||
static void show_trace(long *sp)
|
||||
{
|
||||
int i;
|
||||
long addr;
|
||||
|
||||
sp = sp ? sp : (long *) &sp;
|
||||
|
||||
printk(KERN_NOTICE "Call Trace: ");
|
||||
i = 1;
|
||||
while ((long) sp & (PAGE_SIZE - 1)) {
|
||||
if (__get_user(addr, sp++)) {
|
||||
if (i && ((i % 6) == 0))
|
||||
printk(KERN_NOTICE "\n");
|
||||
printk(KERN_NOTICE " (Bad stack address)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (kernel_text_address(addr)) {
|
||||
if (i && ((i % 6) == 0))
|
||||
printk(KERN_NOTICE "\n");
|
||||
if (i > 40) {
|
||||
printk(KERN_NOTICE " ...");
|
||||
break;
|
||||
}
|
||||
|
||||
printk(KERN_NOTICE " [<%08lx>]", addr);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
printk(KERN_NOTICE "\n");
|
||||
}
|
||||
|
||||
static void show_code(unsigned int *pc)
|
||||
{
|
||||
long i;
|
||||
|
||||
printk(KERN_NOTICE "\nCode:");
|
||||
|
||||
for (i = -3; i < 6; i++) {
|
||||
unsigned long insn;
|
||||
if (__get_user(insn, pc + i)) {
|
||||
printk(KERN_NOTICE " (Bad address in epc)\n");
|
||||
break;
|
||||
}
|
||||
printk(KERN_NOTICE "%c%08lx%c", (i ? ' ' : '<'),
|
||||
insn, (i ? ' ' : '>'));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: really the generic show_regs should take a const pointer argument.
|
||||
*/
|
||||
void show_regs(struct pt_regs *regs)
|
||||
{
|
||||
show_regs_print_info(KERN_DEFAULT);
|
||||
|
||||
printk("r0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
|
||||
regs->regs[0], regs->regs[1], regs->regs[2], regs->regs[3],
|
||||
regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]);
|
||||
printk("r8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
|
||||
regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11],
|
||||
regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]);
|
||||
printk("r16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
|
||||
regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19],
|
||||
regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]);
|
||||
printk("r24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
|
||||
regs->regs[24], regs->regs[25], regs->regs[26], regs->regs[27],
|
||||
regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]);
|
||||
|
||||
printk("CEH : %08lx\n", regs->ceh);
|
||||
printk("CEL : %08lx\n", regs->cel);
|
||||
|
||||
printk("EMA:%08lx, epc:%08lx %s\nPSR: %08lx\nECR:%08lx\nCondition : %08lx\n",
|
||||
regs->cp0_ema, regs->cp0_epc, print_tainted(), regs->cp0_psr,
|
||||
regs->cp0_ecr, regs->cp0_condition);
|
||||
}
|
||||
|
||||
static void show_registers(struct pt_regs *regs)
|
||||
{
|
||||
show_regs(regs);
|
||||
printk(KERN_NOTICE "Process %s (pid: %d, stackpage=%08lx)\n",
|
||||
current->comm, current->pid, (unsigned long) current);
|
||||
show_stack(current_thread_info()->task, (long *) regs->regs[0]);
|
||||
show_trace((long *) regs->regs[0]);
|
||||
show_code((unsigned int *) regs->cp0_epc);
|
||||
printk(KERN_NOTICE "\n");
|
||||
}
|
||||
|
||||
void __die(const char *str, struct pt_regs *regs, const char *file,
|
||||
const char *func, unsigned long line)
|
||||
{
|
||||
console_verbose();
|
||||
printk("%s", str);
|
||||
if (file && func)
|
||||
printk(" in %s:%s, line %ld", file, func, line);
|
||||
printk(":\n");
|
||||
show_registers(regs);
|
||||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
void __die_if_kernel(const char *str, struct pt_regs *regs,
|
||||
const char *file, const char *func, unsigned long line)
|
||||
{
|
||||
if (!user_mode(regs))
|
||||
__die(str, regs, file, func, line);
|
||||
}
|
||||
|
||||
asmlinkage void do_adelinsn(struct pt_regs *regs)
|
||||
{
|
||||
printk("do_ADE-linsn:ema:0x%08lx:epc:0x%08lx\n",
|
||||
regs->cp0_ema, regs->cp0_epc);
|
||||
die_if_kernel("do_ade execution Exception\n", regs);
|
||||
force_sig(SIGBUS, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_adedata(struct pt_regs *regs)
|
||||
{
|
||||
const struct exception_table_entry *fixup;
|
||||
fixup = search_exception_tables(regs->cp0_epc);
|
||||
if (fixup) {
|
||||
regs->cp0_epc = fixup->fixup;
|
||||
return;
|
||||
}
|
||||
printk("do_ADE-data:ema:0x%08lx:epc:0x%08lx\n",
|
||||
regs->cp0_ema, regs->cp0_epc);
|
||||
die_if_kernel("do_ade execution Exception\n", regs);
|
||||
force_sig(SIGBUS, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_pel(struct pt_regs *regs)
|
||||
{
|
||||
die_if_kernel("do_pel execution Exception", regs);
|
||||
force_sig(SIGFPE, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_cee(struct pt_regs *regs)
|
||||
{
|
||||
die_if_kernel("do_cee execution Exception", regs);
|
||||
force_sig(SIGFPE, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_cpe(struct pt_regs *regs)
|
||||
{
|
||||
die_if_kernel("do_cpe execution Exception", regs);
|
||||
force_sig(SIGFPE, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_be(struct pt_regs *regs)
|
||||
{
|
||||
die_if_kernel("do_be execution Exception", regs);
|
||||
force_sig(SIGBUS, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_ov(struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
die_if_kernel("do_ov execution Exception", regs);
|
||||
|
||||
info.si_code = FPE_INTOVF;
|
||||
info.si_signo = SIGFPE;
|
||||
info.si_errno = 0;
|
||||
info.si_addr = (void *)regs->cp0_epc;
|
||||
force_sig_info(SIGFPE, &info, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_tr(struct pt_regs *regs)
|
||||
{
|
||||
die_if_kernel("do_tr execution Exception", regs);
|
||||
force_sig(SIGTRAP, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_ri(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long epc_insn;
|
||||
unsigned long epc = regs->cp0_epc;
|
||||
|
||||
read_tsk_long(current, epc, &epc_insn);
|
||||
if (current->thread.single_step == 1) {
|
||||
if ((epc == current->thread.addr1) ||
|
||||
(epc == current->thread.addr2)) {
|
||||
user_disable_single_step(current);
|
||||
force_sig(SIGTRAP, current);
|
||||
return;
|
||||
} else
|
||||
BUG();
|
||||
} else if ((epc_insn == BREAKPOINT32_INSN) ||
|
||||
((epc_insn & 0x0000FFFF) == 0x7002) ||
|
||||
((epc_insn & 0xFFFF0000) == 0x70020000)) {
|
||||
force_sig(SIGTRAP, current);
|
||||
return;
|
||||
} else {
|
||||
die_if_kernel("do_ri execution Exception", regs);
|
||||
force_sig(SIGILL, current);
|
||||
}
|
||||
}
|
||||
|
||||
asmlinkage void do_ccu(struct pt_regs *regs)
|
||||
{
|
||||
die_if_kernel("do_ccu execution Exception", regs);
|
||||
force_sig(SIGILL, current);
|
||||
}
|
||||
|
||||
asmlinkage void do_reserved(struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* Game over - no way to handle this if it ever occurs. Most probably
|
||||
* caused by a new unknown cpu type or after another deadly
|
||||
* hard/software error.
|
||||
*/
|
||||
die_if_kernel("do_reserved execution Exception", regs);
|
||||
show_regs(regs);
|
||||
panic("Caught reserved exception - should not happen.");
|
||||
}
|
||||
|
||||
/*
|
||||
* NMI exception handler.
|
||||
*/
|
||||
void nmi_exception_handler(struct pt_regs *regs)
|
||||
{
|
||||
die_if_kernel("nmi_exception_handler execution Exception", regs);
|
||||
die("NMI", regs);
|
||||
}
|
||||
|
||||
/* Install CPU exception handler */
|
||||
void *set_except_vector(int n, void *addr)
|
||||
{
|
||||
unsigned long handler = (unsigned long) addr;
|
||||
unsigned long old_handler = exception_handlers[n];
|
||||
|
||||
exception_handlers[n] = handler;
|
||||
return (void *)old_handler;
|
||||
}
|
||||
|
||||
void __init trap_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
pgd_current = (unsigned long)init_mm.pgd;
|
||||
/* DEBUG EXCEPTION */
|
||||
memcpy((void *)DEBUG_VECTOR_BASE_ADDR,
|
||||
&debug_exception_vector, DEBUG_VECTOR_SIZE);
|
||||
/* NMI EXCEPTION */
|
||||
memcpy((void *)GENERAL_VECTOR_BASE_ADDR,
|
||||
&general_exception_vector, GENERAL_VECTOR_SIZE);
|
||||
|
||||
/*
|
||||
* Initialise exception handlers
|
||||
*/
|
||||
for (i = 0; i <= 31; i++)
|
||||
set_except_vector(i, handle_reserved);
|
||||
|
||||
set_except_vector(1, handle_nmi);
|
||||
set_except_vector(2, handle_adelinsn);
|
||||
set_except_vector(3, handle_tlb_refill);
|
||||
set_except_vector(4, handle_tlb_invaild);
|
||||
set_except_vector(5, handle_ibe);
|
||||
set_except_vector(6, handle_pel);
|
||||
set_except_vector(7, handle_sys);
|
||||
set_except_vector(8, handle_ccu);
|
||||
set_except_vector(9, handle_ri);
|
||||
set_except_vector(10, handle_tr);
|
||||
set_except_vector(11, handle_adedata);
|
||||
set_except_vector(12, handle_adedata);
|
||||
set_except_vector(13, handle_tlb_refill);
|
||||
set_except_vector(14, handle_tlb_invaild);
|
||||
set_except_vector(15, handle_mod);
|
||||
set_except_vector(16, handle_cee);
|
||||
set_except_vector(17, handle_cpe);
|
||||
set_except_vector(18, handle_dbe);
|
||||
flush_icache_range(DEBUG_VECTOR_BASE_ADDR, IRQ_VECTOR_BASE_ADDR);
|
||||
|
||||
atomic_inc(&init_mm.mm_count);
|
||||
current->active_mm = &init_mm;
|
||||
cpu_cache_init();
|
||||
}
|
90
arch/score/kernel/vmlinux.lds.S
Normal file
90
arch/score/kernel/vmlinux.lds.S
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* arch/score/kernel/vmlinux.lds.S
|
||||
*
|
||||
* Score Processor version.
|
||||
*
|
||||
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
|
||||
* Chen Liqin <liqin.chen@sunplusct.com>
|
||||
* Lennox Wu <lennox.wu@sunplusct.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
OUTPUT_ARCH(score)
|
||||
ENTRY(_stext)
|
||||
|
||||
jiffies = jiffies_64;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = CONFIG_MEMORY_START + 0x2000;
|
||||
/* read-only */
|
||||
.text : {
|
||||
_text = .; /* Text and read-only data */
|
||||
TEXT_TEXT
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
KPROBES_TEXT
|
||||
*(.text.*)
|
||||
*(.fixup)
|
||||
. = ALIGN (4) ;
|
||||
_etext = .; /* End of text section */
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
_sdata = .; /* Start of data section */
|
||||
RODATA
|
||||
|
||||
EXCEPTION_TABLE(16)
|
||||
|
||||
RW_DATA_SECTION(32, PAGE_SIZE, THREAD_SIZE)
|
||||
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
. = ALIGN(8);
|
||||
.sdata : {
|
||||
*(.sdata)
|
||||
}
|
||||
_edata = .; /* End of data section */
|
||||
|
||||
/* will be freed after init */
|
||||
. = ALIGN(PAGE_SIZE); /* Init code and data */
|
||||
__init_begin = .;
|
||||
|
||||
INIT_TEXT_SECTION(PAGE_SIZE)
|
||||
INIT_DATA_SECTION(16)
|
||||
|
||||
/* .exit.text is discarded at runtime, not link time, to deal with
|
||||
* references from .rodata
|
||||
*/
|
||||
.exit.text : {
|
||||
EXIT_TEXT
|
||||
}
|
||||
.exit.data : {
|
||||
EXIT_DATA
|
||||
}
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
__init_end = .;
|
||||
/* freed after init ends here */
|
||||
|
||||
BSS_SECTION(0, 0, 0)
|
||||
_end = .;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue