diff options
Diffstat (limited to 'arch/mips/kernel')
34 files changed, 3791 insertions, 549 deletions
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 309d54cceda..34e8a256765 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -34,8 +34,11 @@ obj-$(CONFIG_CPU_R6000) += r6000_fpu.o r4k_switch.o obj-$(CONFIG_SMP) += smp.o -obj-$(CONFIG_MIPS_MT_SMP) += smp_mt.o +obj-$(CONFIG_MIPS_MT) += mips-mt.o +obj-$(CONFIG_MIPS_MT_SMTC) += smtc.o smtc-asm.o smtc-proc.o +obj-$(CONFIG_MIPS_MT_SMP) += smp-mt.o +obj-$(CONFIG_MIPS_APSP_KSPD) += kspd.o obj-$(CONFIG_MIPS_VPE_LOADER) += vpe.o obj-$(CONFIG_MIPS_VPE_APSP_API) += rtlx.o diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c index ca6b03c773b..92b28b674d6 100644 --- a/arch/mips/kernel/asm-offsets.c +++ b/arch/mips/kernel/asm-offsets.c @@ -69,6 +69,9 @@ void output_ptreg_defines(void) offset("#define PT_BVADDR ", struct pt_regs, cp0_badvaddr); offset("#define PT_STATUS ", struct pt_regs, cp0_status); offset("#define PT_CAUSE ", struct pt_regs, cp0_cause); +#ifdef CONFIG_MIPS_MT_SMTC + offset("#define PT_TCSTATUS ", struct pt_regs, cp0_tcstatus); +#endif /* CONFIG_MIPS_MT_SMTC */ size("#define PT_SIZE ", struct pt_regs); linefeed; } diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c index 374de839558..b6232d9033c 100644 --- a/arch/mips/kernel/branch.c +++ b/arch/mips/kernel/branch.c @@ -184,7 +184,7 @@ int __compute_return_epc(struct pt_regs *regs) bit = (insn.i_format.rt >> 2); bit += (bit != 0); bit += 23; - switch (insn.i_format.rt) { + switch (insn.i_format.rt & 3) { case 0: /* bc1f */ case 2: /* bc1fl */ if (~fcr31 & (1 << bit)) diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index 83c87fe4ee4..d101d2fb24c 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -17,6 +17,9 @@ #include <asm/isadep.h> #include <asm/thread_info.h> #include <asm/war.h> +#ifdef CONFIG_MIPS_MT_SMTC +#include <asm/mipsmtregs.h> +#endif #ifdef CONFIG_PREEMPT .macro preempt_stop @@ -75,6 +78,37 @@ FEXPORT(syscall_exit) bnez t0, syscall_exit_work FEXPORT(restore_all) # restore full frame +#ifdef CONFIG_MIPS_MT_SMTC +/* Detect and execute deferred IPI "interrupts" */ + move a0,sp + jal deferred_smtc_ipi +/* Re-arm any temporarily masked interrupts not explicitly "acked" */ + mfc0 v0, CP0_TCSTATUS + ori v1, v0, TCSTATUS_IXMT + mtc0 v1, CP0_TCSTATUS + andi v0, TCSTATUS_IXMT + ehb + mfc0 t0, CP0_TCCONTEXT + DMT 9 # dmt t1 + jal mips_ihb + mfc0 t2, CP0_STATUS + andi t3, t0, 0xff00 + or t2, t2, t3 + mtc0 t2, CP0_STATUS + ehb + andi t1, t1, VPECONTROL_TE + beqz t1, 1f + EMT +1: + mfc0 v1, CP0_TCSTATUS + /* We set IXMT above, XOR should cler it here */ + xori v1, v1, TCSTATUS_IXMT + or v1, v0, v1 + mtc0 v1, CP0_TCSTATUS + ehb + xor t0, t0, t3 + mtc0 t0, CP0_TCCONTEXT +#endif /* CONFIG_MIPS_MT_SMTC */ .set noat RESTORE_TEMP RESTORE_AT @@ -120,28 +154,17 @@ syscall_exit_work: jal do_syscall_trace b resume_userspace +#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_MIPS_MT) + /* - * Common spurious interrupt handler. + * MIPS32R2 Instruction Hazard Barrier - must be called + * + * For C code use the inline version named instruction_hazard(). */ -LEAF(spurious_interrupt) - /* - * Someone tried to fool us by sending an interrupt but we - * couldn't find a cause for it. - */ - PTR_LA t1, irq_err_count -#ifdef CONFIG_SMP -1: ll t0, (t1) - addiu t0, 1 - sc t0, (t1) -#if R10000_LLSC_WAR - beqzl t0, 1b -#else - beqz t0, 1b -#endif -#else - lw t0, (t1) - addiu t0, 1 - sw t0, (t1) -#endif - j ret_from_irq - END(spurious_interrupt) +LEAF(mips_ihb) + .set mips32r2 + jr.hb ra + nop + END(mips_ihb) + +#endif /* CONFIG_CPU_MIPSR2 or CONFIG_MIPS_MT */ diff --git a/arch/mips/kernel/gdb-low.S b/arch/mips/kernel/gdb-low.S index 235ad9f6bd3..10f28fb9f00 100644 --- a/arch/mips/kernel/gdb-low.S +++ b/arch/mips/kernel/gdb-low.S @@ -283,11 +283,33 @@ */ 3: +#ifdef CONFIG_MIPS_MT_SMTC + /* Read-modify write of Status must be atomic */ + mfc0 t2, CP0_TCSTATUS + ori t1, t2, TCSTATUS_IXMT + mtc0 t1, CP0_TCSTATUS + andi t2, t2, TCSTATUS_IXMT + ehb + DMT 9 # dmt t1 + jal mips_ihb + nop +#endif /* CONFIG_MIPS_MT_SMTC */ mfc0 t0, CP0_STATUS ori t0, 0x1f xori t0, 0x1f mtc0 t0, CP0_STATUS - +#ifdef CONFIG_MIPS_MT_SMTC + andi t1, t1, VPECONTROL_TE + beqz t1, 9f + nop + EMT # emt +9: + mfc0 t1, CP0_TCSTATUS + xori t1, t1, TCSTATUS_IXMT + or t1, t1, t2 + mtc0 t1, CP0_TCSTATUS + ehb +#endif /* CONFIG_MIPS_MT_SMTC */ LONG_L v0, GDB_FR_STATUS(sp) LONG_L v1, GDB_FR_EPC(sp) mtc0 v0, CP0_STATUS diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c index d4f88e0af24..6ecbdc1fefd 100644 --- a/arch/mips/kernel/gdb-stub.c +++ b/arch/mips/kernel/gdb-stub.c @@ -140,6 +140,7 @@ #include <asm/system.h> #include <asm/gdb-stub.h> #include <asm/inst.h> +#include <asm/smp.h> /* * external low-level support routines @@ -669,6 +670,64 @@ static void kgdb_wait(void *arg) local_irq_restore(flags); } +/* + * GDB stub needs to call kgdb_wait on all processor with interrupts + * disabled, so it uses it's own special variant. + */ +static int kgdb_smp_call_kgdb_wait(void) +{ +#ifdef CONFIG_SMP + struct call_data_struct data; + int i, cpus = num_online_cpus() - 1; + int cpu = smp_processor_id(); + + /* + * Can die spectacularly if this CPU isn't yet marked online + */ + BUG_ON(!cpu_online(cpu)); + + if (!cpus) + return 0; + + if (spin_is_locked(&smp_call_lock)) { + /* + * Some other processor is trying to make us do something + * but we're not going to respond... give up + */ + return -1; + } + + /* + * We will continue here, accepting the fact that + * the kernel may deadlock if another CPU attempts + * to call smp_call_function now... + */ + + data.func = kgdb_wait; + data.info = NULL; + atomic_set(&data.started, 0); + data.wait = 0; + + spin_lock(&smp_call_lock); + call_data = &data; + mb(); + + /* Send a message to all other CPUs and wait for them to respond */ + for (i = 0; i < NR_CPUS; i++) + if (cpu_online(i) && i != cpu) + core_send_ipi(i, SMP_CALL_FUNCTION); + + /* Wait for response */ + /* FIXME: lock-up detection, backtrace on lock-up */ + while (atomic_read(&data.started) != cpus) + barrier(); + + call_data = NULL; + spin_unlock(&smp_call_lock); +#endif + + return 0; +} /* * This function does all command processing for interfacing to gdb. It @@ -718,7 +777,7 @@ void handle_exception (struct gdb_regs *regs) /* * force other cpus to enter kgdb */ - smp_call_function(kgdb_wait, NULL, 0, 0); + kgdb_smp_call_kgdb_wait(); /* * If we're in breakpoint() increment the PC diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index 13f22d1d0e8..ff7af369f28 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -12,6 +12,7 @@ #include <linux/init.h> #include <asm/asm.h> +#include <asm/asmmacro.h> #include <asm/cacheops.h> #include <asm/regdef.h> #include <asm/fpregdef.h> @@ -122,6 +123,20 @@ handle_vcei: .set pop END(except_vec3_r4000) + __FINIT + + .align 5 +NESTED(handle_int, PT_SIZE, sp) + SAVE_ALL + CLI + + PTR_LA ra, ret_from_irq + move a0, sp + j plat_irq_dispatch + END(handle_int) + + __INIT + /* * Special interrupt vector for MIPS64 ISA & embedded MIPS processors. * This is a dedicated interrupt exception vector which reduces the @@ -157,6 +172,15 @@ NESTED(except_vec_vi, 0, sp) SAVE_AT .set push .set noreorder +#ifdef CONFIG_MIPS_MT_SMTC + /* + * To keep from blindly blocking *all* interrupts + * during service by SMTC kernel, we also want to + * pass the IM value to be cleared. + */ +EXPORT(except_vec_vi_mori) + ori a0, $0, 0 +#endif /* CONFIG_MIPS_MT_SMTC */ EXPORT(except_vec_vi_lui) lui v0, 0 /* Patched */ j except_vec_vi_handler @@ -173,6 +197,25 @@ EXPORT(except_vec_vi_end) NESTED(except_vec_vi_handler, 0, sp) SAVE_TEMP SAVE_STATIC +#ifdef CONFIG_MIPS_MT_SMTC + /* + * SMTC has an interesting problem that interrupts are level-triggered, + * and the CLI macro will clear EXL, potentially causing a duplicate + * interrupt service invocation. So we need to clear the associated + * IM bit of Status prior to doing CLI, and restore it after the + * service routine has been invoked - we must assume that the + * service routine will have cleared the state, and any active + * level represents a new or otherwised unserviced event... + */ + mfc0 t1, CP0_STATUS + and t0, a0, t1 + mfc0 t2, CP0_TCCONTEXT + or t0, t0, t2 + mtc0 t0, CP0_TCCONTEXT + xor t1, t1, t0 + mtc0 t1, CP0_STATUS + ehb +#endif /* CONFIG_MIPS_MT_SMTC */ CLI move a0, sp jalr v0 diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S index 2e9122a4213..bdf6f6eff72 100644 --- a/arch/mips/kernel/head.S +++ b/arch/mips/kernel/head.S @@ -18,6 +18,7 @@ #include <linux/threads.h> #include <asm/asm.h> +#include <asm/asmmacro.h> #include <asm/regdef.h> #include <asm/page.h> #include <asm/mipsregs.h> @@ -82,12 +83,33 @@ */ .macro setup_c0_status set clr .set push +#ifdef CONFIG_MIPS_MT_SMTC + /* + * For SMTC, we need to set privilege and disable interrupts only for + * the current TC, using the TCStatus register. + */ + mfc0 t0, CP0_TCSTATUS + /* Fortunately CU 0 is in the same place in both registers */ + /* Set TCU0, TMX, TKSU (for later inversion) and IXMT */ + li t1, ST0_CU0 | 0x08001c00 + or t0, t1 + /* Clear TKSU, leave IXMT */ + xori t0, 0x00001800 + mtc0 t0, CP0_TCSTATUS + ehb + /* We need to leave the global IE bit set, but clear EXL...*/ + mfc0 t0, CP0_STATUS + or t0, ST0_CU0 | ST0_EXL | ST0_ERL | \set | \clr + xor t0, ST0_EXL | ST0_ERL | \clr + mtc0 t0, CP0_STATUS +#else 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 +#endif .set pop .endm @@ -134,6 +156,24 @@ NESTED(kernel_entry, 16, sp) # kernel entry point ARC64_TWIDDLE_PC +#ifdef CONFIG_MIPS_MT_SMTC + /* + * In SMTC kernel, "CLI" is thread-specific, in TCStatus. + * We still need to enable interrupts globally in Status, + * and clear EXL/ERL. + * + * TCContext is used to track interrupt levels under + * service in SMTC kernel. Clear for boot TC before + * allowing any interrupts. + */ + mtc0 zero, CP0_TCCONTEXT + + mfc0 t0, CP0_STATUS + ori t0, t0, 0xff1f + xori t0, t0, 0x001e + mtc0 t0, CP0_STATUS +#endif /* CONFIG_MIPS_MT_SMTC */ + PTR_LA t0, __bss_start # clear .bss LONG_S zero, (t0) PTR_LA t1, __bss_stop - LONGSIZE @@ -166,8 +206,25 @@ NESTED(kernel_entry, 16, sp) # kernel entry point * function after setting up the stack and gp registers. */ NESTED(smp_bootstrap, 16, sp) +#ifdef CONFIG_MIPS_MT_SMTC + /* + * Read-modify-writes of Status must be atomic, and this + * is one case where CLI is invoked without EXL being + * necessarily set. The CLI and setup_c0_status will + * in fact be redundant for all but the first TC of + * each VPE being booted. + */ + DMT 10 # dmt t2 /* t0, t1 are used by CLI and setup_c0_status() */ + jal mips_ihb +#endif /* CONFIG_MIPS_MT_SMTC */ setup_c0_status_sec smp_slave_setup +#ifdef CONFIG_MIPS_MT_SMTC + andi t2, t2, VPECONTROL_TE + beqz t2, 2f + EMT # emt +2: +#endif /* CONFIG_MIPS_MT_SMTC */ j start_secondary END(smp_bootstrap) #endif /* CONFIG_SMP */ diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c index b974ac9057f..2125ba5f1d9 100644 --- a/arch/mips/kernel/i8259.c +++ b/arch/mips/kernel/i8259.c @@ -187,6 +187,10 @@ handle_real_irq: outb(cached_21,0x21); outb(0x60+irq,0x20); /* 'Specific EOI' to master */ } +#ifdef CONFIG_MIPS_MT_SMTC + if (irq_hwmask[irq] & ST0_IM) + set_c0_status(irq_hwmask[irq] & ST0_IM); +#endif /* CONFIG_MIPS_MT_SMTC */ spin_unlock_irqrestore(&i8259A_lock, flags); return; diff --git a/arch/mips/kernel/irq-msc01.c b/arch/mips/kernel/irq-msc01.c index 3f653c7cfbf..97ebdc754b9 100644 --- a/arch/mips/kernel/irq-msc01.c +++ b/arch/mips/kernel/irq-msc01.c @@ -76,6 +76,11 @@ static void level_mask_and_ack_msc_irq(unsigned int irq) mask_msc_irq(irq); if (!cpu_has_veic) MSCIC_WRITE(MSC01_IC_EOI, 0); +#ifdef CONFIG_MIPS_MT_SMTC + /* This actually needs to be a call into platform code */ + if (irq_hwmask[irq] & ST0_IM) + set_c0_status(irq_hwmask[irq] & ST0_IM); +#endif /* CONFIG_MIPS_MT_SMTC */ } /* @@ -92,6 +97,10 @@ static void edge_mask_and_ack_msc_irq(unsigned int irq) MSCIC_WRITE(MSC01_IC_SUP+irq*8, r | ~MSC01_IC_SUP_EDGE_BIT); MSCIC_WRITE(MSC01_IC_SUP+irq*8, r); } +#ifdef CONFIG_MIPS_MT_SMTC + if (irq_hwmask[irq] & ST0_IM) + set_c0_status(irq_hwmask[irq] & ST0_IM); +#endif /* CONFIG_MIPS_MT_SMTC */ } /* diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 3dd76b3d296..3dce742e716 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -38,6 +38,15 @@ void ack_bad_irq(unsigned int irq) atomic_t irq_err_count; +#ifdef CONFIG_MIPS_MT_SMTC +/* + * SMTC Kernel needs to manipulate low-level CPU interrupt mask + * in do_IRQ. These are passed in setup_irq_smtc() and stored + * in this table. + */ +unsigned long irq_hwmask[NR_IRQS]; +#endif /* CONFIG_MIPS_MT_SMTC */ + #undef do_IRQ /* @@ -49,6 +58,7 @@ asmlinkage unsigned int do_IRQ(unsigned int irq, struct pt_regs *regs) { irq_enter(); + __DO_IRQ_SMTC_HOOK(); __do_IRQ(irq, regs); irq_exit(); @@ -101,6 +111,11 @@ skip: return 0; } +asmlinkage void spurious_interrupt(struct pt_regs *regs) +{ + atomic_inc(&irq_err_count); +} + #ifdef CONFIG_KGDB extern void breakpoint(void); extern void set_debug_traps(void); @@ -124,6 +139,9 @@ void __init init_IRQ(void) irq_desc[i].depth = 1; irq_desc[i].handler = &no_irq_type; spin_lock_init(&irq_desc[i].lock); +#ifdef CONFIG_MIPS_MT_SMTC + irq_hwmask[i] = 0; +#endif /* CONFIG_MIPS_MT_SMTC */ } arch_init_irq(); diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c new file mode 100644 index 00000000000..f06a144c788 --- /dev/null +++ b/arch/mips/kernel/kspd.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/unistd.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/syscalls.h> +#include <linux/workqueue.h> +#include <linux/errno.h> +#include <linux/list.h> + +#include <asm/vpe.h> +#include <asm/rtlx.h> +#include <asm/kspd.h> + +static struct workqueue_struct *workqueue = NULL; +static struct work_struct work; + +extern unsigned long cpu_khz; + +struct mtsp_syscall { + int cmd; + unsigned char abi; + unsigned char size; +}; + +struct mtsp_syscall_ret { + int retval; + int errno; +}; + +struct mtsp_syscall_generic { + int arg0; + int arg1; + int arg2; + int arg3; + int arg4; + int arg5; + int arg6; +}; + +static struct list_head kspd_notifylist; +static int sp_stopping = 0; + +/* these should match with those in the SDE kit */ +#define MTSP_SYSCALL_BASE 0 +#define MTSP_SYSCALL_EXIT (MTSP_SYSCALL_BASE + 0) +#define MTSP_SYSCALL_OPEN (MTSP_SYSCALL_BASE + 1) +#define MTSP_SYSCALL_READ (MTSP_SYSCALL_BASE + 2) +#define MTSP_SYSCALL_WRITE (MTSP_SYSCALL_BASE + 3) +#define MTSP_SYSCALL_CLOSE (MTSP_SYSCALL_BASE + 4) +#define MTSP_SYSCALL_LSEEK32 (MTSP_SYSCALL_BASE + 5) +#define MTSP_SYSCALL_ISATTY (MTSP_SYSCALL_BASE + 6) +#define MTSP_SYSCALL_GETTIME (MTSP_SYSCALL_BASE + 7) +#define MTSP_SYSCALL_PIPEFREQ (MTSP_SYSCALL_BASE + 8) +#define MTSP_SYSCALL_GETTOD (MTSP_SYSCALL_BASE + 9) + +#define MTSP_O_RDONLY 0x0000 +#define MTSP_O_WRONLY 0x0001 +#define MTSP_O_RDWR 0x0002 +#define MTSP_O_NONBLOCK 0x0004 +#define MTSP_O_APPEND 0x0008 +#define MTSP_O_SHLOCK 0x0010 +#define MTSP_O_EXLOCK 0x0020 +#define MTSP_O_ASYNC 0x0040 +#define MTSP_O_FSYNC O_SYNC +#define MTSP_O_NOFOLLOW 0x0100 +#define MTSP_O_SYNC 0x0080 +#define MTSP_O_CREAT 0x0200 +#define MTSP_O_TRUNC 0x0400 +#define MTSP_O_EXCL 0x0800 +#define MTSP_O_BINARY 0x8000 + +#define SP_VPE 1 + +struct apsp_table { + int sp; + int ap; +}; + +/* we might want to do the mode flags too */ +struct apsp_table open_flags_table[] = { + { MTSP_O_RDWR, O_RDWR }, + { MTSP_O_WRONLY, O_WRONLY }, + { MTSP_O_CREAT, O_CREAT }, + { MTSP_O_TRUNC, O_TRUNC }, + { MTSP_O_NONBLOCK, O_NONBLOCK }, + { MTSP_O_APPEND, O_APPEND }, + { MTSP_O_NOFOLLOW, O_NOFOLLOW } +}; + +struct apsp_table syscall_command_table[] = { + { MTSP_SYSCALL_OPEN, __NR_open }, + { MTSP_SYSCALL_CLOSE, __NR_close }, + { MTSP_SYSCALL_READ, __NR_read }, + { MTSP_SYSCALL_WRITE, __NR_write }, + { MTSP_SYSCALL_LSEEK32, __NR_lseek } +}; + +static int sp_syscall(int num, int arg0, int arg1, int arg2, int arg3) +{ + register long int _num __asm__ ("$2") = num; + register long int _arg0 __asm__ ("$4") = arg0; + register long int _arg1 __asm__ ("$5") = arg1; + register long int _arg2 __asm__ ("$6") = arg2; + register long int _arg3 __asm__ ("$7") = arg3; + + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + __asm__ __volatile__ ( + " syscall \n" + : "=r" (_num), "=r" (_arg3) + : "r" (_num), "r" (_arg0), "r" (_arg1), "r" (_arg2), "r" (_arg3)); + + set_fs(old_fs); + + /* $a3 is error flag */ + if (_arg3) + return -_num; + + return _num; +} + +static int translate_syscall_command(int cmd) +{ + int i; + int ret = -1; + + for (i = 0; i < ARRAY_SIZE(syscall_command_table); i++) { + if ((cmd == syscall_command_table[i].sp)) + return syscall_command_table[i].ap; + } + + return ret; +} + +static unsigned int translate_open_flags(int flags) +{ + int i; + unsigned int ret = 0; + + for (i = 0; i < (sizeof(open_flags_table) / sizeof(struct apsp_table)); + i++) { + if( (flags & open_flags_table[i].sp) ) { + ret |= open_flags_table[i].ap; + } + } + + return ret; +} + + +static void sp_setfsuidgid( uid_t uid, gid_t gid) +{ + current->fsuid = uid; + current->fsgid = gid; + + key_fsuid_changed(current); + key_fsgid_changed(current); +} + +/* + * Expects a request to be on the sysio channel. Reads it. Decides whether + * its a linux syscall and runs it, or whatever. Puts the return code back + * into the request and sends the whole thing back. + */ +void sp_work_handle_request(void) +{ + struct mtsp_syscall sc; + struct mtsp_syscall_generic generic; + struct mtsp_syscall_ret ret; + struct kspd_notifications *n; + struct timeval tv; + struct timezone tz; + int cmd; + + char *vcwd; + mm_segment_t old_fs; + int size; + + ret.retval = -1; + + if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall), 0)) { + printk(KERN_ERR "Expected request but nothing to read\n"); + return; + } + + size = sc.size; + + if (size) { + if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size, 0)) { + printk(KERN_ERR "Expected request but nothing to read\n"); + return; + } + } + + /* Run the syscall at the priviledge of the user who loaded the + SP program */ + + if (vpe_getuid(SP_VPE)) + sp_setfsuidgid( vpe_getuid(SP_VPE), vpe_getgid(SP_VPE)); + + switch (sc.cmd) { + /* needs the flags argument translating from SDE kit to + linux */ + case MTSP_SYSCALL_PIPEFREQ: + ret.retval = cpu_khz * 1000; + ret.errno = 0; + break; + + case MTSP_SYSCALL_GETTOD: + memset(&tz, 0, sizeof(tz)); + if ((ret.retval = sp_syscall(__NR_gettimeofday, (int)&tv, + (int)&tz, 0,0)) == 0) + ret.retval = tv.tv_sec; + + ret.errno = errno; + break; + + case MTSP_SYSCALL_EXIT: + list_for_each_entry(n, &kspd_notifylist, list) + n->kspd_sp_exit(SP_VPE); + sp_stopping = 1; + + printk(KERN_DEBUG "KSPD got exit syscall from SP exitcode %d\n", + generic.arg0); + break; + + case MTSP_SYSCALL_OPEN: + generic.arg1 = translate_open_flags(generic.arg1); + + vcwd = vpe_getcwd(SP_VPE); + + /* change to the cwd of the process that loaded the SP program */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + sys_chdir(vcwd); + set_fs(old_fs); + + sc.cmd = __NR_open; + + /* fall through */ + + default: + if ((sc.cmd >= __NR_Linux) && + (sc.cmd <= (__NR_Linux + __NR_Linux_syscalls)) ) + cmd = sc.cmd; + else + cmd = translate_syscall_command(sc.cmd); + + if (cmd >= 0) { + ret.retval = sp_syscall(cmd, generic.arg0, generic.arg1, + generic.arg2, generic.arg3); + ret.errno = errno; + } else + printk(KERN_WARNING + "KSPD: Unknown SP syscall number %d\n", sc.cmd); + break; + } /* switch */ + + if (vpe_getuid(SP_VPE)) + sp_setfsuidgid( 0, 0); + + if ((rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(struct mtsp_syscall_ret), 0)) + < sizeof(struct mtsp_syscall_ret)) + printk("KSPD: sp_work_handle_request failed to send to SP\n"); +} + +static void sp_cleanup(void) +{ + struct files_struct *files = current->files; + int i, j; + struct fdtable *fdt; + + j = 0; + + /* + * It is safe to dereference the fd table without RCU or + * ->file_lock + */ + fdt = files_fdtable(files); + for (;;) { + unsigned long set; + i = j * __NFDBITS; + if (i >= fdt->max_fdset || i >= fdt->max_fds) + break; + set = fdt->open_fds->fds_bits[j++]; + while (set) { + if (set & 1) { + struct file * file = xchg(&fdt->fd[i], NULL); + if (file) + filp_close(file, files); + } + i++; + set >>= 1; + } + } +} + +static int channel_open = 0; + +/* the work handler */ +static void sp_work(void *data) +{ + if (!channel_open) { + if( rtlx_open(RTLX_CHANNEL_SYSIO, 1) != 0) { + printk("KSPD: unable to open sp channel\n"); + sp_stopping = 1; + } else { + channel_open++; + printk(KERN_DEBUG "KSPD: SP channel opened\n"); + } + } else { + /* wait for some data, allow it to sleep */ + rtlx_read_poll(RTLX_CHANNEL_SYSIO, 1); + + /* Check we haven't been woken because we are stopping */ + if (!sp_stopping) + sp_work_handle_request(); + } + + if (!sp_stopping) + queue_work(workqueue, &work); + else + sp_cleanup(); +} + +static void startwork(int vpe) +{ + sp_stopping = channel_open = 0; + + if (workqueue == NULL) { + if ((workqueue = create_singlethread_workqueue("kspd")) == NULL) { + printk(KERN_ERR "unable to start kspd\n"); + return; + } + + INIT_WORK(&work, sp_work, NULL); + queue_work(workqueue, &work); + } else + queue_work(workqueue, &work); + +} + +static void stopwork(int vpe) +{ + sp_stopping = 1; + + printk(KERN_DEBUG "KSPD: SP stopping\n"); +} + +void kspd_notify(struct kspd_notifications *notify) +{ + list_add(¬ify->list, &kspd_notifylist); +} + +static struct vpe_notifications notify; +static int kspd_module_init(void) +{ + INIT_LIST_HEAD(&kspd_notifylist); + + notify.start = startwork; + notify.stop = stopwork; + vpe_notify(SP_VPE, ¬ify); + + return 0; +} + +static void kspd_module_exit(void) +{ + +} + +module_init(kspd_module_init); +module_exit(kspd_module_exit); + +MODULE_DESCRIPTION("MIPS KSPD"); +MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc."); + |