diff options
Diffstat (limited to 'arch/mips/kernel')
40 files changed, 733 insertions, 740 deletions
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 49246264cc7c..2fd96d95a39c 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -14,14 +14,15 @@ binfmt_irix-objs := irixelf.o irixinv.o irixioctl.o irixsig.o \ obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_MODULES) += mips_ksyms.o module.o +obj-$(CONFIG_CPU_LOONGSON2) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_MIPS32) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_MIPS64) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_R3000) += r2300_fpu.o r2300_switch.o -obj-$(CONFIG_CPU_TX39XX) += r2300_fpu.o r2300_switch.o -obj-$(CONFIG_CPU_TX49XX) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_R4000) += r4k_fpu.o r4k_switch.o -obj-$(CONFIG_CPU_VR41XX) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_R4300) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_R4X00) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_R5000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R6000) += r6000_fpu.o r4k_switch.o obj-$(CONFIG_CPU_R5432) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_R8000) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_RM7000) += r4k_fpu.o r4k_switch.o @@ -29,13 +30,14 @@ obj-$(CONFIG_CPU_RM9000) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_NEVADA) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_R10000) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_CPU_SB1) += r4k_fpu.o r4k_switch.o -obj-$(CONFIG_CPU_MIPS32) += r4k_fpu.o r4k_switch.o -obj-$(CONFIG_CPU_MIPS64) += r4k_fpu.o r4k_switch.o -obj-$(CONFIG_CPU_R6000) += r6000_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_TX39XX) += r2300_fpu.o r2300_switch.o +obj-$(CONFIG_CPU_TX49XX) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_VR41XX) += r4k_fpu.o r4k_switch.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_MIPS_MT) += mips-mt.o +obj-$(CONFIG_MIPS_MT_FPAFF) += mips-mt-fpaff.o obj-$(CONFIG_MIPS_MT_SMTC) += smtc.o smtc-asm.o smtc-proc.o obj-$(CONFIG_MIPS_MT_SMP) += smp-mt.o @@ -47,8 +49,8 @@ obj-$(CONFIG_I8259) += i8259.o obj-$(CONFIG_IRQ_CPU) += irq_cpu.o obj-$(CONFIG_IRQ_CPU_RM7K) += irq-rm7000.o obj-$(CONFIG_IRQ_CPU_RM9K) += irq-rm9000.o -obj-$(CONFIG_IRQ_MV64340) += irq-mv6434x.o obj-$(CONFIG_MIPS_BOARDS_GEN) += irq-msc01.o +obj-$(CONFIG_IRQ_TXX9) += irq_txx9.o obj-$(CONFIG_32BIT) += scall32-o32.o obj-$(CONFIG_64BIT) += scall64-64.o @@ -62,9 +64,13 @@ obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_64BIT) += cpu-bugs64.o -obj-$(CONFIG_I8253) += i8253.o +obj-$(CONFIG_PCSPEAKER) += pcspeaker.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi) + +obj-$(CONFIG_HAVE_STD_PC_SERIAL_PORT) += 8250-platform.o + +EXTRA_CFLAGS += -Werror diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c index 3b27309d54b1..ca136298acdc 100644 --- a/arch/mips/kernel/asm-offsets.c +++ b/arch/mips/kernel/asm-offsets.c @@ -132,7 +132,6 @@ void output_thread_defines(void) offset("#define THREAD_ECODE ", struct task_struct, \ thread.error_code); offset("#define THREAD_TRAPNO ", struct task_struct, thread.trap_no); - offset("#define THREAD_MFLAGS ", struct task_struct, thread.mflags); offset("#define THREAD_TRAMP ", struct task_struct, \ thread.irix_trampoline); offset("#define THREAD_OLDCTX ", struct task_struct, \ @@ -233,6 +232,10 @@ void output_mm_defines(void) constant("#define _PMD_T_LOG2 ", PMD_T_LOG2); constant("#define _PTE_T_LOG2 ", PTE_T_LOG2); linefeed; + constant("#define _PGD_ORDER ", PGD_ORDER); + constant("#define _PMD_ORDER ", PMD_ORDER); + constant("#define _PTE_ORDER ", PTE_ORDER); + linefeed; constant("#define _PMD_SHIFT ", PMD_SHIFT); constant("#define _PGDIR_SHIFT ", PGDIR_SHIFT); linefeed; diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c index 76fd3f22c766..6b5df8bfab85 100644 --- a/arch/mips/kernel/branch.c +++ b/arch/mips/kernel/branch.c @@ -22,7 +22,8 @@ */ int __compute_return_epc(struct pt_regs *regs) { - unsigned int *addr, bit, fcr31, dspcontrol; + unsigned int __user *addr; + unsigned int bit, fcr31, dspcontrol; long epc; union mips_instruction insn; @@ -33,7 +34,7 @@ int __compute_return_epc(struct pt_regs *regs) /* * Read the instruction */ - addr = (unsigned int *) epc; + addr = (unsigned int __user *) epc; if (__get_user(insn.word, addr)) { force_sig(SIGSEGV, current); return -EFAULT; diff --git a/arch/mips/kernel/cpu-bugs64.c b/arch/mips/kernel/cpu-bugs64.c index c09337b947b9..6648fde20b96 100644 --- a/arch/mips/kernel/cpu-bugs64.c +++ b/arch/mips/kernel/cpu-bugs64.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004 Maciej W. Rozycki + * Copyright (C) 2003, 2004, 2007 Maciej W. Rozycki * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,7 +29,7 @@ static inline void align_mod(const int align, const int mod) ".endr\n\t" ".set pop" : - : "n" (align), "n" (mod)); + : GCC_IMM_ASM (align), GCC_IMM_ASM (mod)); } static inline void mult_sh_align_mod(long *v1, long *v2, long *w, diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index b12eeee0e974..3e004161ebd5 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -75,6 +75,27 @@ static void r4k_wait_irqoff(void) local_irq_enable(); } +/* + * The RM7000 variant has to handle erratum 38. The workaround is to not + * have any pending stores when the WAIT instruction is executed. + */ +static void rm7k_wait_irqoff(void) +{ + local_irq_disable(); + if (!need_resched()) + __asm__( + " .set push \n" + " .set mips3 \n" + " .set noat \n" + " mfc0 $1, $12 \n" + " sync \n" + " mtc0 $1, $12 # stalls until W stage \n" + " wait \n" + " mtc0 $1, $12 # stalls until W stage \n" + " .set pop \n"); + local_irq_enable(); +} + /* The Au1xxx wait is available only if using 32khz counter or * external timer source, but specifically not CP0 Counter. */ int allow_au1k_wait; @@ -132,7 +153,6 @@ static inline void check_wait(void) case CPU_R4700: case CPU_R5000: case CPU_NEVADA: - case CPU_RM7000: case CPU_4KC: case CPU_4KEC: case CPU_4KSC: @@ -142,6 +162,10 @@ static inline void check_wait(void) cpu_wait = r4k_wait; break; + case CPU_RM7000: + cpu_wait = rm7k_wait_irqoff; + break; + case CPU_24K: case CPU_34K: cpu_wait = r4k_wait; @@ -175,7 +199,14 @@ static inline void check_wait(void) if ((c->processor_id & 0xff) <= 0x64) break; - cpu_wait = r4k_wait; + /* + * Another rev is incremeting c0_count at a reduced clock + * rate while in WAIT mode. So we basically have the choice + * between using the cp0 timer as clocksource or avoiding + * the WAIT instruction. Until more details are known, + * disable the use of WAIT for 20Kc entirely. + cpu_wait = r4k_wait; + */ break; case CPU_RM9000: if ((c->processor_id & 0x00ff) >= 0x40) @@ -186,9 +217,29 @@ static inline void check_wait(void) } } +static inline void check_errata(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + + switch (c->cputype) { + case CPU_34K: + /* + * Erratum "RPS May Cause Incorrect Instruction Execution" + * This code only handles VPE0, any SMP/SMTC/RTOS code + * making use of VPE1 will be responsable for that VPE. + */ + if ((c->processor_id & PRID_REV_MASK) <= PRID_REV_34K_V1_0_2) + write_c0_config7(read_c0_config7() | MIPS_CONF7_RPS); + break; + default: + break; + } +} + void __init check_bugs32(void) { check_wait(); + check_errata(); } /* @@ -485,6 +536,14 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c) MIPS_CPU_LLSC; c->tlbsize = 64; break; + case PRID_IMP_LOONGSON2: + c->cputype = CPU_LOONGSON2; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | + MIPS_CPU_FPU | MIPS_CPU_LLSC | + MIPS_CPU_32FPR; + c->tlbsize = 64; + break; } } @@ -588,6 +647,8 @@ static inline unsigned int decode_config3(struct cpuinfo_mips *c) c->options |= MIPS_CPU_VEIC; if (config3 & MIPS_CONF3_MT) c->ases |= MIPS_ASE_MIPSMT; + if (config3 & MIPS_CONF3_ULRI) + c->options |= MIPS_CPU_ULRI; return config3 & MIPS_CONF_M; } diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index 686249c5c328..e29598ae939d 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -84,6 +84,7 @@ FEXPORT(restore_all) # restore full frame LONG_S sp, TI_REGS($28) jal deferred_smtc_ipi LONG_S s0, TI_REGS($28) +#ifdef CONFIG_MIPS_MT_SMTC_IM_BACKSTOP /* Re-arm any temporarily masked interrupts not explicitly "acked" */ mfc0 v0, CP0_TCSTATUS ori v1, v0, TCSTATUS_IXMT @@ -110,6 +111,7 @@ FEXPORT(restore_all) # restore full frame _ehb xor t0, t0, t3 mtc0 t0, CP0_TCCONTEXT +#endif /* CONFIG_MIPS_MT_SMTC_IM_BACKSTOP */ #endif /* CONFIG_MIPS_MT_SMTC */ .set noat RESTORE_TEMP diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c index 7bc882049269..cb5623aad552 100644 --- a/arch/mips/kernel/gdb-stub.c +++ b/arch/mips/kernel/gdb-stub.c @@ -1099,12 +1099,12 @@ void adel(void) * malloc is needed by gdb client in "call func()", even a private one * will make gdb happy */ -static void * __attribute_used__ malloc(size_t size) +static void __used *malloc(size_t size) { return kmalloc(size, GFP_ATOMIC); } -static void __attribute_used__ free (void *where) +static void __used free(void *where) { kfree(where); } diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index 297bd56c2347..c0f19d638b98 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -243,9 +243,11 @@ NESTED(except_vec_vi_handler, 0, sp) */ mfc0 t1, CP0_STATUS and t0, a0, t1 +#ifdef CONFIG_MIPS_MT_SMTC_IM_BACKSTOP mfc0 t2, CP0_TCCONTEXT or t0, t0, t2 mtc0 t0, CP0_TCCONTEXT +#endif /* CONFIG_MIPS_MT_SMTC_IM_BACKSTOP */ xor t1, t1, t0 mtc0 t1, CP0_STATUS _ehb diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S index 6f57ca44291f..e46782b0ebc8 100644 --- a/arch/mips/kernel/head.S +++ b/arch/mips/kernel/head.S @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/threads.h> +#include <asm/addrspace.h> #include <asm/asm.h> #include <asm/asmmacro.h> #include <asm/irqflags.h> @@ -129,24 +130,24 @@ #endif .endm +#ifndef CONFIG_NO_EXCEPT_FILL /* * Reserved space for exception handlers. * Necessary for machines which link their kernels at KSEG0. */ .fill 0x400 +#endif -EXPORT(stext) # used for profiling EXPORT(_stext) -#ifdef CONFIG_MIPS_SIM +#ifndef 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. */ - j kernel_entry -#endif __INIT +#endif NESTED(kernel_entry, 16, sp) # kernel entry point @@ -197,9 +198,7 @@ NESTED(kernel_entry, 16, sp) # kernel entry point j start_kernel END(kernel_entry) -#ifdef CONFIG_QEMU __INIT -#endif #ifdef CONFIG_SMP /* diff --git a/arch/mips/kernel/i8253.c b/arch/mips/kernel/i8253.c deleted file mode 100644 index 475df6904219..000000000000 --- a/arch/mips/kernel/i8253.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2006 IBM Corporation - * - * Implements device information for i8253 timer chip - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation - */ - -#include <linux/platform_device.h> - -static __init int add_pcspkr(void) -{ - struct platform_device *pd; - int ret; - - pd = platform_device_alloc("pcspkr", -1); - if (!pd) - return -ENOMEM; - - ret = platform_device_add(pd); - if (ret) - platform_device_put(pd); - - return ret; -} -device_initcall(add_pcspkr); diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c index 2345160e63fc..3a2d255361bc 100644 --- a/arch/mips/kernel/i8259.c +++ b/arch/mips/kernel/i8259.c @@ -36,6 +36,7 @@ void mask_and_ack_8259A(unsigned int); static struct irq_chip i8259A_chip = { .name = "XT-PIC", .mask = disable_8259A_irq, + .disable = disable_8259A_irq, .unmask = enable_8259A_irq, .mask_ack = mask_and_ack_8259A, }; @@ -176,10 +177,7 @@ handle_real_irq: outb(cached_master_mask, PIC_MASTER_IMR); outb(0x60+irq,PIC_MASTER_CMD); /* '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 */ + smtc_im_ack_irq(irq); spin_unlock_irqrestore(&i8259A_lock, flags); return; diff --git a/arch/mips/kernel/irixsig.c b/arch/mips/kernel/irixsig.c index 6980deb6dced..28b2a8f00911 100644 --- a/arch/mips/kernel/irixsig.c +++ b/arch/mips/kernel/irixsig.c @@ -725,7 +725,7 @@ asmlinkage int irix_getcontext(struct pt_regs *regs) current->comm, current->pid, ctx); #endif - if (!access_ok(VERIFY_WRITE, ctx, sizeof(*ctx))); + if (!access_ok(VERIFY_WRITE, ctx, sizeof(*ctx))) return -EFAULT; error = __put_user(current->thread.irix_oldctx, &ctx->link); diff --git a/arch/mips/kernel/irq-msc01.c b/arch/mips/kernel/irq-msc01.c index 410868b5ea5f..1ecdd50bfc60 100644 --- a/arch/mips/kernel/irq-msc01.c +++ b/arch/mips/kernel/irq-msc01.c @@ -52,11 +52,8 @@ 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 */ + smtc_im_ack_irq(irq); } /* @@ -73,10 +70,7 @@ 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 */ + smtc_im_ack_irq(irq); } /* diff --git a/arch/mips/kernel/irq-mv6434x.c b/arch/mips/kernel/irq-mv6434x.c deleted file mode 100644 index 3dd561832e4c..000000000000 --- a/arch/mips/kernel/irq-mv6434x.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2002 Momentum Computer - * Author: mdharm@momenco.com - * Copyright (C) 2004, 06 Ralf Baechle <ralf@linux-mips.org> - * - * 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/module.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/kernel_stat.h> -#include <linux/mv643xx.h> -#include <linux/sched.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/marvell.h> - -static unsigned int irq_base; - -static inline int ls1bit32(unsigned int x) -{ - int b = 31, s; - - s = 16; if (x << 16 == 0) s = 0; b -= s; x <<= s; - s = 8; if (x << 8 == 0) s = 0; b -= s; x <<= s; - s = 4; if (x << 4 == 0) s = 0; b -= s; x <<= s; - s = 2; if (x << 2 == 0) s = 0; b -= s; x <<= s; - s = 1; if (x << 1 == 0) s = 0; b -= s; - - return b; -} - -/* mask off an interrupt -- 1 is enable, 0 is disable */ -static inline void mask_mv64340_irq(unsigned int irq) -{ - uint32_t value; - - if (irq < (irq_base + 32)) { - value = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); - value &= ~(1 << (irq - irq_base)); - MV_WRITE(MV64340_INTERRUPT0_MASK_0_LOW, value); - } else { - value = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); - value &= ~(1 << (irq - irq_base - 32)); - MV_WRITE(MV64340_INTERRUPT0_MASK_0_HIGH, value); - } -} - -/* unmask an interrupt -- 1 is enable, 0 is disable */ -static inline void unmask_mv64340_irq(unsigned int irq) -{ - uint32_t value; - - if (irq < (irq_base + 32)) { - value = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); - value |= 1 << (irq - irq_base); - MV_WRITE(MV64340_INTERRUPT0_MASK_0_LOW, value); - } else { - value = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); - value |= 1 << (irq - irq_base - 32); - MV_WRITE(MV64340_INTERRUPT0_MASK_0_HIGH, value); - } -} - -/* - * Interrupt handler for interrupts coming from the Marvell chip. - * It could be built in ethernet ports etc... - */ -void ll_mv64340_irq(void) -{ - unsigned int irq_src_low, irq_src_high; - unsigned int irq_mask_low, irq_mask_high; - - /* read the interrupt status registers */ - irq_mask_low = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); - irq_mask_high = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); - irq_src_low = MV_READ(MV64340_MAIN_INTERRUPT_CAUSE_LOW); - irq_src_high = MV_READ(MV64340_MAIN_INTERRUPT_CAUSE_HIGH); - - /* mask for just the interrupts we want */ - irq_src_low &= irq_mask_low; - irq_src_high &= irq_mask_high; - - if (irq_src_low) - do_IRQ(ls1bit32(irq_src_low) + irq_base); - else - do_IRQ(ls1bit32(irq_src_high) + irq_base + 32); -} - -struct irq_chip mv64340_irq_type = { - .name = "MV-64340", - .ack = mask_mv64340_irq, - .mask = mask_mv64340_irq, - .mask_ack = mask_mv64340_irq, - .unmask = unmask_mv64340_irq, -}; - -void __init mv64340_irq_init(unsigned int base) -{ - int i; - - for (i = base; i < base + 64; i++) - set_irq_chip_and_handler(i, &mv64340_irq_type, - handle_level_irq); - - irq_base = base; -} diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index aeded6c17de5..a990aad2f049 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -74,20 +74,12 @@ EXPORT_SYMBOL_GPL(free_irqno); */ void ack_bad_irq(unsigned int irq) { + smtc_im_ack_irq(irq); printk("unexpected IRQ # %d\n", 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 */ - /* * Generic, controller-independent functions: */ diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c index c6580018c94b..cb9a14a1ca5b 100644 --- a/arch/mips/kernel/kspd.c +++ b/arch/mips/kernel/kspd.c @@ -89,7 +89,7 @@ static int sp_stopping = 0; #define MTSP_O_EXCL 0x0800 #define MTSP_O_BINARY 0x8000 -#define SP_VPE 1 +extern int tclimit; struct apsp_table { int sp; @@ -225,8 +225,8 @@ void sp_work_handle_request(void) /* 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)); + if (vpe_getuid(tclimit)) + sp_setfsuidgid(vpe_getuid(tclimit), vpe_getgid(tclimit)); switch (sc.cmd) { /* needs the flags argument translating from SDE kit to @@ -245,7 +245,7 @@ void sp_work_handle_request(void) case MTSP_SYSCALL_EXIT: list_for_each_entry(n, &kspd_notifylist, list) - n->kspd_sp_exit(SP_VPE); + n->kspd_sp_exit(tclimit); sp_stopping = 1; printk(KERN_DEBUG "KSPD got exit syscall from SP exitcode %d\n", @@ -255,7 +255,7 @@ void sp_work_handle_request(void) case MTSP_SYSCALL_OPEN: generic.arg1 = translate_open_flags(generic.arg1); - vcwd = vpe_getcwd(SP_VPE); + vcwd = vpe_getcwd(tclimit); /* change to the cwd of the process that loaded the SP program */ old_fs = get_fs(); @@ -283,7 +283,7 @@ void sp_work_handle_request(void) break; } /* switch */ - if (vpe_getuid(SP_VPE)) + if (vpe_getuid(tclimit)) sp_setfsuidgid( 0, 0); old_fs = get_fs(); @@ -364,10 +364,9 @@ static void startwork(int vpe) } INIT_WORK(&work, sp_work); - queue_work(workqueue, &work); - } else - queue_work(workqueue, &work); + } + queue_work(workqueue, &work); } static void stopwork(int vpe) @@ -389,7 +388,7 @@ static int kspd_module_init(void) notify.start = startwork; notify.stop = stopwork; - vpe_notify(SP_VPE, ¬ify); + vpe_notify(tclimit, ¬ify); return 0; } diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 06e04da211d5..135d9a5fe337 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -566,8 +566,15 @@ asmlinkage long sys32_fadvise64_64(int fd, int __pad, flags); } +asmlinkage long sys32_fallocate(int fd, int mode, unsigned offset_a2, + unsigned offset_a3, unsigned len_a4, unsigned len_a5) +{ + return sys_fallocate(fd, mode, merge_64(offset_a2, offset_a3), + merge_64(len_a4, len_a5)); +} + save_static_function(sys32_clone); -__attribute_used__ noinline static int +static int noinline __used _sys32_clone(nabi_no_regargs struct pt_regs regs) { unsigned long clone_flags; diff --git a/arch/mips/kernel/machine_kexec.c b/arch/mips/kernel/machine_kexec.c index 8f42fa85ac9e..85beb9b0b2d0 100644 --- a/arch/mips/kernel/machine_kexec.c +++ b/arch/mips/kernel/machine_kexec.c @@ -14,7 +14,7 @@ #include <asm/page.h> extern const unsigned char relocate_new_kernel[]; -extern const unsigned int relocate_new_kernel_size; +extern const size_t relocate_new_kernel_size; extern unsigned long kexec_start_address; extern unsigned long kexec_indirection_page; @@ -40,6 +40,8 @@ machine_crash_shutdown(struct pt_regs *regs) { } +typedef void (*noretfun_t)(void) __attribute__((noreturn)); + void machine_kexec(struct kimage *image) { @@ -51,7 +53,8 @@ machine_kexec(struct kimage *image) (unsigned long)page_address(image->control_code_page); kexec_start_address = image->start; - kexec_indirection_page = phys_to_virt(image->head & PAGE_MASK); + kexec_indirection_page = + (unsigned long) phys_to_virt(image->head & PAGE_MASK); memcpy((void*)reboot_code_buffer, relocate_new_kernel, relocate_new_kernel_size); @@ -67,7 +70,7 @@ machine_kexec(struct kimage *image) phys_to_virt(entry & PAGE_MASK) : ptr + 1) { if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || *ptr & IND_DESTINATION) - *ptr = phys_to_virt(*ptr); + *ptr = (unsigned long) phys_to_virt(*ptr); } /* @@ -75,11 +78,8 @@ machine_kexec(struct kimage *image) */ local_irq_disable(); - flush_icache_range(reboot_code_buffer, - reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); - - printk("Will call new kernel at %08x\n", image->start); + printk("Will call new kernel at %08lx\n", image->start); printk("Bye ...\n"); - flush_cache_all(); - ((void (*)(void))reboot_code_buffer)(); + __flush_cache_all(); + ((noretfun_t) reboot_code_buffer)(); } diff --git a/arch/mips/kernel/mips-mt.c b/arch/mips/kernel/mips-mt.c index ba01800b6018..56750b02ab40 100644 --- a/arch/mips/kernel/mips-mt.c +++ b/arch/mips/kernel/mips-mt.c @@ -4,9 +4,9 @@ */ #include <linux/device.h> +#include <linux/kallsyms.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/cpumask.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/security.h> @@ -22,148 +22,27 @@ #include <asm/r4kcache.h> #include <asm/cacheflush.h> -/* - * CPU mask used to set process affinity for MT VPEs/TCs with FPUs - */ - -cpumask_t mt_fpu_cpumask; - -#ifdef CONFIG_MIPS_MT_FPAFF - -#include <linux/cpu.h> -#include <linux/delay.h> -#include <asm/uaccess.h> - -unsigned long mt_fpemul_threshold = 0; - -/* - * Replacement functions for the sys_sched_setaffinity() and - * sys_sched_getaffinity() system calls, so that we can integrate - * FPU affinity with the user's requested processor affinity. - * This code is 98% identical with the sys_sched_setaffinity() - * and sys_sched_getaffinity() system calls, and should be - * updated when kernel/sched.c changes. - */ - -/* - * find_process_by_pid - find a process with a matching PID value. - * used in sys_sched_set/getaffinity() in kernel/sched.c, so - * cloned here. - */ -static inline struct task_struct *find_process_by_pid(pid_t pid) -{ - return pid ? find_task_by_pid(pid) : current; -} +int vpelimit; - -/* - * mipsmt_sys_sched_setaffinity - set the cpu affinity of a process - */ -asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, - unsigned long __user *user_mask_ptr) +static int __init maxvpes(char *str) { - cpumask_t new_mask; - cpumask_t effective_mask; - int retval; - struct task_struct *p; - - if (len < sizeof(new_mask)) - return -EINVAL; - - if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask))) - return -EFAULT; - - lock_cpu_hotplug(); - read_lock(&tasklist_lock); - - p = find_process_by_pid(pid); - if (!p) { - read_unlock(&tasklist_lock); - unlock_cpu_hotplug(); - return -ESRCH; - } + get_option(&str, &vpelimit); - /* - * It is not safe to call set_cpus_allowed with the - * tasklist_lock held. We will bump the task_struct's - * usage count and drop tasklist_lock before invoking - * set_cpus_allowed. - */ - get_task_struct(p); - - retval = -EPERM; - if ((current->euid != p->euid) && (current->euid != p->uid) && - !capable(CAP_SYS_NICE)) { - read_unlock(&tasklist_lock); - goto out_unlock; - } - - retval = security_task_setscheduler(p, 0, NULL); - if (retval) - goto out_unlock; - - /* Record new user-specified CPU set for future reference */ - p->thread.user_cpus_allowed = new_mask; - - /* Unlock the task list */ - read_unlock(&tasklist_lock); - - /* Compute new global allowed CPU set if necessary */ - if( (p->thread.mflags & MF_FPUBOUND) - && cpus_intersects(new_mask, mt_fpu_cpumask)) { - cpus_and(effective_mask, new_mask, mt_fpu_cpumask); - retval = set_cpus_allowed(p, effective_mask); - } else { - p->thread.mflags &= ~MF_FPUBOUND; - retval = set_cpus_allowed(p, new_mask); - } + return 1; +} +__setup("maxvpes=", maxvpes); -out_unlock: - put_task_struct(p); - unlock_cpu_hotplug(); - return retval; -} +int tclimit; -/* - * mipsmt_sys_sched_getaffinity - get the cpu affinity of a process - */ -asmlinkage long mipsmt_sys_sched_getaffinity(pid_t pid, unsigned int len, - unsigned long __user *user_mask_ptr) +static int __init maxtcs(char *str) { - unsigned int real_len; - cpumask_t mask; - int retval; - struct task_struct *p; - - real_len = sizeof(mask); - if (len < real_len) - return -EINVAL; - - lock_cpu_hotplug(); - read_lock(&tasklist_lock); - - retval = -ESRCH; - p = find_process_by_pid(pid); - if (!p) - goto out_unlock; - retval = security_task_getscheduler(p); - if (retval) - goto out_unlock; - - cpus_and(mask, p->thread.user_cpus_allowed, cpu_possible_map); - -out_unlock: - read_unlock(&tasklist_lock); - unlock_cpu_hotplug(); - if (retval) - return retval; - if (copy_to_user(user_mask_ptr, &mask, real_len)) - return -EFAULT; - return real_len; + get_option(&str, &tclimit); + + return 1; } -#endif /* CONFIG_MIPS_MT_FPAFF */ +__setup("maxtcs=", maxtcs); /* * Dump new MIPS MT state for the core. Does not leave TCs halted. @@ -195,27 +74,32 @@ void mips_mt_regdump(unsigned long mvpctl) nvpe = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; ntc = ((mvpconf0 & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT) + 1; printk("-- per-VPE State --\n"); - for(i = 0; i < nvpe; i++) { - for(tc = 0; tc < ntc; tc++) { + for (i = 0; i < nvpe; i++) { + for (tc = 0; tc < ntc; tc++) { settc(tc); - if((read_tc_c0_tcbind() & TCBIND_CURVPE) == i) { - printk(" VPE %d\n", i); - printk(" VPEControl : %08lx\n", read_vpe_c0_vpecontrol()); - printk(" VPEConf0 : %08lx\n", read_vpe_c0_vpeconf0()); - printk(" VPE%d.Status : %08lx\n", - i, read_vpe_c0_status()); - printk(" VPE%d.EPC : %08lx\n", i, read_vpe_c0_epc()); - printk(" VPE%d.Cause : %08lx\n", i, read_vpe_c0_cause()); - printk(" VPE%d.Config7 : %08lx\n", - i, read_vpe_c0_config7()); - break; /* Next VPE */ + if ((read_tc_c0_tcbind() & TCBIND_CURVPE) == i) { + printk(" VPE %d\n", i); + printk(" VPEControl : %08lx\n", + read_vpe_c0_vpecontrol()); + printk(" VPEConf0 : %08lx\n", + read_vpe_c0_vpeconf0()); + printk(" VPE%d.Status : %08lx\n", + i, read_vpe_c0_status()); + printk(" VPE%d.EPC : %08lx ", + i, read_vpe_c0_epc()); + print_symbol("%s\n", read_vpe_c0_epc()); + printk(" VPE%d.Cause : %08lx\n", + i, read_vpe_c0_cause()); + printk(" VPE%d.Config7 : %08lx\n", + i, read_vpe_c0_config7()); + break; /* Next VPE */ + } } - } } printk("-- per-TC State --\n"); - for(tc = 0; tc < ntc; tc++) { + for (tc = 0; tc < ntc; tc++) { settc(tc); - if(read_tc_c0_tcbind() == read_c0_tcbind()) { + if (read_tc_c0_tcbind() == read_c0_tcbind()) { /* Are we dumping ourself? */ haltval = 0; /* Then we're not halted, and mustn't be */ tcstatval = flags; /* And pre-dump TCStatus is flags */ @@ -228,7 +112,8 @@ void mips_mt_regdump(unsigned long mvpctl) } printk(" TCStatus : %08lx\n", tcstatval); printk(" TCBind : %08lx\n", read_tc_c0_tcbind()); - printk(" TCRestart : %08lx\n", read_tc_c0_tcrestart()); + printk(" TCRestart : %08lx ", read_tc_c0_tcrestart()); + print_symbol("%s\n", read_tc_c0_tcrestart()); printk(" TCHalt : %08lx\n", haltval); printk(" TCContext : %08lx\n", read_tc_c0_tccontext()); if (!haltval) @@ -310,17 +195,6 @@ static int __init ndflush(char *s) return 1; } __setup("ndflush=", ndflush); -#ifdef CONFIG_MIPS_MT_FPAFF -static int fpaff_threshold = -1; - -static int __init fpaff_thresh(char *str) -{ - get_option(&str, &fpaff_threshold); - return 1; -} - -__setup("fpaff=", fpaff_thresh); -#endif /* CONFIG_MIPS_MT_FPAFF */ static unsigned int itc_base = 0; @@ -376,20 +250,6 @@ void mips_mt_set_cpuoptions(void) if (mt_n_dflushes != 1) printk("D-Cache Flushes Repeated %d times\n", mt_n_dflushes); -#ifdef CONFIG_MIPS_MT_FPAFF - /* FPU Use Factor empirically derived from experiments on 34K */ -#define FPUSEFACTOR 333 - - if (fpaff_threshold >= 0) { - mt_fpemul_threshold = fpaff_threshold; - } else { - mt_fpemul_threshold = - (FPUSEFACTOR * (loops_per_jiffy/(500000/HZ))) / HZ; - } - printk("FPU Affinity set after %ld emulations\n", - mt_fpemul_threshold); -#endif /* CONFIG_MIPS_MT_FPAFF */ - if (itc_base != 0) { /* * Configure ITC mapping. This code is very diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 5ddc2e9deecf..ec04f5a1a5ea 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -14,7 +14,6 @@ #include <asm/cpu-features.h> #include <asm/mipsregs.h> #include <asm/processor.h> -#include <asm/watch.h> unsigned int vced_count, vcei_count; @@ -84,6 +83,7 @@ static const char *cpu_name[] = { [CPU_VR4181A] = "NEC VR4181A", [CPU_SR71000] = "Sandcraft SR71000", [CPU_PR4450] = "Philips PR4450", + [CPU_LOONGSON2] = "ICT Loongson-2", }; diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 6bdfb5a9fa1a..e6ce943099a0 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -25,7 +25,9 @@ #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/dsp.h> @@ -46,7 +48,7 @@ * power and have a low exit latency (ie sit in a loop waiting for somebody to * say that they'd like to reschedule) */ -ATTRIB_NORET void cpu_idle(void) +void __noreturn cpu_idle(void) { /* endless idle loop with no priority at all */ while (1) { @@ -75,7 +77,7 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) status = regs->cp0_status & ~(ST0_CU0|ST0_CU1|KU_MASK); #ifdef CONFIG_64BIT status &= ~ST0_FR; - status |= (current->thread.mflags & MF_32BIT_REGS) ? 0 : ST0_FR; + status |= test_thread_flag(TIF_32BIT_REGS) ? 0 : ST0_FR; #endif status |= KU_USER; regs->cp0_status = status; @@ -213,7 +215,7 @@ int dump_task_fpu (struct task_struct *t, elf_fpregset_t *fpr) /* * Create a kernel thread */ -static ATTRIB_NORET void kernel_thread_helper(void *arg, int (*fn)(void *)) +static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *)) { do_exit(fn(arg)); } @@ -460,3 +462,15 @@ unsigned long get_wchan(struct task_struct *task) 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/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index b5a7b46bbc49..bbd57b20b43e 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -20,11 +20,11 @@ #include <linux/mm.h> #include <linux/errno.h> #include <linux/ptrace.h> -#include <linux/audit.h> #include <linux/smp.h> #include <linux/user.h> #include <linux/security.h> -#include <linux/signal.h> +#include <linux/audit.h> +#include <linux/seccomp.h> #include <asm/byteorder.h> #include <asm/cpu.h> @@ -174,17 +174,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) 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: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long __user *) data); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* Read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -313,11 +305,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* 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 = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) - == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: { @@ -482,12 +470,17 @@ static inline int audit_arch(void) */ 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; @@ -505,9 +498,10 @@ asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) send_sig(current->exit_code, current, 1); current->exit_code = 0; } - out: + +out: if (unlikely(current->audit_context) && !entryexit) - audit_syscall_entry(audit_arch(), regs->regs[2], + audit_syscall_entry(audit_arch(), regs->regs[0], regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); } diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S index 06729596812f..d9bfae53c43f 100644 --- a/arch/mips/kernel/r4k_switch.S +++ b/arch/mips/kernel/r4k_switch.S @@ -85,12 +85,7 @@ move $28, a2 cpu_restore_nonscratch a1 -#if (_THREAD_SIZE - 32) < 0x10000 - PTR_ADDIU t0, $28, _THREAD_SIZE - 32 -#else - PTR_LI t0, _THREAD_SIZE - 32 - PTR_ADDU t0, $28 -#endif + PTR_ADDU t0, $28, _THREAD_SIZE - 32 set_saved_sp t0, t1, t2 #ifdef CONFIG_MIPS_MT_SMTC /* Read-modify-writes of Status must be atomic on a VPE */ diff --git a/arch/mips/kernel/relocate_kernel.S b/arch/mips/kernel/relocate_kernel.S index a3f0d00c1334..87481f916a61 100644 --- a/arch/mips/kernel/relocate_kernel.S +++ b/arch/mips/kernel/relocate_kernel.S @@ -14,67 +14,69 @@ #include <asm/stackframe.h> #include <asm/addrspace.h> - .globl relocate_new_kernel -relocate_new_kernel: - - PTR_L s0, kexec_indirection_page - PTR_L s1, kexec_start_address +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 + 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 + 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 + 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 + 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 + 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) - INT_ADD s4, s4, SZREG - INT_ADD s2, s2, SZREG - INT_SUB s6, s6, 1 - beq s6, zero, process_entry - b copy_word - b process_entry + 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 + j s1 + END(relocate_new_kernel) - .globl kexec_start_address kexec_start_address: - .long 0x0 + EXPORT(kexec_start_address) + PTR 0x0 + .size kexec_start_address, PTRSIZE - .globl kexec_indirection_page kexec_indirection_page: - .long 0x0 + EXPORT(kexec_indirection_page) + PTR 0 + .size kexec_indirection_page, PTRSIZE relocate_new_kernel_end: - .globl relocate_new_kernel_size relocate_new_kernel_size: - .long relocate_new_kernel_end - relocate_new_kernel + EXPORT(relocate_new_kernel_size) + PTR relocate_new_kernel_end - relocate_new_kernel + .size relocate_new_kernel_size, PTRSIZE diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index bfc8ca168f83..1ba00c15505b 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -40,12 +40,11 @@ #include <asm/atomic.h> #include <asm/cpu.h> #include <asm/processor.h> +#include <asm/mips_mt.h> #include <asm/system.h> #include <asm/vpe.h> #include <asm/rtlx.h> -#define RTLX_TARG_VPE 1 - static struct rtlx_info *rtlx; static int major; static char module_name[] = "rtlx"; @@ -57,8 +56,6 @@ static struct chan_waitqueues { struct mutex mutex; } channel_wqs[RTLX_CHANNELS]; -static struct irqaction irq; -static int irq_num; static struct vpe_notifications notify; static int sp_stopping = 0; @@ -85,7 +82,7 @@ static irqreturn_t rtlx_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static __attribute_used__ void dump_rtlx(void) +static void __used dump_rtlx(void) { int i; @@ -112,7 +109,7 @@ static __attribute_used__ void dump_rtlx(void) static int rtlx_init(struct rtlx_info *rtlxi) { if (rtlxi->id != RTLX_ID) { - printk(KERN_ERR "no valid RTLX id at 0x%p 0x%x\n", rtlxi, rtlxi->id); + printk(KERN_ERR "no valid RTLX id at 0x%p 0x%lx\n", rtlxi, rtlxi->id); return -ENOEXEC; } @@ -165,10 +162,10 @@ int rtlx_open(int index, int can_sleep) } if (rtlx == NULL) { - if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) { + if( (p = vpe_get_shared(tclimit)) == NULL) { if (can_sleep) { __wait_event_interruptible(channel_wqs[index].lx_queue, - (p = vpe_get_shared(RTLX_TARG_VPE)), + (p = vpe_get_shared(tclimit)), ret); if (ret) goto out_fail; @@ -472,11 +469,24 @@ static int rtlx_irq_num = MIPS_CPU_IRQ_BASE + MIPS_CPU_RTLX_IRQ; static char register_chrdev_failed[] __initdata = KERN_ERR "rtlx_module_init: unable to register device\n"; -static int rtlx_module_init(void) +static int __init rtlx_module_init(void) { struct device *dev; int i, err; + if (!cpu_has_mipsmt) { + printk("VPE loader: not a MIPS MT capable processor\n"); + return -ENODEV; + } + + if (tclimit == 0) { + printk(KERN_WARNING "No TCs reserved for AP/SP, not " + "initializing RTLX.\nPass maxtcs=<n> argument as kernel " + "argument\n"); + + return -ENODEV; + } + major = register_chrdev(0, module_name, &rtlx_fops); if (major < 0) { printk(register_chrdev_failed); @@ -501,7 +511,7 @@ static int rtlx_module_init(void) /* set up notifiers */ notify.start = starting; notify.stop = stopping; - vpe_notify(RTLX_TARG_VPE, ¬ify); + vpe_notify(tclimit, ¬ify); if (cpu_has_vint) set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch); diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index ae985d1fcca1..82480a1717d8 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -662,6 +662,7 @@ einval: li v0, -EINVAL sys sys_signalfd 3 sys sys_timerfd 4 sys sys_eventfd 1 + sys sys_fallocate 6 /* 4320 */ .endm /* We pre-compute the number of _instruction_ bytes needed to diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index 7bcd5a1a85f5..c2c10876da2e 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -477,4 +477,5 @@ sys_call_table: PTR sys_signalfd PTR sys_timerfd PTR sys_eventfd + PTR sys_fallocate .size sys_call_table,.-sys_call_table diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 532a2f3b42fc..118be24224f2 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -375,7 +375,7 @@ EXPORT(sysn32_call_table) PTR sys_mkdirat PTR sys_mknodat PTR sys_fchownat - PTR sys_futimesat /* 6255 */ + PTR compat_sys_futimesat /* 6255 */ PTR sys_newfstatat PTR sys_unlinkat PTR sys_renameat @@ -403,4 +403,5 @@ EXPORT(sysn32_call_table) PTR compat_sys_signalfd /* 5280 */ PTR compat_sys_timerfd PTR sys_eventfd + PTR sys_fallocate .size sysn32_call_table,.-sysn32_call_table diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 6bbe0f4ed8ba..dd68afce7da5 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -525,4 +525,5 @@ sys_call_table: PTR compat_sys_signalfd PTR compat_sys_timerfd PTR sys_eventfd + PTR sys32_fallocate /* 4320 */ .size sys_call_table,.-sys_call_table diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 4975da0bfb63..316685fca059 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -20,6 +20,7 @@ #include <linux/highmem.h> #include <linux/console.h> #include <linux/pfn.h> +#include <linux/debugfs.h> #include <asm/addrspace.h> #include <asm/bootinfo.h> @@ -574,3 +575,18 @@ __setup("nodsp", dsp_disable); 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 (IS_ERR(d)) + return PTR_ERR(d); + mips_debugfs_dir = d; + return 0; +} +arch_initcall(debugfs_mips); +#endif diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c index 486b8e5f52d0..64b612a0a622 100644 --- a/arch/mips/kernel/signal32.c +++ b/arch/mips/kernel/signal32.c @@ -18,7 +18,6 @@ #include <linux/errno.h> #include <linux/wait.h> #include <linux/ptrace.h> -#include <linux/compat.h> #include <linux/suspend.h> #include <linux/compiler.h> #include <linux/uaccess.h> diff --git a/arch/mips/kernel/smp-mt.c b/arch/mips/kernel/smp-mt.c index 19b30d6f1727..05dcce416325 100644 --- a/arch/mips/kernel/smp-mt.c +++ b/arch/mips/kernel/smp-mt.c @@ -287,7 +287,7 @@ void __init plat_prepare_cpus(unsigned int max_cpus) * (unsigned long)idle->thread_info the gp * assumes a 1:1 mapping of TC => VPE */ -void prom_boot_secondary(int cpu, struct task_struct *idle) +void __cpuinit prom_boot_secondary(int cpu, struct task_struct *idle) { struct thread_info *gp = task_thread_info(idle); dvpe(); @@ -321,7 +321,7 @@ void prom_boot_secondary(int cpu, struct task_struct *idle) evpe(EVPE_ENABLE); } -void prom_init_secondary(void) +void __cpuinit prom_init_secondary(void) { /* Enable per-cpu interrupts */ @@ -330,7 +330,7 @@ void prom_init_secondary(void) (STATUSF_IP0 | STATUSF_IP1 | STATUSF_IP6 | STATUSF_IP7)); } -void prom_smp_finish(void) +void __cpuinit prom_smp_finish(void) { write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ)); diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 67edfa7ed93a..73b0dab02668 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -30,6 +30,7 @@ #include <linux/sched.h> #include <linux/cpumask.h> #include <linux/cpu.h> +#include <linux/err.h> #include <asm/atomic.h> #include <asm/cpu.h> @@ -51,18 +52,8 @@ int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */ EXPORT_SYMBOL(phys_cpu_present_map); EXPORT_SYMBOL(cpu_online_map); -/* This happens early in bootup, can't really do it better */ -static void smp_tune_scheduling (void) -{ - struct cache_desc *cd = ¤t_cpu_data.scache; - unsigned long cachesize = cd->linesz * cd->sets * cd->ways; - - if (cachesize > max_cache_size) - max_cache_size = cachesize; -} - extern void __init calibrate_delay(void); -extern ATTRIB_NORET void cpu_idle(void); +extern void cpu_idle(void); /* * First C code run on the secondary CPUs after being started up by @@ -203,6 +194,61 @@ void smp_call_function_interrupt(void) } } +int smp_call_function_single(int cpu, void (*func) (void *info), void *info, + int retry, int wait) +{ + struct call_data_struct data; + int me; + + /* + * Can die spectacularly if this CPU isn't yet marked online + */ + if (!cpu_online(cpu)) + return 0; + + me = get_cpu(); + BUG_ON(!cpu_online(me)); + + if (cpu == me) { + local_irq_disable(); + func(info); + local_irq_enable(); + put_cpu(); + return 0; + } + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock(&smp_call_lock); + call_data = &data; + smp_mb(); + + /* Send a message to the other CPU */ + core_send_ipi(cpu, SMP_CALL_FUNCTION); + + /* Wait for response */ + /* FIXME: lock-up detection, backtrace on lock-up */ + while (atomic_read(&data.started) != 1) + barrier(); + + if (wait) + while (atomic_read(&data.finished) != 1) + barrier(); + call_data = NULL; + spin_unlock(&smp_call_lock); + + put_cpu(); + return 0; +} + static void stop_this_cpu(void *dummy) { /* @@ -228,7 +274,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus) { init_new_context(current, &init_mm); current_thread_info()->cpu = 0; - smp_tune_scheduling(); plat_prepare_cpus(max_cpus); #ifndef CONFIG_HOTPLUG_CPU cpu_present_map = cpu_possible_map; diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index 046b03b1705a..f09404377ef1 100644 --- a/arch/mips/kernel/smtc.c +++ b/arch/mips/kernel/smtc.c @@ -25,10 +25,11 @@ #include <asm/smtc_proc.h> /* - * This file should be built into the kernel only if CONFIG_MIPS_MT_SMTC is set. + * 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. */ - -#define MIPS_CPU_IPI_IRQ 1 +unsigned long irq_hwmask[NR_IRQS]; #define LOCK_MT_PRA() \ local_irq_save(flags); \ @@ -86,25 +87,11 @@ unsigned int smtc_status = 0; /* Boot command line configuration overrides */ -static int vpelimit = 0; -static int tclimit = 0; static int ipibuffers = 0; static int nostlb = 0; static int asidmask = 0; unsigned long smtc_asid_mask = 0xff; -static int __init maxvpes(char *str) -{ - get_option(&str, &vpelimit); - return 1; -} - -static int __init maxtcs(char *str) -{ - get_option(&str, &tclimit); - return 1; -} - static int __init ipibufs(char *str) { get_option(&str, &ipibuffers); @@ -137,8 +124,6 @@ static int __init asidmask_set(char *str) return 1; } -__setup("maxvpes=", maxvpes); -__setup("maxtcs=", maxtcs); __setup("ipibufs=", ipibufs); __setup("nostlb", stlb_disable); __setup("asidmask=", asidmask_set); @@ -168,9 +153,9 @@ static int __init tintq(char *str) __setup("tintq=", tintq); -int imstuckcount[2][8]; +static int imstuckcount[2][8]; /* vpemask represents IM/IE bits of per-VPE Status registers, low-to-high */ -int vpemask[2][8] = { +static int vpemask[2][8] = { {0, 0, 1, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 1} }; @@ -540,7 +525,7 @@ void mipsmt_prepare_cpus(void) * (unsigned long)idle->thread_info the gp * */ -void smtc_boot_secondary(int cpu, struct task_struct *idle) +void __cpuinit smtc_boot_secondary(int cpu, struct task_struct *idle) { extern u32 kernelsp[NR_CPUS]; long flags; @@ -876,7 +861,7 @@ void deferred_smtc_ipi(void) * Send clock tick to all TCs except the one executing the funtion */ -void smtc_timer_broadcast(int vpe) +void smtc_timer_broadcast(void) { int cpu; int myTC = cpu_data[smp_processor_id()].tc_id; @@ -975,7 +960,12 @@ static void ipi_irq_dispatch(void) do_IRQ(cpu_ipi_irq); } -static struct irqaction irq_ipi; +static struct irqaction irq_ipi = { + .handler = ipi_interrupt, + .flags = IRQF_DISABLED, + .name = "SMTC_IPI", + .flags = IRQF_PERCPU +}; static void setup_cross_vpe_interrupts(unsigned int nvpe) { @@ -987,13 +977,8 @@ static void setup_cross_vpe_interrupts(unsigned int nvpe) set_vi_handler(MIPS_CPU_IPI_IRQ, ipi_irq_dispatch); - irq_ipi.handler = ipi_interrupt; - irq_ipi.flags = IRQF_DISABLED; - irq_ipi.name = "SMTC_IPI"; - setup_irq_smtc(cpu_ipi_irq, &irq_ipi, (0x100 << MIPS_CPU_IPI_IRQ)); - irq_desc[cpu_ipi_irq].status |= IRQ_PER_CPU; set_irq_handler(cpu_ipi_irq, handle_percpu_irq); } @@ -1104,7 +1089,7 @@ void smtc_idle_loop_hook(void) mtflags = dmt(); pdb_msg = &id_ho_db_msg[0]; im = read_c0_status(); - vpe = cpu_data[smp_processor_id()].vpe_id; + vpe = current_cpu_data.vpe_id; for (bit = 0; bit < 8; bit++) { /* * In current prototype, I/O interrupts diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 9dd5a2df8eac..7c800ec3ff55 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -12,6 +12,7 @@ #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> @@ -167,14 +168,14 @@ sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, } save_static_function(sys_fork); -__attribute_used__ noinline static int +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); -__attribute_used__ noinline static int +static int __used noinline _sys_clone(nabi_no_regargs struct pt_regs regs) { unsigned long clone_flags; @@ -272,25 +273,32 @@ asmlinkage int sys_set_thread_area(unsigned long addr) struct thread_info *ti = task_thread_info(current); ti->tp_value = addr; - - /* If some future MIPS implementation has this register in hardware, - * we will need to update it here (and in context switches). */ + if (cpu_has_userlocal) + write_c0_userlocal(addr); return 0; } asmlinkage int _sys_sysmips(int cmd, long arg1, int arg2, int arg3) { - int tmp; - - switch(cmd) { + switch (cmd) { case MIPS_ATOMIC_SET: printk(KERN_CRIT "How did I get here?\n"); return -EINVAL; case MIPS_FIXADE: - tmp = current->thread.mflags & ~3; - current->thread.mflags = tmp | (arg1 & 3); + 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: diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index d48d1d5bea0a..9a5596bf8571 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/param.h> +#include <linux/profile.h> #include <linux/time.h> #include <linux/timex.h> #include <linux/smp.h> diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 3ea7863c4519..6379003f9d8d 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -39,7 +39,6 @@ #include <asm/traps.h> #include <asm/uaccess.h> #include <asm/mmu_context.h> -#include <asm/watch.h> #include <asm/types.h> #include <asm/stacktrace.h> @@ -70,6 +69,7 @@ extern asmlinkage void handle_reserved(void); extern int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, int has_fpu); +void (*board_watchpoint_handler)(struct pt_regs *regs); void (*board_be_init)(void); int (*board_be_handler)(struct pt_regs *regs, int is_fixup); void (*board_nmi_handler_setup)(void); @@ -131,7 +131,7 @@ static void show_stacktrace(struct task_struct *task, struct pt_regs *regs) const int field = 2 * sizeof(unsigned long); long stackdata; int i; - unsigned long *sp = (unsigned long *)regs->regs[29]; + unsigned long __user *sp = (unsigned long __user *)regs->regs[29]; printk("Stack :"); i = 0; @@ -187,7 +187,7 @@ void dump_stack(void) EXPORT_SYMBOL(dump_stack); -void show_code(unsigned int *pc) +static void show_code(unsigned int __user *pc) { long i; @@ -305,13 +305,13 @@ void show_registers(struct pt_regs *regs) printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", current->comm, current->pid, current_thread_info(), current); show_stacktrace(current, regs); - show_code((unsigned int *) regs->cp0_epc); + show_code((unsigned int __user *) regs->cp0_epc); printk("\n"); } static DEFINE_SPINLOCK(die_lock); -NORET_TYPE void ATTRIB_NORET die(const char * str, struct pt_regs * regs) +void __noreturn die(const char * str, struct pt_regs * regs) { static int die_counter; #ifdef CONFIG_MIPS_MT_SMTC @@ -326,6 +326,7 @@ NORET_TYPE void ATTRIB_NORET die(const char * str, struct pt_regs * regs) #endif /* CONFIG_MIPS_MT_SMTC */ printk("%s[#%d]:\n", str, ++die_counter); show_registers(regs); + add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); if (in_interrupt()) @@ -373,7 +374,7 @@ asmlinkage void do_be(struct pt_regs *regs) action = MIPS_BE_FIXUP; if (board_be_handler) - action = board_be_handler(regs, fixup != 0); + action = board_be_handler(regs, fixup != NULL); switch (action) { case MIPS_BE_DISCARD: @@ -605,6 +606,8 @@ asmlinkage void do_ov(struct pt_regs *regs) */ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) { + siginfo_t info; + die_if_kernel("FP exception in kernel code", regs); if (fcr31 & FPU_CSR_UNI_X) { @@ -640,9 +643,22 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) force_sig(sig, current); return; - } - - force_sig(SIGFPE, current); + } else if (fcr31 & FPU_CSR_INV_X) + info.si_code = FPE_FLTINV; + else if (fcr31 & FPU_CSR_DIV_X) + info.si_code = FPE_FLTDIV; + else if (fcr31 & FPU_CSR_OVF_X) + info.si_code = FPE_FLTOVF; + else if (fcr31 & FPU_CSR_UDF_X) + info.si_code = FPE_FLTUND; + else if (fcr31 & FPU_CSR_INE_X) + info.si_code = FPE_FLTRES; + else + info.si_code = __SI_FAULT; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void __user *) regs->cp0_epc; + force_sig_info(SIGFPE, &info, current); } asmlinkage void do_bp(struct pt_regs *regs) @@ -753,6 +769,33 @@ asmlinkage void do_ri(struct pt_regs *regs) force_sig(SIGILL, current); } +/* + * MIPS MT processors may have fewer FPU contexts than CPU threads. If we've + * emulated more than some threshold number of instructions, force migration to + * a "CPU" that has FP support. + */ +static void mt_ase_fp_affinity(void) +{ +#ifdef CONFIG_MIPS_MT_FPAFF + if (mt_fpemul_threshold > 0 && + ((current->thread.emulated_fp++ > mt_fpemul_threshold))) { + /* + * If there's no FPU present, or if the application has already + * restricted the allowed set to exclude any CPUs with FPUs, + * we'll skip the procedure. + */ + if (cpus_intersects(current->cpus_allowed, mt_fpu_cpumask)) { + cpumask_t tmask; + + cpus_and(tmask, current->thread.user_cpus_allowed, + mt_fpu_cpumask); + set_cpus_allowed(current, tmask); + set_thread_flag(TIF_FPUBOUND); + } + } +#endif /* CONFIG_MIPS_MT_FPAFF */ +} + asmlinkage void do_cpu(struct pt_regs *regs) { unsigned int cpid; @@ -786,36 +829,8 @@ asmlinkage void do_cpu(struct pt_regs *regs) ¤t->thread.fpu, 0); if (sig) force_sig(sig, current); -#ifdef CONFIG_MIPS_MT_FPAFF - else { - /* - * MIPS MT processors may have fewer FPU contexts - * than CPU threads. If we've emulated more than - * some threshold number of instructions, force - * migration to a "CPU" that has FP support. - */ - if(mt_fpemul_threshold > 0 - && ((current->thread.emulated_fp++ - > mt_fpemul_threshold))) { - /* - * If there's no FPU present, or if the - * application has already restricted - * the allowed set to exclude any CPUs - * with FPUs, we'll skip the procedure. - */ - if (cpus_intersects(current->cpus_allowed, - mt_fpu_cpumask)) { - cpumask_t tmask; - - cpus_and(tmask, - current->thread.user_cpus_allowed, - mt_fpu_cpumask); - set_cpus_allowed(current, tmask); - current->thread.mflags |= MF_FPUBOUND; - } - } - } -#endif /* CONFIG_MIPS_MT_FPAFF */ + else + mt_ase_fp_affinity(); } return; @@ -835,6 +850,11 @@ asmlinkage void do_mdmx(struct pt_regs *regs) asmlinkage void do_watch(struct pt_regs *regs) { + if (board_watchpoint_handler) { + (*board_watchpoint_handler)(regs); + return; + } + /* * We use the watch exception where available to detect stack * overflows. @@ -861,7 +881,7 @@ asmlinkage void do_mcheck(struct pt_regs *regs) dump_tlb_all(); } - show_code((unsigned int *) regs->cp0_epc); + show_code((unsigned int __user *) regs->cp0_epc); /* * Some chips may have other causes of machine check (e.g. SB1 @@ -1030,19 +1050,11 @@ void ejtag_exception_handler(struct pt_regs *regs) /* * NMI exception handler. */ -void nmi_exception_handler(struct pt_regs *regs) +NORET_TYPE void ATTRIB_NORET nmi_exception_handler(struct pt_regs *regs) { -#ifdef CONFIG_MIPS_MT_SMTC - unsigned long dvpret = dvpe(); bust_spinlocks(1); printk("NMI taken!!!!\n"); - mips_mt_regdump(dvpret); -#else - bust_spinlocks(1); - printk("NMI taken!!!!\n"); -#endif /* CONFIG_MIPS_MT_SMTC */ die("NMI", regs); - while(1) ; } #define VECTORSPACING 0x100 /* for EI/VI mode */ @@ -1343,7 +1355,14 @@ void __init per_cpu_trap_init(void) set_c0_status(ST0_MX); #ifdef CONFIG_CPU_MIPSR2 - write_c0_hwrena (0x0000000f); /* Allow rdhwr to all registers */ + if (cpu_has_mips_r2) { + unsigned int enable = 0x0000000f; + + if (cpu_has_userlocal) + enable |= (1 << 29); + + write_c0_hwrena(enable); + } #endif #ifdef CONFIG_MIPS_MT_SMTC diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index 18c4a3c45a31..d34b1fb3665d 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -77,6 +77,7 @@ #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> @@ -87,20 +88,27 @@ #define STR(x) __STR(x) #define __STR(x) #x -#ifdef CONFIG_PROC_FS -unsigned long unaligned_instructions; +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 inline int emulate_load_store_insn(struct pt_regs *regs, - void __user *addr, unsigned int __user *pc, - unsigned long **regptr, unsigned long *newvalue) +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; - *regptr=NULL; /* * This load never faults. @@ -169,8 +177,8 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, : "r" (addr), "i" (-EFAULT)); if (res) goto fault; - *newvalue = value; - *regptr = ®s->regs[insn.i_format.rt]; + compute_return_epc(regs); + regs->regs[insn.i_format.rt] = value; break; case lw_op: @@ -199,8 +207,8 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, : "r" (addr), "i" (-EFAULT)); if (res) goto fault; - *newvalue = value; - *regptr = ®s->regs[insn.i_format.rt]; + compute_return_epc(regs); + regs->regs[insn.i_format.rt] = value; break; case lhu_op: @@ -233,8 +241,8 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, : "r" (addr), "i" (-EFAULT)); if (res) goto fault; - *newvalue = value; - *regptr = ®s->regs[insn.i_format.rt]; + compute_return_epc(regs); + regs->regs[insn.i_format.rt] = value; break; case lwu_op: @@ -273,8 +281,8 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, : "r" (addr), "i" (-EFAULT)); if (res) goto fault; - *newvalue = value; - *regptr = ®s->regs[insn.i_format.rt]; + compute_return_epc(regs); + regs->regs[insn.i_format.rt] = value; break; #endif /* CONFIG_64BIT */ @@ -315,8 +323,8 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, : "r" (addr), "i" (-EFAULT)); if (res) goto fault; - *newvalue = value; - *regptr = ®s->regs[insn.i_format.rt]; + compute_return_epc(regs); + regs->regs[insn.i_format.rt] = value; break; #endif /* CONFIG_64BIT */ @@ -357,6 +365,7 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, : "r" (value), "r" (addr), "i" (-EFAULT)); if (res) goto fault; + compute_return_epc(regs); break; case sw_op: @@ -387,6 +396,7 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, : "r" (value), "r" (addr), "i" (-EFAULT)); if (res) goto fault; + compute_return_epc(regs); break; case sd_op: @@ -425,6 +435,7 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, : "r" (value), "r" (addr), "i" (-EFAULT)); if (res) goto fault; + compute_return_epc(regs); break; #endif /* CONFIG_64BIT */ @@ -459,38 +470,35 @@ static inline int emulate_load_store_insn(struct pt_regs *regs, goto sigill; } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_DEBUG_FS unaligned_instructions++; #endif - return 0; + return; fault: /* Did we have an exception handler installed? */ if (fixup_exception(regs)) - return 1; + return; die_if_kernel ("Unhandled kernel unaligned access", regs); send_sig(SIGSEGV, current, 1); - return 0; + return; sigbus: die_if_kernel("Unhandled kernel unaligned access", regs); send_sig(SIGBUS, current, 1); - return 0; + return; sigill: die_if_kernel("Unhandled kernel unaligned access or invalid instruction", regs); send_sig(SIGILL, current, 1); - - return 0; } asmlinkage void do_ade(struct pt_regs *regs) { - unsigned long *regptr, newval; extern int do_dsemulret(struct pt_regs *); unsigned int __user *pc; mm_segment_t seg; @@ -514,8 +522,12 @@ asmlinkage void do_ade(struct pt_regs *regs) goto sigbus; pc = (unsigned int __user *) exception_epc(regs); - if (user_mode(regs) && (current->thread.mflags & MF_FIXADE) == 0) + 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. @@ -524,16 +536,7 @@ asmlinkage void do_ade(struct pt_regs *regs) seg = get_fs(); if (!user_mode(regs)) set_fs(KERNEL_DS); - if (!emulate_load_store_insn(regs, (void __user *)regs->cp0_badvaddr, pc, - ®ptr, &newval)) { - compute_return_epc(regs); - /* - * Now that branch is evaluated, update the dest - * register if necessary - */ - if (regptr) - *regptr = newval; - } + emulate_load_store_insn(regs, (void __user *)regs->cp0_badvaddr, pc); set_fs(seg); return; @@ -546,3 +549,24 @@ sigbus: * 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 (IS_ERR(d)) + return PTR_ERR(d); + d = debugfs_create_u32("unaligned_action", S_IRUGO | S_IWUSR, + mips_debugfs_dir, &unaligned_action); + if (IS_ERR(d)) + return PTR_ERR(d); + return 0; +} +__initcall(debugfs_unaligned); +#endif diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S index 9b9992cd562a..087ab997487d 100644 --- a/arch/mips/kernel/vmlinux.lds.S +++ b/arch/mips/kernel/vmlinux.lds.S @@ -45,6 +45,8 @@ SECTIONS __dbe_table : { *(__dbe_table) } __stop___dbe_table = .; + NOTES + RODATA /* writeable */ @@ -119,10 +121,7 @@ SECTIONS .init.ramfs : { *(.init.ramfs) } __initramfs_end = .; #endif - . = ALIGN(_PAGE_SIZE); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(_PAGE_SIZE) . = ALIGN(_PAGE_SIZE); __init_end = .; /* freed after init ends here */ @@ -145,15 +144,16 @@ SECTIONS *(.exitcall.exit) /* ABI crap starts here */ - *(.comment) *(.MIPS.options) - *(.note) *(.options) *(.pdr) *(.reginfo) - *(.mdebug*) } + /* 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) } diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c index 9e66354dee8b..3c09b9785f4c 100644 --- a/arch/mips/kernel/vpe.c +++ b/arch/mips/kernel/vpe.c @@ -27,7 +27,6 @@ * To load and run, simply cat a SP 'program file' to /dev/vpe1. * i.e cat spapp >/dev/vpe1. */ - #include <linux/kernel.h> #include <linux/device.h> #include <linux/module.h> @@ -54,6 +53,7 @@ #include <asm/system.h> #include <asm/vpe.h> #include <asm/kspd.h> +#include <asm/mips_mt.h> typedef void *vpe_handle; @@ -64,6 +64,10 @@ typedef void *vpe_handle; /* If this is set, the section belongs in the init part of the module */ #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) +/* + * The number of TCs and VPEs physically available on the core + */ +static int hw_tcs, hw_vpes; static char module_name[] = "vpe"; static int major; static const int minor = 1; /* fixed for now */ @@ -126,20 +130,17 @@ struct vpe { /* the list of who wants to know when something major happens */ struct list_head notify; + + unsigned int ntcs; }; struct tc { enum tc_state state; int index; - /* parent VPE */ - struct vpe *pvpe; - - /* The list of TC's with this VPE */ - struct list_head tc; - - /* The global list of tc's */ - struct list_head list; + struct vpe *pvpe; /* parent VPE */ + struct list_head tc; /* The list of TC's with this VPE */ + struct list_head list; /* The global list of tc's */ }; struct { @@ -154,7 +155,6 @@ struct { }; static void release_progmem(void *ptr); -/* static __attribute_used__ void dump_vpe(struct vpe * v); */ extern void save_gp_address(unsigned int secbase, unsigned int rel); /* get the vpe associated with this minor */ @@ -218,18 +218,17 @@ struct vpe *alloc_vpe(int minor) /* allocate a tc. At startup only tc0 is running, all other can be halted. */ struct tc *alloc_tc(int index) { - struct tc *t; + struct tc *tc; - if ((t = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) { - return NULL; - } - - INIT_LIST_HEAD(&t->tc); - list_add_tail(&t->list, &vpecontrol.tc_list); + if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) + goto out; - t->index = index; + INIT_LIST_HEAD(&tc->tc); + tc->index = index; + list_add_tail(&tc->list, &vpecontrol.tc_list); - return t; +out: + return tc; } /* clean up and free everything */ @@ -664,66 +663,48 @@ static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex, } #endif -static void dump_tc(struct tc *t) -{ - unsigned long val; - - settc(t->index); - printk(KERN_DEBUG "VPE loader: TC index %d targtc %ld " - "TCStatus 0x%lx halt 0x%lx\n", - t->index, read_c0_vpecontrol() & VPECONTROL_TARGTC, - read_tc_c0_tcstatus(), read_tc_c0_tchalt()); - - printk(KERN_DEBUG " tcrestart 0x%lx\n", read_tc_c0_tcrestart()); - printk(KERN_DEBUG " tcbind 0x%lx\n", read_tc_c0_tcbind()); - - val = read_c0_vpeconf0(); - printk(KERN_DEBUG " VPEConf0 0x%lx MVP %ld\n", val, - (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT); - - printk(KERN_DEBUG " c0 status 0x%lx\n", read_vpe_c0_status()); - printk(KERN_DEBUG " c0 cause 0x%lx\n", read_vpe_c0_cause()); - - printk(KERN_DEBUG " c0 badvaddr 0x%lx\n", read_vpe_c0_badvaddr()); - printk(KERN_DEBUG " c0 epc 0x%lx\n", read_vpe_c0_epc()); -} - -static void dump_tclist(void) -{ - struct tc *t; - - list_for_each_entry(t, &vpecontrol.tc_list, list) { - dump_tc(t); - } -} - /* We are prepared so configure and start the VPE... */ static int vpe_run(struct vpe * v) { + unsigned long flags, val, dmt_flag; struct vpe_notifications *n; - unsigned long val, dmt_flag; + unsigned int vpeflags; struct tc *t; /* check we are the Master VPE */ + local_irq_save(flags); val = read_c0_vpeconf0(); if (!(val & VPECONF0_MVP)) { printk(KERN_WARNING "VPE loader: only Master VPE's are allowed to configure MT\n"); + local_irq_restore(flags); + return -1; } - /* disable MT (using dvpe) */ - dvpe(); + dmt_flag = dmt(); + vpeflags = dvpe(); if (!list_empty(&v->tc)) { if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) { - printk(KERN_WARNING "VPE loader: TC %d is already in use.\n", - t->index); + evpe(vpeflags); + emt(dmt_flag); + local_irq_restore(flags); + + printk(KERN_WARNING + "VPE loader: TC %d is already in use.\n", + t->index); return -ENOEXEC; } } else { - printk(KERN_WARNING "VPE loader: No TC's associated with VPE %d\n", + evpe(vpeflags); + emt(dmt_flag); + local_irq_restore(flags); + + printk(KERN_WARNING + "VPE loader: No TC's associated with VPE %d\n", v->minor); + return -ENOEXEC; } @@ -734,21 +715,20 @@ static int vpe_run(struct vpe * v) /* should check it is halted, and not activated */ if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) { - printk(KERN_WARNING "VPE loader: TC %d is already doing something!\n", + evpe(vpeflags); + emt(dmt_flag); + local_irq_restore(flags); + + printk(KERN_WARNING "VPE loader: TC %d is already active!\n", t->index); - dump_tclist(); + return -ENOEXEC; } - /* - * Disable multi-threaded execution whilst we activate, clear the - * halt bit and bound the tc to the other VPE... - */ - dmt_flag = dmt(); - /* Write the address we want it to start running from in the TCPC register. */ write_tc_c0_tcrestart((unsigned long)v->__start); write_tc_c0_tccontext((unsigned long)0); + /* * Mark the TC as activated, not interrupt exempt and not dynamically * allocatable @@ -764,15 +744,15 @@ static int vpe_run(struct vpe * v) * here... Or set $a3 to zero and define DFLT_STACK_SIZE and * DFLT_HEAP_SIZE when you compile your program */ - mttgpr(7, physical_memsize); - + mttgpr(6, v->ntcs); + mttgpr(7, physical_memsize); /* set up VPE1 */ /* * bind the TC to VPE 1 as late as possible so we only have the final * VPE registers to set up, and so an EJTAG probe can trigger on it */ - write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | v->minor); + write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1); write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA)); @@ -794,15 +774,16 @@ static int vpe_run(struct vpe * v) /* take system out of configuration state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); - /* now safe to re-enable multi-threading */ - emt(dmt_flag); - - /* set it running */ +#ifdef CONFIG_SMP evpe(EVPE_ENABLE); +#else + evpe(vpeflags); +#endif + emt(dmt_flag); + local_irq_restore(flags); - list_for_each_entry(n, &v->notify, list) { - n->start(v->minor); - } + list_for_each_entry(n, &v->notify, list) + n->start(minor); return 0; } @@ -1024,23 +1005,15 @@ static int vpe_elfload(struct vpe * v) return 0; } -__attribute_used__ void dump_vpe(struct vpe * v) -{ - struct tc *t; - - settc(v->minor); - - printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol()); - printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0()); - - list_for_each_entry(t, &vpecontrol.tc_list, list) - dump_tc(t); -} - static void cleanup_tc(struct tc *tc) { + unsigned long flags; + unsigned int mtflags, vpflags; int tmp; + local_irq_save(flags); + mtflags = dmt(); + vpflags = dvpe(); /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); @@ -1055,9 +1028,12 @@ static void cleanup_tc(struct tc *tc) write_tc_c0_tchalt(TCHALT_H); /* bind it to anything other than VPE1 */ - write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE +// write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE clear_c0_mvpcontrol(MVPCONTROL_VPC); + evpe(vpflags); + emt(mtflags); + local_irq_restore(flags); } static int getcwd(char *buff, int size) @@ -1078,36 +1054,32 @@ static int getcwd(char *buff, int size) /* checks VPE is unused and gets ready to load program */ static int vpe_open(struct inode *inode, struct file *filp) { - int minor, ret; enum vpe_state state; - struct vpe *v; struct vpe_notifications *not; + struct vpe *v; + int ret; - /* assume only 1 device at the mo. */ - if ((minor = iminor(inode)) != 1) { + if (minor != iminor(inode)) { + /* assume only 1 device at the moment. */ printk(KERN_WARNING "VPE loader: only vpe1 is supported\n"); return -ENODEV; } - if ((v = get_vpe(minor)) == NULL) { + if ((v = get_vpe(tclimit)) == NULL) { printk(KERN_WARNING "VPE loader: unable to get vpe\n"); return -ENODEV; } state = xchg(&v->state, VPE_STATE_INUSE); if (state != VPE_STATE_UNUSED) { - dvpe(); - printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n"); - dump_tc(get_tc(minor)); - list_for_each_entry(not, &v->notify, list) { - not->stop(minor); + not->stop(tclimit); } release_progmem(v->load_addr); - cleanup_tc(get_tc(minor)); + cleanup_tc(get_tc(tclimit)); } /* this of-course trashes what was there before... */ @@ -1134,26 +1106,25 @@ static int vpe_open(struct inode *inode, struct file *filp) v->shared_ptr = NULL; v->__start = 0; + return 0; } static int vpe_release(struct inode *inode, struct file *filp) { - int minor, ret = 0; struct vpe *v; Elf_Ehdr *hdr; + int ret = 0; - minor = iminor(inode); - if ((v = get_vpe(minor)) == NULL) + v = get_vpe(tclimit); + if (v == NULL) return -ENODEV; - // simple case of fire and forget, so tell the VPE to run... - hdr = (Elf_Ehdr *) v->pbuffer; if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) { - if (vpe_elfload(v) >= 0) + if (vpe_elfload(v) >= 0) { vpe_run(v); - else { + } else { printk(KERN_WARNING "VPE loader: ELF load failed.\n"); ret = -ENOEXEC; } @@ -1180,12 +1151,14 @@ static int vpe_release(struct inode *inode, struct file *filp) static ssize_t vpe_write(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { - int minor; size_t ret = count; struct vpe *v; - minor = iminor(file->f_path.dentry->d_inode); - if ((v = get_vpe(minor)) == NULL) + if (iminor(file->f_path.dentry->d_inode) != minor) + return -ENODEV; + + v = get_vpe(tclimit); + if (v == NULL) return -ENODEV; if (v->pbuffer == NULL) { @@ -1367,62 +1340,173 @@ static void kspd_sp_exit( int sp_id) } #endif -static struct device *vpe_dev; +static ssize_t store_kill(struct class_device *dev, const char *buf, size_t len) +{ + struct vpe *vpe = get_vpe(tclimit); + struct vpe_notifications *not; + + list_for_each_entry(not, &vpe->notify, list) { + not->stop(tclimit); + } + + release_progmem(vpe->load_addr); + cleanup_tc(get_tc(tclimit)); + vpe_stop(vpe); + vpe_free(vpe); + + return len; +} + +static ssize_t show_ntcs(struct class_device *cd, char *buf) +{ + struct vpe *vpe = get_vpe(tclimit); + + return sprintf(buf, "%d\n", vpe->ntcs); +} + +static ssize_t store_ntcs(struct class_device *dev, const char *buf, size_t len) +{ + struct vpe *vpe = get_vpe(tclimit); + unsigned long new; + char *endp; + + new = simple_strtoul(buf, &endp, 0); + if (endp == buf) + goto out_einval; + + if (new == 0 || new > (hw_tcs - tclimit)) + goto out_einval; + + vpe->ntcs = new; + + return len; + +out_einval: + return -EINVAL;; +} + +static struct class_device_attribute vpe_class_attributes[] = { + __ATTR(kill, S_IWUSR, NULL, store_kill), + __ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs), + {} +}; + +static void vpe_class_device_release(struct class_device *cd) +{ + kfree(cd); +} + +struct class vpe_class = { + .name = "vpe", + .owner = THIS_MODULE, + .release = vpe_class_device_release, + .class_dev_attrs = vpe_class_attributes, +}; + +struct class_device vpe_device; static int __init vpe_module_init(void) { + unsigned int mtflags, vpflags; + unsigned long flags, val; struct vpe *v = NULL; - struct device *dev; struct tc *t; - unsigned long val; - int i, err; + int tc, err; if (!cpu_has_mipsmt) { printk("VPE loader: not a MIPS MT capable processor\n"); return -ENODEV; } + if (vpelimit == 0) { + printk(KERN_WARNING "No VPEs reserved for AP/SP, not " + "initializing VPE loader.\nPass maxvpes=<n> argument as " + "kernel argument\n"); + + return -ENODEV; + } + + if (tclimit == 0) { + printk(KERN_WARNING "No TCs reserved for AP/SP, not " + "initializing VPE loader.\nPass maxtcs=<n> argument as " + "kernel argument\n"); + + return -ENODEV; + } + major = register_chrdev(0, module_name, &vpe_fops); if (major < 0) { printk("VPE loader: unable to register character device\n"); return major; } - dev = device_create(mt_class, NULL, MKDEV(major, minor), - "tc%d", minor); - if (IS_ERR(dev)) { - err = PTR_ERR(dev); + err = class_register(&vpe_class); + if (err) { + printk(KERN_ERR "vpe_class registration failed\n"); goto out_chrdev; } - vpe_dev = dev; - dmt(); - dvpe(); + class_device_initialize(&vpe_device); + vpe_device.class = &vpe_class, + vpe_device.parent = NULL, + strlcpy(vpe_device.class_id, "vpe1", BUS_ID_SIZE); + vpe_device.devt = MKDEV(major, minor); + err = class_device_add(&vpe_device); + if (err) { + printk(KERN_ERR "Adding vpe_device failed\n"); + goto out_class; + } + + local_irq_save(flags); + mtflags = dmt(); + vpflags = dvpe(); /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); /* dump_mtregs(); */ - val = read_c0_mvpconf0(); - for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) { - t = alloc_tc(i); + hw_tcs = (val & MVPCONF0_PTC) + 1; + hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; + + for (tc = tclimit; tc < hw_tcs; tc++) { + /* + * Must re-enable multithreading temporarily or in case we + * reschedule send IPIs or similar we might hang. + */ + clear_c0_mvpcontrol(MVPCONTROL_VPC); + evpe(vpflags); + emt(mtflags); + local_irq_restore(flags); + t = alloc_tc(tc); + if (!t) { + err = -ENOMEM; + goto out; + } + + local_irq_save(flags); + mtflags = dmt(); + vpflags = dvpe(); + set_c0_mvpcontrol(MVPCONTROL_VPC); /* VPE's */ - if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) { - settc(i); + if (tc < hw_tcs) { + settc(tc); - if ((v = alloc_vpe(i)) == NULL) { + if ((v = alloc_vpe(tc)) == NULL) { printk(KERN_WARNING "VPE: unable to allocate VPE\n"); - return -ENODEV; + + goto out_reenable; } + v->ntcs = hw_tcs - tclimit; + /* add the tc to the list of this vpe's tc's. */ list_add(&t->tc, &v->tc); /* deactivate all but vpe0 */ - if (i != 0) { + if (tc >= tclimit) { unsigned long tmp = read_vpe_c0_vpeconf0(); tmp &= ~VPECONF0_VPA; @@ -1435,7 +1519,7 @@ static int __init vpe_module_init(void) /* disable multi-threading with TC's */ write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); - if (i != 0) { + if (tc >= vpelimit) { /* * Set config to be the same as vpe0, * particularly kseg0 coherency alg @@ -1447,10 +1531,10 @@ static int __init vpe_module_init(void) /* TC's */ t->pvpe = v; /* set the parent vpe */ - if (i != 0) { + if (tc >= tclimit) { unsigned long tmp; - settc(i); + settc(tc); /* Any TC that is bound to VPE0 gets left as is - in case we are running SMTC on VPE0. A TC that is bound to any @@ -1480,17 +1564,25 @@ static int __init vpe_module_init(void) } } +out_reenable: /* release config state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); + evpe(vpflags); + emt(mtflags); + local_irq_restore(flags); + #ifdef CONFIG_MIPS_APSP_KSPD kspd_events.kspd_sp_exit = kspd_sp_exit; #endif return 0; +out_class: + class_unregister(&vpe_class); out_chrdev: unregister_chrdev(major, module_name); +out: return err; } @@ -1504,7 +1596,7 @@ static void __exit vpe_module_exit(void) } } - device_destroy(mt_class, MKDEV(major, minor)); + class_device_del(&vpe_device); unregister_chrdev(major, module_name); } |