diff options
Diffstat (limited to 'arch/score/kernel')
-rw-r--r-- | arch/score/kernel/Makefile | 10 | ||||
-rw-r--r-- | arch/score/kernel/asm-offsets.c | 216 | ||||
-rw-r--r-- | arch/score/kernel/entry.S | 542 | ||||
-rw-r--r-- | arch/score/kernel/head.S | 70 | ||||
-rw-r--r-- | arch/score/kernel/init_task.c | 49 | ||||
-rw-r--r-- | arch/score/kernel/irq.c | 135 | ||||
-rw-r--r-- | arch/score/kernel/module.c | 164 | ||||
-rw-r--r-- | arch/score/kernel/process.c | 165 | ||||
-rw-r--r-- | arch/score/kernel/ptrace.c | 465 | ||||
-rw-r--r-- | arch/score/kernel/setup.c | 157 | ||||
-rw-r--r-- | arch/score/kernel/signal.c | 355 | ||||
-rw-r--r-- | arch/score/kernel/sys_score.c | 147 | ||||
-rw-r--r-- | arch/score/kernel/time.c | 99 | ||||
-rw-r--r-- | arch/score/kernel/traps.c | 349 | ||||
-rw-r--r-- | arch/score/kernel/vmlinux.lds.S | 148 |
15 files changed, 3071 insertions, 0 deletions
diff --git a/arch/score/kernel/Makefile b/arch/score/kernel/Makefile new file mode 100644 index 00000000000..1e5de89d5c7 --- /dev/null +++ b/arch/score/kernel/Makefile @@ -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 diff --git a/arch/score/kernel/asm-offsets.c b/arch/score/kernel/asm-offsets.c new file mode 100644 index 00000000000..57788f44c6f --- /dev/null +++ b/arch/score/kernel/asm-offsets.c @@ -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(); +} diff --git a/arch/score/kernel/entry.S b/arch/score/kernel/entry.S new file mode 100644 index 00000000000..6c6b7ea58af --- /dev/null +++ b/arch/score/kernel/entry.S @@ -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 diff --git a/arch/score/kernel/head.S b/arch/score/kernel/head.S new file mode 100644 index 00000000000..22a7e3c7292 --- /dev/null +++ b/arch/score/kernel/head.S @@ -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 diff --git a/arch/score/kernel/init_task.c b/arch/score/kernel/init_task.c new file mode 100644 index 00000000000..9eecde00932 --- /dev/null +++ b/arch/score/kernel/init_task.c @@ -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); diff --git a/arch/score/kernel/irq.c b/arch/score/kernel/irq.c new file mode 100644 index 00000000000..55474e8e972 --- /dev/null +++ b/arch/score/kernel/irq.c @@ -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; +} diff --git a/arch/score/kernel/module.c b/arch/score/kernel/module.c new file mode 100644 index 00000000000..4ffce7fe156 --- /dev/null +++ b/arch/score/kernel/module.c @@ -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) {} diff --git a/arch/score/kernel/process.c b/arch/score/kernel/process.c new file mode 100644 index 00000000000..aaa3085251f --- /dev/null +++ b/arch/score/kernel/process.c @@ -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(®s, 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, ®s, 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; +} diff --git a/arch/score/kernel/ptrace.c b/arch/score/kernel/ptrace.c new file mode 100644 index 00000000000..8fe7209355a --- /dev/null +++ b/arch/score/kernel/ptrace.c @@ -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; + } +} diff --git a/arch/score/kernel/setup.c b/arch/score/kernel/setup.c new file mode 100644 index 00000000000..a172ce170f6 --- /dev/null +++ b/arch/score/kernel/setup.c @@ -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); diff --git a/arch/score/kernel/signal.c b/arch/score/kernel/signal.c new file mode 100644 index 00000000000..b4ed1b3f807 --- /dev/null +++ b/arch/score/kernel/signal.c @@ -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(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->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(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->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 = ¤t->saved_sigmask; + else + oldset = ¤t->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, ¤t->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); +} diff --git a/arch/score/kernel/sys_score.c b/arch/score/kernel/sys_score.c new file mode 100644 index 00000000000..6a60d1ee533 --- /dev/null +++ b/arch/score/kernel/sys_score.c @@ -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(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, + pgoff >> (PAGE_SHIFT - 12)); + up_write(¤t->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; +} diff --git a/arch/score/kernel/time.c b/arch/score/kernel/time.c new file mode 100644 index 00000000000..cd66ba327ca --- /dev/null +++ b/arch/score/kernel/time.c @@ -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); +} diff --git a/arch/score/kernel/traps.c b/arch/score/kernel/traps.c new file mode 100644 index 00000000000..957ae9eb356 --- /dev/null +++ b/arch/score/kernel/traps.c @@ -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(); +} diff --git a/arch/score/kernel/vmlinux.lds.S b/arch/score/kernel/vmlinux.lds.S new file mode 100644 index 00000000000..f85569831d5 --- /dev/null +++ b/arch/score/kernel/vmlinux.lds.S @@ -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 = .; +} |