diff options
author | Roman Yeryomin <roman@advem.lv> | 2012-09-13 00:40:35 +0300 |
---|---|---|
committer | Roman Yeryomin <roman@advem.lv> | 2012-12-03 00:13:21 +0200 |
commit | 5deb3317cb51ac52de922bb55f8492624018906d (patch) | |
tree | c2fbe6346699d9bb0f2100490c3029519bb8fde8 /target/linux/realtek/files/arch/rlx/kernel | |
parent | 0239d37124f9184b478a42de8a7fa1bc85a6a6fe (diff) |
Add realtek target files
Signed-off-by: Roman Yeryomin <roman@advem.lv>
Diffstat (limited to 'target/linux/realtek/files/arch/rlx/kernel')
36 files changed, 7907 insertions, 0 deletions
diff --git a/target/linux/realtek/files/arch/rlx/kernel/Makefile b/target/linux/realtek/files/arch/rlx/kernel/Makefile new file mode 100644 index 000000000..5e866732a --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the Linux/MIPS kernel. +# + +extra-y := head.o init_task.o + +obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \ + ptrace.o reset.o setup.o signal.o \ + topology.o traps.o unaligned.o + +obj-y += rlx-switch.o syscall.o scall32-o32.o +obj-y += rlx-time.o rlx-cevt.o + +obj-$(CONFIG_STACKTRACE) += stacktrace.o +obj-$(CONFIG_MODULES) += mips_ksyms.o module.o +obj-$(CONFIG_IRQ_CPU) += irq_cpu.o +obj-$(CONFIG_IRQ_VEC) += irq_vec.o + +obj-$(CONFIG_KGDB) += kgdb.o +obj-$(CONFIG_PROC_FS) += proc.o + +obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +EXTRA_CFLAGS += -Werror +EXTRA_CFLAGS += -I$(DIR_LINUX)/drivers/net/rtl819x/AsicDriver +EXTRA_CFLAGS += -I$(DIR_LINUX)/drivers/net/rtl819x/common diff --git a/target/linux/realtek/files/arch/rlx/kernel/asm-offsets.c b/target/linux/realtek/files/arch/rlx/kernel/asm-offsets.c new file mode 100644 index 000000000..835e11c82 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/asm-offsets.c @@ -0,0 +1,237 @@ +/* + * offset.c: Calculate pt_regs and task_struct offsets. + * + * Copyright (C) 1996 David S. Miller + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * + * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. + */ +#include <linux/compat.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/kbuild.h> +#include <asm/ptrace.h> +#include <asm/processor.h> + +void output_ptreg_defines(void) +{ + COMMENT("MIPS 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_HI, pt_regs, hi); + OFFSET(PT_LO, pt_regs, lo); + OFFSET(PT_EPC, pt_regs, cp0_epc); + OFFSET(PT_BVADDR, pt_regs, cp0_badvaddr); + OFFSET(PT_STATUS, pt_regs, cp0_status); + OFFSET(PT_CAUSE, pt_regs, cp0_cause); +#ifdef CONFIG_CPU_HAS_RADIAX + OFFSET(PT_ESTATUS, pt_regs, estatus); + OFFSET(PT_ECAUSE, pt_regs, ecause); + OFFSET(PT_INTVEC, pt_regs, intvec); + OFFSET(PT_CBS0, pt_regs, radiax[0]); + OFFSET(PT_CBS1, pt_regs, radiax[1]); + OFFSET(PT_CBS2, pt_regs, radiax[2]); + OFFSET(PT_CBE0, pt_regs, radiax[3]); + OFFSET(PT_CBE1, pt_regs, radiax[4]); + OFFSET(PT_CBE2, pt_regs, radiax[5]); + OFFSET(PT_LPS0, pt_regs, radiax[6]); + OFFSET(PT_LPE0, pt_regs, radiax[7]); + OFFSET(PT_LPC0, pt_regs, radiax[8]); + OFFSET(PT_MMD , pt_regs, radiax[9]); + OFFSET(PT_M0LL, pt_regs, radiax[10]); + OFFSET(PT_M0LH, pt_regs, radiax[11]); + OFFSET(PT_M0HL, pt_regs, radiax[12]); + OFFSET(PT_M0HH, pt_regs, radiax[13]); + OFFSET(PT_M1LL, pt_regs, radiax[14]); + OFFSET(PT_M1LH, pt_regs, radiax[15]); + OFFSET(PT_M1HL, pt_regs, radiax[16]); + OFFSET(PT_M1HH, pt_regs, radiax[17]); + OFFSET(PT_M2LL, pt_regs, radiax[18]); + OFFSET(PT_M2LH, pt_regs, radiax[19]); + OFFSET(PT_M2HL, pt_regs, radiax[20]); + OFFSET(PT_M2HH, pt_regs, radiax[21]); + OFFSET(PT_M3LL, pt_regs, radiax[22]); + OFFSET(PT_M3LH, pt_regs, radiax[23]); + OFFSET(PT_M3HL, pt_regs, radiax[24]); + OFFSET(PT_M3HH, pt_regs, radiax[25]); +#endif + + DEFINE(PT_SIZE, sizeof(struct pt_regs)); + BLANK(); +} + +void output_task_defines(void) +{ + COMMENT("MIPS 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("MIPS 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(_THREAD_SIZE, THREAD_SIZE); + DEFINE(_THREAD_MASK, THREAD_MASK); + BLANK(); +} + +void output_thread_defines(void) +{ + COMMENT("MIPS specific thread_struct offsets."); + 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_REG22, task_struct, thread.reg22); + OFFSET(THREAD_REG23, task_struct, thread.reg23); + OFFSET(THREAD_REG29, task_struct, thread.reg29); + OFFSET(THREAD_REG30, task_struct, thread.reg30); + OFFSET(THREAD_REG31, task_struct, thread.reg31); + OFFSET(THREAD_STATUS, task_struct, thread.cp0_status); + + OFFSET(THREAD_BVADDR, task_struct, thread.cp0_badvaddr); + OFFSET(THREAD_BUADDR, 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(_PMD_T_SIZE, sizeof(pmd_t)); + DEFINE(_PTE_T_SIZE, sizeof(pte_t)); + BLANK(); + DEFINE(_PGD_T_LOG2, PGD_T_LOG2); + DEFINE(_PMD_T_LOG2, PMD_T_LOG2); + DEFINE(_PTE_T_LOG2, PTE_T_LOG2); + BLANK(); + DEFINE(_PGD_ORDER, PGD_ORDER); + DEFINE(_PMD_ORDER, PMD_ORDER); + DEFINE(_PTE_ORDER, PTE_ORDER); + BLANK(); + DEFINE(_PMD_SHIFT, PMD_SHIFT); + DEFINE(_PGDIR_SHIFT, PGDIR_SHIFT); + BLANK(); + DEFINE(_PTRS_PER_PGD, PTRS_PER_PGD); + DEFINE(_PTRS_PER_PMD, PTRS_PER_PMD); + 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_MDHI, sigcontext, sc_mdhi); + OFFSET(SC_MDLO, sigcontext, sc_mdlo); + OFFSET(SC_PC, sigcontext, sc_pc); + 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(_SIGEMT, SIGEMT); + 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(); +} + +void output_irq_cpustat_t_defines(void) +{ + COMMENT("Linux irq_cpustat_t offsets."); + DEFINE(IC_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); + DEFINE(IC_IRQ_CPUSTAT_T, sizeof(irq_cpustat_t)); + BLANK(); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/binfmt_elfo32.c b/target/linux/realtek/files/arch/rlx/kernel/binfmt_elfo32.c new file mode 100644 index 000000000..e1333d731 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/binfmt_elfo32.c @@ -0,0 +1,147 @@ +/* + * Support for o32 Linux/MIPS ELF binaries. + * + * Copyright (C) 1999, 2001 Ralf Baechle + * Copyright (C) 1999, 2001 Silicon Graphics, Inc. + * + * Heavily inspired by the 32-bit Sparc compat code which is + * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) + * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) + */ + +#define ELF_ARCH EM_MIPS +#define ELF_CLASS ELFCLASS32 +#ifdef __MIPSEB__ +#define ELF_DATA ELFDATA2MSB; +#else /* __MIPSEL__ */ +#define ELF_DATA ELFDATA2LSB; +#endif + +/* ELF register definitions */ +#define ELF_NGREG 45 +#define ELF_NFPREG 33 + +typedef unsigned int elf_greg_t; +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +typedef double elf_fpreg_t; +typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(hdr) \ +({ \ + int __res = 1; \ + struct elfhdr *__h = (hdr); \ + \ + if (__h->e_machine != EM_MIPS) \ + __res = 0; \ + if (__h->e_ident[EI_CLASS] != ELFCLASS32) \ + __res = 0; \ + if ((__h->e_flags & EF_MIPS_ABI2) != 0) \ + __res = 0; \ + if (((__h->e_flags & EF_MIPS_ABI) != 0) && \ + ((__h->e_flags & EF_MIPS_ABI) != EF_MIPS_ABI_O32)) \ + __res = 0; \ + \ + __res; \ +}) + +#define TASK32_SIZE 0x7fff8000UL +#undef ELF_ET_DYN_BASE +#define ELF_ET_DYN_BASE (TASK32_SIZE / 3 * 2) + +#include <asm/processor.h> +#include <linux/module.h> +#include <linux/elfcore.h> +#include <linux/compat.h> +#include <linux/math64.h> + +#define elf_prstatus elf_prstatus32 +struct elf_prstatus32 +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned int pr_sigpend; /* Set of pending signals */ + unsigned int pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct compat_timeval pr_utime; /* User time */ + struct compat_timeval pr_stime; /* System time */ + struct compat_timeval pr_cutime;/* Cumulative user time */ + struct compat_timeval pr_cstime;/* Cumulative system time */ + elf_gregset_t pr_reg; /* GP registers */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + +#define elf_prpsinfo elf_prpsinfo32 +struct elf_prpsinfo32 +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + unsigned int pr_flag; /* flags */ + __kernel_uid_t pr_uid; + __kernel_gid_t pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +#define elf_caddr_t u32 +#define init_elf_binfmt init_elf32_binfmt + +#define jiffies_to_timeval jiffies_to_compat_timeval +static inline void +jiffies_to_compat_timeval(unsigned long jiffies, struct compat_timeval *value) +{ + /* + * Convert jiffies to nanoseconds and separate with + * one divide. + */ + u64 nsec = (u64)jiffies * TICK_NSEC; + u32 rem; + value->tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem); + value->tv_usec = rem / NSEC_PER_USEC; +} + +#undef ELF_CORE_COPY_REGS +#define ELF_CORE_COPY_REGS(_dest, _regs) elf32_core_copy_regs(_dest, _regs); + +void elf32_core_copy_regs(elf_gregset_t grp, struct pt_regs *regs) +{ + int i; + + for (i = 0; i < EF_R0; i++) + grp[i] = 0; + grp[EF_R0] = 0; + for (i = 1; i <= 31; i++) + grp[EF_R0 + i] = (elf_greg_t) regs->regs[i]; + grp[EF_R26] = 0; + grp[EF_R27] = 0; + grp[EF_LO] = (elf_greg_t) regs->lo; + grp[EF_HI] = (elf_greg_t) regs->hi; + grp[EF_CP0_EPC] = (elf_greg_t) regs->cp0_epc; + grp[EF_CP0_BADVADDR] = (elf_greg_t) regs->cp0_badvaddr; + grp[EF_CP0_STATUS] = (elf_greg_t) regs->cp0_status; + grp[EF_CP0_CAUSE] = (elf_greg_t) regs->cp0_cause; +#ifdef EF_UNUSED0 + grp[EF_UNUSED0] = 0; +#endif +} + +MODULE_DESCRIPTION("Binary format loader for compatibility with o32 Linux/MIPS binaries"); +MODULE_AUTHOR("Ralf Baechle (ralf@linux-mips.org)"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#undef TASK_SIZE +#define TASK_SIZE TASK_SIZE32 + +#include "../../../fs/binfmt_elf.c" diff --git a/target/linux/realtek/files/arch/rlx/kernel/branch.c b/target/linux/realtek/files/arch/rlx/kernel/branch.c new file mode 100644 index 000000000..4ae9d4fce --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/branch.c @@ -0,0 +1,167 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 97, 2000, 2001 by Ralf Baechle + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <asm/branch.h> +#include <asm/cpu.h> +#include <asm/cpu-features.h> +#include <asm/inst.h> +#include <asm/ptrace.h> +#include <asm/uaccess.h> + +/* + * Compute the return address and do emulate branch simulation, if required. + */ +int __compute_return_epc(struct pt_regs *regs) +{ + unsigned int __user *addr; + long epc; + union mips_instruction insn; + + epc = regs->cp0_epc; + if (epc & 3) + goto unaligned; + + /* + * Read the instruction + */ + addr = (unsigned int __user *) epc; + if (__get_user(insn.word, addr)) { + force_sig(SIGSEGV, current); + return -EFAULT; + } + + regs->regs[0] = 0; + switch (insn.i_format.opcode) { + /* + * jr and jalr are in r_format format. + */ + case spec_op: + switch (insn.r_format.func) { + case jalr_op: + regs->regs[insn.r_format.rd] = epc + 8; + /* Fall through */ + case jr_op: + regs->cp0_epc = regs->regs[insn.r_format.rs]; + break; + } + break; + + /* + * This group contains: + * bltz_op, bgez_op, bltzl_op, bgezl_op, + * bltzal_op, bgezal_op, bltzall_op, bgezall_op. + */ + case bcond_op: + switch (insn.i_format.rt) { + case bltz_op: + case bltzl_op: + if ((long)regs->regs[insn.i_format.rs] < 0) + epc = epc + 4 + (insn.i_format.simmediate << 2); + else + epc += 8; + regs->cp0_epc = epc; + break; + + case bgez_op: + case bgezl_op: + if ((long)regs->regs[insn.i_format.rs] >= 0) + epc = epc + 4 + (insn.i_format.simmediate << 2); + else + epc += 8; + regs->cp0_epc = epc; + break; + + case bltzal_op: + case bltzall_op: + regs->regs[31] = epc + 8; + if ((long)regs->regs[insn.i_format.rs] < 0) + epc = epc + 4 + (insn.i_format.simmediate << 2); + else + epc += 8; + regs->cp0_epc = epc; + break; + + case bgezal_op: + case bgezall_op: + regs->regs[31] = epc + 8; + if ((long)regs->regs[insn.i_format.rs] >= 0) + epc = epc + 4 + (insn.i_format.simmediate << 2); + else + epc += 8; + regs->cp0_epc = epc; + break; + } + break; + + /* + * These are unconditional and in j_format. + */ + case jal_op: + regs->regs[31] = regs->cp0_epc + 8; + case j_op: + epc += 4; + epc >>= 28; + epc <<= 28; + epc |= (insn.j_format.target << 2); + regs->cp0_epc = epc; + break; + + /* + * These are conditional and in i_format. + */ + case beq_op: + case beql_op: + if (regs->regs[insn.i_format.rs] == + regs->regs[insn.i_format.rt]) + epc = epc + 4 + (insn.i_format.simmediate << 2); + else + epc += 8; + regs->cp0_epc = epc; + break; + + case bne_op: + case bnel_op: + if (regs->regs[insn.i_format.rs] != + regs->regs[insn.i_format.rt]) + epc = epc + 4 + (insn.i_format.simmediate << 2); + else + epc += 8; + regs->cp0_epc = epc; + break; + + case blez_op: /* not really i_format */ + case blezl_op: + /* rt field assumed to be zero */ + if ((long)regs->regs[insn.i_format.rs] <= 0) + epc = epc + 4 + (insn.i_format.simmediate << 2); + else + epc += 8; + regs->cp0_epc = epc; + break; + + case bgtz_op: + case bgtzl_op: + /* rt field assumed to be zero */ + if ((long)regs->regs[insn.i_format.rs] > 0) + epc = epc + 4 + (insn.i_format.simmediate << 2); + else + epc += 8; + regs->cp0_epc = epc; + break; + } + + return 0; + +unaligned: + printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); + force_sig(SIGBUS, current); + return -EFAULT; +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/cpu-probe.c b/target/linux/realtek/files/arch/rlx/kernel/cpu-probe.c new file mode 100644 index 000000000..99539380c --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/cpu-probe.c @@ -0,0 +1,40 @@ +/* + * Processor capabilities determination functions. + * + * Copyright (C) xxxx the Anonymous + * Copyright (C) 1994 - 2006 Ralf Baechle + * Copyright (C) 2003, 2004 Maciej W. Rozycki + * Copyright (C) 2001, 2004 MIPS Inc. + * + * 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. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/stddef.h> + +#include <asm/cpu.h> +#include <asm/rlxregs.h> +#include <asm/system.h> + +const char *__cpu_name[NR_CPUS]; + +void __cpuinit cpu_probe(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + + c->processor_id = PRID_IMP_UNKNOWN; + c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE | MIPS_CPU_NOFPUEX; + c->tlbsize = cpu_tlb_entry; /* defined in bspcpu.h */ + c->processor_id = read_c0_prid(); +} + +void __cpuinit cpu_report(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + + printk("CPU revision is: %08x\n", c->processor_id); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/early_printk.c b/target/linux/realtek/files/arch/rlx/kernel/early_printk.c new file mode 100644 index 000000000..b1983af9c --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/early_printk.c @@ -0,0 +1,117 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2002, 2003, 06, 07 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2007 MIPS Technologies, Inc. + * written by Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/console.h> +#include <linux/init.h> + +#include <asm/setup.h> + +#ifdef CONFIG_SERIAL_SC16IS7X0_CONSOLE +extern int __init early_sc16is7x0_init_i2c_and_check( void ); +extern int __init early_sc16is7x0_setup(struct console * console, char * option); +static int early_sc16is7x0_workable = -1; +#endif + +#include "bspchip.h" + +//extern void prom_putchar(char); +int prom_putchar(char c) +{ +#ifdef CONFIG_SERIAL_SC16IS7X0_CONSOLE + extern unsigned int sc16is7x0_serial_out_i2c(int offset, int value); + extern unsigned int sc16is7x0_serial_in_i2c(int offset); +#endif + +#define UART0_BASE 0xB8002000 +#define UART0_THR (UART0_BASE + 0x000) +#define UART0_FCR (UART0_BASE + 0x008) +#define UART0_LSR (UART0_BASE + 0x014) +#define TXRST 0x04 +#define CHAR_TRIGGER_14 0xC0 +#define LSR_THRE 0x20 +#define TxCHAR_AVAIL 0x00 +#define TxCHAR_EMPTY 0x20 + unsigned int busy_cnt = 0; + +#ifndef CONFIG_SERIAL_SC16IS7X0_CONSOLE + do + { + /* Prevent Hanging */ + if (busy_cnt++ >= 30000) + { + /* Reset Tx FIFO */ + REG8(UART0_FCR) = TXRST | CHAR_TRIGGER_14; + return 0; + } + } while ((REG8(UART0_LSR) & LSR_THRE) == TxCHAR_AVAIL); + + /* Send Character */ + REG8(UART0_THR) = c; +#endif + + // ------------------------------------------------------- + +#ifdef CONFIG_SERIAL_SC16IS7X0_CONSOLE + if( early_sc16is7x0_workable < 0 ) + return 0; + + #define MEM2REG( x ) ( ( x - UART0_BASE ) / 4 ) + + do + { + /* Prevent Hanging */ + if (busy_cnt++ >= 30000) + { + /* Reset Tx FIFO */ + sc16is7x0_serial_out_i2c( MEM2REG(UART0_FCR), TXRST | CHAR_TRIGGER_14 ); + + return 0; + } + } while ((sc16is7x0_serial_in_i2c( MEM2REG(UART0_LSR) ) & LSR_THRE) == TxCHAR_AVAIL); + + /* Send Character */ + sc16is7x0_serial_out_i2c( MEM2REG(UART0_THR), c ); + + #undef MEM2REG +#endif + + return 1; +} + +static void __init +early_console_write(struct console *con, const char *s, unsigned n) +{ + while (n-- && *s) { + if (*s == '\n') + prom_putchar('\r'); + prom_putchar(*s); + s++; + } +} + +static struct console early_console __initdata = { + .name = "early", + .write = early_console_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1 +}; + +static int early_console_initialized __initdata; + +void __init setup_early_printk(void) +{ + if (early_console_initialized) + return; + early_console_initialized = 1; + +#ifdef CONFIG_SERIAL_SC16IS7X0_CONSOLE + early_sc16is7x0_workable = early_sc16is7x0_setup( NULL, NULL ); +#endif + register_console(&early_console); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/entry.S b/target/linux/realtek/files/arch/rlx/kernel/entry.S new file mode 100644 index 000000000..0bf1749c8 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/entry.S @@ -0,0 +1,136 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1994 - 2000, 2001, 2003 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ + +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/regdef.h> +#include <asm/rlxregs.h> +#include <asm/stackframe.h> +#include <asm/isadep.h> +#include <asm/thread_info.h> + +#ifndef CONFIG_PREEMPT +#define resume_kernel restore_all +#else +#define __ret_from_irq ret_from_exception +#endif + + .text + .align 5 +#ifndef CONFIG_PREEMPT +FEXPORT(ret_from_exception) + local_irq_disable # preempt stop + b __ret_from_irq +#endif +FEXPORT(ret_from_irq) + LONG_S s0, TI_REGS($28) +FEXPORT(__ret_from_irq) + LONG_L t0, PT_STATUS(sp) # returning to kernel mode? + andi t0, t0, KU_USER + beqz t0, resume_kernel + +resume_userspace: + local_irq_disable # make sure we dont miss an + # interrupt setting need_resched + # between sampling and return + LONG_L a2, TI_FLAGS($28) # current->work + andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace) + bnez t0, work_pending + j restore_all + +#ifdef CONFIG_PREEMPT +resume_kernel: + local_irq_disable + lw t0, TI_PRE_COUNT($28) + bnez t0, restore_all +need_resched: + LONG_L t0, TI_FLAGS($28) + andi t1, t0, _TIF_NEED_RESCHED + beqz t1, restore_all + LONG_L t0, PT_STATUS(sp) # Interrupts off? + andi t0, 1 + beqz t0, restore_all + jal preempt_schedule_irq + b need_resched +#endif + +FEXPORT(ret_from_fork) + jal schedule_tail # a0 = struct task_struct *prev + +FEXPORT(syscall_exit) + local_irq_disable # make sure need_resched and + # signals dont change between + # sampling and return + LONG_L a2, TI_FLAGS($28) # current->work + li t0, _TIF_ALLWORK_MASK + and t0, a2, t0 + bnez t0, syscall_exit_work + +FEXPORT(restore_all) # restore full frame + .set noat + RESTORE_TEMP + RESTORE_AT + RESTORE_STATIC +FEXPORT(restore_partial) # restore partial frame +#ifdef CONFIG_TRACE_IRQFLAGS + SAVE_STATIC + SAVE_AT + SAVE_TEMP + LONG_L v0, PT_STATUS(sp) + and v0, ST0_IEP + beqz v0, 1f + jal trace_hardirqs_on + b 2f +1: jal trace_hardirqs_off +2: + RESTORE_TEMP + RESTORE_AT + RESTORE_STATIC +#endif + RESTORE_SOME + RESTORE_SP_AND_RET + .set at + +work_pending: + andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS + beqz t0, work_notifysig +work_resched: + jal schedule + + local_irq_disable # make sure need_resched and + # signals dont change between + # sampling and return + LONG_L a2, TI_FLAGS($28) + andi t0, a2, _TIF_WORK_MASK # is there any work to be done + # other than syscall tracing? + beqz t0, restore_all + andi t0, a2, _TIF_NEED_RESCHED + bnez t0, work_resched + +work_notifysig: # deal with pending signals and + # notify-resume requests + move a0, sp + li a1, 0 + jal do_notify_resume # a2 already loaded + j resume_userspace + +FEXPORT(syscall_exit_work_partial) + SAVE_STATIC +syscall_exit_work: + li t0, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + and t0, a2 # a2 is preloaded with TI_FLAGS + beqz t0, work_pending # trace bit set? + local_irq_enable # could let do_syscall_trace() + # call schedule() instead + move a0, sp + li a1, 1 + jal do_syscall_trace + b resume_userspace + diff --git a/target/linux/realtek/files/arch/rlx/kernel/genex.S b/target/linux/realtek/files/arch/rlx/kernel/genex.S new file mode 100644 index 000000000..0c3be93d9 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/genex.S @@ -0,0 +1,217 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1994 - 2000, 2001, 2003 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + * Copyright (C) 2002, 2007 Maciej W. Rozycki + */ +#include <linux/init.h> + +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/cacheops.h> +#include <asm/irqflags.h> +#include <asm/regdef.h> +#include <asm/rlxregs.h> +#include <asm/stackframe.h> +#include <asm/page.h> +#include <asm/thread_info.h> + +#define PANIC_PIC(msg) \ + .set push; \ + .set reorder; \ + PTR_LA a0,8f; \ + .set noat; \ + PTR_LA AT, panic; \ + jr AT; \ +9: b 9b; \ + .set pop; \ + TEXT(msg) + + __INIT + +/* + * rlx_trap_dispatch: exception vector for all RLX CPUs + * + * Be careful when changing this, it has to be at most 128 bytes + * to fit into space reserved for the exception handler. + */ +NESTED(rlx_trap_dispatch, 0, sp) + .set push + .set noat + mfc0 k1, CP0_CAUSE + andi k1, k1, 0x7c + PTR_L k0, exception_handlers(k1) + jr k0 + .set pop + END(rlx_trap_dispatch) + + __FINIT + + .align 5 +NESTED(rlx_irq_dispatch, PT_SIZE, sp) +#ifdef CONFIG_TRACE_IRQFLAGS + /* + * Check to see if the interrupted code has just disabled + * interrupts and ignore this interrupt for now if so. + * + * local_irq_disable() disables interrupts and then calls + * trace_hardirqs_off() to track the state. If an interrupt is taken + * after interrupts are disabled but before the state is updated + * it will appear to restore_all that it is incorrectly returning with + * interrupts disabled + */ + .set push + .set noat + mfc0 k0, CP0_STATUS + and k0, ST0_IEP + bnez k0, 1f + + mfc0 k0, CP0_EPC + .set noreorder + j k0 + rfe +1: + .set pop +#endif + SAVE_ALL + CLI + TRACE_IRQS_OFF + + LONG_L s0, TI_REGS($28) + LONG_S sp, TI_REGS($28) + PTR_LA ra, ret_from_irq + j bsp_irq_dispatch + END(rlx_irq_dispatch) + + .align 6 +NESTED(rlx_vec_dispatch, PT_SIZE, sp) + .set push + .set noreorder + b 0f + nop + b 1f + nop + b 2f + nop + b 3f + nop + b 4f + nop + b 5f + nop + b 6f + nop + b 7f + nop + +0: SAVE_SP + b 9f + li k1, 0 + +1: SAVE_SP + b 9f + li k1, 1 + +2: SAVE_SP + b 9f + li k1, 2 + +3: SAVE_SP + b 9f + li k1, 3 + +4: SAVE_SP + b 9f + li k1, 4 + +5: SAVE_SP + b 9f + li k1, 5 + +6: SAVE_SP + b 9f + li k1, 6 + +7: SAVE_SP + li k1, 7 + .set pop + +9: SAVE_ALL_BUT_SP + CLI + TRACE_IRQS_OFF + + LONG_L s0, TI_REGS($28) + LONG_S sp, TI_REGS($28) + PTR_LA ra, ret_from_irq + move a0, k1 + j rlx_do_lopi_IRQ + END(rlx_vec_dispatch) + + .macro __build_clear_none + .endm + + .macro __build_clear_sti + TRACE_IRQS_ON + STI + .endm + + .macro __build_clear_cli + CLI + TRACE_IRQS_OFF + .endm + + .macro __build_clear_ade + MFC0 t0, CP0_BADVADDR + PTR_S t0, PT_BVADDR(sp) + KMODE + .endm + + .macro __BUILD_silent exception + .endm + + /* Gas tries to parse the PRINT argument as a string containing + string escapes and emits bogus warnings if it believes to + recognize an unknown escape code. So make the arguments + start with an n and gas will believe \n is ok ... */ + .macro __BUILD_verbose nexception + LONG_L a1, PT_EPC(sp) + PRINT("Got \nexception at %08lx\012") + .endm + + .macro __BUILD_count exception + LONG_L t0,exception_count_\exception + LONG_ADDIU t0, 1 + LONG_S t0,exception_count_\exception + .comm exception_count\exception, 8, 8 + .endm + + .macro __BUILD_HANDLER exception handler clear verbose ext + .align 5 + NESTED(handle_\exception, PT_SIZE, sp) + .set noat + SAVE_ALL + FEXPORT(handle_\exception\ext) + __BUILD_clear_\clear + .set at + __BUILD_\verbose \exception + move a0, sp + PTR_LA ra, ret_from_exception + j do_\handler + END(handle_\exception) + .endm + + .macro BUILD_HANDLER exception handler clear verbose + __BUILD_HANDLER \exception \handler \clear \verbose _int + .endm + + BUILD_HANDLER adel ade ade silent /* #4 */ + BUILD_HANDLER ades ade ade silent /* #5 */ + BUILD_HANDLER bp bp sti silent /* #9 */ + BUILD_HANDLER ri ri sti silent /* #10 */ + BUILD_HANDLER cpu cpu sti silent /* #11 */ + BUILD_HANDLER ov ov sti silent /* #12 */ + BUILD_HANDLER reserved reserved sti verbose /* others */ diff --git a/target/linux/realtek/files/arch/rlx/kernel/head.S b/target/linux/realtek/files/arch/rlx/kernel/head.S new file mode 100644 index 000000000..0888ef62f --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/head.S @@ -0,0 +1,160 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * Written by Ralf Baechle and Andreas Busse + * Copyright (C) 1994 - 99, 2003, 06 Ralf Baechle + * Copyright (C) 1996 Paul M. Antoine + * Modified for DECStation and hence R3000 support by Paul M. Antoine + * Further modifications by David S. Miller and Harald Koerfgen + * Copyright (C) 1999 Silicon Graphics, Inc. + * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/init.h> +#include <linux/threads.h> + +#include <asm/addrspace.h> +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/irqflags.h> +#include <asm/regdef.h> +#include <asm/page.h> +#include <asm/pgtable-bits.h> +#include <asm/rlxregs.h> +#include <asm/stackframe.h> + +#include <bspinit.h> + + /* + * inputs are the text nasid in t1, data nasid in t2. + */ + .macro MAPPED_KERNEL_SETUP_TLB +#ifdef CONFIG_MAPPED_KERNEL + /* + * This needs to read the nasid - assume 0 for now. + * Drop in 0xffffffffc0000000 in tlbhi, 0+VG in tlblo_0, + * 0+DVG in tlblo_1. + */ + dli t0, 0xffffffffc0000000 + dmtc0 t0, CP0_ENTRYHI + li t0, 0x1c000 # Offset of text into node memory + dsll t1, NASID_SHFT # Shift text nasid into place + dsll t2, NASID_SHFT # Same for data nasid + or t1, t1, t0 # Physical load address of kernel text + or t2, t2, t0 # Physical load address of kernel data + dsrl t1, 12 # 4K pfn + dsrl t2, 12 # 4K pfn + dsll t1, 6 # Get pfn into place + dsll t2, 6 # Get pfn into place + li t0, ((_PAGE_GLOBAL|_PAGE_VALID| _CACHE_CACHABLE_COW) >> 6) + or t0, t0, t1 + mtc0 t0, CP0_ENTRYLO0 # physaddr, VG, cach exlwr + li t0, ((_PAGE_GLOBAL|_PAGE_VALID| _PAGE_DIRTY|_CACHE_CACHABLE_COW) >> 6) + or t0, t0, t2 + mtc0 t0, CP0_ENTRYLO1 # physaddr, DVG, cach exlwr + li t0, 0x1ffe000 # MAPPED_KERN_TLBMASK, TLBPGMASK_16M + mtc0 t0, CP0_PAGEMASK + li t0, 0 # KMAP_INX + mtc0 t0, CP0_INDEX + li t0, 1 + mtc0 t0, CP0_WIRED + tlbwi +#else + mtc0 zero, CP0_WIRED +#endif + .endm + + /* + * For the moment disable interrupts, mark the kernel mode and + * set ST0_KX so that the CPU does not spit fire when using + * 64-bit addresses. A full initialization of the CPU's status + * register is done later in per_cpu_trap_init(). + */ + .macro setup_c0_status set clr + .set push + mfc0 t0, CP0_STATUS + or t0, ST0_CU0|\set|0x1f|\clr + xor t0, 0x1f|\clr + mtc0 t0, CP0_STATUS + .set noreorder + sll zero,3 # ehb + .set pop + .endm + + .macro setup_c0_status_pri + setup_c0_status 0 0 + .endm + + .macro setup_c0_status_sec + setup_c0_status 0 ST0_BEV + .endm + +#ifndef CONFIG_NO_EXCEPT_FILL + /* + * Reserved space for exception handlers. + * Necessary for machines which link their kernels at KSEG0. + */ +#ifdef CONFIG_RTL_8198_NFBI_BOARD + .fill 0x8080 +#else + .fill 0x400 +#endif +#endif + +EXPORT(_stext) + +#ifdef CONFIG_BOOT_RAW + /* + * Give us a fighting chance of running if execution beings at the + * kernel load address. This is needed because this platform does + * not have a ELF loader yet. + */ +FEXPORT(__kernel_entry) + j kernel_entry +#endif + + __REF + +NESTED(kernel_entry, 16, sp) # kernel entry point + + kernel_entry_setup # cpu specific setup + + setup_c0_status_pri + + /* We might not get launched at the address the kernel is linked to, + so we jump there. */ + PTR_LA t0, 0f + jr t0 +0: + + PTR_LA t0, __bss_start # clear .bss + LONG_S zero, (t0) + PTR_LA t1, __bss_stop - LONGSIZE +1: + PTR_ADDIU t0, LONGSIZE + LONG_S zero, (t0) + bne t0, t1, 1b + + LONG_S a0, fw_arg0 # firmware arguments + LONG_S a1, fw_arg1 + LONG_S a2, fw_arg2 + LONG_S a3, fw_arg3 + + MTC0 zero, CP0_CONTEXT # clear context register + PTR_LA $28, init_thread_union + /* Set the SP after an empty pt_regs. */ + PTR_LI sp, _THREAD_SIZE - 32 - PT_SIZE + PTR_ADDU sp, $28 + back_to_back_c0_hazard + set_saved_sp sp, t0, t1 + PTR_SUBU sp, 4 * SZREG # init stack pointer + + j start_kernel + END(kernel_entry) + + __CPUINIT + + __FINIT diff --git a/target/linux/realtek/files/arch/rlx/kernel/init_task.c b/target/linux/realtek/files/arch/rlx/kernel/init_task.c new file mode 100644 index 000000000..149cd9145 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/init_task.c @@ -0,0 +1,40 @@ +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init_task.h> +#include <linux/fs.h> +#include <linux/mqueue.h> + +#include <asm/thread_info.h> +#include <asm/uaccess.h> +#include <asm/pgtable.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 8192-byte aligned due to the + * way process stacks are handled. This is done by making sure + * the linker maps this in the .text segment right after head.S, + * and making head.S ensure the proper alignment. + * + * The things we do for performance.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"), + __aligned__(THREAD_SIZE))) = + { 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/target/linux/realtek/files/arch/rlx/kernel/irq.c b/target/linux/realtek/files/arch/rlx/kernel/irq.c new file mode 100644 index 000000000..51e9442b7 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/irq.c @@ -0,0 +1,257 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Code to handle x86 style IRQs plus some generic interrupt stuff. + * + * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + */ +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/module.h> +#include <linux/proc_fs.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/random.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <linux/kallsyms.h> +#include <linux/kgdb.h> + +#include <asm/atomic.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +#ifdef CONFIG_KGDB +int kgdb_early_setup; +#endif + +static unsigned long irq_map[NR_IRQS / BITS_PER_LONG]; + +int allocate_irqno(void) +{ + int irq; + +again: + irq = find_first_zero_bit(irq_map, NR_IRQS); + + if (irq >= NR_IRQS) + return -ENOSPC; + + if (test_and_set_bit(irq, irq_map)) + goto again; + + return irq; +} + +/* + * Allocate the 16 legacy interrupts for i8259 devices. This happens early + * in the kernel initialization so treating allocation failure as BUG() is + * ok. + */ +void __init alloc_legacy_irqno(void) +{ + int i; + + for (i = 0; i <= 16; i++) + BUG_ON(test_and_set_bit(i, irq_map)); +} + +void free_irqno(unsigned int irq) +{ + smp_mb__before_clear_bit(); + clear_bit(irq, irq_map); + smp_mb__after_clear_bit(); +} + +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves. + */ +void ack_bad_irq(unsigned int irq) +{ + printk("unexpected IRQ # %d\n", irq); +} + +//by rock 2010-04-06 +#if defined(CONFIG_RTK_VOIP) || defined(CONFIG_RTL_819X) +static atomic_t irq_err_count[SPURIOS_INT_MAX]; +#else +static atomic_t irq_err_count; +#endif + +#ifdef CONFIG_CPU_RLX5281 + +static void get_int_counts(u32 *hw_int, u32 *vec_int) +{ + // enable hardware, vercor interrupts + u32 mode = (0x2e << 0) | (0x2f << 8); + u64 cnt[4]; + + __asm__ __volatile__ ( + // enable cp3 + "mfc0 $8, $12 \n\t" + "nop \n\t" + "nop \n\t" + "or $8, 0x80000000 \n\t" + "mtc0 $8, $12 \n\t" + "nop \n\t" + "nop \n\t" + // stop counter + "ctc3 $0,$0 \n\t" + "nop \n\t" + "nop \n\t" + // save counter + "mfc3 %L[cnt0],$8 \n\t" + "mfc3 %M[cnt0],$9 \n\t" + "mfc3 %L[cnt1],$10 \n\t" + "mfc3 %M[cnt1],$11 \n\t" + "mfc3 %L[cnt2],$12 \n\t" + "mfc3 %M[cnt2],$13 \n\t" + "mfc3 %L[cnt3],$14 \n\t" + "mfc3 %M[cnt3],$15 \n\t" + "nop \n\t" + "nop \n\t" + // start counter + "ctc3 %[mode],$0 \n\t" + "nop \n\t" + "nop \n\t" + :[cnt0] "=&r" (cnt[0]), [cnt1] "=&r" (cnt[1]), [cnt2] "=&r" (cnt[2]), [cnt3] "=&r" (cnt[3]) + :[mode] "r" (mode) + ); + + *hw_int = (u32) cnt[0]; + *vec_int = (u32) cnt[1]; +} + +#endif + +/* + * Generic, controller-independent functions: + */ + +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, j; + struct irqaction * action; + unsigned long flags; + struct irq_desc *rtl_irq_desc; + + if (i == 0) { + seq_printf(p, " "); + for_each_online_cpu(j) + seq_printf(p, "CPU%d ", j); + seq_putc(p, '\n'); + } + + if (i < NR_IRQS) { + rtl_irq_desc = irq_to_desc(i); + spin_lock_irqsave(rtl_irq_desc->lock, flags); + action = rtl_irq_desc->action; + if (!action) + goto skip; + seq_printf(p, "%3d: ", i); + seq_printf(p, "%10u ", kstat_irqs(i)); + seq_printf(p, " %14s", rtl_irq_desc->chip->name); + seq_printf(p, " %s (0x%lx)", action->name, action->flags); + + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s (0x%lx)", action->name, action->flags); + + seq_putc(p, '\n'); +skip: + spin_unlock_irqrestore(rtl_irq_desc->lock, flags); + } else if (i == NR_IRQS) { +#ifdef CONFIG_CPU_RLX5281 + u32 hw_int, vec_int; + static int first_dump = 1; + static int hw_int_off, vec_int_off; +#endif + + seq_printf(p, "\n"); +#if defined(CONFIG_RTK_VOIP) || defined(CONFIG_RTL_819X) + for (j=0; j<ARRAY_SIZE(irq_err_count); j++) + seq_printf(p, "ER%d: %10u\n", j, atomic_read(&irq_err_count[j])); +#else + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); +#endif + +#ifdef CONFIG_CPU_RLX5281 + get_int_counts(&hw_int, &vec_int); + + if (first_dump) + { + first_dump = 0; + hw_int_off = 0; + vec_int_off = 0; + for (j=0; j<NR_IRQS; j++) + { + rtl_irq_desc = irq_to_desc(j); + if (rtl_irq_desc->action == NULL) + continue; + + if (strcmp(rtl_irq_desc->chip->name, "RLX") == 0) + hw_int_off += kstat_irqs(j); + else if (strcmp(rtl_irq_desc->chip->name, "RLX LOPI") == 0) + vec_int_off += kstat_irqs(j); + else if (strcmp(rtl_irq_desc->chip->name, "ICTL") == 0) + hw_int_off += kstat_irqs(j); + } + + // rock: use sw counter to calc hw counter offset on first time + // (first dump info is sw info) + hw_int_off -= hw_int; + vec_int_off -= vec_int; + } + + seq_printf(p, "\n HW: %10u\n", hw_int + hw_int_off); + seq_printf(p, "VEC: %10u\n", vec_int + vec_int_off); +#endif + + } + return 0; +} + +#if defined(CONFIG_RTK_VOIP) || defined(CONFIG_RTL_819X) +asmlinkage void spurious_interrupt(int i) +{ + atomic_inc(&irq_err_count[i]); +} +#else +asmlinkage void spurious_interrupt() +{ + atomic_inc(&irq_err_count); +} +#endif + +void __init init_IRQ(void) +{ + extern void bsp_irq_init(void); + int i; + +#ifdef CONFIG_KGDB + if (kgdb_early_setup) + return; +#endif + + for (i = 0; i < NR_IRQS; i++) + set_irq_noprobe(i); + + bsp_irq_init(); + +#ifdef CONFIG_KGDB + if (!kgdb_early_setup) + kgdb_early_setup = 1; +#endif + +#if defined(CONFIG_RTK_VOIP) || defined(CONFIG_RTL_819X) + for (i=0;i<SPURIOS_INT_MAX;i++) { + atomic_set(&irq_err_count[i], 0); + } +#endif +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/irq_cpu.c b/target/linux/realtek/files/arch/rlx/kernel/irq_cpu.c new file mode 100644 index 000000000..5ae21cac9 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/irq_cpu.c @@ -0,0 +1,113 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Copyright (C) 2001 Ralf Baechle + * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. + * Author: Maciej W. Rozycki <macro@mips.com> + * + * This file define the irq handler for MIPS CPU interrupts. + * + * 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. + */ + +/* + * Almost all MIPS CPUs define 8 interrupt sources. They are typically + * level triggered (i.e., cannot be cleared from CPU; must be cleared from + * device). The first two are software interrupts which we don't really + * use or support. The last one is usually the CPU timer interrupt if + * counter register is present or, for CPUs with an external FPU, by + * convention it's the FPU exception interrupt. + * + * Don't even think about using this on SMP. You have been warned. + * + * This file exports one global function: + * void rlx_cpu_irq_init(int irq_base); + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> + +#include <asm/irq_cpu.h> +#include <asm/rlxregs.h> +#include <asm/system.h> +#include <net/rtl/rtl_types.h> + +#include "bspchip.h" + +__IRAM_GEN +static inline void unmask_rlx_irq(unsigned int irq) +{ + set_c0_status(0x100 << (irq - BSP_IRQ_CPU_BASE)); + irq_enable_hazard(); +} + +__IRAM_GEN +static inline void mask_rlx_irq(unsigned int irq) +{ + clear_c0_status(0x100 << (irq - BSP_IRQ_CPU_BASE)); + irq_disable_hazard(); +} + +static struct irq_chip rlx_cpu_irq_controller = { + .name = "RLX", + .ack = mask_rlx_irq, + .mask = mask_rlx_irq, + .mask_ack = mask_rlx_irq, + .unmask = unmask_rlx_irq, + .eoi = unmask_rlx_irq, +}; + +void __init rlx_cpu_irq_init(int irq_base) +{ + int i; + + /* Mask interrupts. */ + clear_c0_status(ST0_IM); + clear_c0_cause(CAUSEF_IP); + + for (i = irq_base + 2; i < irq_base + BSP_IRQ_CPU_NUM; i++) + set_irq_chip_and_handler(i, &rlx_cpu_irq_controller, handle_percpu_irq); + + #if 0 + /* enable global interrupt mask */ + REG32(BSP_GIMR) = BSP_TC0_IE | BSP_UART0_IE ; + + #ifdef CONFIG_SERIAL_RTL8198_UART1 + REG32(BSP_GIMR) |= BSP_UART1_IE; + #endif + + #if defined(CONFIG_RTL8192CD) + #if (defined(CONFIG_RTL_8196C) || defined(CONFIG_RTL_8196CT) || defined(CONFIG_RTL_8196CS) || !defined(CONFIG_RTL_92D_DMDP)) + REG32(BSP_GIMR) |= (BSP_PCIE_IE); + #endif + #if defined(CONFIG_RTL_DUAL_PCIESLOT_BIWLAN_D) || (defined(CONFIG_RTL_8198)&&defined(CONFIG_RTL_92D_SUPPORT)) + REG32(BSP_GIMR) |= (BSP_PCIE2_IE); + #endif + #endif + + #if defined(CONFIG_USB) + REG32(BSP_GIMR) |= BSP_USB_H_IE; + #endif + + #if defined(CONFIG_RTL_819X) || defined(CONFIG_RTL_ICTEST_SWITCH) || defined(CONFIG_RTL_865X_ETH) + REG32(BSP_GIMR) |= (BSP_SW_IE); + #endif + + #if defined(CONFIG_RTL_NFBI_MDIO) + REG32(BSP_GIMR) |= BSP_NFBI_IE; + #endif + + #ifdef CONFIG_RTL_8198_NFBI_BOARD + setup_reboot_addr(0x80700000); + #endif + + #if defined(CONFIG_RTK_VOIP) + REG32(BSP_GIMR) |= (BSP_PCM_IE | BSP_I2S_IE); + REG32(BSP_GIMR) |= (BSP_GPIO_ABCD_IE | BSP_GPIO_EFGH_IE); + #endif + #endif +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/irq_vec.c b/target/linux/realtek/files/arch/rlx/kernel/irq_vec.c new file mode 100644 index 000000000..0fcda8735 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/irq_vec.c @@ -0,0 +1,123 @@ +/* + * Realtek Semiconductor Corp. + * + * This file define the irq handler for RLX CPU interrupts. + * + * Tony Wu (tonywu@realtek.com.tw) + * Feb. 28, 2008 + */ + +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> + +#include <asm/irq_vec.h> +#include <asm/rlxregs.h> +#include <asm/system.h> +#include <net/rtl/rtl_types.h> +#include "bspchip.h" + +__IRAM_GEN +static void unmask_rlx_vec_irq(unsigned int irq) +{ + set_lxc0_estatus(0x10000 << (irq - BSP_IRQ_LOPI_BASE)); + irq_enable_hazard(); +} + +__IRAM_GEN +static void mask_rlx_vec_irq(unsigned int irq) +{ + clear_lxc0_estatus(0x10000 << (irq - BSP_IRQ_LOPI_BASE)); + irq_disable_hazard(); +} + +static struct irq_chip rlx_vec_irq_controller = { + .typename = "RLX LOPI", + .ack = mask_rlx_vec_irq, + .mask = mask_rlx_vec_irq, + .mask_ack = mask_rlx_vec_irq, + .unmask = unmask_rlx_vec_irq, + .eoi = unmask_rlx_vec_irq, +}; + +//static struct irq_desc *rlx_vec_irq_desc; + +void __init rlx_vec_irq_init(int irq_base) +{ + int i; + extern char rlx_vec_dispatch; + + /* Mask interrupts. */ + clear_lxc0_estatus(EST0_IM); + clear_lxc0_ecause(ECAUSEF_IP); + + // rlx_vec_irq_desc = irq_desc + irq_base; + + for (i = irq_base; i < irq_base + BSP_IRQ_LOPI_NUM; i++) + set_irq_chip_and_handler(i, &rlx_vec_irq_controller, handle_percpu_irq); + + write_lxc0_intvec(&rlx_vec_dispatch); + + #if 1 + /* enable global interrupt mask */ + REG32(BSP_GIMR) = BSP_TC0_IE | BSP_UART0_IE ; + + #ifdef CONFIG_SERIAL_RTL8198_UART1 + REG32(BSP_GIMR) |= BSP_UART1_IE; + #endif + + #if defined(CONFIG_RTL8192CD) + #if (defined(CONFIG_RTL_8196C) || defined(CONFIG_RTL_8196CT) || defined(CONFIG_RTL_8196CS) || !defined(CONFIG_RTL_92D_DMDP)) + REG32(BSP_GIMR) |= (BSP_PCIE_IE); + #endif + #if defined(CONFIG_RTL_DUAL_PCIESLOT_BIWLAN_D) || (defined(CONFIG_RTL_8198)&&defined(CONFIG_RTL_92D_SUPPORT)) + REG32(BSP_GIMR) |= (BSP_PCIE2_IE); + #endif + #endif + + #if defined(CONFIG_USB) + REG32(BSP_GIMR) |= BSP_USB_H_IE; + #endif + + #if defined(CONFIG_RTL_819X) || defined(CONFIG_RTL_ICTEST_SWITCH) || defined(CONFIG_RTL_865X_ETH) + REG32(BSP_GIMR) |= (BSP_SW_IE); + #endif + + #if defined(CONFIG_RTL_NFBI_MDIO) + REG32(BSP_GIMR) |= BSP_NFBI_IE; + #endif + + #ifdef CONFIG_RTL_8198_NFBI_BOARD + setup_reboot_addr(0x80700000); + #endif + + #if defined(CONFIG_RTK_VOIP) + REG32(BSP_GIMR) |= (BSP_PCM_IE | BSP_I2S_IE); + REG32(BSP_GIMR) |= (BSP_GPIO_ABCD_IE | BSP_GPIO_EFGH_IE); + #endif + #endif + +} + +__IRAM_GEN +asmlinkage void rlx_do_lopi_IRQ(int irq_offset) +{ + unsigned int pending; + unsigned int cause; + unsigned int status; + + cause = read_lxc0_ecause(); + status = read_lxc0_estatus(); + pending = cause & status & EST0_IM; + + if (pending & (_ULCAST_(1) << (irq_offset + 16))) { + do_IRQ(BSP_IRQ_LOPI_BASE + irq_offset); + } else { + #if defined(CONFIG_RTK_VOIP) || defined(CONFIG_RTL_819X) + spurious_interrupt(SPURIOS_INT_LOPI); + #else + spurious_interrupt(); + #endif + } +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/kgdb.c b/target/linux/realtek/files/arch/rlx/kernel/kgdb.c new file mode 100644 index 000000000..88f0de2fd --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/kgdb.c @@ -0,0 +1,260 @@ +/* + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Copyright (C) 2004-2005 MontaVista Software Inc. + * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com + * + * Copyright (C) 2007-2008 Wind River Systems, Inc. + * Author/Maintainer: Jason Wessel, jason.wessel@windriver.com + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/ptrace.h> /* for linux pt_regs struct */ +#include <linux/kgdb.h> +#include <linux/kdebug.h> +#include <linux/sched.h> +#include <asm/inst.h> +#include <asm/cacheflush.h> +#include <asm/processor.h> +#include <asm/sigcontext.h> + +static struct hard_trap_info { + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 6, SIGBUS }, /* instruction bus error */ + { 7, SIGBUS }, /* data bus error */ + { 9, SIGTRAP }, /* break */ +/* { 11, SIGILL }, */ /* CPU unusable */ + { 12, SIGFPE }, /* overflow */ + { 13, SIGTRAP }, /* trap */ + { 14, SIGSEGV }, /* virtual instruction cache coherency */ + { 15, SIGFPE }, /* floating point exception */ + { 23, SIGSEGV }, /* watch */ + { 31, SIGSEGV }, /* virtual data cache coherency */ + { 0, 0} /* Must be last */ +}; + +void arch_kgdb_breakpoint(void) +{ + __asm__ __volatile__( + ".globl breakinst\n\t" + ".set\tnoreorder\n\t" + "nop\n" + "breakinst:\tbreak\n\t" + "nop\n\t" + ".set\treorder"); +} + +static void kgdb_call_nmi_hook(void *ignored) +{ + kgdb_nmicallback(raw_smp_processor_id(), NULL); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ + local_irq_enable(); + smp_call_function(kgdb_call_nmi_hook, NULL, 0); + local_irq_disable(); +} + +static int compute_signal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int reg; + +#if (KGDB_GDB_REG_SIZE == 32) + u32 *ptr = (u32 *)gdb_regs; +#else + u64 *ptr = (u64 *)gdb_regs; +#endif + + for (reg = 0; reg < 32; reg++) + *(ptr++) = regs->regs[reg]; + + *(ptr++) = regs->cp0_status; + *(ptr++) = regs->lo; + *(ptr++) = regs->hi; + *(ptr++) = regs->cp0_badvaddr; + *(ptr++) = regs->cp0_cause; + *(ptr++) = regs->cp0_epc; +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int reg; + +#if (KGDB_GDB_REG_SIZE == 32) + const u32 *ptr = (u32 *)gdb_regs; +#else + const u64 *ptr = (u64 *)gdb_regs; +#endif + + for (reg = 0; reg < 32; reg++) + regs->regs[reg] = *(ptr++); + + regs->cp0_status = *(ptr++); + regs->lo = *(ptr++); + regs->hi = *(ptr++); + regs->cp0_badvaddr = *(ptr++); + regs->cp0_cause = *(ptr++); + regs->cp0_epc = *(ptr++); +} + +/* + * Similar to regs_to_gdb_regs() except that process is sleeping and so + * we may not be able to get all the info. + */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + int reg; + struct thread_info *ti = task_thread_info(p); + unsigned long ksp = (unsigned long)ti + THREAD_SIZE - 32; + struct pt_regs *regs = (struct pt_regs *)ksp - 1; +#if (KGDB_GDB_REG_SIZE == 32) + u32 *ptr = (u32 *)gdb_regs; +#else + u64 *ptr = (u64 *)gdb_regs; +#endif + + for (reg = 0; reg < 16; reg++) + *(ptr++) = regs->regs[reg]; + + /* S0 - S7 */ + for (reg = 16; reg < 24; reg++) + *(ptr++) = regs->regs[reg]; + + for (reg = 24; reg < 28; reg++) + *(ptr++) = 0; + + /* GP, SP, FP, RA */ + for (reg = 28; reg < 32; reg++) + *(ptr++) = regs->regs[reg]; + + *(ptr++) = regs->cp0_status; + *(ptr++) = regs->lo; + *(ptr++) = regs->hi; + *(ptr++) = regs->cp0_badvaddr; + *(ptr++) = regs->cp0_cause; + *(ptr++) = regs->cp0_epc; +} + +/* + * Calls linux_debug_hook before the kernel dies. If KGDB is enabled, + * then try to fall into the debugger + */ +static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd, + void *ptr) +{ + struct die_args *args = (struct die_args *)ptr; + struct pt_regs *regs = args->regs; + int trap = (regs->cp0_cause & 0x7c) >> 2; + + /* Userpace events, ignore. */ + if (user_mode(regs)) + return NOTIFY_DONE; + + if (atomic_read(&kgdb_active) != -1) + kgdb_nmicallback(smp_processor_id(), regs); + + if (kgdb_handle_exception(trap, compute_signal(trap), 0, regs)) + return NOTIFY_DONE; + + if (atomic_read(&kgdb_setting_breakpoint)) + if ((trap == 9) && (regs->cp0_epc == (unsigned long)breakinst)) + regs->cp0_epc += 4; + + /* In SMP mode, __flush_cache_all does IPI */ + local_irq_enable(); + __flush_cache_all(); + + return NOTIFY_STOP; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_mips_notify, +}; + +/* + * Handle the 's' and 'c' commands + */ +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long address; + int cpu = smp_processor_id(); + + switch (remcom_in_buffer[0]) { + case 's': + case 'c': + /* handle the optional parameter */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &address)) + regs->cp0_epc = address; + + atomic_set(&kgdb_cpu_doing_single_step, -1); + if (remcom_in_buffer[0] == 's') + atomic_set(&kgdb_cpu_doing_single_step, cpu); + + return 0; + } + + return -1; +} + +struct kgdb_arch arch_kgdb_ops; + +/* + * We use kgdb_early_setup so that functions we need to call now don't + * cause trouble when called again later. + */ +int kgdb_arch_init(void) +{ + union mips_instruction insn = { + .r_format = { + .opcode = spec_op, + .func = break_op, + } + }; + memcpy(arch_kgdb_ops.gdb_bpt_instr, insn.byte, BREAK_INSTR_SIZE); + + register_die_notifier(&kgdb_notifier); + + return 0; +} + +/* + * kgdb_arch_exit - Perform any architecture specific uninitalization. + * + * This function will handle the uninitalization of any architecture + * specific callbacks, for dynamic registration and unregistration. + */ +void kgdb_arch_exit(void) +{ + unregister_die_notifier(&kgdb_notifier); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/machine_kexec.c b/target/linux/realtek/files/arch/rlx/kernel/machine_kexec.c new file mode 100644 index 000000000..85beb9b0b --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/machine_kexec.c @@ -0,0 +1,85 @@ +/* + * machine_kexec.c for kexec + * Created by <nschichan@corp.free.fr> on Thu Oct 12 15:15:06 2006 + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kexec.h> +#include <linux/mm.h> +#include <linux/delay.h> + +#include <asm/cacheflush.h> +#include <asm/page.h> + +extern const unsigned char relocate_new_kernel[]; +extern const size_t relocate_new_kernel_size; + +extern unsigned long kexec_start_address; +extern unsigned long kexec_indirection_page; + +int +machine_kexec_prepare(struct kimage *kimage) +{ + return 0; +} + +void +machine_kexec_cleanup(struct kimage *kimage) +{ +} + +void +machine_shutdown(void) +{ +} + +void +machine_crash_shutdown(struct pt_regs *regs) +{ +} + +typedef void (*noretfun_t)(void) __attribute__((noreturn)); + +void +machine_kexec(struct kimage *image) +{ + unsigned long reboot_code_buffer; + unsigned long entry; + unsigned long *ptr; + + reboot_code_buffer = + (unsigned long)page_address(image->control_code_page); + + kexec_start_address = image->start; + kexec_indirection_page = + (unsigned long) phys_to_virt(image->head & PAGE_MASK); + + memcpy((void*)reboot_code_buffer, relocate_new_kernel, + relocate_new_kernel_size); + + /* + * The generic kexec code builds a page list with physical + * addresses. they are directly accessible through KSEG0 (or + * CKSEG0 or XPHYS if on 64bit system), hence the + * pys_to_virt() call. + */ + for (ptr = &image->head; (entry = *ptr) && !(entry &IND_DONE); + ptr = (entry & IND_INDIRECTION) ? + phys_to_virt(entry & PAGE_MASK) : ptr + 1) { + if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || + *ptr & IND_DESTINATION) + *ptr = (unsigned long) phys_to_virt(*ptr); + } + + /* + * we do not want to be bothered. + */ + local_irq_disable(); + + printk("Will call new kernel at %08lx\n", image->start); + printk("Bye ...\n"); + __flush_cache_all(); + ((noretfun_t) reboot_code_buffer)(); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/mips_ksyms.c b/target/linux/realtek/files/arch/rlx/kernel/mips_ksyms.c new file mode 100644 index 000000000..225755d0c --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/mips_ksyms.c @@ -0,0 +1,53 @@ +/* + * Export MIPS-specific functions needed for loadable modules. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 97, 98, 99, 2000, 01, 03, 04, 05 by Ralf Baechle + * Copyright (C) 1999, 2000, 01 Silicon Graphics, Inc. + */ +#include <linux/interrupt.h> +#include <linux/module.h> +#include <asm/checksum.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> + +extern void *__bzero(void *__s, size_t __count); +extern long __strncpy_from_user_nocheck_asm(char *__to, + const char *__from, long __len); +extern long __strncpy_from_user_asm(char *__to, const char *__from, + long __len); +extern long __strlen_user_nocheck_asm(const char *s); +extern long __strlen_user_asm(const char *s); +extern long __strnlen_user_nocheck_asm(const char *s); +extern long __strnlen_user_asm(const char *s); + +/* + * String functions + */ +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); + +EXPORT_SYMBOL(kernel_thread); + +/* + * Userspace access stuff. + */ +EXPORT_SYMBOL(__copy_user); +EXPORT_SYMBOL(__copy_user_inatomic); +EXPORT_SYMBOL(__bzero); +EXPORT_SYMBOL(__strncpy_from_user_nocheck_asm); +EXPORT_SYMBOL(__strncpy_from_user_asm); +EXPORT_SYMBOL(__strlen_user_nocheck_asm); +EXPORT_SYMBOL(__strlen_user_asm); +EXPORT_SYMBOL(__strnlen_user_nocheck_asm); +EXPORT_SYMBOL(__strnlen_user_asm); + +EXPORT_SYMBOL(csum_partial); +EXPORT_SYMBOL(csum_partial_copy_nocheck); +EXPORT_SYMBOL(__csum_partial_copy_user); + +EXPORT_SYMBOL(invalid_pte_table); diff --git a/target/linux/realtek/files/arch/rlx/kernel/module.c b/target/linux/realtek/files/arch/rlx/kernel/module.c new file mode 100644 index 000000000..ac9370f09 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/module.c @@ -0,0 +1,409 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2001 Rusty Russell. + * Copyright (C) 2003, 2004 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2005 Thiemo Seufer + */ + +#undef DEBUG + +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <asm/pgtable.h> /* MODULE_START */ + +struct mips_hi16 { + struct mips_hi16 *next; + Elf_Addr *addr; + Elf_Addr value; +}; + +static struct mips_hi16 *mips_hi16_list; + +static LIST_HEAD(dbe_list); +static DEFINE_SPINLOCK(dbe_lock); + +void *module_alloc(unsigned long size) +{ +#ifdef MODULE_START + struct vm_struct *area; + + size = PAGE_ALIGN(size); + if (!size) + return NULL; + + area = __get_vm_area(size, VM_ALLOC, MODULE_START, MODULE_END); + if (!area) + return NULL; + + return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL); +#else + if (size == 0) + return NULL; + return vmalloc(size); +#endif +} + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, + char *secstrings, struct module *mod) +{ + return 0; +} + +static int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) +{ + return 0; +} + +static int apply_r_mips_32_rel(struct module *me, u32 *location, Elf_Addr v) +{ + *location += v; + + return 0; +} + +static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v) +{ + *location = v; + + return 0; +} + +static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) +{ + if (v % 4) { + pr_err("module %s: dangerous R_MIPS_26 REL relocation\n", me->name); + return -ENOEXEC; + } + + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { + printk(KERN_ERR "module %s: relocation overflow\n", me->name); + return -ENOEXEC; + } + + *location = (*location & ~0x03ffffff) | + ((*location + (v >> 2)) & 0x03ffffff); + + return 0; +} + +static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v) +{ + if (v % 4) { + pr_err("module %s: dangerous R_MIPS_26 RELA relocation\n", me->name); + return -ENOEXEC; + } + + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { + printk(KERN_ERR + "module %s: relocation overflow\n", + me->name); + return -ENOEXEC; + } + + *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff); + + return 0; +} + +static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v) +{ + struct mips_hi16 *n; + + /* + * We cannot relocate this one now because we don't know the value of + * the carry we need to add. Save the information, and let LO16 do the + * actual relocation. + */ + n = kmalloc(sizeof *n, GFP_KERNEL); + if (!n) + return -ENOMEM; + + n->addr = (Elf_Addr *)location; + n->value = v; + n->next = mips_hi16_list; + mips_hi16_list = n; + + return 0; +} + +static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v) +{ + *location = (*location & 0xffff0000) | + ((((long long) v + 0x8000LL) >> 16) & 0xffff); + + return 0; +} + +static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) +{ + unsigned long insnlo = *location; + Elf_Addr val, vallo; + + /* Sign extend the addend we extract from the lo insn. */ + vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; + + if (mips_hi16_list != NULL) { + struct mips_hi16 *l; + + l = mips_hi16_list; + while (l != NULL) { + struct mips_hi16 *next; + unsigned long insn; + + /* + * The value for the HI16 had best be the same. + */ + if (v != l->value) + goto out_danger; + + /* + * Do the HI16 relocation. Note that we actually don't + * need to know anything about the LO16 itself, except + * where to find the low 16 bits of the addend needed + * by the LO16. + */ + insn = *l->addr; + val = ((insn & 0xffff) << 16) + vallo; + val += v; + + /* + * Account for the sign extension that will happen in + * the low bits. + */ + val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff; + + insn = (insn & ~0xffff) | val; + *l->addr = insn; + + next = l->next; + kfree(l); + l = next; + } + + mips_hi16_list = NULL; + } + + /* + * Ok, we're done with the HI16 relocs. Now deal with the LO16. + */ + val = v + vallo; + insnlo = (insnlo & ~0xffff) | (val & 0xffff); + *location = insnlo; + + return 0; + +out_danger: + pr_err("module %s: dangerous R_MIPS_LO16 REL relocation\n", me->name); + + return -ENOEXEC; +} + +static int apply_r_mips_lo16_rela(struct module *me, u32 *location, Elf_Addr v) +{ + *location = (*location & 0xffff0000) | (v & 0xffff); + + return 0; +} + +static int apply_r_mips_64_rela(struct module *me, u32 *location, Elf_Addr v) +{ + *(Elf_Addr *)location = v; + + return 0; +} + +static int apply_r_mips_higher_rela(struct module *me, u32 *location, + Elf_Addr v) +{ + *location = (*location & 0xffff0000) | + ((((long long) v + 0x80008000LL) >> 32) & 0xffff); + + return 0; +} + +static int apply_r_mips_highest_rela(struct module *me, u32 *location, + Elf_Addr v) +{ + *location = (*location & 0xffff0000) | + ((((long long) v + 0x800080008000LL) >> 48) & 0xffff); + + return 0; +} + +static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, + Elf_Addr v) = { + [R_MIPS_NONE] = apply_r_mips_none, + [R_MIPS_32] = apply_r_mips_32_rel, + [R_MIPS_26] = apply_r_mips_26_rel, + [R_MIPS_HI16] = apply_r_mips_hi16_rel, + [R_MIPS_LO16] = apply_r_mips_lo16_rel +}; + +static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, + Elf_Addr v) = { + [R_MIPS_NONE] = apply_r_mips_none, + [R_MIPS_32] = apply_r_mips_32_rela, + [R_MIPS_26] = apply_r_mips_26_rela, + [R_MIPS_HI16] = apply_r_mips_hi16_rela, + [R_MIPS_LO16] = apply_r_mips_lo16_rela, + [R_MIPS_64] = apply_r_mips_64_rela, + [R_MIPS_HIGHER] = apply_r_mips_higher_rela, + [R_MIPS_HIGHEST] = apply_r_mips_highest_rela +}; + +int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, + unsigned int symindex, unsigned int relsec, + struct module *me) +{ + Elf_Mips_Rel *rel = (void *) sechdrs[relsec].sh_addr; + Elf_Sym *sym; + u32 *location; + unsigned int i; + Elf_Addr v; + int res; + + pr_debug("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf_Sym *)sechdrs[symindex].sh_addr + + ELF_MIPS_R_SYM(rel[i]); + if (IS_ERR_VALUE(sym->st_value)) { + /* Ignore unresolved weak symbol */ + if (ELF_ST_BIND(sym->st_info) == STB_WEAK) + continue; + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + v = sym->st_value; + + res = reloc_handlers_rel[ELF_MIPS_R_TYPE(rel[i])](me, location, v); + if (res) + return res; + } + + return 0; +} + +int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, + unsigned int symindex, unsigned int relsec, + struct module *me) +{ + Elf_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr; + Elf_Sym *sym; + u32 *location; + unsigned int i; + Elf_Addr v; + int res; + + pr_debug("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf_Sym *)sechdrs[symindex].sh_addr + + ELF_MIPS_R_SYM(rel[i]); + if (IS_ERR_VALUE(sym->st_value)) { + /* Ignore unresolved weak symbol */ + if (ELF_ST_BIND(sym->st_info) == STB_WEAK) + continue; + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + v = sym->st_value + rel[i].r_addend; + + res = reloc_handlers_rela[ELF_MIPS_R_TYPE(rel[i])](me, location, v); + if (res) + return res; + } + + return 0; +} + +/* Given an address, look for it in the module exception tables. */ +const struct exception_table_entry *search_module_dbetables(unsigned long addr) +{ + unsigned long flags; + const struct exception_table_entry *e = NULL; + struct mod_arch_specific *dbe; + + spin_lock_irqsave(&dbe_lock, flags); + list_for_each_entry(dbe, &dbe_list, dbe_list) { + e = search_extable(dbe->dbe_start, dbe->dbe_end - 1, addr); + if (e) + break; + } + spin_unlock_irqrestore(&dbe_lock, flags); + + /* Now, if we found one, we are running inside it now, hence + we cannot unload the module, hence no refcnt needed. */ + return e; +} + +/* Put in dbe list if necessary. */ +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + const Elf_Shdr *s; + char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + + INIT_LIST_HEAD(&me->arch.dbe_list); + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { + if (strcmp("__dbe_table", secstrings + s->sh_name) != 0) + continue; + me->arch.dbe_start = (void *)s->sh_addr; + me->arch.dbe_end = (void *)s->sh_addr + s->sh_size; + spin_lock_irq(&dbe_lock); + list_add(&me->arch.dbe_list, &dbe_list); + spin_unlock_irq(&dbe_lock); + } + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ + spin_lock_irq(&dbe_lock); + list_del(&mod->arch.dbe_list); + spin_unlock_irq(&dbe_lock); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/proc.c b/target/linux/realtek/files/arch/rlx/kernel/proc.c new file mode 100644 index 000000000..ad4a0843b --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/proc.c @@ -0,0 +1,62 @@ +/* + * linux/arch/mips/kernel/proc.c + * + * Copyright (C) 1995, 1996, 2001 Ralf Baechle + * Copyright (C) 2001, 2004 MIPS Technologies, Inc. + * Copyright (C) 2004 Maciej W. Rozycki + */ +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/cpu-features.h> +#include <asm/rlxregs.h> +#include <asm/processor.h> + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + unsigned long n = (unsigned long) v - 1; + + /* + * For the first processor also print the system type + */ + if (n == 0) + seq_printf(m, "system type\t\t: %s\n", get_system_type()); + + seq_printf(m, "processor\t\t: %ld\n", n); + seq_printf(m, "cpu model\t\t: %d\n", cpu_data[n].processor_id); + seq_printf(m, "BogoMIPS\t\t: %d.%02d\n", + cpu_data[n].udelay_val / (500000/HZ), + (cpu_data[n].udelay_val / (5000/HZ)) % 100); + + seq_printf(m, "tlb_entries\t\t: %d\n", cpu_data[n].tlbsize); + seq_printf(m, "mips16 implemented\t: yes\n"); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + unsigned long i = *pos; + + return i < NR_CPUS ? (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, +}; diff --git a/target/linux/realtek/files/arch/rlx/kernel/process.c b/target/linux/realtek/files/arch/rlx/kernel/process.c new file mode 100644 index 000000000..74d670741 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/process.c @@ -0,0 +1,419 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1994 - 1999, 2000 by Ralf Baechle and others. + * Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2004 Thiemo Seufer + */ +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/tick.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/mman.h> +#include <linux/personality.h> +#include <linux/sys.h> +#include <linux/user.h> +#include <linux/init.h> +#include <linux/completion.h> +#include <linux/kallsyms.h> +#include <linux/random.h> + +#include <asm/asm.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/rlxregs.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/elf.h> +#include <asm/isadep.h> +#include <asm/inst.h> +#include <asm/stacktrace.h> + +/* + * 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) { + tick_nohz_stop_sched_tick(1); + while (!need_resched()) { + cpu_wait(); + } + + tick_nohz_restart_sched_tick(); + 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_status & ~(ST0_CU0|ST0_FR|KU_MASK); + status |= KU_USER; + regs->cp0_status = status; + regs->cp0_epc = pc; + regs->regs[29] = sp; + current_thread_info()->addr_limit = USER_DS; +} + +void exit_thread(void) +{ +} + +void flush_thread(void) +{ +} + +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; + unsigned long childksp; + p->set_child_tid = p->clear_child_tid = NULL; + + childksp = (unsigned long)task_stack_page(p) + THREAD_SIZE - 32; + + /* set up new TSS. */ + childregs = (struct pt_regs *) childksp - 1; + /* Put the stack after the struct pt_regs. */ + childksp = (unsigned long) childregs; + *childregs = *regs; + childregs->regs[7] = 0; /* Clear error flag */ + + childregs->regs[2] = 0; /* Child gets zero as return value */ + regs->regs[2] = p->pid; + + if (childregs->cp0_status & ST0_CU0) { + childregs->regs[28] = (unsigned long) ti; + childregs->regs[29] = childksp; + ti->addr_limit = KERNEL_DS; + } else { + childregs->regs[29] = usp; + ti->addr_limit = USER_DS; + } + + p->thread.reg29 = (unsigned long) childregs; + p->thread.reg31 = (unsigned long) ret_from_fork; + p->thread.cp0_status = read_c0_status(); + + if (clone_flags & CLONE_SETTLS) + ti->tp_value = regs->regs[7]; + + return 0; +} + +void elf_dump_regs(elf_greg_t *gp, struct pt_regs *regs) +{ + int i; + + for (i = 0; i < EF_R0; i++) + gp[i] = 0; + gp[EF_R0] = 0; + for (i = 1; i <= 31; i++) + gp[EF_R0 + i] = regs->regs[i]; + gp[EF_R26] = 0; + gp[EF_R27] = 0; + gp[EF_LO] = regs->lo; + gp[EF_HI] = regs->hi; + gp[EF_CP0_EPC] = regs->cp0_epc; + gp[EF_CP0_BADVADDR] = regs->cp0_badvaddr; + gp[EF_CP0_STATUS] = regs->cp0_status; + gp[EF_CP0_CAUSE] = regs->cp0_cause; +#ifdef EF_UNUSED0 + gp[EF_UNUSED0] = 0; +#endif +} + +int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) +{ + elf_dump_regs(*regs, task_pt_regs(tsk)); + return 1; +} + +/* + * Create a kernel thread + */ +static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *)) +{ + do_exit(fn(arg)); +} + +long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + regs.regs[4] = (unsigned long) arg; + regs.regs[5] = (unsigned long) fn; + regs.cp0_epc = (unsigned long) kernel_thread_helper; + regs.cp0_status = read_c0_status(); + regs.cp0_status = (regs.cp0_status & ~(ST0_KUP | ST0_IEP | ST0_IEC)) | + ((regs.cp0_status & (ST0_KUC | ST0_IEC)) << 2); + + /* Ok, create the new process.. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); +} + +/* + * + */ +struct mips_frame_info { + void *func; + unsigned long func_size; + int frame_size; + int pc_offset; +}; + +static inline int is_ra_save_ins(union mips_instruction *ip) +{ + /* sw / sd $ra, offset($sp) */ + return (ip->i_format.opcode == sw_op) && + ip->i_format.rs == 29 && + ip->i_format.rt == 31; +} + +static inline int is_jal_jalr_jr_ins(union mips_instruction *ip) +{ + if (ip->j_format.opcode == jal_op) + return 1; + + if (ip->r_format.opcode != spec_op) + return 0; + + return ip->r_format.func == jalr_op || ip->r_format.func == jr_op; +} + +static inline int is_sp_move_ins(union mips_instruction *ip) +{ + /* addiu/daddiu sp,sp,-imm */ + if (ip->i_format.rs != 29 || ip->i_format.rt != 29) + return 0; + + if (ip->i_format.opcode == addiu_op) + return 1; + + return 0; +} + +static int get_frame_info(struct mips_frame_info *info) +{ + union mips_instruction *ip = info->func; + unsigned max_insns = info->func_size / sizeof(union mips_instruction); + unsigned i; + + info->pc_offset = -1; + info->frame_size = 0; + + if (!ip) + goto err; + + if (max_insns == 0) + max_insns = 128U; /* unknown function size */ + + max_insns = min(128U, max_insns); + + for (i = 0; i < max_insns; i++, ip++) { + + if (is_jal_jalr_jr_ins(ip)) + break; + + if (!info->frame_size) { + if (is_sp_move_ins(ip)) + info->frame_size = - ip->i_format.simmediate; + continue; + } + + if (info->pc_offset == -1 && is_ra_save_ins(ip)) { + info->pc_offset = + ip->i_format.simmediate / sizeof(long); + break; + } + } + if (info->frame_size && info->pc_offset >= 0) /* nested */ + return 0; + if (info->pc_offset < 0) /* leaf */ + return 1; + /* prologue seems boggus... */ +err: + return -1; +} + +static struct mips_frame_info schedule_mfi __read_mostly; + +static int __init frame_info_init(void) +{ + unsigned long size = 0; +#ifdef CONFIG_KALLSYMS + unsigned long ofs; + + kallsyms_lookup_size_offset((unsigned long)schedule, &size, &ofs); +#endif + schedule_mfi.func = schedule; + schedule_mfi.func_size = size; + + get_frame_info(&schedule_mfi); + + /* + * Without schedule() frame info, result given by + * thread_saved_pc() and get_wchan() are not reliable. + */ + if (schedule_mfi.pc_offset < 0) + printk("Can't analyze schedule() prologue at %p\n", schedule); + + return 0; +} + +arch_initcall(frame_info_init); + +/* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + struct thread_struct *t = &tsk->thread; + + /* New born processes are a special case */ + if (t->reg31 == (unsigned long) ret_from_fork) + return t->reg31; + if (schedule_mfi.pc_offset < 0) + return 0; + return ((unsigned long *)t->reg29)[schedule_mfi.pc_offset]; +} + + +#ifdef CONFIG_KALLSYMS +/* used by show_backtrace() */ +unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, + unsigned long pc, unsigned long *ra) +{ + unsigned long stack_page; + struct mips_frame_info info; + unsigned long size, ofs; + int leaf; + extern void ret_from_irq(void); + extern void ret_from_exception(void); + + stack_page = (unsigned long)task_stack_page(task); + if (!stack_page) + return 0; + + /* + * If we reached the bottom of interrupt context, + * return saved pc in pt_regs. + */ + if (pc == (unsigned long)ret_from_irq || + pc == (unsigned long)ret_from_exception) { + struct pt_regs *regs; + if (*sp >= stack_page && + *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) { + regs = (struct pt_regs *)*sp; + pc = regs->cp0_epc; + if (__kernel_text_address(pc)) { + *sp = regs->regs[29]; + *ra = regs->regs[31]; + return pc; + } + } + return 0; + } + if (!kallsyms_lookup_size_offset(pc, &size, &ofs)) + return 0; + /* + * Return ra if an exception occured at the first instruction + */ + if (unlikely(ofs == 0)) { + pc = *ra; + *ra = 0; + return pc; + } + + info.func = (void *)(pc - ofs); + info.func_size = ofs; /* analyze from start to ofs */ + leaf = get_frame_info(&info); + if (leaf < 0) + return 0; + + if (*sp < stack_page || + *sp + info.frame_size > stack_page + THREAD_SIZE - 32) + return 0; + + if (leaf) + /* + * For some extreme cases, get_frame_info() can + * consider wrongly a nested function as a leaf + * one. In that cases avoid to return always the + * same value. + */ + pc = pc != *ra ? *ra : 0; + else + pc = ((unsigned long *)(*sp))[info.pc_offset]; + + *sp += info.frame_size; + *ra = 0; + return __kernel_text_address(pc) ? pc : 0; +} +#endif + +/* + * get_wchan - a maintenance nightmare^W^Wpain in the ass ... + */ +unsigned long get_wchan(struct task_struct *task) +{ + unsigned long pc = 0; +#ifdef CONFIG_KALLSYMS + unsigned long sp; + unsigned long ra = 0; +#endif + + if (!task || task == current || task->state == TASK_RUNNING) + goto out; + if (!task_stack_page(task)) + goto out; + + pc = thread_saved_pc(task); + +#ifdef CONFIG_KALLSYMS + sp = task->thread.reg29 + schedule_mfi.frame_size; + + while (in_sched_functions(pc)) + pc = unwind_stack(task, &sp, pc, &ra); +#endif + +out: + return pc; +} + +/* + * Don't forget that the stack pointer must be aligned on a 8 bytes + * boundary for 32-bits ABI and 16 bytes for 64-bits ABI. + */ +unsigned long arch_align_stack(unsigned long sp) +{ + if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) + sp -= get_random_int() & ~PAGE_MASK; + + return sp & ALMASK; +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/ptrace.c b/target/linux/realtek/files/arch/rlx/kernel/ptrace.c new file mode 100644 index 000000000..85d370f4d --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/ptrace.c @@ -0,0 +1,282 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 Ross Biro + * Copyright (C) Linus Torvalds + * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle + * Copyright (C) 1996 David S. Miller + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999 MIPS Technologies, Inc. + * Copyright (C) 2000 Ulf Carlsson + * + * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit + * binaries. + */ +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/smp.h> +#include <linux/user.h> +#include <linux/security.h> +#include <linux/audit.h> +#include <linux/seccomp.h> + +#include <asm/byteorder.h> +#include <asm/cpu.h> +#include <asm/rlxregs.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/bootinfo.h> +#include <asm/reg.h> + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void ptrace_disable(struct task_struct *child) +{ + /* Don't load the watchpoint registers for the ex-child. */ + //clear_tsk_thread_flag(child, TIF_LOAD_WATCH); +} + +/* + * Read a general register set. We always use the 64-bit format, even + * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. + * Registers are sign extended to fill the available space. + */ +int ptrace_getregs(struct task_struct *child, __s64 __user *data) +{ + struct pt_regs *regs; + int i; + + if (!access_ok(VERIFY_WRITE, data, 38 * 8)) + return -EIO; + + regs = task_pt_regs(child); + + for (i = 0; i < 32; i++) + __put_user((long)regs->regs[i], data + i); + __put_user((long)regs->lo, data + EF_LO - EF_R0); + __put_user((long)regs->hi, data + EF_HI - EF_R0); + __put_user((long)regs->cp0_epc, data + EF_CP0_EPC - EF_R0); + __put_user((long)regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0); + __put_user((long)regs->cp0_status, data + EF_CP0_STATUS - EF_R0); + __put_user((long)regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0); + + return 0; +} + +/* + * Write a general register set. As for PTRACE_GETREGS, we always use + * the 64-bit format. On a 32-bit kernel only the lower order half + * (according to endianness) will be used. + */ +int ptrace_setregs(struct task_struct *child, __s64 __user *data) +{ + struct pt_regs *regs; + int i; + + if (!access_ok(VERIFY_READ, data, 38 * 8)) + return -EIO; + + regs = task_pt_regs(child); + + for (i = 0; i < 32; i++) + __get_user(regs->regs[i], data + i); + __get_user(regs->lo, data + EF_LO - EF_R0); + __get_user(regs->hi, data + EF_HI - EF_R0); + __get_user(regs->cp0_epc, data + EF_CP0_EPC - EF_R0); + + /* badvaddr, status, and cause may not be written. */ + + return 0; +} + +long arch_ptrace(struct task_struct *child, long request, long addr, long data) +{ + int ret; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); + break; + + /* Read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + struct pt_regs *regs; + unsigned long tmp = 0; + + regs = task_pt_regs(child); + ret = 0; /* Default return value. */ + + switch (addr) { + case 0 ... 31: + tmp = regs->regs[addr]; + break; + case PC: + tmp = regs->cp0_epc; + break; + case CAUSE: + tmp = regs->cp0_cause; + break; + case BADVADDR: + tmp = regs->cp0_badvaddr; + break; + case MMHI: + tmp = regs->hi; + break; + case MMLO: + tmp = regs->lo; + break; + default: + tmp = 0; + ret = -EIO; + goto out; + } + ret = put_user(tmp, (unsigned long __user *) data); + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = generic_ptrace_pokedata(child, addr, data); + break; + + 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 MMHI: + regs->hi = data; + break; + case MMLO: + regs->lo = data; + break; + default: + /* The rest are not allowed. */ + ret = -EIO; + break; + } + break; + } + + case PTRACE_GETREGS: + ret = ptrace_getregs(child, (__s64 __user *) data); + break; + + case PTRACE_SETREGS: + ret = ptrace_setregs(child, (__s64 __user *) data); + 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->exit_state == EXIT_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + + case PTRACE_GET_THREAD_AREA: + ret = put_user(task_thread_info(child)->tp_value, + (unsigned long __user *) data); + break; + + default: + ret = ptrace_request(child, request, addr, data); + break; + } + out: + return ret; +} + +static inline int audit_arch(void) +{ + int arch = EM_MIPS; +#ifdef CONFIG_CPU_LITTLE_ENDIAN + arch |= __AUDIT_ARCH_LE; +#endif + return arch; +} + +/* + * Notification of system call entry/exit + * - triggered by current->work.syscall_trace + */ +asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) +{ + /* do the secure computing check first */ + if (!entryexit) + secure_computing(regs->regs[0]); + + if (unlikely(current->audit_context) && entryexit) + audit_syscall_exit(AUDITSC_RESULT(regs->regs[2]), + regs->regs[2]); + + if (!(current->ptrace & PT_PTRACED)) + goto out; + + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + goto out; + + /* 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; + } + +out: + if (unlikely(current->audit_context) && !entryexit) + audit_syscall_entry(audit_arch(), regs->regs[0], + regs->regs[4], regs->regs[5], + regs->regs[6], regs->regs[7]); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/relocate_kernel.S b/target/linux/realtek/files/arch/rlx/kernel/relocate_kernel.S new file mode 100644 index 000000000..9ba2755b0 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/relocate_kernel.S @@ -0,0 +1,82 @@ +/* + * relocate_kernel.S for kexec + * Created by <nschichan@corp.free.fr> on Thu Oct 12 17:49:57 2006 + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/regdef.h> +#include <asm/page.h> +#include <asm/rlxregs.h> +#include <asm/stackframe.h> +#include <asm/addrspace.h> + +LEAF(relocate_new_kernel) + PTR_L s0, kexec_indirection_page + PTR_L s1, kexec_start_address + +process_entry: + PTR_L s2, (s0) + PTR_ADD s0, s0, SZREG + + /* destination page */ + and s3, s2, 0x1 + beq s3, zero, 1f + and s4, s2, ~0x1 /* store destination addr in s4 */ + move a0, s4 + b process_entry + +1: + /* indirection page, update s0 */ + and s3, s2, 0x2 + beq s3, zero, 1f + and s0, s2, ~0x2 + b process_entry + +1: + /* done page */ + and s3, s2, 0x4 + beq s3, zero, 1f + b done +1: + /* source page */ + and s3, s2, 0x8 + beq s3, zero, process_entry + and s2, s2, ~0x8 + li s6, (1 << PAGE_SHIFT) / SZREG + +copy_word: + /* copy page word by word */ + REG_L s5, (s2) + REG_S s5, (s4) + PTR_ADD s4, s4, SZREG + PTR_ADD s2, s2, SZREG + LONG_SUB s6, s6, 1 + beq s6, zero, process_entry + b copy_word + b process_entry + +done: + /* jump to kexec_start_address */ + j s1 + END(relocate_new_kernel) + +kexec_start_address: + EXPORT(kexec_start_address) + PTR 0x0 + .size kexec_start_address, PTRSIZE + +kexec_indirection_page: + EXPORT(kexec_indirection_page) + PTR 0 + .size kexec_indirection_page, PTRSIZE + +relocate_new_kernel_end: + +relocate_new_kernel_size: + EXPORT(relocate_new_kernel_size) + PTR relocate_new_kernel_end - relocate_new_kernel + .size relocate_new_kernel_size, PTRSIZE diff --git a/target/linux/realtek/files/arch/rlx/kernel/reset.c b/target/linux/realtek/files/arch/rlx/kernel/reset.c new file mode 100644 index 000000000..060563a71 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/reset.c @@ -0,0 +1,44 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001, 06 by Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/types.h> +#include <linux/reboot.h> + +#include <asm/reboot.h> + +/* + * Urgs ... Too many MIPS machines to handle this in a generic way. + * So handle all using function pointers to machine specific + * functions. + */ +void (*_machine_restart)(char *command); +void (*_machine_halt)(void); +void (*pm_power_off)(void); + +EXPORT_SYMBOL(pm_power_off); + +void machine_restart(char *command) +{ + if (_machine_restart) + _machine_restart(command); +} + +void machine_halt(void) +{ + if (_machine_halt) + _machine_halt(); +} + +void machine_power_off(void) +{ + if (pm_power_off) + pm_power_off(); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/rlx-cevt.c b/target/linux/realtek/files/arch/rlx/kernel/rlx-cevt.c new file mode 100644 index 000000000..92db566d4 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/rlx-cevt.c @@ -0,0 +1,244 @@ +/* + * DS1287 clockevent driver + * + * Copyright (C) 2008 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/clockchips.h> +#include <linux/init.h> +#include <linux/interrupt.h> + +#include <asm/time.h> + +#ifdef CONFIG_RTL_TIMER_ADJUSTMENT +#include <net/rtl/rtl_types.h> +#include <rtl865xc_asicregs.h> +#endif + +#if defined(CONFIG_RTL_WTDOG) + int is_fault=0; // kernel fault flag +#endif + +#ifdef CONFIG_RTL_TIMER_ADJUSTMENT +#define MHZ 100 +#define TIMER0_ADJUSTMENT_THRESHOLD 20 + + +//static unsigned long tuRemainder=0; /* unit is the same as TC0DATA, say "timer unit". */ +static unsigned long tuRemainder=0; +#ifdef CONFIG_RTL8196C_REVISION_B +static unsigned long previousTC1CNT=0xFFFFFFF0; /* Previous value of Timer 1 */ +#else +static unsigned long previousTC1CNT=0xFFFFFF00; /* Previous value of Timer 1 */ +#endif +static u64 timer0AccJiffies=0; +static u64 timer1AccJiffies=0; /* accumulated jiffies, unit is the same as jiffies, 1/HZ. */ +static unsigned int timer0IntCnt=0; +#define TICK_SIZE (tick_nsec / 1000) +unsigned long rtl865x_getTimer1PassedJiffies(void) +{ + unsigned long jifPassed=0; + unsigned long currTC1CNT; + unsigned long tc0data; + + //tc0data = READ_MEM32(TC0DATA)>>TCD_OFFSET; + tc0data =((MHZ * 250) / HZ); + /* compute passed time since last time executed this function */ + #ifdef CONFIG_RTL8196C_REVISION_B + currTC1CNT = READ_MEM32(TC1CNT) & 0xfffffff0; + #else + currTC1CNT = READ_MEM32(TC1CNT) & 0xffffff00; + #endif + +#if defined(CONFIG_RTL_819X) + /* + In RTL865xC, timer / counter is incremental + */ + #ifdef CONFIG_RTL8196C_REVISION_B + if ( previousTC1CNT <= currTC1CNT ) + { + /* No wrap happend. */ + tuRemainder += (currTC1CNT-previousTC1CNT)>>4; /* how many units are passed since last check? */ + } + else + { + /* Timer1 wrapped!! */ + tuRemainder += (currTC1CNT+(0xfffffff0-previousTC1CNT)+(0x1<<4))>>4; /* how many units are passed since last check? */ + } + #else + if ( previousTC1CNT <= currTC1CNT ) + { + /* No wrap happend. */ + tuRemainder += (currTC1CNT-previousTC1CNT)>>TCD_OFFSET; /* how many units are passed since last check? */ + } + else + { + /* Timer1 wrapped!! */ + tuRemainder += (currTC1CNT+(0xffffff00-previousTC1CNT)+(0x1<<TCD_OFFSET))>>TCD_OFFSET; /* how many units are passed since last check? */ + } + #endif +#endif + previousTC1CNT = currTC1CNT; /* keep TC1CNT value for next time check */ + + /* If tc0data is zero, it means 'time is frozen.' */ + if ( tc0data == 0 ) + { + jifPassed = 0; + } + else + { + jifPassed = tuRemainder / tc0data; + tuRemainder = tuRemainder % tc0data; + } + + timer1AccJiffies += jifPassed; + return jifPassed; +} +#endif + +int rlx_timer_state(void) +{ + return 0; +} + +int rlx_timer_set_base_clock(unsigned int hz) +{ + return 0; +} + +static int rlx_timer_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + return -EINVAL; +} + +static void rlx_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + return; +} + +static void rlx_timer_event_handler(struct clock_event_device *dev) +{ +} + +static struct clock_event_device rlx_clockevent = { + .name = "rlx timer", + .features = CLOCK_EVT_FEAT_PERIODIC, + .set_next_event = rlx_timer_set_next_event, + .set_mode = rlx_timer_set_mode, + .event_handler = rlx_timer_event_handler, +}; + +static irqreturn_t rlx_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *cd = &rlx_clockevent; + extern void bsp_timer_ack(void); + +#if defined(CONFIG_RTL_WTDOG) + if (!is_fault){ + #ifdef CONFIG_RTK_VOIP + extern int bBspWatchdog; + + *(volatile unsigned long *)(0xB800311c) |= + ( bBspWatchdog ? ( 1 << 23 ) : ( ( 1 << 23 ) | ( 0xA5 << 24 ) ) ); + #else + *(volatile unsigned long *)(0xB800311c) |= 1 << 23; + #endif + }else { + #ifdef CONFIG_RTK_VOIP + // run gdb cause Break instruction exception and call do_bp() + extern int bBspWatchdog; + + if( !bBspWatchdog ) + is_fault = 0; + else + #endif + { + // quick fix for warn reboot fail issue +#if defined(CONFIG_RTL8192SE) || defined(CONFIG_RTL8192CD) +#if !defined(CONFIG_RTL865X_PANAHOST) && !defined(CONFIG_RTL8197B_PANA) + extern void force_stop_wlan_hw(void); + force_stop_wlan_hw(); +#endif +#endif + local_irq_disable(); + *(volatile unsigned long *)(0xB800311c)=0; /*this is to enable 865xc watch dog reset*/ + for(;;); + } + } +#endif + +#if defined(CONFIG_RTL_TIMER_ADJUSTMENT) + /*notice:do_timer(1) will be called in tick_periodic()*/ + timer0AccJiffies++; + timer0IntCnt++; + if((timer0IntCnt%TIMER0_ADJUSTMENT_THRESHOLD)==0) + { + rtl865x_getTimer1PassedJiffies(); + if(timer1AccJiffies>timer0AccJiffies) + { + /*to compensate system jiffied, because timer0's interrupt may be disabled by nic driver*/ + //do_timer((timer1AccJiffies-timer0AccJiffies)+1); + //printk("%s:%d,diff jiffies is %u\n",__FUNCTION__,__LINE__,(unsigned int)(timer1AccJiffies-timer0AccJiffies)); + do_timer((timer1AccJiffies-timer0AccJiffies)); + timer0AccJiffies=timer1AccJiffies; + } + else + { + /*for timer1 start later than timer0*/ + if(timer1AccJiffies<timer0AccJiffies) + { + timer1AccJiffies=timer0AccJiffies; + } + //do_timer(1); + } + } + else + { + //do_timer(1); + } +#endif + + /* Ack the RTC interrupt. */ + bsp_timer_ack(); + + cd->event_handler(cd); + return IRQ_HANDLED; +} + +static struct irqaction rlx_irqaction = { + .handler = rlx_timer_interrupt, + .flags = IRQF_DISABLED | IRQF_PERCPU | IRQF_TIMER, + .name = "rlx timer", +}; + +int __init rlx_clockevent_init(int irq) +{ + struct clock_event_device *cd; + + cd = &rlx_clockevent; + cd->rating = 100; + cd->irq = irq; + clockevent_set_clock(cd, 32768); + cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); + cd->min_delta_ns = clockevent_delta2ns(0x300, cd); + cd->cpumask = cpumask_of(0); + + clockevents_register_device(&rlx_clockevent); + + return setup_irq(irq, &rlx_irqaction); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/rlx-switch.S b/target/linux/realtek/files/arch/rlx/kernel/rlx-switch.S new file mode 100644 index 000000000..b3e27a5f1 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/rlx-switch.S @@ -0,0 +1,66 @@ +/* + * r2300_switch.S: R2300 specific task switching code. + * + * Copyright (C) 1994, 1995, 1996, 1999 by Ralf Baechle + * Copyright (C) 1994, 1995, 1996 by Andreas Busse + * + * Multi-cpu abstraction and macros for easier reading: + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * Further modifications to make this work: + * Copyright (c) 1998-2000 Harald Koerfgen + */ +#include <asm/asm.h> +#include <asm/cachectl.h> +#include <asm/rlxregs.h> +#include <asm/asm-offsets.h> +#include <asm/page.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/thread_info.h> + +#include <asm/asmmacro.h> + + .set mips1 + .align 5 + +/* + * Offset to the current process status flags, the first 32 bytes of the + * stack are not used. + */ +#define ST_OFF (_THREAD_SIZE - 32 - PT_SIZE + PT_STATUS) + +/* + * task_struct *resume(task_struct *prev, task_struct *next, + * struct thread_info *next_ti) ) + */ +LEAF(resume) +#ifndef CONFIG_CPU_HAS_LLSC + sw zero, ll_bit +#endif + mfc0 t1, CP0_STATUS + sw t1, THREAD_STATUS(a0) + cpu_save_nonscratch a0 + sw ra, THREAD_REG31(a0) + + /* + * The order of restoring the registers takes care of the race + * updating $28, $29 and kernelsp without disabling ints. + */ + move $28, a2 + cpu_restore_nonscratch a1 + + addiu t1, $28, _THREAD_SIZE - 32 + sw t1, kernelsp + + mfc0 t1, CP0_STATUS /* Do we really need this? */ + li a3, 0xff01 + and t1, a3 + lw a2, THREAD_STATUS(a1) + nor a3, $0, a3 + and a2, a3 + or a2, t1 + mtc0 a2, CP0_STATUS + move v0, a0 + jr ra + END(resume) diff --git a/target/linux/realtek/files/arch/rlx/kernel/rlx-time.c b/target/linux/realtek/files/arch/rlx/kernel/rlx-time.c new file mode 100644 index 000000000..834eaa210 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/rlx-time.c @@ -0,0 +1,111 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * Copyright (c) 2003, 2004 Maciej W. Rozycki + * + * Common time service routines for MIPS machines. + * + * 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. + */ +#include <linux/bug.h> +#include <linux/clockchips.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/param.h> +#include <linux/time.h> +#include <linux/timex.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/module.h> + +#include <asm/cpu-features.h> +#include <asm/time.h> + +/* + * forward reference + */ +DEFINE_SPINLOCK(rtc_lock); +EXPORT_SYMBOL(rtc_lock); + +int __weak rtc_mips_set_time(unsigned long sec) +{ + return 0; +} + +int __weak rtc_mips_set_mmss(unsigned long nowtime) +{ + return rtc_mips_set_time(nowtime); +} + +int update_persistent_clock(struct timespec now) +{ + return rtc_mips_set_mmss(now.tv_sec); +} + +#if 0 +/* + * time_init() - it does the following things. + * + * 1) rlx_time_init() - + * a) (optional) set up RTC routines, + * b) (optional) calibrate and set the mips_hpt_frequency + * (only needed if you intended to use cpu counter as timer interrupt + * source) + * 2) calculate a couple of cached variables for later usage + */ + +unsigned int mips_hpt_frequency; + +void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock) +{ + u64 temp; + u32 shift; + + /* Find a shift value */ + for (shift = 32; shift > 0; shift--) { + temp = (u64) NSEC_PER_SEC << shift; + do_div(temp, clock); + if ((temp >> 32) == 0) + break; + } + cs->shift = shift; + cs->mult = (u32) temp; +} +#endif + +void __cpuinit clockevent_set_clock(struct clock_event_device *cd, + unsigned int clock) +{ + u64 temp; + u32 shift; + + /* Find a shift value */ + for (shift = 32; shift > 0; shift--) { + temp = (u64) clock << shift; + do_div(temp, NSEC_PER_SEC); + if ((temp >> 32) == 0) + break; + } + cd->shift = shift; + cd->mult = (u32) temp; +} + +/* + * This function exists in order to cause an error due to a duplicate + * definition if platform code should have its own implementation. The hook + * to use instead is bsp_time_init. bsp_time_init does not receive the + * irqaction pointer argument anymore. This is because any function which + * initializes an interrupt timer now takes care of its own request_irq rsp. + * setup_irq calls and each clock_event_device should use its own + * struct irqrequest. + */ +void __init time_init(void) +{ + extern void bsp_timer_init(void); + bsp_timer_init(); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/scall32-o32.S b/target/linux/realtek/files/arch/rlx/kernel/scall32-o32.S new file mode 100644 index 000000000..a1ee9df13 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/scall32-o32.S @@ -0,0 +1,653 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1995-99, 2000- 02, 06 Ralf Baechle <ralf@linux-mips.org> + * Copyright (C) 2001 MIPS Technologies, Inc. + * Copyright (C) 2004 Thiemo Seufer + */ +#include <linux/errno.h> +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/irqflags.h> +#include <asm/rlxregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/isadep.h> +#include <asm/sysmips.h> +#include <asm/thread_info.h> +#include <asm/unistd.h> +#include <asm/asm-offsets.h> + +/* Highest syscall used of any syscall flavour */ +#define MAX_SYSCALL_NO __NR_O32_Linux + __NR_O32_Linux_syscalls + + .align 5 +NESTED(handle_sys, PT_SIZE, sp) + .set noat + SAVE_SOME + TRACE_IRQS_ON_RELOAD + STI + .set at + + lw t1, PT_EPC(sp) # skip syscall on return + + subu v0, v0, __NR_O32_Linux # check syscall number + sltiu t0, v0, __NR_O32_Linux_syscalls + 1 + addiu t1, 4 # skip to next instruction + sw t1, PT_EPC(sp) + beqz t0, illegal_syscall + + sll t0, v0, 3 + la t1, sys_call_table + addu t1, t0 + lw t2, (t1) # syscall routine + lw t3, 4(t1) # >= 0 if we need stack arguments + beqz t2, illegal_syscall + + sw a3, PT_R26(sp) # save a3 for syscall restarting + bgez t3, stackargs + +stack_done: + lw t0, TI_FLAGS($28) # syscall tracing enabled? + li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + and t0, t1 + bnez t0, syscall_trace_entry # -> yes + + jalr t2 # Do The Real Thing (TM) + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sw t0, PT_R7(sp) # set error flag + beqz t0, 1f + + negu v0 # error + sw v0, PT_R0(sp) # set flag for syscall + # restarting +1: sw v0, PT_R2(sp) # result + +o32_syscall_exit: + local_irq_disable # make sure need_resched and + # signals dont change between + # sampling and return + lw a2, TI_FLAGS($28) # current->work + li t0, _TIF_ALLWORK_MASK + and t0, a2 + bnez t0, o32_syscall_exit_work + + j restore_partial + +o32_syscall_exit_work: + j syscall_exit_work_partial + +/* ------------------------------------------------------------------------ */ + +syscall_trace_entry: + SAVE_STATIC + move s0, t2 + move a0, sp + li a1, 0 + jal do_syscall_trace + + move t0, s0 + RESTORE_STATIC + lw a0, PT_R4(sp) # Restore argument registers + lw a1, PT_R5(sp) + lw a2, PT_R6(sp) + lw a3, PT_R7(sp) + jalr t0 + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sw t0, PT_R7(sp) # set error flag + beqz t0, 1f + + negu v0 # error + sw v0, PT_R0(sp) # set flag for syscall + # restarting +1: sw v0, PT_R2(sp) # result + + j syscall_exit + +/* ------------------------------------------------------------------------ */ + + /* + * More than four arguments. Try to deal with it by copying the + * stack arguments from the user stack to the kernel stack. + * This Sucks (TM). + */ +stackargs: + lw t0, PT_R29(sp) # get old user stack pointer + + /* + * We intentionally keep the kernel stack a little below the top of + * userspace so we don't have to do a slower byte accurate check here. + */ + lw t5, TI_ADDR_LIMIT($28) + addu t4, t0, 32 + and t5, t4 + bltz t5, bad_stack # -> sp is bad + + /* Ok, copy the args from the luser stack to the kernel stack. + * t3 is the precomputed number of instruction bytes needed to + * load or store arguments 6-8. + */ + + la t1, 5f # load up to 3 arguments + subu t1, t3 +1: lw t5, 16(t0) # argument #5 from usp + .set push + .set noreorder + .set nomacro + jr t1 + addiu t1, 6f - 5f + +2: lw t8, 28(t0) # argument #8 from usp +3: lw t7, 24(t0) # argument #7 from usp +4: lw t6, 20(t0) # argument #6 from usp +5: jr t1 + sw t5, 16(sp) # argument #5 to ksp + + sw t8, 28(sp) # argument #8 to ksp + sw t7, 24(sp) # argument #7 to ksp + sw t6, 20(sp) # argument #6 to ksp +6: j stack_done # go back + nop + .set pop + + .section __ex_table,"a" + PTR 1b,bad_stack + PTR 2b,bad_stack + PTR 3b,bad_stack + PTR 4b,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: + negu v0 # error + sw v0, PT_R0(sp) + sw v0, PT_R2(sp) + li t0, 1 # set error flag + sw t0, PT_R7(sp) + j o32_syscall_exit + + /* + * The system call does not exist in this kernel + */ +illegal_syscall: + li v0, ENOSYS # error + sw v0, PT_R2(sp) + li t0, 1 # set error flag + sw t0, PT_R7(sp) + j o32_syscall_exit + END(handle_sys) + + LEAF(mips_atomic_set) + andi v0, a1, 3 # must be word aligned + bnez v0, bad_alignment + + lw v1, TI_ADDR_LIMIT($28) # in legal address range? + addiu a0, a1, 4 + or a0, a0, a1 + and a0, a0, v1 + bltz a0, bad_address + +#ifdef CONFIG_CPU_HAS_LLSC + /* Ok, this is the ll/sc case. World is sane :-) */ +1: ll v0, (a1) + move a0, a2 +2: sc a0, (a1) + beqz a0, 1b + + .section __ex_table,"a" + PTR 1b, bad_stack + PTR 2b, bad_stack + .previous +#else + sw a1, 16(sp) + sw a2, 20(sp) + + move a0, sp + move a2, a1 + li a1, 1 + jal do_page_fault + + lw a1, 16(sp) + lw a2, 20(sp) + + /* + * At this point the page should be readable and writable unless + * there was no more memory available. + */ +1: lw v0, (a1) +2: sw a2, (a1) + + .section __ex_table,"a" + PTR 1b, no_mem + PTR 2b, no_mem + .previous +#endif + + sw zero, PT_R7(sp) # success + sw v0, PT_R2(sp) # result + + j o32_syscall_exit # continue like a normal syscall + +no_mem: li v0, -ENOMEM + jr ra + +bad_address: + li v0, -EFAULT + jr ra + +bad_alignment: + li v0, -EINVAL + jr ra + END(mips_atomic_set) + + LEAF(sys_sysmips) + beq a0, MIPS_ATOMIC_SET, mips_atomic_set + j _sys_sysmips + END(sys_sysmips) + + LEAF(sys_syscall) + subu t0, a0, __NR_O32_Linux # check syscall number + sltiu v0, t0, __NR_O32_Linux_syscalls + 1 + beqz t0, einval # do not recurse + sll t1, t0, 3 + beqz v0, einval + lw t2, sys_call_table(t1) # syscall routine + + /* Some syscalls like execve get their arguments from struct pt_regs + and claim zero arguments in the syscall table. Thus we have to + assume the worst case and shuffle around all potential arguments. + If you want performance, don't use indirect syscalls. */ + + move a0, a1 # shift argument registers + move a1, a2 + move a2, a3 + lw a3, 16(sp) + lw t4, 20(sp) + lw t5, 24(sp) + lw t6, 28(sp) + sw t4, 16(sp) + sw t5, 20(sp) + sw t6, 24(sp) + sw a0, PT_R4(sp) # .. and push back a0 - a3, some + sw a1, PT_R5(sp) # syscalls expect them there + sw a2, PT_R6(sp) + sw a3, PT_R7(sp) + sw a3, PT_R26(sp) # update a3 for syscall restarting + jr t2 + /* Unreached */ + +einval: li v0, -ENOSYS + jr ra + END(sys_syscall) + + .macro fifty ptr, nargs, from=1, to=50 + sys \ptr \nargs + .if \to-\from + fifty \ptr,\nargs,"(\from+1)",\to + .endif + .endm + + .macro mille ptr, nargs, from=1, to=20 + fifty \ptr,\nargs + .if \to-\from + mille \ptr,\nargs,"(\from+1)",\to + .endif + .endm + + .macro syscalltable + sys sys_syscall 8 /* 4000 */ + sys sys_exit 1 + sys sys_fork 0 + sys sys_read 3 + sys sys_write 3 + sys sys_open 3 /* 4005 */ + sys sys_close 1 + sys sys_waitpid 3 + sys sys_creat 2 + sys sys_link 2 + sys sys_unlink 1 /* 4010 */ + sys sys_execve 0 + sys sys_chdir 1 + sys sys_time 1 + sys sys_mknod 3 + sys sys_chmod 2 /* 4015 */ + sys sys_lchown 3 + sys sys_ni_syscall 0 + sys sys_ni_syscall 0 /* was sys_stat */ + sys sys_lseek 3 + sys sys_getpid 0 /* 4020 */ + sys sys_mount 5 + sys sys_oldumount 1 + sys sys_setuid 1 + sys sys_getuid 0 + sys sys_stime 1 /* 4025 */ + sys sys_ptrace 4 + sys sys_alarm 1 + sys sys_ni_syscall 0 /* was sys_fstat */ + sys sys_pause 0 + sys sys_utime 2 /* 4030 */ + sys sys_ni_syscall 0 + sys sys_ni_syscall 0 + sys sys_access 2 + sys sys_nice 1 + sys sys_ni_syscall 0 /* 4035 */ + sys sys_sync 0 + sys sys_kill 2 + sys sys_rename 2 + sys sys_mkdir 2 + sys sys_rmdir 1 /* 4040 */ + sys sys_dup 1 + sys sysm_pipe 0 + sys sys_times 1 + sys sys_ni_syscall 0 + sys sys_brk 1 /* 4045 */ + sys sys_setgid 1 + sys sys_getgid 0 + sys sys_ni_syscall 0 /* was signal(2) */ + sys sys_geteuid 0 + sys sys_getegid 0 /* 4050 */ + sys sys_acct 1 + sys sys_umount 2 + sys sys_ni_syscall 0 + sys sys_ioctl 3 + sys sys_fcntl 3 /* 4055 */ + sys sys_ni_syscall 2 + sys sys_setpgid 2 + sys sys_ni_syscall 0 + sys sys_olduname 1 + sys sys_umask 1 /* 4060 */ + sys sys_chroot 1 + sys sys_ustat 2 + sys sys_dup2 2 + sys sys_getppid 0 + sys sys_getpgrp 0 /* 4065 */ + sys sys_setsid 0 + sys sys_sigaction 3 + sys sys_sgetmask 0 + sys sys_ssetmask 1 + sys sys_setreuid 2 /* 4070 */ + sys sys_setregid 2 + sys sys_sigsuspend 0 + sys sys_sigpending 1 + sys sys_sethostname 2 + sys sys_setrlimit 2 /* 4075 */ + sys sys_getrlimit 2 + sys sys_getrusage 2 + sys sys_gettimeofday 2 + sys sys_settimeofday 2 + sys sys_getgroups 2 /* 4080 */ + sys sys_setgroups 2 + sys sys_ni_syscall 0 /* old_select */ + sys sys_symlink 2 + sys sys_ni_syscall 0 /* was sys_lstat */ + sys sys_readlink 3 /* 4085 */ + sys sys_uselib 1 + sys sys_swapon 2 + sys sys_reboot 3 + sys sys_old_readdir 3 + sys sys_mips_mmap 6 /* 4090 */ + sys sys_munmap 2 + sys sys_truncate 2 + sys sys_ftruncate 2 + sys sys_fchmod 2 + sys sys_fchown 3 /* 4095 */ + sys sys_getpriority 2 + sys sys_setpriority 3 + sys sys_ni_syscall 0 + sys sys_statfs 2 + sys sys_fstatfs 2 /* 4100 */ + sys sys_ni_syscall 0 /* was ioperm(2) */ + sys sys_socketcall 2 + sys sys_syslog 3 + sys sys_setitimer 3 + sys sys_getitimer 2 /* 4105 */ + sys sys_newstat 2 + sys sys_newlstat 2 + sys sys_newfstat 2 + sys sys_uname 1 + sys sys_ni_syscall 0 /* 4110 was iopl(2) */ + sys sys_vhangup 0 + sys sys_ni_syscall 0 /* was sys_idle() */ + sys sys_ni_syscall 0 /* was sys_vm86 */ + sys sys_wait4 4 + sys sys_swapoff 1 /* 4115 */ + sys sys_sysinfo 1 + sys sys_ipc 6 + sys sys_fsync 1 + sys sys_sigreturn 0 + sys sys_clone 0 /* 4120 */ + sys sys_setdomainname 2 + sys sys_newuname 1 + sys sys_ni_syscall 0 /* sys_modify_ldt */ + sys sys_adjtimex 1 + sys sys_mprotect 3 /* 4125 */ + sys sys_sigprocmask 3 + sys sys_ni_syscall 0 /* was create_module */ + sys sys_init_module 5 + sys sys_delete_module 1 + sys sys_ni_syscall 0 /* 4130 was get_kernel_syms */ + sys sys_quotactl 4 + sys sys_getpgid 1 + sys sys_fchdir 1 + sys sys_bdflush 2 + sys sys_sysfs 3 /* 4135 */ + sys sys_personality 1 + sys sys_ni_syscall 0 /* for afs_syscall */ + sys sys_setfsuid 1 + sys sys_setfsgid 1 + sys sys_llseek 5 /* 4140 */ + sys sys_getdents 3 + sys sys_select 5 + sys sys_flock 2 + sys sys_msync 3 + sys sys_readv 3 /* 4145 */ + sys sys_writev 3 + sys sys_cacheflush 3 + sys sys_cachectl 3 + sys sys_sysmips 4 + sys sys_ni_syscall 0 /* 4150 */ + sys sys_getsid 1 + sys sys_fdatasync 1 + sys sys_sysctl 1 + sys sys_mlock 2 + sys sys_munlock 2 /* 4155 */ + sys sys_mlockall 1 + sys sys_munlockall 0 + sys sys_sched_setparam 2 + sys sys_sched_getparam 2 + sys sys_sched_setscheduler 3 /* 4160 */ + sys sys_sched_getscheduler 1 + sys sys_sched_yield 0 + sys sys_sched_get_priority_max 1 + sys sys_sched_get_priority_min 1 + sys sys_sched_rr_get_interval 2 /* 4165 */ + sys sys_nanosleep, 2 + sys sys_mremap, 5 + sys sys_accept 3 + sys sys_bind 3 + sys sys_connect 3 /* 4170 */ + sys sys_getpeername 3 + sys sys_getsockname 3 + sys sys_getsockopt 5 + sys sys_listen 2 + sys sys_recv 4 /* 4175 */ + sys sys_recvfrom 6 + sys sys_recvmsg 3 + sys sys_send 4 + sys sys_sendmsg 3 + sys sys_sendto 6 /* 4180 */ + sys sys_setsockopt 5 + sys sys_shutdown 2 + sys sys_socket 3 + sys sys_socketpair 4 + sys sys_setresuid 3 /* 4185 */ + sys sys_getresuid 3 + sys sys_ni_syscall 0 /* was sys_query_module */ + sys sys_poll 3 + sys sys_nfsservctl 3 + sys sys_setresgid 3 /* 4190 */ + sys sys_getresgid 3 + sys sys_prctl 5 + sys sys_rt_sigreturn 0 + sys sys_rt_sigaction 4 + sys sys_rt_sigprocmask 4 /* 4195 */ + sys sys_rt_sigpending 2 + sys sys_rt_sigtimedwait 4 + sys sys_rt_sigqueueinfo 3 + sys sys_rt_sigsuspend 0 + sys sys_pread64 6 /* 4200 */ + sys sys_pwrite64 6 + sys sys_chown 3 + sys sys_getcwd 2 + sys sys_capget 2 + sys sys_capset 2 /* 4205 */ + sys sys_sigaltstack 0 + sys sys_sendfile 4 + sys sys_ni_syscall 0 + sys sys_ni_syscall 0 + sys sys_mips_mmap2 6 /* 4210 */ + sys sys_truncate64 4 + sys sys_ftruncate64 4 + sys sys_stat64 2 + sys sys_lstat64 2 + sys sys_fstat64 2 /* 4215 */ + sys sys_pivot_root 2 + sys sys_mincore 3 + sys sys_madvise 3 + sys sys_getdents64 3 + sys sys_fcntl64 3 /* 4220 */ + sys sys_ni_syscall 0 + sys sys_gettid 0 + sys sys_readahead 5 + sys sys_setxattr 5 + sys sys_lsetxattr 5 /* 4225 */ + sys sys_fsetxattr 5 + sys sys_getxattr 4 + sys sys_lgetxattr 4 + sys sys_fgetxattr 4 + sys sys_listxattr 3 /* 4230 */ + sys sys_llistxattr 3 + sys sys_flistxattr 3 + sys sys_removexattr 2 + sys sys_lremovexattr 2 + sys sys_fremovexattr 2 /* 4235 */ + sys sys_tkill 2 + sys sys_sendfile64 5 + sys sys_futex 6 + sys sys_sched_setaffinity 3 + sys sys_sched_getaffinity 3 /* 4240 */ + sys sys_io_setup 2 + sys sys_io_destroy 1 + sys sys_io_getevents 5 + sys sys_io_submit 3 + sys sys_io_cancel 3 /* 4245 */ + sys sys_exit_group 1 + sys sys_lookup_dcookie 4 + sys sys_epoll_create 1 + sys sys_epoll_ctl 4 + sys sys_epoll_wait 3 /* 4250 */ + sys sys_remap_file_pages 5 + sys sys_set_tid_address 1 + sys sys_restart_syscall 0 + sys sys_fadvise64_64 7 + sys sys_statfs64 3 /* 4255 */ + sys sys_fstatfs64 2 + sys sys_timer_create 3 + sys sys_timer_settime 4 + sys sys_timer_gettime 2 + sys sys_timer_getoverrun 1 /* 4260 */ + sys sys_timer_delete 1 + sys sys_clock_settime 2 + sys sys_clock_gettime 2 + sys sys_clock_getres 2 + sys sys_clock_nanosleep 4 /* 4265 */ + sys sys_tgkill 3 + sys sys_utimes 2 + sys sys_mbind 4 + sys sys_ni_syscall 0 /* sys_get_mempolicy */ + sys sys_ni_syscall 0 /* 4270 sys_set_mempolicy */ + sys sys_mq_open 4 + sys sys_mq_unlink 1 + sys sys_mq_timedsend 5 + sys sys_mq_timedreceive 5 + sys sys_mq_notify 2 /* 4275 */ + sys sys_mq_getsetattr 3 + sys sys_ni_syscall 0 /* sys_vserver */ + sys sys_waitid 5 + sys sys_ni_syscall 0 /* available, was setaltroot */ + sys sys_add_key 5 /* 4280 */ + sys sys_request_key 4 + sys sys_keyctl 5 + sys sys_set_thread_area 1 + sys sys_inotify_init 0 + sys sys_inotify_add_watch 3 /* 4285 */ + sys sys_inotify_rm_watch 2 + sys sys_migrate_pages 4 + sys sys_openat 4 + sys sys_mkdirat 3 + sys sys_mknodat 4 /* 4290 */ + sys sys_fchownat 5 + sys sys_futimesat 3 + sys sys_fstatat64 4 + sys sys_unlinkat 3 + sys sys_renameat 4 /* 4295 */ + sys sys_linkat 5 + sys sys_symlinkat 3 + sys sys_readlinkat 4 + sys sys_fchmodat 3 + sys sys_faccessat 3 /* 4300 */ + sys sys_pselect6 6 + sys sys_ppoll 5 + sys sys_unshare 1 + sys sys_splice 6 + sys sys_sync_file_range 7 /* 4305 */ + sys sys_tee 4 + sys sys_vmsplice 4 + sys sys_move_pages 6 + sys sys_set_robust_list 2 + sys sys_get_robust_list 3 /* 4310 */ + sys sys_kexec_load 4 + sys sys_getcpu 3 + sys sys_epoll_pwait 6 + sys sys_ioprio_set 3 + sys sys_ioprio_get 2 /* 4315 */ + sys sys_utimensat 4 + sys sys_signalfd 3 + sys sys_ni_syscall 0 + sys sys_eventfd 1 + sys sys_fallocate 6 /* 4320 */ + sys sys_timerfd_create 2 + sys sys_timerfd_gettime 2 + sys sys_timerfd_settime 4 + sys sys_signalfd4 4 + sys sys_eventfd2 2 /* 4325 */ + sys sys_epoll_create1 1 + sys sys_dup3 3 + sys sys_pipe2 2 + sys sys_inotify_init1 1 + sys sys_preadv 6 /* 4330 */ + sys sys_pwritev 6 + .endm + + /* We pre-compute the number of _instruction_ bytes needed to + load or store the arguments 6-8. Negative values are ignored. */ + + .macro sys function, nargs + PTR \function + LONG (\nargs << 2) - (5 << 2) + .endm + + .align 3 + .type sys_call_table,@object +EXPORT(sys_call_table) + syscalltable + .size sys_call_table, . - sys_call_table diff --git a/target/linux/realtek/files/arch/rlx/kernel/setup.c b/target/linux/realtek/files/arch/rlx/kernel/setup.c new file mode 100644 index 000000000..c177a19ed --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/setup.c @@ -0,0 +1,579 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 1995 Waldorf Electronics + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle + * Copyright (C) 1996 Stoned Elipot + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 2000, 2001, 2002, 2007 Maciej W. Rozycki + */ +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/screen_info.h> +#include <linux/bootmem.h> +#include <linux/initrd.h> +#include <linux/root_dev.h> +#include <linux/highmem.h> +#include <linux/console.h> +#include <linux/pfn.h> +#include <linux/debugfs.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/bugs.h> +#include <asm/cache.h> +#include <asm/cpu.h> +#include <asm/sections.h> +#include <asm/setup.h> +#include <asm/system.h> + +struct cpuinfo_mips cpu_data[NR_CPUS] __read_mostly +#ifdef CONFIG_SERIAL_SC16IS7X0_CONSOLE += { + { + .udelay_val = 2490368, // @498.07 + }, +} +#endif +; + +EXPORT_SYMBOL(cpu_data); + +#ifdef CONFIG_VT +struct screen_info screen_info; +#endif + +/* + * Despite it's name this variable is even if we don't have PCI + */ +unsigned int PCI_DMA_BUS_IS_PHYS; + +EXPORT_SYMBOL(PCI_DMA_BUS_IS_PHYS); + +/* + * Setup information + * + * These are initialized so they are in the .data section + */ +//unsigned long mips_machtype __read_mostly = MACH_UNKNOWN; +//EXPORT_SYMBOL(mips_machtype); + +struct boot_mem_map boot_mem_map; + +static char command_line[CL_SIZE]; + char arcs_cmdline[CL_SIZE]=CONFIG_CMDLINE; + +/* + * mips_io_port_base is the begin of the address space to which x86 style + * I/O ports are mapped. + */ +const unsigned long mips_io_port_base __read_mostly = -1; +EXPORT_SYMBOL(mips_io_port_base); + +static struct resource code_resource = { .name = "Kernel code", }; +static struct resource data_resource = { .name = "Kernel data", }; + +void __init add_memory_region(phys_t start, phys_t size, long type) +{ + int x = boot_mem_map.nr_map; + struct boot_mem_map_entry *prev = boot_mem_map.map + x - 1; + + /* Sanity check */ + if (start + size < start) { + pr_warning("Trying to add an invalid memory region, skipped\n"); + return; + } + + /* + * Try to merge with previous entry if any. This is far less than + * perfect but is sufficient for most real world cases. + */ + if (x && prev->addr + prev->size == start && prev->type == type) { + prev->size += size; + return; + } + + if (x == BOOT_MEM_MAP_MAX) { + pr_err("Ooops! Too many entries in the memory map!\n"); + return; + } + + boot_mem_map.map[x].addr = start; + boot_mem_map.map[x].size = size; + boot_mem_map.map[x].type = type; + boot_mem_map.nr_map++; +} + +static void __init print_memory_map(void) +{ + int i; + const int field = 2 * sizeof(unsigned long); + + for (i = 0; i < boot_mem_map.nr_map; i++) { + printk(KERN_INFO " memory: %0*Lx @ %0*Lx ", + field, (unsigned long long) boot_mem_map.map[i].size, + field, (unsigned long long) boot_mem_map.map[i].addr); + + switch (boot_mem_map.map[i].type) { + case BOOT_MEM_RAM: + printk(KERN_CONT "(usable)\n"); + break; + case BOOT_MEM_ROM_DATA: + printk(KERN_CONT "(ROM data)\n"); + break; + case BOOT_MEM_RESERVED: + printk(KERN_CONT "(reserved)\n"); + break; + default: + printk(KERN_CONT "type %lu\n", boot_mem_map.map[i].type); + break; + } + } +} + +/* + * Manage initrd + */ +#ifdef CONFIG_BLK_DEV_INITRD + +static int __init rd_start_early(char *p) +{ + unsigned long start = memparse(p, &p); + + initrd_start = start; + initrd_end += start; + return 0; +} +early_param("rd_start", rd_start_early); + +static int __init rd_size_early(char *p) +{ + initrd_end += memparse(p, &p); + return 0; +} +early_param("rd_size", rd_size_early); + +/* it returns the next free pfn after initrd */ +static unsigned long __init init_initrd(void) +{ + unsigned long end; + + /* + * Board specific code or command line parser should have + * already set up initrd_start and initrd_end. In these cases + * perfom sanity checks and use them if all looks good. + */ + if (!initrd_start || initrd_end <= initrd_start) { +#ifdef CONFIG_PROBE_INITRD_HEADER + u32 *initrd_header; + + /* + * See if initrd has been added to the kernel image by + * arch/mips/boot/addinitrd.c. In that case a header is + * prepended to initrd and is made up by 8 bytes. The first + * word is a magic number and the second one is the size of + * initrd. Initrd start must be page aligned in any cases. + */ + initrd_header = __va(PAGE_ALIGN(__pa_symbol(&_end) + 8)) - 8; + if (initrd_header[0] != 0x494E5244) + goto disable; + initrd_start = (unsigned long)(initrd_header + 2); + initrd_end = initrd_start + initrd_header[1]; +#else + goto disable; +#endif + } + + if (initrd_start & ~PAGE_MASK) { + pr_err("initrd start must be page aligned\n"); + goto disable; + } + if (initrd_start < PAGE_OFFSET) { + pr_err("initrd start < PAGE_OFFSET\n"); + goto disable; + } + + /* + * Sanitize initrd addresses. For example firmware + * can't guess if they need to pass them through + * 64-bits values if the kernel has been built in pure + * 32-bit. We need also to switch from KSEG0 to XKPHYS + * addresses now, so the code can now safely use __pa(). + */ + end = __pa(initrd_end); + initrd_end = (unsigned long)__va(end); + initrd_start = (unsigned long)__va(__pa(initrd_start)); + + ROOT_DEV = Root_RAM0; + return PFN_UP(end); +disable: + initrd_start = 0; + initrd_end = 0; + return 0; +} + +static void __init finalize_initrd(void) +{ + unsigned long size = initrd_end - initrd_start; + + 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_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; +} + +#else /* !CONFIG_BLK_DEV_INITRD */ + +static unsigned long __init init_initrd(void) +{ + return 0; +} + +#define finalize_initrd() do {} while (0) + +#endif + +/* + * Initialize the bootmem allocator. It also setup initrd related data + * if needed. + */ +static void __init bootmem_init(void) +{ + unsigned long reserved_end; + unsigned long mapstart = ~0UL; + unsigned long bootmap_size; + int i; + + /* + * Init any data related to initrd. It's a nop if INITRD is + * not selected. Once that done we can determine the low bound + * of usable memory. + */ + reserved_end = max(init_initrd(), + (unsigned long) PFN_UP(__pa_symbol(&_end))); + + /* + * max_low_pfn is not a number of pages. The number of pages + * of the system is given by 'max_low_pfn - min_low_pfn'. + */ + min_low_pfn = ~0UL; + max_low_pfn = 0; + + /* + * Find the highest page frame number we have available. + */ + for (i = 0; i < boot_mem_map.nr_map; i++) { + unsigned long start, end; + + if (boot_mem_map.map[i].type != BOOT_MEM_RAM) + continue; + + start = PFN_UP(boot_mem_map.map[i].addr); + end = PFN_DOWN(boot_mem_map.map[i].addr + + boot_mem_map.map[i].size); + + if (end > max_low_pfn) + max_low_pfn = end; + if (start < min_low_pfn) + min_low_pfn = start; + if (end <= reserved_end) + continue; + if (start >= mapstart) + continue; + mapstart = max(reserved_end, start); + } + + if (min_low_pfn >= max_low_pfn) + panic("Incorrect memory mapping !!!"); + if (min_low_pfn > ARCH_PFN_OFFSET) { + pr_info("Wasting %lu bytes for tracking %lu unused pages\n", + (min_low_pfn - ARCH_PFN_OFFSET) * sizeof(struct page), + min_low_pfn - ARCH_PFN_OFFSET); + } else if (min_low_pfn < ARCH_PFN_OFFSET) { + pr_info("%lu free pages won't be used\n", + ARCH_PFN_OFFSET - min_low_pfn); + } + min_low_pfn = ARCH_PFN_OFFSET; + + /* + * Determine low and high memory ranges + */ + max_pfn = max_low_pfn; + if (max_low_pfn > PFN_DOWN(HIGHMEM_START)) { +#ifdef CONFIG_HIGHMEM + highstart_pfn = PFN_DOWN(HIGHMEM_START); + highend_pfn = max_low_pfn; +#endif + max_low_pfn = PFN_DOWN(HIGHMEM_START); + } + + /* + * Initialize the boot-time allocator with low memory only. + */ + bootmap_size = init_bootmem_node(NODE_DATA(0), mapstart, + min_low_pfn, max_low_pfn); + + + for (i = 0; i < boot_mem_map.nr_map; i++) { + unsigned long start, end; + + start = PFN_UP(boot_mem_map.map[i].addr); + end = PFN_DOWN(boot_mem_map.map[i].addr + + boot_mem_map.map[i].size); + + if (start <= min_low_pfn) + start = min_low_pfn; + if (start >= end) + continue; + +#ifndef CONFIG_HIGHMEM + if (end > max_low_pfn) + end = max_low_pfn; + + /* + * ... finally, is the area going away? + */ + if (end <= start) + continue; +#endif + + add_active_range(0, start, end); + } + + /* + * Register fully available low RAM pages with the bootmem allocator. + */ + for (i = 0; i < boot_mem_map.nr_map; i++) { + unsigned long start, end, size; + + /* + * Reserve usable memory. + */ + if (boot_mem_map.map[i].type != BOOT_MEM_RAM) + continue; + + start = PFN_UP(boot_mem_map.map[i].addr); + end = PFN_DOWN(boot_mem_map.map[i].addr + + boot_mem_map.map[i].size); + /* + * We are rounding up the start address of usable memory + * and at the end of the usable range downwards. + */ + if (start >= max_low_pfn) + continue; + if (start < reserved_end) + start = reserved_end; + if (end > max_low_pfn) + end = max_low_pfn; + + /* + * ... finally, is the area going away? + */ + if (end <= start) + continue; + size = end - start; + + /* Register lowmem ranges */ + free_bootmem(PFN_PHYS(start), size << PAGE_SHIFT); + memory_present(0, start, end); + } + + /* + * Reserve the bootmap memory. + */ + reserve_bootmem(PFN_PHYS(mapstart), bootmap_size, BOOTMEM_DEFAULT); + + /* + * Reserve initrd memory if needed. + */ + finalize_initrd(); +} + +/* + * arch_mem_init - initialize memory management subsystem + * + * o plat_mem_setup() detects the memory configuration and will record detected + * memory areas using add_memory_region. + * + * At this stage the memory configuration of the system is known to the + * kernel but generic memory management system is still entirely uninitialized. + * + * o bootmem_init() + * o sparse_init() + * o paging_init() + * + * At this stage the bootmem allocator is ready to use. + * + * NOTE: historically plat_mem_setup did the entire platform initialization. + * This was rather impractical because it meant plat_mem_setup had to + * get away without any kind of memory allocator. To keep old code from + * breaking bsp_setup was just renamed to bsp_setup and a second platform + * initialization hook for anything else was introduced. + */ + +static int usermem __initdata = 0; + +static int __init early_parse_mem(char *p) +{ + unsigned long start, size; + + /* + * If a user specifies memory size, we + * blow away any automatically generated + * size. + */ + if (usermem == 0) { + boot_mem_map.nr_map = 0; + usermem = 1; + } + start = 0; + size = memparse(p, &p); + if (*p == '@') + start = memparse(p + 1, &p); + + add_memory_region(start, size, BOOT_MEM_RAM); + return 0; +} +early_param("mem", early_parse_mem); + +static void __init arch_mem_init(char **cmdline_p) +{ + extern void bsp_setup(void); + + /* call board setup routine */ + bsp_setup(); + + pr_info("Determined physical RAM map:\n"); + print_memory_map(); + + strlcpy(command_line, arcs_cmdline, sizeof(command_line)); + strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); + + *cmdline_p = command_line; + + parse_early_param(); + + if (usermem) { + pr_info("User-defined physical RAM map:\n"); + print_memory_map(); + } + + bootmem_init(); + sparse_init(); + paging_init(); +} + +static void __init resource_init(void) +{ + int i; + + if (UNCAC_BASE != IO_BASE) + return; + + code_resource.start = __pa_symbol(&_text); + code_resource.end = __pa_symbol(&_etext) - 1; + data_resource.start = __pa_symbol(&_etext); + data_resource.end = __pa_symbol(&_edata) - 1; + + /* + * Request address space for all standard RAM. + */ + for (i = 0; i < boot_mem_map.nr_map; i++) { + struct resource *res; + unsigned long start, end; + + start = boot_mem_map.map[i].addr; + end = boot_mem_map.map[i].addr + boot_mem_map.map[i].size - 1; + if (start >= HIGHMEM_START) + continue; + if (end >= HIGHMEM_START) + end = HIGHMEM_START - 1; + + res = alloc_bootmem(sizeof(struct resource)); + switch (boot_mem_map.map[i].type) { + case BOOT_MEM_RAM: + case BOOT_MEM_ROM_DATA: + res->name = "System RAM"; + break; + case BOOT_MEM_RESERVED: + default: + res->name = "reserved"; + } + + res->start = start; + res->end = end; + + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + request_resource(&iomem_resource, res); + + /* + * We don't know which RAM region contains kernel data, + * so we try it repeatedly and let the resource manager + * test it. + */ + request_resource(res, &code_resource); + request_resource(res, &data_resource); + } +} + +extern void bsp_init (void); + +void __init setup_arch(char **cmdline_p) +{ + cpu_probe(); + bsp_init(); + +#ifdef CONFIG_EARLY_PRINTK + setup_early_printk(); +#endif + cpu_report(); + +#if defined(CONFIG_VT) +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + + arch_mem_init(cmdline_p); + + resource_init(); +} + +unsigned long kernelsp[NR_CPUS]; +unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; + +#ifdef CONFIG_DEBUG_FS +struct dentry *mips_debugfs_dir; +static int __init debugfs_mips(void) +{ + struct dentry *d; + + d = debugfs_create_dir("mips", NULL); + if (!d) + return -ENOMEM; + mips_debugfs_dir = d; + return 0; +} +arch_initcall(debugfs_mips); +#endif diff --git a/target/linux/realtek/files/arch/rlx/kernel/signal-common.h b/target/linux/realtek/files/arch/rlx/kernel/signal-common.h new file mode 100644 index 000000000..58451943e --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/signal-common.h @@ -0,0 +1,34 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ + +#ifndef __SIGNAL_COMMON_H +#define __SIGNAL_COMMON_H + +/* #define DEBUG_SIG */ + +#ifdef DEBUG_SIG +# define DEBUGP(fmt, args...) printk("%s: " fmt, __func__, ##args) +#else +# define DEBUGP(fmt, args...) +#endif + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* + * Determine which stack to use.. + */ +extern void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size); +/* + * install trampoline code to get back from the sig handler + */ +extern int install_sigtramp(unsigned int __user *tramp, unsigned int syscall); + +#endif /* __SIGNAL_COMMON_H */ diff --git a/target/linux/realtek/files/arch/rlx/kernel/signal.c b/target/linux/realtek/files/arch/rlx/kernel/signal.c new file mode 100644 index 000000000..93aca32a3 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/signal.c @@ -0,0 +1,550 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/cache.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/personality.h> +#include <linux/smp.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/compiler.h> +#include <linux/syscalls.h> +#include <linux/uaccess.h> + +#include <asm/abi.h> +#include <asm/asm.h> +#include <linux/bitops.h> +#include <asm/cacheflush.h> +#include <asm/sim.h> +#include <asm/ucontext.h> +#include <asm/cpu-features.h> + +#include "signal-common.h" + +/* + * Horribly complicated - with the bloody RM9000 workarounds enabled + * the signal trampolines is moving to the end of the structure so we can + * increase the alignment without breaking software compatibility. + */ +struct sigframe { + u32 sf_ass[4]; /* argument save space for o32 */ + u32 sf_code[2]; /* signal trampoline */ + struct sigcontext sf_sc; + sigset_t sf_mask; +}; + +struct rt_sigframe { + u32 rs_ass[4]; /* argument save space for o32 */ + u32 rs_code[2]; /* signal trampoline */ + struct siginfo rs_info; + struct ucontext rs_uc; +}; + +/* + * Helper routines + */ +int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) +{ + int err = 0; + int i; + + err |= __put_user(0, &sc->sc_regs[0]); + for (i = 1; i < 32; i++) + err |= __put_user(regs->regs[i], &sc->sc_regs[i]); + + err |= __put_user(regs->hi, &sc->sc_mdhi); + err |= __put_user(regs->lo, &sc->sc_mdlo); + err |= __put_user(regs->cp0_epc, &sc->sc_pc); + + return err; +} + +int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) +{ + int err = 0; + int i; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + err |= __get_user(regs->cp0_epc, &sc->sc_pc); + err |= __get_user(regs->lo, &sc->sc_mdlo); + err |= __get_user(regs->hi, &sc->sc_mdhi); + + for (i = 1; i < 32; i++) + err |= __get_user(regs->regs[i], &sc->sc_regs[i]); + + return err; +} + +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[29]; + + /* + * FPU emulator may have it's own trampoline active just + * above the user stack, 16-bytes before the next lowest + * 16 byte boundary. Try to avoid trashing it. + */ + sp -= 32; + + /* This is the X/Open sanctioned signal stack switching. */ + if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0)) + sp = current->sas_ss_sp + current->sas_ss_size; + + return (void __user *)((sp - frame_size) & ALMASK); +} + +int install_sigtramp(unsigned int __user *tramp, unsigned int syscall) +{ + int err; + + /* + * Set up the return code ... + * + * li v0, __NR__foo_sigreturn + * syscall + */ + + err = __put_user(0x24020000 + syscall, tramp + 0); + err |= __put_user(0x0000000c , tramp + 1); + + flush_cache_sigtramp((unsigned long) tramp); + + return err; +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ + +#ifdef CONFIG_TRAD_SIGNALS +asmlinkage int sys_sigsuspend(nabi_no_regargs struct pt_regs regs) +{ + sigset_t newset; + sigset_t __user *uset; + + uset = (sigset_t __user *) regs.regs[4]; + if (copy_from_user(&newset, uset, sizeof(sigset_t))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + current->saved_sigmask = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} +#endif + +asmlinkage int sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) +{ + sigset_t newset; + sigset_t __user *unewset; + size_t sigsetsize; + + /* XXX Don't preclude handling different sized sigset_t's. */ + sigsetsize = regs.regs[5]; + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + unewset = (sigset_t __user *) regs.regs[4]; + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + current->saved_sigmask = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} + +#ifdef CONFIG_TRAD_SIGNALS +SYSCALL_DEFINE3(sigaction, int, sig, const struct sigaction __user *, act, + struct sigaction __user *, oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + int err = 0; + + if (act) { + old_sigset_t mask; + + if (!access_ok(VERIFY_READ, act, sizeof(*act))) + return -EFAULT; + err |= __get_user(new_ka.sa.sa_handler, &act->sa_handler); + err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + err |= __get_user(mask, &act->sa_mask.sig[0]); + if (err) + return -EFAULT; + + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) + return -EFAULT; + err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler); + err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig); + err |= __put_user(0, &oact->sa_mask.sig[1]); + err |= __put_user(0, &oact->sa_mask.sig[2]); + err |= __put_user(0, &oact->sa_mask.sig[3]); + if (err) + return -EFAULT; + } + + return ret; +} +#endif + +asmlinkage int sys_sigaltstack(nabi_no_regargs struct pt_regs regs) +{ + const stack_t __user *uss = (const stack_t __user *) regs.regs[4]; + stack_t __user *uoss = (stack_t __user *) regs.regs[5]; + unsigned long usp = regs.regs[29]; + + return do_sigaltstack(uss, uoss, usp); +} + +#ifdef CONFIG_TRAD_SIGNALS +asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) +{ + struct sigframe __user *frame; + sigset_t blocked; + int sig; + + frame = (struct sigframe __user *) regs.regs[29]; + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&blocked, &frame->sf_mask, sizeof(blocked))) + goto badframe; + + sigdelsetmask(&blocked, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = blocked; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + sig = restore_sigcontext(®s, &frame->sf_sc); + if (sig < 0) + goto badframe; + else if (sig) + force_sig(sig, current); + + /* + * Don't let your children do this ... + */ + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ + +badframe: + force_sig(SIGSEGV, current); +} +#endif /* CONFIG_TRAD_SIGNALS */ + +asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) +{ + struct rt_sigframe __user *frame; + sigset_t set; + stack_t st; + int sig; + + frame = (struct rt_sigframe __user *) regs.regs[29]; + 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(®s, &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[29]); + + /* + * Don't let your children do this ... + */ + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ + +badframe: + force_sig(SIGSEGV, current); +} + +#ifdef CONFIG_TRAD_SIGNALS +static int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set) +{ + struct sigframe __user *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + goto give_sigsegv; + + err |= install_sigtramp(frame->sf_code, __NR_sigreturn); + + err |= setup_sigcontext(regs, &frame->sf_sc); + err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to struct sigcontext + * + * $25 and c0_epc point to the signal handler, $29 points to the + * struct sigframe. + */ + regs->regs[ 4] = signr; + regs->regs[ 5] = 0; + regs->regs[ 6] = (unsigned long) &frame->sf_sc; + regs->regs[29] = (unsigned long) frame; + regs->regs[31] = (unsigned long) frame->sf_code; + regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; + + DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", + current->comm, current->pid, + frame, regs->cp0_epc, regs->regs[31]); + return 0; + +give_sigsegv: + force_sigsegv(signr, current); + return -EFAULT; +} +#endif + +static int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set, siginfo_t *info) +{ + struct rt_sigframe __user *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + goto give_sigsegv; + + err |= install_sigtramp(frame->rs_code, __NR_rt_sigreturn); + + /* Create siginfo. */ + err |= copy_siginfo_to_user(&frame->rs_info, info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->rs_uc.uc_flags); + err |= __put_user(NULL, &frame->rs_uc.uc_link); + err |= __put_user((void __user *)current->sas_ss_sp, + &frame->rs_uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->regs[29]), + &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; + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to ucontext + * + * $25 and c0_epc point to the signal handler, $29 points to + * the struct rt_sigframe. + */ + 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) frame; + regs->regs[31] = (unsigned long) frame->rs_code; + regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; + + DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", + current->comm, current->pid, + frame, regs->cp0_epc, regs->regs[31]); + + return 0; + +give_sigsegv: + force_sigsegv(signr, current); + return -EFAULT; +} + +struct mips_abi mips_abi = { +#ifdef CONFIG_TRAD_SIGNALS + .setup_frame = setup_frame, +#endif + .setup_rt_frame = setup_rt_frame, + .restart = __NR_restart_syscall +}; + +static int handle_signal(unsigned long sig, siginfo_t *info, + struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) +{ + int ret; + + switch(regs->regs[0]) { + case ERESTART_RESTARTBLOCK: + case ERESTARTNOHAND: + regs->regs[2] = EINTR; + break; + case ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->regs[2] = EINTR; + break; + } + /* fallthrough */ + case ERESTARTNOINTR: /* Userland will reload $v0. */ + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 8; + } + + regs->regs[0] = 0; /* Don't deal with this again. */ + + if (sig_uses_siginfo(ka)) + ret = current->thread.abi->setup_rt_frame(ka, regs, sig, oldset, info); + else + ret = current->thread.abi->setup_frame(ka, regs, sig, oldset); + + 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; +} + +static 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) { + /* Whee! 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; + } + + /* + * Who's code doesn't conform to the restartable syscall convention + * dies here!!! The li instruction, a single machine instruction, + * must directly be followed by the syscall instruction. + */ + if (regs->regs[0]) { + if (regs->regs[2] == ERESTARTNOHAND || + regs->regs[2] == ERESTARTSYS || + regs->regs[2] == ERESTARTNOINTR) { + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 8; + } + if (regs->regs[2] == ERESTART_RESTARTBLOCK) { + regs->regs[2] = current->thread.abi->restart; + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 4; + } + regs->regs[0] = 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/target/linux/realtek/files/arch/rlx/kernel/stacktrace.c b/target/linux/realtek/files/arch/rlx/kernel/stacktrace.c new file mode 100644 index 000000000..dc228d5ad --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/stacktrace.c @@ -0,0 +1,89 @@ +/* + * arch/rlx/kernel/stacktrace.c + * + * Stack trace management functions + * + * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> + */ +#include <linux/sched.h> +#include <linux/stacktrace.h> +#include <linux/module.h> +#include <asm/stacktrace.h> + +/* + * Save stack-backtrace addresses into a stack_trace buffer: + */ +static void save_raw_context_stack(struct stack_trace *trace, + unsigned long reg29) +{ + unsigned long *sp = (unsigned long *)reg29; + unsigned long addr; + + while (!kstack_end(sp)) { + addr = *sp++; + if (__kernel_text_address(addr)) { + if (trace->skip > 0) + trace->skip--; + else + trace->entries[trace->nr_entries++] = addr; + if (trace->nr_entries >= trace->max_entries) + break; + } + } +} + +static void save_context_stack(struct stack_trace *trace, + struct task_struct *tsk, struct pt_regs *regs) +{ + unsigned long sp = regs->regs[29]; +#ifdef CONFIG_KALLSYMS + unsigned long ra = regs->regs[31]; + unsigned long pc = regs->cp0_epc; + + if (raw_show_trace || !__kernel_text_address(pc)) { + unsigned long stack_page = + (unsigned long)task_stack_page(tsk); + if (stack_page && sp >= stack_page && + sp <= stack_page + THREAD_SIZE - 32) + save_raw_context_stack(trace, sp); + return; + } + do { + if (trace->skip > 0) + trace->skip--; + else + trace->entries[trace->nr_entries++] = pc; + if (trace->nr_entries >= trace->max_entries) + break; + pc = unwind_stack(tsk, &sp, pc, &ra); + } while (pc); +#else + save_raw_context_stack(trace, sp); +#endif +} + +/* + * Save stack-backtrace addresses into a stack_trace buffer. + */ +void save_stack_trace(struct stack_trace *trace) +{ + save_stack_trace_tsk(current, trace); +} +EXPORT_SYMBOL_GPL(save_stack_trace); + +void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +{ + struct pt_regs dummyregs; + struct pt_regs *regs = &dummyregs; + + WARN_ON(trace->nr_entries || !trace->max_entries); + + if (tsk != current) { + regs->regs[29] = tsk->thread.reg29; + regs->regs[31] = 0; + regs->cp0_epc = tsk->thread.reg31; + } else + prepare_frametrace(regs); + save_context_stack(trace, tsk, regs); +} +EXPORT_SYMBOL_GPL(save_stack_trace_tsk); diff --git a/target/linux/realtek/files/arch/rlx/kernel/syscall.c b/target/linux/realtek/files/arch/rlx/kernel/syscall.c new file mode 100644 index 000000000..edba10ec3 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/syscall.c @@ -0,0 +1,450 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1995, 1996, 1997, 2000, 2001, 05 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/capability.h> +#include <linux/errno.h> +#include <linux/linkage.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/smp.h> +#include <linux/mman.h> +#include <linux/ptrace.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/slab.h> +#include <linux/utsname.h> +#include <linux/unistd.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/compiler.h> +#include <linux/module.h> +#include <linux/ipc.h> + +#include <asm/branch.h> +#include <asm/cachectl.h> +#include <asm/cacheflush.h> +#include <asm/asm-offsets.h> +#include <asm/signal.h> +#include <asm/sim.h> +#include <asm/shmparam.h> +#include <asm/sysmips.h> +#include <asm/uaccess.h> + +/* + * For historic reasons the pipe(2) syscall on MIPS has an unusual calling + * convention. It returns results in registers $v0 / $v1 which means there + * is no need for it to do verify the validity of a userspace pointer + * argument. Historically that used to be expensive in Linux. These days + * the performance advantage is negligible. + */ +asmlinkage int sysm_pipe(nabi_no_regargs volatile struct pt_regs regs) +{ + int fd[2]; + int error, res; + + error = do_pipe_flags(fd, 0); + if (error) { + res = error; + goto out; + } + regs.regs[3] = fd[1]; + res = fd[0]; +out: + return res; +} + +unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */ + +EXPORT_SYMBOL(shm_align_mask); + +#define COLOUR_ALIGN(addr,pgoff) \ + ((((addr) + shm_align_mask) & ~shm_align_mask) + \ + (((pgoff) << PAGE_SHIFT) & shm_align_mask)) + +unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct vm_area_struct * vmm; + int do_color_align; + unsigned long task_size; + + task_size = STACK_TOP; + + if (len > task_size) + return -ENOMEM; + + if (flags & MAP_FIXED) { + /* Even MAP_FIXED mappings must reside within task_size. */ + if (task_size - len < addr) + return -EINVAL; + + /* + * We do not accept a shared mapping if it would violate + * cache aliasing constraints. + */ + if ((flags & MAP_SHARED) && (addr & shm_align_mask)) + return -EINVAL; + return addr; + } + + do_color_align = 0; + if (filp || (flags & MAP_SHARED)) + do_color_align = 1; + if (addr) { + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + else + addr = PAGE_ALIGN(addr); + vmm = find_vma(current->mm, addr); + if (task_size - len >= addr && + (!vmm || addr + len <= vmm->vm_start)) + return addr; + } + addr = TASK_UNMAPPED_BASE; + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + else + addr = PAGE_ALIGN(addr); + + for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { + /* At this point: (!vmm || addr < vmm->vm_end). */ + if (task_size - len < addr) + return -ENOMEM; + if (!vmm || addr + len <= vmm->vm_start) + return addr; + addr = vmm->vm_end; + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + } +} + +/* common code for old and new mmaps */ +static inline unsigned long +do_mmap2(unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long pgoff) +{ + unsigned long error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +SYSCALL_DEFINE6(mips_mmap, unsigned long, addr, unsigned long, len, + unsigned long, prot, unsigned long, flags, unsigned long, + fd, off_t, offset) +{ + unsigned long result; + + result = -EINVAL; + if (offset & ~PAGE_MASK) + goto out; + + result = do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); + +out: + return result; +} + +SYSCALL_DEFINE6(mips_mmap2, unsigned long, addr, unsigned long, len, + unsigned long, prot, unsigned long, flags, unsigned long, fd, + unsigned long, pgoff) +{ + if (pgoff & (~PAGE_MASK >> 12)) + return -EINVAL; + + return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT-12)); +} + +save_static_function(sys_fork); +static int __used noinline +_sys_fork(nabi_no_regargs struct pt_regs regs) +{ + return do_fork(SIGCHLD, regs.regs[29], ®s, 0, NULL, NULL); +} + +save_static_function(sys_clone); +static int __used noinline +_sys_clone(nabi_no_regargs 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[29]; + parent_tidptr = (int __user *) regs.regs[6]; + + /* We need to fetch the fifth argument off the stack. */ + child_tidptr = NULL; + if (clone_flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) { + int __user *__user *usp = (int __user *__user *) regs.regs[29]; + if (regs.regs[2] == __NR_syscall) { + if (get_user (child_tidptr, &usp[5])) + return -EFAULT; + } + else if (get_user (child_tidptr, &usp[4])) + return -EFAULT; + } + + return do_fork(clone_flags, newsp, ®s, 0, + parent_tidptr, child_tidptr); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs) +{ + int error; + char * filename; + + filename = getname((char __user *) (long)regs.regs[4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char __user *__user *) (long)regs.regs[5], + (char __user *__user *) (long)regs.regs[6], ®s); + putname(filename); + +out: + return error; +} + +/* + * Compacrapability ... + */ +SYSCALL_DEFINE1(uname, struct old_utsname __user *, name) +{ + if (name && !copy_to_user(name, utsname(), sizeof (*name))) + return 0; + return -EFAULT; +} + +/* + * Compacrapability ... + */ +SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname))) + return -EFAULT; + + error = __copy_to_user(&name->sysname, &utsname()->sysname, + __OLD_UTS_LEN); + error -= __put_user(0, name->sysname + __OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename, &utsname()->nodename, + __OLD_UTS_LEN); + error -= __put_user(0, name->nodename + __OLD_UTS_LEN); + error -= __copy_to_user(&name->release, &utsname()->release, + __OLD_UTS_LEN); + error -= __put_user(0, name->release + __OLD_UTS_LEN); + error -= __copy_to_user(&name->version, &utsname()->version, + __OLD_UTS_LEN); + error -= __put_user(0, name->version + __OLD_UTS_LEN); + error -= __copy_to_user(&name->machine, &utsname()->machine, + __OLD_UTS_LEN); + error = __put_user(0, name->machine + __OLD_UTS_LEN); + error = error ? -EFAULT : 0; + + return error; +} + +SYSCALL_DEFINE1(set_thread_area, unsigned long, addr) +{ + struct thread_info *ti = task_thread_info(current); + + ti->tp_value = addr; + return 0; +} + +asmlinkage int _sys_sysmips(long cmd, long arg1, long arg2, long arg3) +{ + switch (cmd) { + case MIPS_ATOMIC_SET: + printk(KERN_CRIT "How did I get here?\n"); + return -EINVAL; + + case MIPS_FIXADE: + if (arg1 & ~3) + return -EINVAL; + + if (arg1 & 1) + set_thread_flag(TIF_FIXADE); + else + clear_thread_flag(TIF_FIXADE); + if (arg1 & 2) + set_thread_flag(TIF_LOGADE); + else + clear_thread_flag(TIF_FIXADE); + + return 0; + + case FLUSH_CACHE: + __flush_cache_all(); + return 0; + } + + return -EINVAL; +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, int, second, + unsigned long, third, void __user *, ptr, long, fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, NULL); + case SEMTIMEDOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, + (const struct timespec __user *)fifth); + case SEMGET: + return sys_semget(first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void __user *__user *) ptr)) + return -EFAULT; + return sys_semctl(first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd(first, (struct msgbuf __user *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge __user *) ptr, + sizeof(tmp))) + return -EFAULT; + return sys_msgrcv(first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv(first, + (struct msgbuf __user *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget((key_t) first, second); + case MSGCTL: + return sys_msgctl(first, second, + (struct msqid_ds __user *) ptr); + + case SHMAT: + switch (version) { + default: { + unsigned long raddr; + ret = do_shmat(first, (char __user *) ptr, second, + &raddr); + if (ret) + return ret; + return put_user(raddr, (unsigned long __user *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return do_shmat(first, (char __user *) ptr, second, + (unsigned long *) third); + } + case SHMDT: + return sys_shmdt((char __user *)ptr); + case SHMGET: + return sys_shmget(first, second, third); + case SHMCTL: + return sys_shmctl(first, second, + (struct shmid_ds __user *) ptr); + default: + return -ENOSYS; + } +} + +/* + * No implemented yet ... + */ +SYSCALL_DEFINE3(cachectl, char *, addr, int, nbytes, int, op) +{ + return -ENOSYS; +} + +/* + * 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 __a0 asm("$4") = (unsigned long) filename; + register unsigned long __a1 asm("$5") = (unsigned long) argv; + register unsigned long __a2 asm("$6") = (unsigned long) envp; + register unsigned long __a3 asm("$7"); + unsigned long __v0; + + __asm__ volatile (" \n" + " .set noreorder \n" + " li $2, %5 # __NR_execve \n" + " syscall \n" + " move %0, $2 \n" + " .set reorder \n" + : "=&r" (__v0), "=r" (__a3) + : "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_execve) + : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", + "memory"); + + if (__a3 == 0) + return __v0; + + return -__v0; +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/topology.c b/target/linux/realtek/files/arch/rlx/kernel/topology.c new file mode 100644 index 000000000..660e44ed4 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/topology.c @@ -0,0 +1,29 @@ +#include <linux/cpu.h> +#include <linux/cpumask.h> +#include <linux/init.h> +#include <linux/node.h> +#include <linux/nodemask.h> +#include <linux/percpu.h> + +static DEFINE_PER_CPU(struct cpu, cpu_devices); + +static int __init topology_init(void) +{ + int i, ret; + +#ifdef CONFIG_NUMA + for_each_online_node(i) + register_one_node(i); +#endif /* CONFIG_NUMA */ + + for_each_present_cpu(i) { + ret = register_cpu(&per_cpu(cpu_devices, i), i); + if (ret) + printk(KERN_WARNING "topology_init: register_cpu %d " + "failed (%d)\n", i, ret); + } + + return 0; +} + +subsys_initcall(topology_init); diff --git a/target/linux/realtek/files/arch/rlx/kernel/traps.c b/target/linux/realtek/files/arch/rlx/kernel/traps.c new file mode 100644 index 000000000..f75fd5291 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/traps.c @@ -0,0 +1,735 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1994 - 1999, 2000, 01, 06 Ralf Baechle + * Copyright (C) 1995, 1996 Paul M. Antoine + * Copyright (C) 1998 Ulf Carlsson + * Copyright (C) 1999 Silicon Graphics, Inc. + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000, 01 MIPS Technologies, Inc. + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Maciej W. Rozycki + */ +#include <linux/bug.h> +#include <linux/compiler.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/kallsyms.h> +#include <linux/bootmem.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/kgdb.h> +#include <linux/kdebug.h> + +#include <asm/bootinfo.h> +#include <asm/branch.h> +#include <asm/break.h> +#include <asm/cpu.h> +#include <asm/rlxregs.h> +#include <asm/module.h> +#include <asm/pgtable.h> +#include <asm/ptrace.h> +#include <asm/sections.h> +#include <asm/system.h> +#include <asm/tlbdebug.h> +#include <asm/traps.h> +#include <asm/uaccess.h> +#include <asm/mmu_context.h> +#include <asm/types.h> +#include <asm/stacktrace.h> +#include <asm/irq.h> + +#include <linux/time.h> +#include <linux/kernel_stat.h> +#include <asm/cputime.h> + +#ifdef CONFIG_PANIC_PRINTK +#define printk panic_printk +#endif + +extern asmlinkage void rlx_irq_dispatch(void); +extern asmlinkage void handle_tlbm(void); +extern asmlinkage void handle_tlbl(void); +extern asmlinkage void handle_tlbs(void); +extern asmlinkage void handle_adel(void); +extern asmlinkage void handle_ades(void); +extern asmlinkage void handle_sys(void); +extern asmlinkage void handle_bp(void); +extern asmlinkage void handle_ri(void); +extern asmlinkage void handle_cpu(void); +extern asmlinkage void handle_ov(void); +extern asmlinkage void handle_reserved(void); + +static void show_raw_backtrace(unsigned long reg29) +{ + unsigned long *sp = (unsigned long *)(reg29 & ~3); + unsigned long addr; + + printk("Call Trace:"); +#ifdef CONFIG_KALLSYMS + printk("\n"); +#endif + while (!kstack_end(sp)) { + unsigned long __user *p = + (unsigned long __user *)(unsigned long)sp++; + if (__get_user(addr, p)) { + printk(" (Bad stack address)"); + break; + } + if (__kernel_text_address(addr)) + print_ip_sym(addr); + } + printk("\n"); +} + +#ifdef CONFIG_KALLSYMS +int raw_show_trace; +static int __init set_raw_show_trace(char *str) +{ + raw_show_trace = 1; + return 1; +} +__setup("raw_show_trace", set_raw_show_trace); +#endif + +static void show_backtrace(struct task_struct *task, const struct pt_regs *regs) +{ + unsigned long sp = regs->regs[29]; + unsigned long ra = regs->regs[31]; + unsigned long pc = regs->cp0_epc; + + if (raw_show_trace || !__kernel_text_address(pc)) { + show_raw_backtrace(sp); + return; + } + printk("Call Trace:\n"); + do { + print_ip_sym(pc); + pc = unwind_stack(task, &sp, pc, &ra); + } while (pc); + printk("\n"); +} + +/* + * This routine abuses get_user()/put_user() to reference pointers + * with at least a bit of error checking ... + */ +static void show_stacktrace(struct task_struct *task, const struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + long stackdata; + int i; + unsigned long __user *sp = (unsigned long __user *)regs->regs[29]; + + printk("Stack :"); + i = 0; + while ((unsigned long) sp & (PAGE_SIZE - 1)) { + if (i && ((i % (64 / field)) == 0)) + printk("\n "); + if (i > 39) { + printk(" ..."); + break; + } + + if (__get_user(stackdata, sp++)) { + printk(" (Bad stack address)"); + break; + } + + printk(" %0*lx", field, stackdata); + i++; + } + printk("\n"); + show_backtrace(task, regs); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + struct pt_regs regs; + if (sp) { + regs.regs[29] = (unsigned long)sp; + regs.regs[31] = 0; + regs.cp0_epc = 0; + } else { + if (task && task != current) { + regs.regs[29] = task->thread.reg29; + regs.regs[31] = 0; + regs.cp0_epc = task->thread.reg31; + } else { + prepare_frametrace(®s); + } + } + show_stacktrace(task, ®s); +} + +void dump_uptime(void) +{ + struct timespec uptime; + struct timespec idle; + int i; + cputime_t idletime = cputime_zero; + + for_each_possible_cpu(i) + idletime = cputime64_add(idletime, kstat_cpu(i).cpustat.idle); + + do_posix_clock_monotonic_gettime(&uptime); + monotonic_to_bootbased(&uptime); + cputime_to_timespec(idletime, &idle); + printk("uptime: %lu.%02lu %lu.%02lu\n", + (unsigned long) uptime.tv_sec, + (uptime.tv_nsec / (NSEC_PER_SEC / 100)), + (unsigned long) idle.tv_sec, + (idle.tv_nsec / (NSEC_PER_SEC / 100))); +} + +/* + * The architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + struct pt_regs regs; + + dump_uptime(); + prepare_frametrace(®s); + show_backtrace(current, ®s); +} + +EXPORT_SYMBOL(dump_stack); + +static void show_code(unsigned int __user *pc) +{ + long i; + unsigned short __user *pc16 = NULL; + + printk("\nCode:"); + + if ((unsigned long)pc & 1) + pc16 = (unsigned short __user *)((unsigned long)pc & ~1); + for(i = -3 ; i < 6 ; i++) { + unsigned int insn; + if (pc16 ? __get_user(insn, pc16 + i) : __get_user(insn, pc + i)) { + printk(" (Bad address in epc)\n"); + break; + } + printk("%c%0*x%c", (i?' ':'<'), pc16 ? 4 : 8, insn, (i?' ':'>')); + } +} + +static void __show_regs(const struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + unsigned int cause = regs->cp0_cause; + int i; + + printk("Cpu %d\n", smp_processor_id()); + + /* + * Saved main processor registers + */ + for (i = 0; i < 32; ) { + if ((i % 4) == 0) + printk("$%2d :", i); + if (i == 0) + printk(" %0*lx", field, 0UL); + else if (i == 26 || i == 27) + printk(" %*s", field, ""); + else + printk(" %0*lx", field, regs->regs[i]); + + i++; + if ((i % 4) == 0) + printk("\n"); + } + + printk("Hi : %0*lx\n", field, regs->hi); + printk("Lo : %0*lx\n", field, regs->lo); + + /* + * Saved cp0 registers + */ + printk("epc : %0*lx %pS\n", field, regs->cp0_epc, + (void *) regs->cp0_epc); + printk(" %s\n", print_tainted()); + printk("ra : %0*lx %pS\n", field, regs->regs[31], + (void *) regs->regs[31]); + + printk("Status: %08x ", (uint32_t) regs->cp0_status); + + if (regs->cp0_status & ST0_KUO) printk("KUo "); + if (regs->cp0_status & ST0_IEO) printk("IEo "); + if (regs->cp0_status & ST0_KUP) printk("KUp "); + if (regs->cp0_status & ST0_IEP) printk("IEp "); + if (regs->cp0_status & ST0_KUC) printk("KUc "); + if (regs->cp0_status & ST0_IEC) printk("IEc "); + + printk("\n"); + + printk("Cause : %08x\n", cause); + + cause = (cause & CAUSEF_EXCCODE) >> CAUSEB_EXCCODE; + if (1 <= cause && cause <= 5) + printk("BadVA : %0*lx\n", field, regs->cp0_badvaddr); + + printk("PrId : %08x (%s)\n", read_c0_prid(), cpu_name_string()); +} + +/* + * FIXME: really the generic show_regs should take a const pointer argument. + */ +void show_regs(struct pt_regs *regs) +{ + __show_regs((struct pt_regs *)regs); +} + +void show_registers(const struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + + __show_regs(regs); + print_modules(); + printk("Process %s (pid: %d, threadinfo=%p, task=%p, tls=%0*lx)\n", + current->comm, current->pid, current_thread_info(), current, + field, current_thread_info()->tp_value); + + show_stacktrace(current, regs); + show_code((unsigned int __user *) regs->cp0_epc); + printk("\n"); +} + +static DEFINE_SPINLOCK(die_lock); + +void __noreturn die(const char * str, const struct pt_regs * regs) +{ + static int die_counter; + + console_verbose(); + spin_lock_irq(&die_lock); + bust_spinlocks(1); + printk("%s[#%d]:\n", str, ++die_counter); + show_registers(regs); + add_taint(TAINT_DIE); +#if defined(CONFIG_RTL_WTDOG) + { extern int is_fault; is_fault=1; } // set kernel fault flag +#endif + spin_unlock_irq(&die_lock); + + if (in_interrupt()) + panic("Fatal exception in interrupt"); + + if (panic_on_oops) { + printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n"); + ssleep(5); + panic("Fatal exception"); + } + + do_exit(SIGSEGV); +} + + +#define OPCODE 0xfc000000 +#define BASE 0x03e00000 +#define RT 0x001f0000 +#define OFFSET 0x0000ffff +#define LL 0xc0000000 +#define SC 0xe0000000 +#define SPEC0 0x00000000 +#define SPEC3 0x7c000000 +#define RD 0x0000f800 +#define FUNC 0x0000003f +#define SYNC 0x0000000f +#define RDHWR 0x0000003b + +/* ll/sc emulation */ +#ifndef CONFIG_CPU_HAS_LLSC + +/* The ll_bit is cleared by r*_switch.S */ + +unsigned long ll_bit; + +static struct task_struct *ll_task = NULL; + +static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode) +{ + unsigned long value, __user *vaddr; + long offset; + + /* + * analyse the ll instruction that just caused a ri exception + * and put the referenced address to addr. + */ + + /* sign extend offset */ + offset = opcode & OFFSET; + offset <<= 16; + offset >>= 16; + + vaddr = (unsigned long __user *) + ((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset); + + if ((unsigned long)vaddr & 3) + return SIGBUS; + if (get_user(value, vaddr)) + return SIGSEGV; + + preempt_disable(); + + if (ll_task == NULL || ll_task == current) { + ll_bit = 1; + } else { + ll_bit = 0; + } + ll_task = current; + + preempt_enable(); + + regs->regs[(opcode & RT) >> 16] = value; + + return 0; +} + +static inline int simulate_sc(struct pt_regs *regs, unsigned int opcode) +{ + unsigned long __user *vaddr; + unsigned long reg; + long offset; + + /* + * analyse the sc instruction that just caused a ri exception + * and put the referenced address to addr. + */ + + /* sign extend offset */ + offset = opcode & OFFSET; + offset <<= 16; + offset >>= 16; + + vaddr = (unsigned long __user *) + ((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset); + reg = (opcode & RT) >> 16; + + if ((unsigned long)vaddr & 3) + return SIGBUS; + + preempt_disable(); + + if (ll_bit == 0 || ll_task != current) { + regs->regs[reg] = 0; + preempt_enable(); + return 0; + } + + preempt_enable(); + + if (put_user(regs->regs[reg], vaddr)) + return SIGSEGV; + + regs->regs[reg] = 1; + + return 0; +} + +/* + * ll uses the opcode of lwc0 and sc uses the opcode of swc0. That is both + * opcodes are supposed to result in coprocessor unusable exceptions if + * executed on ll/sc-less processors. That's the theory. In practice a + * few processors such as NEC's VR4100 throw reserved instruction exceptions + * instead, so we're doing the emulation thing in both exception handlers. + */ +static int simulate_llsc(struct pt_regs *regs, unsigned int opcode) +{ + if ((opcode & OPCODE) == LL) + return simulate_ll(regs, opcode); + if ((opcode & OPCODE) == SC) + return simulate_sc(regs, opcode); + + return -1; /* Must be something else ... */ +} +#endif + +#ifndef CONFIG_CPU_HAS_SYNC +static int simulate_sync(struct pt_regs *regs, unsigned int opcode) +{ + if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC) + return 0; + + return -1; /* Must be something else ... */ +} +#endif + +asmlinkage void do_ov(struct pt_regs *regs) +{ + siginfo_t info; + + die_if_kernel("Integer overflow", regs); + + info.si_code = FPE_INTOVF; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void __user *) regs->cp0_epc; + force_sig_info(SIGFPE, &info, current); +} + +static void do_trap_or_bp(struct pt_regs *regs, unsigned int code, const char *str) +{ + siginfo_t info; + char b[40]; + + if (notify_die(DIE_TRAP, str, regs, code, 0, 0) == NOTIFY_STOP) + return; + + /* + * A short test says that IRIX 5.3 sends SIGTRAP for all trap + * insns, even for trap and break codes that indicate arithmetic + * failures. Weird ... + * But should we continue the brokenness??? --macro + */ + switch (code) { + case BRK_OVERFLOW: + case BRK_DIVZERO: + scnprintf(b, sizeof(b), "%s instruction in kernel code", str); + die_if_kernel(b, regs); + if (code == BRK_DIVZERO) + info.si_code = FPE_INTDIV; + else + info.si_code = FPE_INTOVF; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void __user *) regs->cp0_epc; + force_sig_info(SIGFPE, &info, current); + break; + case BRK_BUG: + die_if_kernel("Kernel bug detected", regs); + force_sig(SIGTRAP, current); + break; + default: + scnprintf(b, sizeof(b), "%s instruction in kernel code", str); + die_if_kernel(b, regs); + force_sig(SIGTRAP, current); + } +} + +asmlinkage void do_bp(struct pt_regs *regs) +{ + unsigned int opcode, bcode; + + if (__get_user(opcode, (unsigned int __user *) exception_epc(regs))) + goto out_sigsegv; + + /* + * There is the ancient bug in the MIPS assemblers that the break + * code starts left to bit 16 instead to bit 6 in the opcode. + * Gas is bug-compatible, but not always, grrr... + * We handle both cases with a simple heuristics. --macro + */ + bcode = ((opcode >> 6) & ((1 << 20) - 1)); + if (bcode >= (1 << 10)) + bcode >>= 10; + + do_trap_or_bp(regs, bcode, "Break"); +#if defined(CONFIG_RTL_WTDOG) + if (bcode == 7){ // divided by zero + die("Oops", regs); + { extern int is_fault; is_fault=1;} // set kernel fault flag + } +#endif + return; + +out_sigsegv: + force_sig(SIGSEGV, current); +} + +asmlinkage void do_ri(struct pt_regs *regs) +{ + unsigned int __user *epc = (unsigned int __user *)exception_epc(regs); + unsigned long old_epc = regs->cp0_epc; + unsigned int opcode = 0; + int status = -1; + + if (notify_die(DIE_RI, "RI Fault", regs, SIGSEGV, 0, 0) + == NOTIFY_STOP) + return; + + die_if_kernel("Reserved instruction in kernel code", regs); + + if (unlikely(compute_return_epc(regs) < 0)) + return; + + if (unlikely(get_user(opcode, epc) < 0)) + status = SIGSEGV; + +#ifndef CONFIG_CPU_HAS_LLSC + if (status < 0) + status = simulate_llsc(regs, opcode); +#endif + +#if 0 + if (status < 0) + status = simulate_rdhwr(regs, opcode); +#endif + +#ifndef CONFIG_CPU_HAS_SYNC + if (status < 0) + status = simulate_sync(regs, opcode); +#endif + + if (status < 0) + status = SIGILL; + + if (unlikely(status > 0)) { + regs->cp0_epc = old_epc; /* Undo skip-over. */ + force_sig(status, current); + } +} + +asmlinkage void do_cpu(struct pt_regs *regs) +{ + unsigned int __user *epc; + unsigned long old_epc; + unsigned int opcode; + unsigned int cpid; + int status; + unsigned long __maybe_unused flags; + + die_if_kernel("do_cpu invoked from kernel context!", regs); + + cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; + if (cpid == 0) + { + epc = (unsigned int __user *)exception_epc(regs); + old_epc = regs->cp0_epc; + opcode = 0; + status = -1; + + if (unlikely(compute_return_epc(regs) < 0)) + return; + + if (unlikely(get_user(opcode, epc) < 0)) + status = SIGSEGV; + +#ifndef CONFIG_CPU_HAS_LLSC + if (status < 0) + status = simulate_llsc(regs, opcode); +#endif + +#if 0 + if (status < 0) + status = simulate_rdhwr(regs, opcode); +#endif + + if (status < 0) + status = SIGILL; + + if (unlikely(status > 0)) { + regs->cp0_epc = old_epc; /* Undo skip-over. */ + force_sig(status, current); + } + + return; + } + + 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. + */ + show_regs(regs); + panic("Caught reserved exception %ld - should not happen.", + (regs->cp0_cause & 0x7f) >> 2); +} + +unsigned long ebase; +unsigned long exception_handlers[32]; + +/* + * As a side effect of the way this is implemented we're limited + * to interrupt handlers in the address range from + * KSEG0 <= x < KSEG0 + 256mb on the Nevada. Oh well ... + */ +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; +} + +extern void cpu_cache_init(void); +extern void tlb_init(void); +extern void flush_tlb_handlers(void); + +void __cpuinit per_cpu_trap_init(void) +{ + unsigned int cpu = smp_processor_id(); + unsigned int status_set = ST0_CU0; + + /* clear CU* and BEV, enable CU0 */ + change_c0_status(ST0_CU|ST0_BEV, status_set); + + cpu_data[cpu].asid_cache = ASID_FIRST_VERSION; + TLBMISS_HANDLER_SETUP(); + + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + BUG_ON(current->mm); + enter_lazy_tlb(&init_mm, current); + + cpu_cache_init(); + tlb_init(); +} + +#define RLX_TRAP_VEC_BASE 0x80000080 +#define RLX_TRAP_VEC_SIZE 0x80 + +void __init trap_init(void) +{ + extern char rlx_trap_dispatch; + unsigned long i; + +#if defined(CONFIG_KGDB) + if (kgdb_early_setup) + return; /* Already done */ +#endif + + ebase = CAC_BASE; + + per_cpu_trap_init(); + + /* + * Setup default vectors + */ + for (i = 0; i <= 31; i++) + set_except_vector(i, handle_reserved); + + /* + * Initialise interrupt handlers + */ + set_except_vector(0, rlx_irq_dispatch); + set_except_vector(1, handle_tlbm); + set_except_vector(2, handle_tlbl); + set_except_vector(3, handle_tlbs); + + set_except_vector(4, handle_adel); + set_except_vector(5, handle_ades); + + set_except_vector(8, handle_sys); + set_except_vector(9, handle_bp); + set_except_vector(10, handle_ri); + set_except_vector(11, handle_cpu); + set_except_vector(12, handle_ov); + + memcpy ((void *) (RLX_TRAP_VEC_BASE), &rlx_trap_dispatch, RLX_TRAP_VEC_SIZE); + + flush_icache_range (RLX_TRAP_VEC_BASE, RLX_TRAP_VEC_BASE+RLX_TRAP_VEC_SIZE); + flush_tlb_handlers (); +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/unaligned.c b/target/linux/realtek/files/arch/rlx/kernel/unaligned.c new file mode 100644 index 000000000..facf59195 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/unaligned.c @@ -0,0 +1,449 @@ +/* + * Handle unaligned accesses by emulation. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * This file contains exception handler for address error exception with the + * special capability to execute faulting instructions in software. The + * handler does not try to handle the case when the program counter points + * to an address not aligned to a word boundary. + * + * Putting data to unaligned addresses is a bad practice even on Intel where + * only the performance is affected. Much worse is that such code is non- + * portable. Due to several programs that die on MIPS due to alignment + * problems I decided to implement this handler anyway though I originally + * didn't intend to do this at all for user code. + * + * For now I enable fixing of address errors by default to make life easier. + * I however intend to disable this somewhen in the future when the alignment + * problems with user programs have been fixed. For programmers this is the + * right way to go. + * + * Fixing address errors is a per process option. The option is inherited + * across fork(2) and execve(2) calls. If you really want to use the + * option in your user programs - I discourage the use of the software + * emulation strongly - use the following code in your userland stuff: + * + * #include <sys/sysmips.h> + * + * ... + * sysmips(MIPS_FIXADE, x); + * ... + * + * The argument x is 0 for disabling software emulation, enabled otherwise. + * + * Below a little program to play around with this feature. + * + * #include <stdio.h> + * #include <sys/sysmips.h> + * + * struct foo { + * unsigned char bar[8]; + * }; + * + * main(int argc, char *argv[]) + * { + * struct foo x = {0, 1, 2, 3, 4, 5, 6, 7}; + * unsigned int *p = (unsigned int *) (x.bar + 3); + * int i; + * + * if (argc > 1) + * sysmips(MIPS_FIXADE, atoi(argv[1])); + * + * printf("*p = %08lx\n", *p); + * + * *p = 0xdeadface; + * + * for(i = 0; i <= 7; i++) + * printf("%02x ", x.bar[i]); + * printf("\n"); + * } + * + * Coprocessor loads are not supported; I think this case is unimportant + * in the practice. + * + * TODO: Handle ndc (attempted store to doubleword in uncached memory) + * exception for the R6000. + * A store crossing a page boundary might be executed only partially. + * Undo the partial store in this case. + */ +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/signal.h> +#include <linux/smp.h> +#include <linux/sched.h> +#include <linux/debugfs.h> +#include <asm/asm.h> +#include <asm/branch.h> +#include <asm/byteorder.h> +#include <asm/inst.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +#define STR(x) __STR(x) +#define __STR(x) #x + +enum { + UNALIGNED_ACTION_QUIET, + UNALIGNED_ACTION_SIGNAL, + UNALIGNED_ACTION_SHOW, +}; +#ifdef CONFIG_DEBUG_FS +static u32 unaligned_instructions; +static u32 unaligned_action; +#else +#define unaligned_action UNALIGNED_ACTION_QUIET +#endif +extern void show_registers(struct pt_regs *regs); + +static void emulate_load_store_insn(struct pt_regs *regs, + void __user *addr, unsigned int __user *pc) +{ + union mips_instruction insn; + unsigned long value; + unsigned int res; + + regs->regs[0] = 0; + + /* + * This load never faults. + */ + __get_user(insn.word, pc); + + switch (insn.i_format.opcode) { + /* + * These are instructions that a compiler doesn't generate. We + * can assume therefore that the code is MIPS-aware and + * really buggy. Emulating these instructions would break the + * semantics anyway. + */ + case ll_op: + case sc_op: + case lb_op: + case lbu_op: + case sb_op: + + case lwl_op: + case lwr_op: + case swl_op: + case swr_op: + goto sigbus; + + /* + * The remaining opcodes are the ones that are really of interest. + */ + case lh_op: + if (!access_ok(VERIFY_READ, addr, 2)) + goto sigbus; + + __asm__ __volatile__ (".set\tnoat\n" +#ifdef __BIG_ENDIAN + "1:\tlb\t%0, 0(%2)\n" + "2:\tlbu\t$1, 1(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tlb\t%0, 1(%2)\n" + "2:\tlbu\t$1, 0(%2)\n\t" +#endif + "sll\t%0, 0x8\n\t" + "or\t%0, $1\n\t" + "li\t%1, 0\n" + "3:\t.set\tat\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + compute_return_epc(regs); + regs->regs[insn.i_format.rt] = value; + break; + + case lw_op: + if (!access_ok(VERIFY_READ, addr, 4)) + goto sigbus; + + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + #ifdef CONFIG_CPU_HAS_ULS + "1:\tlwl\t%0, (%2)\n" + "2:\tlwr\t%0, 3(%2)\n\t" + #else + "1: lb %0, 0(%2)\n" + " lb %1, 1(%2)\n" + " sll %0, 8\n" + " or %0, %1\n" + " lb %1, 2(%2)\n" + " sll %0, 8\n" + " or %0, %1\n" + " lb %1, 3(%2)\n" + " sll %0, 8\n" + " or %0, %1\n" + #endif +#endif +#ifdef __LITTLE_ENDIAN + #ifdef CONFIG_CPU_HAS_ULS + "1:\tlwl\t%0, 3(%2)\n" + "2:\tlwr\t%0, (%2)\n\t" + #else + "1: lb %0, 3(%2)\n" + " lb %1, 2(%2)\n" + " sll %0, 8\n" + " or %0, %1\n" + " lb %1, 1(%2)\n" + " sll %0, 8\n" + " or %0, %1\n" + " lb %1, 0(%2)\n" + " sll %0, 8\n" + " or %0, %1\n" + #endif +#endif + "li\t%1, 0\n" + "3:\t.section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + compute_return_epc(regs); + regs->regs[insn.i_format.rt] = value; + break; + + case lhu_op: + if (!access_ok(VERIFY_READ, addr, 2)) + goto sigbus; + + __asm__ __volatile__ ( + ".set\tnoat\n" +#ifdef __BIG_ENDIAN + "1:\tlbu\t%0, 0(%2)\n" + "2:\tlbu\t$1, 1(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tlbu\t%0, 1(%2)\n" + "2:\tlbu\t$1, 0(%2)\n\t" +#endif + "sll\t%0, 0x8\n\t" + "or\t%0, $1\n\t" + "li\t%1, 0\n" + "3:\t.set\tat\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + compute_return_epc(regs); + regs->regs[insn.i_format.rt] = value; + break; + + case sh_op: + if (!access_ok(VERIFY_WRITE, addr, 2)) + goto sigbus; + + value = regs->regs[insn.i_format.rt]; + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + ".set\tnoat\n" + "1:\tsb\t%1, 1(%2)\n\t" + "srl\t$1, %1, 0x8\n" + "2:\tsb\t$1, 0(%2)\n\t" + ".set\tat\n\t" +#endif +#ifdef __LITTLE_ENDIAN + ".set\tnoat\n" + "1:\tsb\t%1, 0(%2)\n\t" + "srl\t$1,%1, 0x8\n" + "2:\tsb\t$1, 1(%2)\n\t" + ".set\tat\n\t" +#endif + "li\t%0, 0\n" + "3:\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%0, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=r" (res) + : "r" (value), "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + compute_return_epc(regs); + break; + + case sw_op: + if (!access_ok(VERIFY_WRITE, addr, 4)) + goto sigbus; + + value = regs->regs[insn.i_format.rt]; + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + #ifdef CONFIG_CPU_HAS_ULS + "1:\tswl\t%1,(%2)\n" + "2:\tswr\t%1, 3(%2)\n\t" + #else + "1: or %0, %1, $0\n" + " sb %0, 3(%2)\n" + " srl %0, 8\n" + " sb %0, 2(%2)\n" + " srl %0, 8\n" + " sb %0, 1(%2)\n" + " srl %0, 8\n" + " sb %0, 0(%2)\n" + #endif +#endif +#ifdef __LITTLE_ENDIAN + #ifdef CONFIG_CPU_HAS_ULS + "1:\tswl\t%1, 3(%2)\n" + "2:\tswr\t%1, (%2)\n\t" + #else + "1: or %0, %1, $0\n" + " sb %0, 0(%2)\n" + " srl %0, 8\n" + " sb %0, 1(%2)\n" + " srl %0, 8\n" + " sb %0, 2(%2)\n" + " srl %0, 8\n" + " sb %0, 3(%2)\n" + #endif +#endif + "li\t%0, 0\n" + "3:\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%0, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=r" (res) + : "r" (value), "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + compute_return_epc(regs); + break; + + case lwu_op: + default: + /* + * Pheeee... We encountered an yet unknown instruction or + * cache coherence problem. Die sucker, die ... + */ + goto sigill; + } + +#ifdef CONFIG_DEBUG_FS + unaligned_instructions++; +#endif + + return; + +fault: + /* Did we have an exception handler installed? */ + if (fixup_exception(regs)) + return; + + die_if_kernel("Unhandled kernel unaligned access", regs); + force_sig(SIGSEGV, current); + + return; + +sigbus: + die_if_kernel("Unhandled kernel unaligned access", regs); + force_sig(SIGBUS, current); + + return; + +sigill: + die_if_kernel("Unhandled kernel unaligned access or invalid instruction", regs); + force_sig(SIGILL, current); +} + +asmlinkage void do_ade(struct pt_regs *regs) +{ + unsigned int __user *pc; + mm_segment_t seg; + + /* + * Did we catch a fault trying to load an instruction? + * Or are we running in MIPS16 mode? + */ + if ((regs->cp0_badvaddr == regs->cp0_epc) || (regs->cp0_epc & 0x1)) + goto sigbus; + + pc = (unsigned int __user *) exception_epc(regs); + if (user_mode(regs) && !test_thread_flag(TIF_FIXADE)) + goto sigbus; + if (unaligned_action == UNALIGNED_ACTION_SIGNAL) + goto sigbus; + else if (unaligned_action == UNALIGNED_ACTION_SHOW) + show_registers(regs); + + /* + * Do branch emulation only if we didn't forward the exception. + * This is all so but ugly ... + */ + seg = get_fs(); + if (!user_mode(regs)) + set_fs(KERNEL_DS); + emulate_load_store_insn(regs, (void __user *)regs->cp0_badvaddr, pc); + set_fs(seg); + + return; + +sigbus: + die_if_kernel("Kernel unaligned instruction access", regs); + force_sig(SIGBUS, current); + + /* + * XXX On return from the signal handler we should advance the epc + */ +} + +#ifdef CONFIG_DEBUG_FS +extern struct dentry *mips_debugfs_dir; +static int __init debugfs_unaligned(void) +{ + struct dentry *d; + + if (!mips_debugfs_dir) + return -ENODEV; + d = debugfs_create_u32("unaligned_instructions", S_IRUGO, + mips_debugfs_dir, &unaligned_instructions); + if (!d) + return -ENOMEM; + d = debugfs_create_u32("unaligned_action", S_IRUGO | S_IWUSR, + mips_debugfs_dir, &unaligned_action); + if (!d) + return -ENOMEM; + return 0; +} +__initcall(debugfs_unaligned); +#endif diff --git a/target/linux/realtek/files/arch/rlx/kernel/vmlinux.lds.S b/target/linux/realtek/files/arch/rlx/kernel/vmlinux.lds.S new file mode 100644 index 000000000..0cc10b577 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/vmlinux.lds.S @@ -0,0 +1,253 @@ +#include <asm/asm-offsets.h> +#include <asm-generic/vmlinux.lds.h> + +#undef mips +#define mips mips +OUTPUT_ARCH(mips) +ENTRY(kernel_entry) +PHDRS { + text PT_LOAD FLAGS(7); /* RWX */ + note PT_NOTE FLAGS(4); /* R__ */ +} +jiffies = JIFFIES; + +SECTIONS +{ +#ifdef CONFIG_BOOT_ELF64 + /* Read-only sections, merged into text segment: */ + /* . = 0xc000000000000000; */ + + /* This is the value for an Origin kernel, taken from an IRIX kernel. */ + /* . = 0xc00000000001c000; */ + + /* Set the vaddr for the text segment to a value + * >= 0xa800 0000 0001 9000 if no symmon is going to configured + * >= 0xa800 0000 0030 0000 otherwise + */ + + /* . = 0xa800000000300000; */ + . = 0xffffffff80300000; +#endif + . = LOADADDR; + /* read-only */ + _text = .; /* Text and read-only data */ + .text : { + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT + KPROBES_TEXT + *(.text.*) + *(.fixup) + *(.gnu.warning) + } :text = 0 + _etext = .; /* End of text section */ + + /* Exception table */ + . = ALIGN(16); + __ex_table : { + __start___ex_table = .; + *(__ex_table) + __stop___ex_table = .; + } + + /* Exception table for data bus errors */ + __dbe_table : { + __start___dbe_table = .; + *(__dbe_table) + __stop___dbe_table = .; + } + + NOTES :text :note + .dummy : { *(.dummy) } :text + + RODATA + + . = ALIGN(16384); + __iram = . ; + .iram : + { + *(.iram-gen) /* general usage (essential) */ + *(.iram-fwd) /* general packet forwarding used */ + *(.iram-rtkwlan) /* realtek wlan driver */ + *(.iram-l2-fwd) /* L2 packet forwarding */ + *(.iram-l34-fwd) /* L34 packet forwarding */ + *(.iram-tx) /* protocol stack TX */ + *(.iram-extdev) /* ROMEDRV extension device fwd */ + *(.iram-crypto) /* authetication / crypto-engine */ + *(.iram-voip) /* voip */ + *(.iram) /* other usage */ + *(.iram.1) + } + + . = ALIGN(8192); + __dram = . ; + __dram_start = . ; + .dram : + { + *(.dram-gen) /* general usage (essential) */ + *(.dram-fwd) /* general packet forwarding used */ + *(.dram-l2-fwd) /* L2 packet forwarding */ + *(.dram-l34-fwd) /* L34 packet forwarding */ + *(.dram-extdev) /* ROMEDRV extension device fwd */ + *(.dram-wapi) /*wapi encryption/decryption used*/ + *(.dram-rtkwlan) /* realtek wlan driver */ + *(.dram-crypto) /* authetication / crypto-engine */ + *(.dram-voip) /* voip */ + *(.dram-tx) /* protocol stack TX */ + *(.dram) /* other usage */ + *(.dram.1) + } + . = ALIGN(8192); + __dram_end = . ; + + /* writeable */ + .data : { /* Data */ + . = . + DATAOFFSET; /* for CONFIG_MAPPED_KERNEL */ + /* + * This ALIGN is needed as a workaround for a bug a + * gcc bug upto 4.1 which limits the maximum alignment + * to at most 32kB and results in the following + * warning: + * + * CC arch/mips/kernel/init_task.o + * arch/mips/kernel/init_task.c:30: warning: alignment + * of ‘init_thread_union’ is greater than maximum + * object file alignment. Using 32768 + */ + . = ALIGN(_PAGE_SIZE); + *(.data.init_task) + + DATA_DATA + CONSTRUCTORS + } + _gp = . + 0x8000; + .lit8 : { + *(.lit8) + } + .lit4 : { + *(.lit4) + } + /* 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. */ + .sdata : { + *(.sdata) + } + + . = ALIGN(_PAGE_SIZE); + .data_nosave : { + __nosave_begin = .; + *(.data.nosave) + } + . = ALIGN(_PAGE_SIZE); + __nosave_end = .; + + . = ALIGN(1 << CONFIG_MIPS_L1_CACHE_SHIFT); + .data.cacheline_aligned : { + *(.data.cacheline_aligned) + } + _edata = .; /* End of data section */ + + /* will be freed after init */ + . = ALIGN(_PAGE_SIZE); /* Init code and data */ + __init_begin = .; + .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) + . = ALIGN(_PAGE_SIZE); + .init.ramfs : { + __initramfs_start = .; + *(.init.ramfs) + __initramfs_end = .; + } +#endif + PERCPU(_PAGE_SIZE) + . = ALIGN(_PAGE_SIZE); + __init_end = .; + /* freed after init ends here */ + + __bss_start = .; /* BSS */ + .sbss : { + *(.sbss) + *(.scommon) + } + .bss : { + *(.bss) + *(COMMON) + } + __bss_stop = .; + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exitcall.exit) + + /* ABI crap starts here */ + *(.MIPS.options) + *(.options) + *(.pdr) + *(.reginfo) + } + + /* These mark the ABI of the kernel for debuggers. */ + .mdebug.abi32 : { + KEEP(*(.mdebug.abi32)) + } + .mdebug.abi64 : { + KEEP(*(.mdebug.abi64)) + } + + /* This is the MIPS specific mdebug section. */ + .mdebug : { + *(.mdebug) + } + + STABS_DEBUG + DWARF_DEBUG + + /* These must appear regardless of . */ + .gptab.sdata : { + *(.gptab.data) + *(.gptab.sdata) + } + .gptab.sbss : { + *(.gptab.bss) + *(.gptab.sbss) + } +} diff --git a/target/linux/realtek/files/arch/rlx/kernel/watch.c b/target/linux/realtek/files/arch/rlx/kernel/watch.c new file mode 100644 index 000000000..c15406968 --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/watch.c @@ -0,0 +1,188 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 David Daney + */ + +#include <linux/sched.h> + +#include <asm/processor.h> +#include <asm/watch.h> + +/* + * Install the watch registers for the current thread. A maximum of + * four registers are installed although the machine may have more. + */ +void mips_install_watch_registers(void) +{ + struct mips3264_watch_reg_state *watches = + ¤t->thread.watch.mips3264; + switch (current_cpu_data.watch_reg_use_cnt) { + default: + BUG(); + case 4: + write_c0_watchlo3(watches->watchlo[3]); + /* Write 1 to the I, R, and W bits to clear them, and + 1 to G so all ASIDs are trapped. */ + write_c0_watchhi3(0x40000007 | watches->watchhi[3]); + case 3: + write_c0_watchlo2(watches->watchlo[2]); + write_c0_watchhi2(0x40000007 | watches->watchhi[2]); + case 2: + write_c0_watchlo1(watches->watchlo[1]); + write_c0_watchhi1(0x40000007 | watches->watchhi[1]); + case 1: + write_c0_watchlo0(watches->watchlo[0]); + write_c0_watchhi0(0x40000007 | watches->watchhi[0]); + } +} + +/* + * Read back the watchhi registers so the user space debugger has + * access to the I, R, and W bits. A maximum of four registers are + * read although the machine may have more. + */ +void mips_read_watch_registers(void) +{ + struct mips3264_watch_reg_state *watches = + ¤t->thread.watch.mips3264; + switch (current_cpu_data.watch_reg_use_cnt) { + default: + BUG(); + case 4: + watches->watchhi[3] = (read_c0_watchhi3() & 0x0fff); + case 3: + watches->watchhi[2] = (read_c0_watchhi2() & 0x0fff); + case 2: + watches->watchhi[1] = (read_c0_watchhi1() & 0x0fff); + case 1: + watches->watchhi[0] = (read_c0_watchhi0() & 0x0fff); + } + if (current_cpu_data.watch_reg_use_cnt == 1 && + (watches->watchhi[0] & 7) == 0) { + /* Pathological case of release 1 architecture that + * doesn't set the condition bits. We assume that + * since we got here, the watch condition was met and + * signal that the conditions requested in watchlo + * were met. */ + watches->watchhi[0] |= (watches->watchlo[0] & 7); + } + } + +/* + * Disable all watch registers. Although only four registers are + * installed, all are cleared to eliminate the possibility of endless + * looping in the watch handler. + */ +void mips_clear_watch_registers(void) +{ + switch (current_cpu_data.watch_reg_count) { + default: + BUG(); + case 8: + write_c0_watchlo7(0); + case 7: + write_c0_watchlo6(0); + case 6: + write_c0_watchlo5(0); + case 5: + write_c0_watchlo4(0); + case 4: + write_c0_watchlo3(0); + case 3: + write_c0_watchlo2(0); + case 2: + write_c0_watchlo1(0); + case 1: + write_c0_watchlo0(0); + } +} + +__cpuinit void mips_probe_watch_registers(struct cpuinfo_mips *c) +{ + unsigned int t; + + if ((c->options & MIPS_CPU_WATCH) == 0) + return; + /* + * Check which of the I,R and W bits are supported, then + * disable the register. + */ + write_c0_watchlo0(7); + t = read_c0_watchlo0(); + write_c0_watchlo0(0); + c->watch_reg_masks[0] = t & 7; + + /* Write the mask bits and read them back to determine which + * can be used. */ + c->watch_reg_count = 1; + c->watch_reg_use_cnt = 1; + t = read_c0_watchhi0(); + write_c0_watchhi0(t | 0xff8); + t = read_c0_watchhi0(); + c->watch_reg_masks[0] |= (t & 0xff8); + if ((t & 0x80000000) == 0) + return; + + write_c0_watchlo1(7); + t = read_c0_watchlo1(); + write_c0_watchlo1(0); + c->watch_reg_masks[1] = t & 7; + + c->watch_reg_count = 2; + c->watch_reg_use_cnt = 2; + t = read_c0_watchhi1(); + write_c0_watchhi1(t | 0xff8); + t = read_c0_watchhi1(); + c->watch_reg_masks[1] |= (t & 0xff8); + if ((t & 0x80000000) == 0) + return; + + write_c0_watchlo2(7); + t = read_c0_watchlo2(); + write_c0_watchlo2(0); + c->watch_reg_masks[2] = t & 7; + + c->watch_reg_count = 3; + c->watch_reg_use_cnt = 3; + t = read_c0_watchhi2(); + write_c0_watchhi2(t | 0xff8); + t = read_c0_watchhi2(); + c->watch_reg_masks[2] |= (t & 0xff8); + if ((t & 0x80000000) == 0) + return; + + write_c0_watchlo3(7); + t = read_c0_watchlo3(); + write_c0_watchlo3(0); + c->watch_reg_masks[3] = t & 7; + + c->watch_reg_count = 4; + c->watch_reg_use_cnt = 4; + t = read_c0_watchhi3(); + write_c0_watchhi3(t | 0xff8); + t = read_c0_watchhi3(); + c->watch_reg_masks[3] |= (t & 0xff8); + if ((t & 0x80000000) == 0) + return; + + /* We use at most 4, but probe and report up to 8. */ + c->watch_reg_count = 5; + t = read_c0_watchhi4(); + if ((t & 0x80000000) == 0) + return; + + c->watch_reg_count = 6; + t = read_c0_watchhi5(); + if ((t & 0x80000000) == 0) + return; + + c->watch_reg_count = 7; + t = read_c0_watchhi6(); + if ((t & 0x80000000) == 0) + return; + + c->watch_reg_count = 8; +} |