score: Add support for Sunplus S+core architecture

This is the complete set of new arch Score's files for linux.
Score instruction set support 16bits, 32bits and 64bits instruction,
Score SOC had been used in game machine and LCD TV.

Signed-off-by: Chen Liqin <liqin.chen@sunplusct.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Chen Liqin
2009-06-12 22:01:00 +08:00
committed by Arnd Bergmann
parent 0732f87761
commit 6bc9a3966f
126 changed files with 8566 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
#
# Makefile for the Linux/SCORE kernel.
#
extra-y := head.o vmlinux.lds
obj-y += entry.o init_task.o irq.o process.o ptrace.o \
setup.o signal.o sys_score.o time.o traps.o
obj-$(CONFIG_MODULES) += module.o

View 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();
}

542
arch/score/kernel/entry.S Normal file
View File

@@ -0,0 +1,542 @@
/*
* 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/errno.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 r8, 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_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
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
bgtu illegal_syscall
slli r8, r27, 3 # get syscall routine
la r11, sys_call_table
add r11, r11, r8
lw r10, [r11] # get syscall entry
lw r11, [r11, 4] # get number of args
cmpz.c r10
beq illegal_syscall
cmpi.c r11, 4 # more than 4 arguments?
bgtu stackargs
stack_done:
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, -EMAXERRNO-1 # -EMAXERRNO - 1=-1134
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, -EMAXERRNO - 1 # error?
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
stackargs:
lw r8, [r0, PT_R0]
andri.c r9, r8, 3 # test whether user sp is align a word
bne bad_stack
subi r11, 5
slli r9, r11, 2
add.c r9, r9, r8
bmi bad_stack
la r9, 3f # calculate branch address
slli r11, r11, 3
sub r9, r9, r11
br r9
2: lw r9, [r8, 20] # argument 6 from usp
sw r9, [r0, 20]
3: lw r9, [r8, 16] # argument 5 from usp
sw r9, [r0, 16]
j stack_done
.section __ex_table,"a"
.word 2b, bad_stack
.word 3b, bad_stack
.previous
/*
* The stackpointer for a call with more than 4 arguments is bad.
* We probably should handle this case a bit more drastic.
*/
bad_stack:
neg r27, r27 # error
sw r27, [r0, PT_ORIG_R4]
sw r27, [r0, PT_R4]
ldi r8, 1 # set error flag
sw r8, [r0, PT_R7]
j syscall_return
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_execve)
mv r4, r0
la r8, score_execve
br r8
ENTRY(sys_clone)
mv r4, r0
la r8, score_clone
br r8
ENTRY(sys_rt_sigreturn)
mv r4, r0
la r8, score_rt_sigreturn
br r8
ENTRY(sys_sigaltstack)
mv r4, r0
la r8, score_sigaltstack
br r8

70
arch/score/kernel/head.S Normal file
View 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

View File

@@ -0,0 +1,49 @@
/*
* arch/score/kernel/init_task.c
*
* Score Processor version.
*
* Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
*
* 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_task.h>
#include <linux/mqueue.h>
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
struct mm_struct init_mm = INIT_MM(init_mm);
EXPORT_SYMBOL(init_mm);
/*
* Initial thread structure.
*
* We need to make sure that this is THREAD_SIZE aligned due to the
* way process stacks are handled. This is done by having a special
* "init_task" linker map entry..
*/
union thread_union init_thread_union
__attribute__((__section__(".data.init_task"))) =
{ INIT_THREAD_INFO(init_task) };
/*
* Initial task structure.
*
* All other task structs will be allocated on slabs in fork.c
*/
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);

135
arch/score/kernel/irq.c Normal file
View File

@@ -0,0 +1,135 @@
/*
* 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>
/*
* handles all normal device IRQs
*/
asmlinkage void do_IRQ(int irq)
{
irq_enter();
generic_handle_irq(irq);
irq_exit();
}
static void score_mask(unsigned int irq_nr)
{
unsigned int irq_source = 63 - irq_nr;
if (irq_source < 32)
__raw_writel((__raw_readl((void *)P_INT_MASKL) | \
(1 << irq_source)), (void *)P_INT_MASKL);
else
__raw_writel((__raw_readl((void *)P_INT_MASKH) | \
(1 << (irq_source - 32))), (void *)P_INT_MASKH);
}
static void score_unmask(unsigned int irq_nr)
{
unsigned int irq_source = 63 - irq_nr;
if (irq_source < 32)
__raw_writel((__raw_readl((void *)P_INT_MASKL) & \
~(1 << irq_source)), (void *)P_INT_MASKL);
else
__raw_writel((__raw_readl((void *)P_INT_MASKH) & \
~(1 << (irq_source - 32))), (void *)P_INT_MASKH);
}
struct irq_chip score_irq_chip = {
.name = "Score7-level",
.mask = score_mask,
.mask_ack = score_mask,
.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)
set_irq_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, (void *)P_INT_MASKL);
__raw_writel(0xffffffff, (void *)P_INT_MASKH);
__asm__ __volatile__(
"mtcr %0, cr3\n\t"
: : "r" (EXCEPTION_VECTOR_BASE_ADDR | \
VECTOR_ADDRESS_OFFSET_MODE16));
}
/*
* Generic, controller-independent functions:
*/
int show_interrupts(struct seq_file *p, void *v)
{
int i = *(loff_t *)v, cpu;
struct irqaction *action;
unsigned long flags;
if (i == 0) {
seq_puts(p, " ");
for_each_online_cpu(cpu)
seq_printf(p, "CPU%d ", cpu);
seq_putc(p, '\n');
}
if (i < NR_IRQS) {
spin_lock_irqsave(&irq_desc[i].lock, flags);
action = irq_desc[i].action;
if (!action)
goto unlock;
seq_printf(p, "%3d: ", i);
seq_printf(p, "%10u ", kstat_irqs(i));
seq_printf(p, " %8s", irq_desc[i].chip->name ? : "-");
seq_printf(p, " %s", action->name);
for (action = action->next; action; action = action->next)
seq_printf(p, ", %s", action->name);
seq_putc(p, '\n');
unlock:
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
}
return 0;
}

164
arch/score/kernel/module.c Normal file
View File

@@ -0,0 +1,164 @@
/*
* 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/module.h>
#include <linux/vmalloc.h>
void *module_alloc(unsigned long size)
{
return size ? vmalloc(size) : NULL;
}
/* Free memory returned from module_alloc */
void module_free(struct module *mod, void *module_region)
{
vfree(module_region);
}
int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *mod)
{
return 0;
}
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 offset;
offset = ELF32_R_SYM(rel->r_info);
if ((offset < 0) ||
(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) + 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;
}
int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
unsigned int symindex, unsigned int relsec,
struct module *me)
{
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 0;
}
/* Put in dbe list if necessary. */
int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
struct module *me)
{
return 0;
}
void module_arch_cleanup(struct module *mod) {}

165
arch/score/kernel/process.c Normal file
View File

@@ -0,0 +1,165 @@
/*
* 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>
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) {}
/*
* The idle thread. There's no useful work to be
* done, so just try to conserve power and have a
* low exit latency (ie sit in a loop waiting for
* somebody to say that they'd like to reschedule)
*/
void __noreturn cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
while (!need_resched())
barrier();
preempt_enable_no_resched();
schedule();
preempt_disable();
}
}
asmlinkage void ret_from_fork(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 unused,
struct task_struct *p, struct pt_regs *regs)
{
struct thread_info *ti = task_thread_info(p);
struct pt_regs *childregs = task_pt_regs(p);
p->set_child_tid = NULL;
p->clear_child_tid = NULL;
*childregs = *regs;
childregs->regs[7] = 0; /* Clear error flag */
childregs->regs[4] = 0; /* Child gets zero as return value */
regs->regs[4] = p->pid;
if (childregs->cp0_psr & 0x8) { /* test kernel fork or user fork */
childregs->regs[0] = usp; /* user fork */
} else {
childregs->regs[28] = (unsigned long) ti; /* kernel fork */
childregs->regs[0] = (unsigned long) childregs;
}
p->thread.reg0 = (unsigned long) childregs;
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;
}
static void __noreturn
kernel_thread_helper(void *unused0, int (*fn)(void *),
void *arg, void *unused1)
{
do_exit(fn(arg));
}
/*
* Create a kernel thread.
*/
long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.regs[6] = (unsigned long) arg;
regs.regs[5] = (unsigned long) fn;
regs.cp0_epc = (unsigned long) kernel_thread_helper;
regs.cp0_psr = (regs.cp0_psr & ~(0x1|0x4|0x8)) | \
((regs.cp0_psr & 0x3) << 2);
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, \
0, &regs, 0, NULL, NULL);
}
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;
}

465
arch/score/kernel/ptrace.c Normal file
View File

@@ -0,0 +1,465 @@
/*
* 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/kernel.h>
#include <linux/ptrace.h>
#include <asm/uaccess.h>
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 set_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 clear_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) {}
long
arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
int ret;
if (request == PTRACE_TRACEME) {
/* are we already being traced? */
if (current->ptrace & PT_PTRACED)
return -EPERM;
/* set the ptrace bit in the process flags. */
current->ptrace |= PT_PTRACED;
return 0;
}
ret = -ESRCH;
if (!child)
return ret;
ret = -EPERM;
if (request == PTRACE_ATTACH) {
ret = ptrace_attach(child);
return ret;
}
ret = ptrace_check_attach(child, request == PTRACE_KILL);
if (ret < 0)
return ret;
switch (request) {
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA: {
unsigned long tmp;
int copied;
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
ret = -EIO;
if (copied != sizeof(tmp))
break;
ret = put_user(tmp, (unsigned long *) data);
return ret;
}
/* Read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
struct pt_regs *regs;
unsigned long tmp;
regs = task_pt_regs(child);
tmp = 0; /* Default return value. */
switch (addr) {
case 0 ... 31:
tmp = regs->regs[addr];
break;
case PC:
tmp = regs->cp0_epc;
break;
case ECR:
tmp = regs->cp0_ecr;
break;
case EMA:
tmp = regs->cp0_ema;
break;
case CEH:
tmp = regs->ceh;
break;
case CEL:
tmp = regs->cel;
break;
case CONDITION:
tmp = regs->cp0_condition;
break;
case PSR:
tmp = regs->cp0_psr;
break;
case COUNTER:
tmp = regs->sr0;
break;
case LDCR:
tmp = regs->sr1;
break;
case STCR:
tmp = regs->sr2;
break;
default:
tmp = 0;
return -EIO;
}
ret = put_user(tmp, (unsigned long *) data);
return ret;
}
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
ret = 0;
if (access_process_vm(child, addr, &data, sizeof(data), 1)
== sizeof(data))
break;
ret = -EIO;
return ret;
case PTRACE_POKEUSR: {
struct pt_regs *regs;
ret = 0;
regs = task_pt_regs(child);
switch (addr) {
case 0 ... 31:
regs->regs[addr] = data;
break;
case PC:
regs->cp0_epc = data;
break;
case CEH:
regs->ceh = data;
break;
case CEL:
regs->cel = data;
break;
case CONDITION:
regs->cp0_condition = data;
break;
case PSR:
case COUNTER:
case STCR:
case LDCR:
break; /* user can't write the reg */
default:
/* The rest are not allowed. */
ret = -EIO;
break;
}
break;
}
case PTRACE_SYSCALL: /* continue and stop at next
(return from) syscall. */
case PTRACE_CONT: { /* restart after signal. */
ret = -EIO;
if (!valid_signal(data))
break;
if (request == PTRACE_SYSCALL)
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
else
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
wake_up_process(child);
ret = 0;
break;
}
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
case PTRACE_KILL:
ret = 0;
if (child->state == EXIT_ZOMBIE) /* already dead. */
break;
child->exit_code = SIGKILL;
clear_single_step(child);
wake_up_process(child);
break;
case PTRACE_SINGLESTEP: { /* set the trap flag. */
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
set_single_step(child);
child->exit_code = data;
/* give it a chance to run. */
wake_up_process(child);
ret = 0;
break;
}
case PTRACE_DETACH: /* detach a process that was attached. */
ret = ptrace_detach(child, data);
break;
case PTRACE_SETOPTIONS:
if (data & PTRACE_O_TRACESYSGOOD)
child->ptrace |= PT_TRACESYSGOOD;
else
child->ptrace &= ~PT_TRACESYSGOOD;
ret = 0;
break;
default:
ret = -EIO;
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;
}
}

157
arch/score/kernel/setup.c Normal file
View File

@@ -0,0 +1,157 @@
/*
* 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/seq_file.h>
#include <linux/screen_info.h>
#include <asm-generic/sections.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 reserved_end, bootmap_size;
unsigned long size = initrd_end - initrd_start;
reserved_end = (unsigned long)_end;
min_low_pfn = 0;
max_low_pfn = MEM_SIZE / PAGE_SIZE;
/* Initialize the boot-time allocator with low memory only. */
bootmap_size = init_bootmem_node(NODE_DATA(0), reserved_end,
min_low_pfn, max_low_pfn);
add_active_range(0, min_low_pfn, max_low_pfn);
free_bootmem(PFN_PHYS(reserved_end),
(max_low_pfn - reserved_end) << PAGE_SHIFT);
memory_present(0, reserved_end, max_low_pfn);
/* Reserve space for the bootmem bitmap. */
reserve_bootmem(PFN_PHYS(reserved_end), 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 = (unsigned long)_text;
code_resource.end = (unsigned long)_etext - 1;
data_resource.start = (unsigned long)_etext;
data_resource.end = (unsigned long)_edata - 1;
res = alloc_bootmem(sizeof(struct resource));
res->name = "System RAM";
res->start = 0;
res->end = MEM_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);

355
arch/score/kernel/signal.c Normal file
View File

@@ -0,0 +1,355 @@
/*
* 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/unistd.h>
#include <linux/uaccess.h>
#include <asm-generic/ucontext.h>
#include <asm/cacheflush.h>
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
struct rt_sigframe {
u32 rs_ass[4]; /* argument save space */
u32 rs_code[2]; /* signal trampoline */
struct siginfo rs_info;
struct ucontext rs_uc;
};
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;
}
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..
*/
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 *)((sp - frame_size) & ~7);
}
asmlinkage int score_sigaltstack(struct pt_regs *regs)
{
const stack_t *uss = (const stack_t *) regs->regs[4];
stack_t *uoss = (stack_t *) regs->regs[5];
unsigned long usp = regs->regs[0];
return do_sigaltstack(uss, uoss, usp);
}
asmlinkage void score_rt_sigreturn(struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
sigset_t set;
stack_t st;
int sig;
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;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
sig = restore_sigcontext(regs, &frame->rs_uc.uc_mcontext);
if (sig < 0)
goto badframe;
else if (sig)
force_sig(sig, current);
if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st)))
goto badframe;
/* It is more difficult to avoid calling this function than to
call it and ignore errors. */
do_sigaltstack((stack_t __user *)&st, NULL, regs->regs[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);
}
int setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,
int signr, sigset_t *set, siginfo_t *info)
{
struct rt_sigframe *frame;
int err = 0;
frame = get_sigframe(ka, regs, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
/*
* 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, info);
err |= __put_user(0, &frame->rs_uc.uc_flags);
err |= __put_user(0, &frame->rs_uc.uc_link);
err |= __put_user((void *)current->sas_ss_sp,
&frame->rs_uc.uc_stack.ss_sp);
err |= __put_user(sas_ss_flags(regs->regs[0]),
&frame->rs_uc.uc_stack.ss_flags);
err |= __put_user(current->sas_ss_size,
&frame->rs_uc.uc_stack.ss_size);
err |= setup_sigcontext(regs, &frame->rs_uc.uc_mcontext);
err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set));
if (err)
goto give_sigsegv;
regs->regs[0] = (unsigned long) frame;
regs->regs[3] = (unsigned long) frame->rs_code;
regs->regs[4] = signr;
regs->regs[5] = (unsigned long) &frame->rs_info;
regs->regs[6] = (unsigned long) &frame->rs_uc;
regs->regs[29] = (unsigned long) ka->sa.sa_handler;
regs->cp0_epc = (unsigned long) ka->sa.sa_handler;
return 0;
give_sigsegv:
if (signr == SIGSEGV)
ka->sa.sa_handler = SIG_DFL;
force_sig(SIGSEGV, current);
return -EFAULT;
}
int handle_signal(unsigned long sig, siginfo_t *info,
struct k_sigaction *ka, sigset_t *oldset, 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 (!(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(ka, regs, sig, oldset, info);
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
if (!(ka->sa.sa_flags & SA_NODEFER))
sigaddset(&current->blocked, sig);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
return ret;
}
asmlinkage void do_signal(struct pt_regs *regs)
{
struct k_sigaction ka;
sigset_t *oldset;
siginfo_t info;
int signr;
/*
* 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 (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
else
oldset = &current->blocked;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
/* Actually deliver the signal. */
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
/*
* A signal was successfully delivered; the saved
* sigmask will have been stored in the signal frame,
* and will be restored by sigreturn, so we can simply
* clear the TIF_RESTORE_SIGMASK flag.
*/
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
}
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
*/
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
}
}
/*
* 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 | _TIF_RESTORE_SIGMASK))
do_signal(regs);
}

View File

@@ -0,0 +1,147 @@
/*
* 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/mman.h>
#include <linux/module.h>
#include <linux/unistd.h>
unsigned long shm_align_mask = PAGE_SIZE - 1;
EXPORT_SYMBOL(shm_align_mask);
asmlinkage unsigned long
sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
unsigned long flags, unsigned long fd, unsigned long pgoff)
{
int error = -EBADF;
struct file *file = NULL;
if (pgoff & (~PAGE_MASK >> 12))
return -EINVAL;
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
if (!(flags & MAP_ANONYMOUS)) {
file = fget(fd);
if (!file)
return error;
}
down_write(&current->mm->mmap_sem);
error = do_mmap_pgoff(file, addr, len, prot, flags,
pgoff >> (PAGE_SHIFT - 12));
up_write(&current->mm->mmap_sem);
if (file)
fput(file);
return error;
}
/*
* Clone a task - this clones the calling program thread.
* This is called indirectly via a small wrapper
*/
asmlinkage int
score_clone(struct pt_regs *regs)
{
unsigned long clone_flags;
unsigned long newsp;
int __user *parent_tidptr, *child_tidptr;
clone_flags = regs->regs[4];
newsp = regs->regs[5];
if (!newsp)
newsp = regs->regs[0];
parent_tidptr = (int __user *)regs->regs[6];
child_tidptr = NULL;
if (clone_flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) {
int __user *__user *usp = (int __user *__user *)regs->regs[0];
if (get_user(child_tidptr, &usp[4]))
return -EFAULT;
}
return do_fork(clone_flags, newsp, regs, 0,
parent_tidptr, child_tidptr);
}
/*
* sys_execve() executes a new program.
* This is called indirectly via a small wrapper
*/
asmlinkage int score_execve(struct pt_regs *regs)
{
int error;
char *filename;
filename = getname((char *) (long) regs->regs[4]);
error = PTR_ERR(filename);
if (IS_ERR(filename))
return error;
error = do_execve(filename, (char **) (long) regs->regs[5],
(char **) (long) regs->regs[6], regs);
putname(filename);
return error;
}
/*
* If we ever come here the user sp is bad. Zap the process right away.
* Due to the bad stack signaling wouldn't work.
*/
asmlinkage void bad_stack(void)
{
do_exit(SIGSEGV);
}
/*
* Do a system call from kernel instead of calling sys_execve so we
* end up with proper pt_regs.
*/
int kernel_execve(const char *filename, char *const argv[], char *const envp[])
{
register unsigned long __r4 asm("r4") = (unsigned long) filename;
register unsigned long __r5 asm("r5") = (unsigned long) argv;
register unsigned long __r6 asm("r6") = (unsigned long) envp;
register unsigned long __r7 asm("r7");
__asm__ __volatile__ (" \n"
"ldi r27, %5 \n"
"syscall \n"
"mv %0, r4 \n"
"mv %1, r7 \n"
: "=&r" (__r4), "=r" (__r7)
: "r" (__r4), "r" (__r5), "r" (__r6), "i" (__NR_execve)
: "r8", "r9", "r10", "r11", "r22", "r23", "r24", "r25",
"r26", "r27", "memory");
if (__r7 == 0)
return __r4;
return -__r4;
}

99
arch/score/kernel/time.c Normal file
View 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>
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_DISABLED | 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);
}

349
arch/score/kernel/traps.c Normal file
View File

@@ -0,0 +1,349 @@
/*
* 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)
{
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");
}
/*
* The architecture-independent dump_stack generator
*/
void dump_stack(void)
{
show_stack(current_thread_info()->task,
(long *) get_irq_regs()->regs[0]);
}
EXPORT_SYMBOL(dump_stack);
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)) {
clear_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();
}

View File

@@ -0,0 +1,148 @@
/*
* 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>
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);
RODATA
/* Exception table */
. = ALIGN(16);
__ex_table : {
__start___ex_table = .;
*(__ex_table)
__stop___ex_table = .;
}
/* writeable */
.data ALIGN (4096): {
*(.data.init_task)
DATA_DATA
CONSTRUCTORS
}
/* 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)
}
. = ALIGN(32);
.data.cacheline_aligned : {
*(.data.cacheline_aligned)
}
_edata = .; /* End of data section */
/* will be freed after init */
. = ALIGN(4096); /* Init code and data */
__init_begin = .;
. = ALIGN(4096);
.init.text : {
_sinittext = .;
INIT_TEXT
_einittext = .;
}
.init.data : {
INIT_DATA
}
. = ALIGN(16);
.init.setup : {
__setup_start = .;
*(.init.setup)
__setup_end = .;
}
.initcall.init : {
__initcall_start = .;
INITCALLS
__initcall_end = .;
}
.con_initcall.init : {
__con_initcall_start = .;
*(.con_initcall.init)
__con_initcall_end = .;
}
SECURITY_INIT
/* .exit.text is discarded at runtime, not link time, to deal with
* references from .rodata
*/
.exit.text : {
EXIT_TEXT
}
.exit.data : {
EXIT_DATA
}
#if defined(CONFIG_BLK_DEV_INITRD)
.init.ramfs ALIGN(4096): {
__initramfs_start = .;
*(.init.ramfs)
__initramfs_end = .;
. = ALIGN(4);
LONG(0);
}
#endif
. = ALIGN(4096);
__init_end = .;
/* freed after init ends here */
__bss_start = .; /* BSS */
.sbss : {
*(.sbss)
*(.scommon)
}
.bss : {
*(.bss)
*(COMMON)
}
__bss_stop = .;
_end = .;
}