···13131414source "init/Kconfig"15151616+config CPU_MIPS321717+ bool1818+ default y if CPU_MIPS32_R1 || CPU_MIPS32_R21919+2020+config CPU_MIPS642121+ bool2222+ default y if CPU_MIPS64_R1 || CPU_MIPS64_R22323+2424+config CPU_MIPSR12525+ bool2626+ default y if CPU_MIPS32_R1 || CPU_MIPS64_R12727+2828+config CPU_MIPSR22929+ bool3030+ default y if CPU_MIPS32_R2 || CPU_MIPS64_R23131+1632config SYS_SUPPORTS_32BIT_KERNEL1733 bool1834config SYS_SUPPORTS_64BIT_KERNEL···249233 bool "Support for Galileo EV64120 Evaluation board (EXPERIMENTAL)"250234 depends on EXPERIMENTAL251235 select DMA_NONCOHERENT236236+ select IRQ_CPU252237 select HW_HAS_PCI253238 select MIPS_GT64120254239 select SYS_SUPPORTS_32BIT_KERNEL···361344 select BOOT_ELF32362345 select HAVE_STD_PC_SERIAL_PORT363346 select DMA_NONCOHERENT347347+ select IRQ_CPU364348 select GENERIC_ISA_DMA365349 select HW_HAS_PCI366350 select I8259···12951277 bool "Enable prefetches" if CPU_SB1 && !CPU_SB1_PASS_212961278 default y if CPU_MIPS32 || CPU_MIPS64 || CPU_RM7000 || CPU_RM9000 || CPU_R100001297127912801280+config MIPS_MT12811281+ bool "Enable MIPS MT"12821282+12831283+config MIPS_VPE_LOADER12841284+ bool "VPE loader support."12851285+ depends on MIPS_MT12861286+ help12871287+ Includes a loader for loading an elf relocatable object12881288+ onto another VPE and running it.12891289+12901290+config MIPS_VPE_LOADER_TOM12911291+ bool "Load VPE program into memory hidden from linux"12921292+ depends on MIPS_VPE_LOADER12931293+ default y12941294+ help12951295+ The loader can use memory that is present but has been hidden from12961296+ Linux using the kernel command line option "mem=xxMB". It's up to12971297+ you to ensure the amount you put in the option and the space your12981298+ program requires is less or equal to the amount physically present.12991299+13001300+# this should possibly be in drivers/char, but it is rather cpu related. Hmmm13011301+config MIPS_VPE_APSP_API13021302+ bool "Enable support for AP/SP API (RTLX)"13031303+ depends on MIPS_VPE_LOADER13041304+12981305config VTAG_ICACHE12991306 bool "Support for Virtual Tagged I-cache" if CPU_MIPS64 || CPU_MIPS3213001307 default y if CPU_SB1···13771334 Say N here for slightly better performance. You must say Y here for13781335 machines which require flushing of write buffers in software. Saying13791336 Y is the safe option; N may result in kernel malfunction and crashes.13371337+13381338+menu "MIPSR2 Interrupt handling"13391339+ depends on CPU_MIPSR2 && CPU_ADVANCED13401340+13411341+config CPU_MIPSR2_IRQ_VI13421342+ bool "Vectored interrupt mode"13431343+ help13441344+ Vectored interrupt mode allowing faster dispatching of interrupts.13451345+ The board support code needs to be written to take advantage of this13461346+ mode. Compatibility code is included to allow the kernel to run on13471347+ a CPU that does not support vectored interrupts. It's safe to13481348+ say Y here.13491349+13501350+config CPU_MIPSR2_IRQ_EI13511351+ bool "External interrupt controller mode"13521352+ help13531353+ Extended interrupt mode takes advantage of an external interrupt13541354+ controller to allow fast dispatching from many possible interrupt13551355+ sources. Say N unless you know that external interrupt support is13561356+ required.13571357+13581358+config CPU_MIPSR2_SRS13591359+ bool "Make shadow set registers available for interrupt handlers"13601360+ depends on CPU_MIPSR2_IRQ_VI || CPU_MIPSR2_IRQ_EI13611361+ help13621362+ Allow the kernel to use shadow register sets for fast interrupts.13631363+ Interrupt handlers must be specially written to use shadow sets.13641364+ Say N unless you know that shadow register set upport is needed.13651365+endmenu1380136613811367config CPU_HAS_SYNC13821368 bool
···148148 __FINIT149149150150/*151151+ * Vectored interrupt handler.152152+ * This prototype is copied to ebase + n*IntCtl.VS and patched153153+ * to invoke the handler154154+ */155155+NESTED(except_vec_vi, 0, sp)156156+ SAVE_SOME157157+ SAVE_AT158158+ .set push159159+ .set noreorder160160+EXPORT(except_vec_vi_lui)161161+ lui v0, 0 /* Patched */162162+ j except_vec_vi_handler163163+EXPORT(except_vec_vi_ori)164164+ ori v0, 0 /* Patched */165165+ .set pop166166+ END(except_vec_vi)167167+EXPORT(except_vec_vi_end)168168+169169+/*170170+ * Common Vectored Interrupt code171171+ * Complete the register saves and invoke the handler which is passed in $v0172172+ */173173+NESTED(except_vec_vi_handler, 0, sp)174174+ SAVE_TEMP175175+ SAVE_STATIC176176+ CLI177177+ move a0, sp178178+ jalr v0179179+ j ret_from_irq180180+ END(except_vec_vi_handler)181181+182182+/*151183 * EJTAG debug exception handler.152184 */153185NESTED(ejtag_debug_handler, PT_SIZE, sp)
+4-4
arch/mips/kernel/irq-msc01.c
···7474static void level_mask_and_ack_msc_irq(unsigned int irq)7575{7676 mask_msc_irq(irq);7777- if (!cpu_has_ei)7777+ if (!cpu_has_veic)7878 MSCIC_WRITE(MSC01_IC_EOI, 0);7979}8080···8484static void edge_mask_and_ack_msc_irq(unsigned int irq)8585{8686 mask_msc_irq(irq);8787- if (!cpu_has_ei)8787+ if (!cpu_has_veic)8888 MSCIC_WRITE(MSC01_IC_EOI, 0);8989 else {9090 u32 r;···166166 switch (imp->im_type) {167167 case MSC01_IRQ_EDGE:168168 irq_desc[base+n].handler = &msc_edgeirq_type;169169- if (cpu_has_ei)169169+ if (cpu_has_veic)170170 MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT);171171 else172172 MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT | imp->im_lvl);173173 break;174174 case MSC01_IRQ_LEVEL:175175 irq_desc[base+n].handler = &msc_levelirq_type;176176- if (cpu_has_ei)176176+ if (cpu_has_veic)177177 MSCIC_WRITE(MSC01_IC_SUP+n*8, 0);178178 else179179 MSCIC_WRITE(MSC01_IC_SUP+n*8, imp->im_lvl);
+341
arch/mips/kernel/rtlx.c
···11+/*22+ * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved.33+ *44+ * This program is free software; you can distribute it and/or modify it55+ * under the terms of the GNU General Public License (Version 2) as66+ * published by the Free Software Foundation.77+ *88+ * This program is distributed in the hope it will be useful, but WITHOUT99+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or1010+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License1111+ * for more details.1212+ *1313+ * You should have received a copy of the GNU General Public License along1414+ * with this program; if not, write to the Free Software Foundation, Inc.,1515+ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.1616+ *1717+ */1818+1919+#include <linux/kernel.h>2020+#include <linux/module.h>2121+#include <linux/fs.h>2222+#include <linux/init.h>2323+#include <asm/uaccess.h>2424+#include <linux/slab.h>2525+#include <linux/list.h>2626+#include <linux/vmalloc.h>2727+#include <linux/elf.h>2828+#include <linux/seq_file.h>2929+#include <linux/syscalls.h>3030+#include <linux/moduleloader.h>3131+#include <linux/interrupt.h>3232+#include <linux/poll.h>3333+#include <linux/sched.h>3434+#include <linux/wait.h>3535+#include <asm/mipsmtregs.h>3636+#include <asm/cacheflush.h>3737+#include <asm/atomic.h>3838+#include <asm/cpu.h>3939+#include <asm/processor.h>4040+#include <asm/system.h>4141+#include <asm/rtlx.h>4242+4343+#define RTLX_MAJOR 644444+#define RTLX_TARG_VPE 14545+4646+struct rtlx_info *rtlx;4747+static int major;4848+static char module_name[] = "rtlx";4949+static inline int spacefree(int read, int write, int size);5050+5151+static struct chan_waitqueues {5252+ wait_queue_head_t rt_queue;5353+ wait_queue_head_t lx_queue;5454+} channel_wqs[RTLX_CHANNELS];5555+5656+static struct irqaction irq;5757+static int irq_num;5858+5959+extern void *vpe_get_shared(int index);6060+6161+static void rtlx_dispatch(struct pt_regs *regs)6262+{6363+ do_IRQ(MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ, regs);6464+}6565+6666+irqreturn_t rtlx_interrupt(int irq, void *dev_id, struct pt_regs *regs)6767+{6868+ irqreturn_t r = IRQ_HANDLED;6969+ int i;7070+7171+ for (i = 0; i < RTLX_CHANNELS; i++) {7272+ struct rtlx_channel *chan = &rtlx->channel[i];7373+7474+ if (chan->lx_read != chan->lx_write)7575+ wake_up_interruptible(&channel_wqs[i].lx_queue);7676+ }7777+7878+ return r;7979+}8080+8181+void dump_rtlx(void)8282+{8383+ int i;8484+8585+ printk("id 0x%lx state %d\n", rtlx->id, rtlx->state);8686+8787+ for (i = 0; i < RTLX_CHANNELS; i++) {8888+ struct rtlx_channel *chan = &rtlx->channel[i];8989+9090+ printk(" rt_state %d lx_state %d buffer_size %d\n",9191+ chan->rt_state, chan->lx_state, chan->buffer_size);9292+9393+ printk(" rt_read %d rt_write %d\n",9494+ chan->rt_read, chan->rt_write);9595+9696+ printk(" lx_read %d lx_write %d\n",9797+ chan->lx_read, chan->lx_write);9898+9999+ printk(" rt_buffer <%s>\n", chan->rt_buffer);100100+ printk(" lx_buffer <%s>\n", chan->lx_buffer);101101+ }102102+}103103+104104+/* call when we have the address of the shared structure from the SP side. */105105+static int rtlx_init(struct rtlx_info *rtlxi)106106+{107107+ int i;108108+109109+ if (rtlxi->id != RTLX_ID) {110110+ printk(KERN_WARNING "no valid RTLX id at 0x%p\n", rtlxi);111111+ return (-ENOEXEC);112112+ }113113+114114+ /* initialise the wait queues */115115+ for (i = 0; i < RTLX_CHANNELS; i++) {116116+ init_waitqueue_head(&channel_wqs[i].rt_queue);117117+ init_waitqueue_head(&channel_wqs[i].lx_queue);118118+ }119119+120120+ /* set up for interrupt handling */121121+ memset(&irq, 0, sizeof(struct irqaction));122122+123123+ if (cpu_has_vint) {124124+ set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch);125125+ }126126+127127+ irq_num = MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ;128128+ irq.handler = rtlx_interrupt;129129+ irq.flags = SA_INTERRUPT;130130+ irq.name = "RTLX";131131+ irq.dev_id = rtlx;132132+ setup_irq(irq_num, &irq);133133+134134+ rtlx = rtlxi;135135+ return (0);136136+}137137+138138+/* only allow one open process at a time to open each channel */139139+static int rtlx_open(struct inode *inode, struct file *filp)140140+{141141+ int minor, ret;142142+ struct rtlx_channel *chan;143143+144144+ /* assume only 1 device at the mo. */145145+ minor = MINOR(inode->i_rdev);146146+147147+ if (rtlx == NULL) {148148+ struct rtlx_info **p;149149+ if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) {150150+ printk(" vpe_get_shared is NULL. Has an SP program been loaded?\n");151151+ return (-EFAULT);152152+ }153153+154154+ if (*p == NULL) {155155+ printk(" vpe_shared %p %p\n", p, *p);156156+ return (-EFAULT);157157+ }158158+159159+ if ((ret = rtlx_init(*p)) < 0)160160+ return (ret);161161+ }162162+163163+ chan = &rtlx->channel[minor];164164+165165+ /* already open? */166166+ if (chan->lx_state == RTLX_STATE_OPENED)167167+ return (-EBUSY);168168+169169+ chan->lx_state = RTLX_STATE_OPENED;170170+ return (0);171171+}172172+173173+static int rtlx_release(struct inode *inode, struct file *filp)174174+{175175+ int minor;176176+177177+ minor = MINOR(inode->i_rdev);178178+ rtlx->channel[minor].lx_state = RTLX_STATE_UNUSED;179179+ return (0);180180+}181181+182182+static unsigned int rtlx_poll(struct file *file, poll_table * wait)183183+{184184+ int minor;185185+ unsigned int mask = 0;186186+ struct rtlx_channel *chan;187187+188188+ minor = MINOR(file->f_dentry->d_inode->i_rdev);189189+ chan = &rtlx->channel[minor];190190+191191+ poll_wait(file, &channel_wqs[minor].rt_queue, wait);192192+ poll_wait(file, &channel_wqs[minor].lx_queue, wait);193193+194194+ /* data available to read? */195195+ if (chan->lx_read != chan->lx_write)196196+ mask |= POLLIN | POLLRDNORM;197197+198198+ /* space to write */199199+ if (spacefree(chan->rt_read, chan->rt_write, chan->buffer_size))200200+ mask |= POLLOUT | POLLWRNORM;201201+202202+ return (mask);203203+}204204+205205+static ssize_t rtlx_read(struct file *file, char __user * buffer, size_t count,206206+ loff_t * ppos)207207+{208208+ size_t fl = 0L;209209+ int minor;210210+ struct rtlx_channel *lx;211211+ DECLARE_WAITQUEUE(wait, current);212212+213213+ minor = MINOR(file->f_dentry->d_inode->i_rdev);214214+ lx = &rtlx->channel[minor];215215+216216+ /* data available? */217217+ if (lx->lx_write == lx->lx_read) {218218+ if (file->f_flags & O_NONBLOCK)219219+ return (0); // -EAGAIN makes cat whinge220220+221221+ /* go to sleep */222222+ add_wait_queue(&channel_wqs[minor].lx_queue, &wait);223223+ set_current_state(TASK_INTERRUPTIBLE);224224+225225+ while (lx->lx_write == lx->lx_read)226226+ schedule();227227+228228+ set_current_state(TASK_RUNNING);229229+ remove_wait_queue(&channel_wqs[minor].lx_queue, &wait);230230+231231+ /* back running */232232+ }233233+234234+ /* find out how much in total */235235+ count = min( count,236236+ (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) % lx->buffer_size);237237+238238+ /* then how much from the read pointer onwards */239239+ fl = min( count, (size_t)lx->buffer_size - lx->lx_read);240240+241241+ copy_to_user (buffer, &lx->lx_buffer[lx->lx_read], fl);242242+243243+ /* and if there is anything left at the beginning of the buffer */244244+ if ( count - fl )245245+ copy_to_user (buffer + fl, lx->lx_buffer, count - fl);246246+247247+ /* update the index */248248+ lx->lx_read += count;249249+ lx->lx_read %= lx->buffer_size;250250+251251+ return (count);252252+}253253+254254+static inline int spacefree(int read, int write, int size)255255+{256256+ if (read == write) {257257+ /* never fill the buffer completely, so indexes are always equal if empty258258+ and only empty, or !equal if data available */259259+ return (size - 1);260260+ }261261+262262+ return ((read + size - write) % size) - 1;263263+}264264+265265+static ssize_t rtlx_write(struct file *file, const char __user * buffer,266266+ size_t count, loff_t * ppos)267267+{268268+ int minor;269269+ struct rtlx_channel *rt;270270+ size_t fl;271271+ DECLARE_WAITQUEUE(wait, current);272272+273273+ minor = MINOR(file->f_dentry->d_inode->i_rdev);274274+ rt = &rtlx->channel[minor];275275+276276+ /* any space left... */277277+ if (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) {278278+279279+ if (file->f_flags & O_NONBLOCK)280280+ return (-EAGAIN);281281+282282+ add_wait_queue(&channel_wqs[minor].rt_queue, &wait);283283+ set_current_state(TASK_INTERRUPTIBLE);284284+285285+ while (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size))286286+ schedule();287287+288288+ set_current_state(TASK_RUNNING);289289+ remove_wait_queue(&channel_wqs[minor].rt_queue, &wait);290290+ }291291+292292+ /* total number of bytes to copy */293293+ count = min( count, (size_t)spacefree(rt->rt_read, rt->rt_write, rt->buffer_size) );294294+295295+ /* first bit from write pointer to the end of the buffer, or count */296296+ fl = min(count, (size_t) rt->buffer_size - rt->rt_write);297297+298298+ copy_from_user(&rt->rt_buffer[rt->rt_write], buffer, fl);299299+300300+ /* if there's any left copy to the beginning of the buffer */301301+ if( count - fl )302302+ copy_from_user(rt->rt_buffer, buffer + fl, count - fl);303303+304304+ rt->rt_write += count;305305+ rt->rt_write %= rt->buffer_size;306306+307307+ return(count);308308+}309309+310310+static struct file_operations rtlx_fops = {311311+ .owner = THIS_MODULE,312312+ .open = rtlx_open,313313+ .release = rtlx_release,314314+ .write = rtlx_write,315315+ .read = rtlx_read,316316+ .poll = rtlx_poll317317+};318318+319319+static int rtlx_module_init(void)320320+{321321+ if ((major = register_chrdev(RTLX_MAJOR, module_name, &rtlx_fops)) < 0) {322322+ printk("rtlx_module_init: unable to register device\n");323323+ return (-EBUSY);324324+ }325325+326326+ if (major == 0)327327+ major = RTLX_MAJOR;328328+329329+ return (0);330330+}331331+332332+static void rtlx_module_exit(void)333333+{334334+ unregister_chrdev(major, module_name);335335+}336336+337337+module_init(rtlx_module_init);338338+module_exit(rtlx_module_exit);339339+MODULE_DESCRIPTION("MIPS RTLX");340340+MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc");341341+MODULE_LICENSE("GPL");
+214-13
arch/mips/kernel/traps.c
···2020#include <linux/smp_lock.h>2121#include <linux/spinlock.h>2222#include <linux/kallsyms.h>2323+#include <linux/bootmem.h>23242425#include <asm/bootinfo.h>2526#include <asm/branch.h>···65646665void (*board_be_init)(void);6766int (*board_be_handler)(struct pt_regs *regs, int is_fixup);6767+void (*board_nmi_handler_setup)(void);6868+void (*board_ejtag_handler_setup)(void);6969+void (*board_bind_eic_interrupt)(int irq, int regset);68706971/*7072 * These constant is for searching for possible module text segments.···817813 (regs->cp0_cause & 0x7f) >> 2);818814}819815816816+asmlinkage void do_default_vi(struct pt_regs *regs)817817+{818818+ show_regs(regs);819819+ panic("Caught unexpected vectored interrupt.");820820+}821821+820822/*821823 * Some MIPS CPUs can enable/disable for cache parity detection, but do822824 * it different ways.···931921 while(1) ;932922}933923924924+#define VECTORSPACING 0x100 /* for EI/VI mode */925925+926926+unsigned long ebase;934927unsigned long exception_handlers[32];928928+unsigned long vi_handlers[64];935929936930/*937931 * As a side effect of the way this is implemented we're limited···949935950936 exception_handlers[n] = handler;951937 if (n == 0 && cpu_has_divec) {952952- *(volatile u32 *)(CAC_BASE + 0x200) = 0x08000000 |938938+ *(volatile u32 *)(ebase + 0x200) = 0x08000000 |953939 (0x03ffffff & (handler >> 2));954954- flush_icache_range(CAC_BASE + 0x200, CAC_BASE + 0x204);940940+ flush_icache_range(ebase + 0x200, ebase + 0x204);955941 }956942 return (void *)old_handler;957943}944944+945945+#ifdef CONFIG_CPU_MIPSR2946946+/*947947+ * Shadow register allocation948948+ * FIXME: SMP...949949+ */950950+951951+/* MIPSR2 shadow register sets */952952+struct shadow_registers {953953+ spinlock_t sr_lock; /* */954954+ int sr_supported; /* Number of shadow register sets supported */955955+ int sr_allocated; /* Bitmap of allocated shadow registers */956956+} shadow_registers;957957+958958+void mips_srs_init(void)959959+{960960+#ifdef CONFIG_CPU_MIPSR2_SRS961961+ shadow_registers.sr_supported = ((read_c0_srsctl() >> 26) & 0x0f) + 1;962962+ printk ("%d MIPSR2 register sets available\n", shadow_registers.sr_supported);963963+#else964964+ shadow_registers.sr_supported = 1;965965+#endif966966+ shadow_registers.sr_allocated = 1; /* Set 0 used by kernel */967967+ spin_lock_init(&shadow_registers.sr_lock);968968+}969969+970970+int mips_srs_max(void)971971+{972972+ return shadow_registers.sr_supported;973973+}974974+975975+int mips_srs_alloc (void)976976+{977977+ struct shadow_registers *sr = &shadow_registers;978978+ unsigned long flags;979979+ int set;980980+981981+ spin_lock_irqsave(&sr->sr_lock, flags);982982+983983+ for (set = 0; set < sr->sr_supported; set++) {984984+ if ((sr->sr_allocated & (1 << set)) == 0) {985985+ sr->sr_allocated |= 1 << set;986986+ spin_unlock_irqrestore(&sr->sr_lock, flags);987987+ return set;988988+ }989989+ }990990+991991+ /* None available */992992+ spin_unlock_irqrestore(&sr->sr_lock, flags);993993+ return -1;994994+}995995+996996+void mips_srs_free (int set)997997+{998998+ struct shadow_registers *sr = &shadow_registers;999999+ unsigned long flags;10001000+10011001+ spin_lock_irqsave(&sr->sr_lock, flags);10021002+ sr->sr_allocated &= ~(1 << set);10031003+ spin_unlock_irqrestore(&sr->sr_lock, flags);10041004+}10051005+10061006+void *set_vi_srs_handler (int n, void *addr, int srs)10071007+{10081008+ unsigned long handler;10091009+ unsigned long old_handler = vi_handlers[n];10101010+ u32 *w;10111011+ unsigned char *b;10121012+10131013+ if (!cpu_has_veic && !cpu_has_vint)10141014+ BUG();10151015+10161016+ if (addr == NULL) {10171017+ handler = (unsigned long) do_default_vi;10181018+ srs = 0;10191019+ }10201020+ else10211021+ handler = (unsigned long) addr;10221022+ vi_handlers[n] = (unsigned long) addr;10231023+10241024+ b = (unsigned char *)(ebase + 0x200 + n*VECTORSPACING);10251025+10261026+ if (srs >= mips_srs_max())10271027+ panic("Shadow register set %d not supported", srs);10281028+10291029+ if (cpu_has_veic) {10301030+ if (board_bind_eic_interrupt)10311031+ board_bind_eic_interrupt (n, srs);10321032+ }10331033+ else if (cpu_has_vint) {10341034+ /* SRSMap is only defined if shadow sets are implemented */10351035+ if (mips_srs_max() > 1)10361036+ change_c0_srsmap (0xf << n*4, srs << n*4);10371037+ }10381038+10391039+ if (srs == 0) {10401040+ /*10411041+ * If no shadow set is selected then use the default handler10421042+ * that does normal register saving and a standard interrupt exit10431043+ */10441044+10451045+ extern char except_vec_vi, except_vec_vi_lui;10461046+ extern char except_vec_vi_ori, except_vec_vi_end;10471047+ const int handler_len = &except_vec_vi_end - &except_vec_vi;10481048+ const int lui_offset = &except_vec_vi_lui - &except_vec_vi;10491049+ const int ori_offset = &except_vec_vi_ori - &except_vec_vi;10501050+10511051+ if (handler_len > VECTORSPACING) {10521052+ /*10531053+ * Sigh... panicing won't help as the console10541054+ * is probably not configured :(10551055+ */10561056+ panic ("VECTORSPACING too small");10571057+ }10581058+10591059+ memcpy (b, &except_vec_vi, handler_len);10601060+ w = (u32 *)(b + lui_offset);10611061+ *w = (*w & 0xffff0000) | (((u32)handler >> 16) & 0xffff);10621062+ w = (u32 *)(b + ori_offset);10631063+ *w = (*w & 0xffff0000) | ((u32)handler & 0xffff);10641064+ flush_icache_range((unsigned long)b, (unsigned long)(b+handler_len));10651065+ }10661066+ else {10671067+ /*10681068+ * In other cases jump directly to the interrupt handler10691069+ *10701070+ * It is the handlers responsibility to save registers if required10711071+ * (eg hi/lo) and return from the exception using "eret"10721072+ */10731073+ w = (u32 *)b;10741074+ *w++ = 0x08000000 | (((u32)handler >> 2) & 0x03fffff); /* j handler */10751075+ *w = 0;10761076+ flush_icache_range((unsigned long)b, (unsigned long)(b+8));10771077+ }10781078+10791079+ return (void *)old_handler;10801080+}10811081+10821082+void *set_vi_handler (int n, void *addr)10831083+{10841084+ return set_vi_srs_handler (n, addr, 0);10851085+}10861086+#endif95810879591088/*9601089 * This is used by native signal handling···11731016 if (cpu_has_dsp)11741017 set_c0_status(ST0_MX);1175101810191019+#ifdef CONFIG_CPU_MIPSR210201020+ write_c0_hwrena (0x0000000f); /* Allow rdhwr to all registers */10211021+#endif10221022+11761023 /*11771177- * Some MIPS CPUs have a dedicated interrupt vector which reduces the11781178- * interrupt processing overhead. Use it where available.10241024+ * Interrupt handling.11791025 */10261026+ if (cpu_has_veic || cpu_has_vint) {10271027+ write_c0_ebase (ebase);10281028+ /* Setting vector spacing enables EI/VI mode */10291029+ change_c0_intctl (0x3e0, VECTORSPACING);10301030+ }11801031 if (cpu_has_divec)11811032 set_c0_cause(CAUSEF_IV);11821033···12001035 tlb_init();12011036}1202103710381038+/* Install CPU exception handler */10391039+void __init set_handler (unsigned long offset, void *addr, unsigned long size)10401040+{10411041+ memcpy((void *)(ebase + offset), addr, size);10421042+ flush_icache_range(ebase + offset, ebase + offset + size);10431043+}10441044+10451045+/* Install uncached CPU exception handler */10461046+void __init set_uncached_handler (unsigned long offset, void *addr, unsigned long size)10471047+{10481048+#ifdef CONFIG_32BIT10491049+ unsigned long uncached_ebase = KSEG1ADDR(ebase);10501050+#endif10511051+#ifdef CONFIG_64BIT10521052+ unsigned long uncached_ebase = TO_UNCAC(ebase);10531053+#endif10541054+10551055+ memcpy((void *)(uncached_ebase + offset), addr, size);10561056+}10571057+12031058void __init trap_init(void)12041059{12051060 extern char except_vec3_generic, except_vec3_r4000;12061206- extern char except_vec_ejtag_debug;12071061 extern char except_vec4;12081062 unsigned long i;10631063+10641064+ if (cpu_has_veic || cpu_has_vint)10651065+ ebase = (unsigned long) alloc_bootmem_low_pages (0x200 + VECTORSPACING*64);10661066+ else10671067+ ebase = CAC_BASE;10681068+10691069+#ifdef CONFIG_CPU_MIPSR210701070+ mips_srs_init();10711071+#endif1209107212101073 per_cpu_trap_init();12111074···12421049 * This will be overriden later as suitable for a particular12431050 * configuration.12441051 */12451245- memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80);10521052+ set_handler(0x180, &except_vec3_generic, 0x80);1246105312471054 /*12481055 * Setup default vectors···12541061 * Copy the EJTAG debug exception vector handler code to it's final12551062 * destination.12561063 */12571257- if (cpu_has_ejtag)12581258- memcpy((void *)(CAC_BASE + 0x300), &except_vec_ejtag_debug, 0x80);10641064+ if (cpu_has_ejtag && board_ejtag_handler_setup)10651065+ board_ejtag_handler_setup ();1259106612601067 /*12611068 * Only some CPUs have the watch exceptions.···12641071 set_except_vector(23, handle_watch);1265107212661073 /*12671267- * Some MIPS CPUs have a dedicated interrupt vector which reduces the12681268- * interrupt processing overhead. Use it where available.10741074+ * Initialise interrupt handlers12691075 */12701270- if (cpu_has_divec)12711271- memcpy((void *)(CAC_BASE + 0x200), &except_vec4, 0x8);10761076+ if (cpu_has_veic || cpu_has_vint) {10771077+ int nvec = cpu_has_veic ? 64 : 8;10781078+ for (i = 0; i < nvec; i++)10791079+ set_vi_handler (i, NULL);10801080+ }10811081+ else if (cpu_has_divec)10821082+ set_handler(0x200, &except_vec4, 0x8);1272108312731084 /*12741085 * Some CPUs can enable/disable for cache parity detection, but does···13191122 //set_except_vector(15, handle_ndc);13201123 }1321112411251125+11261126+ if (board_nmi_handler_setup)11271127+ board_nmi_handler_setup();11281128+13221129 if (cpu_has_fpu && !cpu_has_nofpuex)13231130 set_except_vector(15, handle_fpe);13241131···13471146 signal32_init();13481147#endif1349114813501350- flush_icache_range(CAC_BASE, CAC_BASE + 0x400);11491149+ flush_icache_range(ebase, ebase + 0x400);13511150}
+1295
arch/mips/kernel/vpe.c
···11+/*22+ * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved.33+ *44+ * This program is free software; you can distribute it and/or modify it55+ * under the terms of the GNU General Public License (Version 2) as66+ * published by the Free Software Foundation.77+ *88+ * This program is distributed in the hope it will be useful, but WITHOUT99+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or1010+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License1111+ * for more details.1212+ *1313+ * You should have received a copy of the GNU General Public License along1414+ * with this program; if not, write to the Free Software Foundation, Inc.,1515+ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.1616+ *1717+ */1818+1919+/*2020+ * VPE support module2121+ *2222+ * Provides support for loading a MIPS SP program on VPE1.2323+ * The SP enviroment is rather simple, no tlb's. It needs to be relocatable2424+ * (or partially linked). You should initialise your stack in the startup2525+ * code. This loader looks for the symbol __start and sets up2626+ * execution to resume from there. The MIPS SDE kit contains suitable examples.2727+ *2828+ * To load and run, simply cat a SP 'program file' to /dev/vpe1.2929+ * i.e cat spapp >/dev/vpe1.3030+ *3131+ * You'll need to have the following device files.3232+ * mknod /dev/vpe0 c 63 03333+ * mknod /dev/vpe1 c 63 13434+ */3535+3636+#include <linux/kernel.h>3737+#include <linux/module.h>3838+#include <linux/fs.h>3939+#include <linux/init.h>4040+#include <asm/uaccess.h>4141+#include <linux/slab.h>4242+#include <linux/list.h>4343+#include <linux/vmalloc.h>4444+#include <linux/elf.h>4545+#include <linux/seq_file.h>4646+#include <linux/syscalls.h>4747+#include <linux/moduleloader.h>4848+#include <linux/interrupt.h>4949+#include <linux/poll.h>5050+#include <linux/bootmem.h>5151+#include <asm/mipsregs.h>5252+#include <asm/cacheflush.h>5353+#include <asm/atomic.h>5454+#include <asm/cpu.h>5555+#include <asm/processor.h>5656+#include <asm/system.h>5757+5858+typedef void *vpe_handle;5959+6060+// defined here because the kernel module loader doesn't have6161+// anything to do with it.6262+#define SHN_MIPS_SCOMMON 0xff036363+6464+#ifndef ARCH_SHF_SMALL6565+#define ARCH_SHF_SMALL 06666+#endif6767+6868+/* If this is set, the section belongs in the init part of the module */6969+#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))7070+7171+// temp number,7272+#define VPE_MAJOR 637373+7474+static char module_name[] = "vpe";7575+static int major = 0;7676+7777+/* grab the likely amount of memory we will need. */7878+#ifdef CONFIG_MIPS_VPE_LOADER_TOM7979+#define P_SIZE (2 * 1024 * 1024)8080+#else8181+/* add an overhead to the max kmalloc size for non-striped symbols/etc */8282+#define P_SIZE (256 * 1024)8383+#endif8484+8585+#define MAX_VPES 168686+8787+enum vpe_state {8888+ VPE_STATE_UNUSED = 0,8989+ VPE_STATE_INUSE,9090+ VPE_STATE_RUNNING9191+};9292+9393+enum tc_state {9494+ TC_STATE_UNUSED = 0,9595+ TC_STATE_INUSE,9696+ TC_STATE_RUNNING,9797+ TC_STATE_DYNAMIC9898+};9999+100100+struct vpe;101101+typedef struct tc {102102+ enum tc_state state;103103+ int index;104104+105105+ /* parent VPE */106106+ struct vpe *pvpe;107107+108108+ /* The list of TC's with this VPE */109109+ struct list_head tc;110110+111111+ /* The global list of tc's */112112+ struct list_head list;113113+} tc_t;114114+115115+typedef struct vpe {116116+ enum vpe_state state;117117+118118+ /* (device) minor associated with this vpe */119119+ int minor;120120+121121+ /* elfloader stuff */122122+ void *load_addr;123123+ u32 len;124124+ char *pbuffer;125125+ u32 plen;126126+127127+ unsigned long __start;128128+129129+ /* tc's associated with this vpe */130130+ struct list_head tc;131131+132132+ /* The list of vpe's */133133+ struct list_head list;134134+135135+ /* shared symbol address */136136+ void *shared_ptr;137137+} vpe_t;138138+139139+struct vpecontrol_ {140140+ /* Virtual processing elements */141141+ struct list_head vpe_list;142142+143143+ /* Thread contexts */144144+ struct list_head tc_list;145145+} vpecontrol;146146+147147+static void release_progmem(void *ptr);148148+static void dump_vpe(vpe_t * v);149149+extern void save_gp_address(unsigned int secbase, unsigned int rel);150150+151151+/* get the vpe associated with this minor */152152+struct vpe *get_vpe(int minor)153153+{154154+ struct vpe *v;155155+156156+ list_for_each_entry(v, &vpecontrol.vpe_list, list) {157157+ if (v->minor == minor)158158+ return v;159159+ }160160+161161+ printk(KERN_DEBUG "VPE: get_vpe minor %d not found\n", minor);162162+ return NULL;163163+}164164+165165+/* get the vpe associated with this minor */166166+struct tc *get_tc(int index)167167+{168168+ struct tc *t;169169+170170+ list_for_each_entry(t, &vpecontrol.tc_list, list) {171171+ if (t->index == index)172172+ return t;173173+ }174174+175175+ printk(KERN_DEBUG "VPE: get_tc index %d not found\n", index);176176+177177+ return NULL;178178+}179179+180180+struct tc *get_tc_unused(void)181181+{182182+ struct tc *t;183183+184184+ list_for_each_entry(t, &vpecontrol.tc_list, list) {185185+ if (t->state == TC_STATE_UNUSED)186186+ return t;187187+ }188188+189189+ printk(KERN_DEBUG "VPE: All TC's are in use\n");190190+191191+ return NULL;192192+}193193+194194+/* allocate a vpe and associate it with this minor (or index) */195195+struct vpe *alloc_vpe(int minor)196196+{197197+ struct vpe *v;198198+199199+ if ((v = kmalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {200200+ printk(KERN_WARNING "VPE: alloc_vpe no mem\n");201201+ return NULL;202202+ }203203+204204+ memset(v, 0, sizeof(struct vpe));205205+206206+ INIT_LIST_HEAD(&v->tc);207207+ list_add_tail(&v->list, &vpecontrol.vpe_list);208208+209209+ v->minor = minor;210210+ return v;211211+}212212+213213+/* allocate a tc. At startup only tc0 is running, all other can be halted. */214214+struct tc *alloc_tc(int index)215215+{216216+ struct tc *t;217217+218218+ if ((t = kmalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) {219219+ printk(KERN_WARNING "VPE: alloc_tc no mem\n");220220+ return NULL;221221+ }222222+223223+ memset(t, 0, sizeof(struct tc));224224+225225+ INIT_LIST_HEAD(&t->tc);226226+ list_add_tail(&t->list, &vpecontrol.tc_list);227227+228228+ t->index = index;229229+230230+ return t;231231+}232232+233233+/* clean up and free everything */234234+void release_vpe(struct vpe *v)235235+{236236+ list_del(&v->list);237237+ if (v->load_addr)238238+ release_progmem(v);239239+ kfree(v);240240+}241241+242242+void dump_mtregs(void)243243+{244244+ unsigned long val;245245+246246+ val = read_c0_config3();247247+ printk("config3 0x%lx MT %ld\n", val,248248+ (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);249249+250250+ val = read_c0_mvpconf0();251251+ printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,252252+ (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,253253+ val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);254254+255255+ val = read_c0_mvpcontrol();256256+ printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,257257+ (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,258258+ (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,259259+ (val & MVPCONTROL_EVP));260260+261261+ val = read_c0_vpeconf0();262262+ printk("VPEConf0 0x%lx MVP %ld\n", val,263263+ (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT);264264+}265265+266266+/* Find some VPE program space */267267+static void *alloc_progmem(u32 len)268268+{269269+#ifdef CONFIG_MIPS_VPE_LOADER_TOM270270+ /* this means you must tell linux to use less memory than you physically have */271271+ return (void *)((max_pfn * PAGE_SIZE) + KSEG0);272272+#else273273+ // simple grab some mem for now274274+ return kmalloc(len, GFP_KERNEL);275275+#endif276276+}277277+278278+static void release_progmem(void *ptr)279279+{280280+#ifndef CONFIG_MIPS_VPE_LOADER_TOM281281+ kfree(ptr);282282+#endif283283+}284284+285285+/* Update size with this section: return offset. */286286+static long get_offset(unsigned long *size, Elf_Shdr * sechdr)287287+{288288+ long ret;289289+290290+ ret = ALIGN(*size, sechdr->sh_addralign ? : 1);291291+ *size = ret + sechdr->sh_size;292292+ return ret;293293+}294294+295295+/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld296296+ might -- code, read-only data, read-write data, small data. Tally297297+ sizes, and place the offsets into sh_entsize fields: high bit means it298298+ belongs in init. */299299+static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,300300+ Elf_Shdr * sechdrs, const char *secstrings)301301+{302302+ static unsigned long const masks[][2] = {303303+ /* NOTE: all executable code must be the first section304304+ * in this array; otherwise modify the text_size305305+ * finder in the two loops below */306306+ {SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},307307+ {SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},308308+ {SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},309309+ {ARCH_SHF_SMALL | SHF_ALLOC, 0}310310+ };311311+ unsigned int m, i;312312+313313+ for (i = 0; i < hdr->e_shnum; i++)314314+ sechdrs[i].sh_entsize = ~0UL;315315+316316+ for (m = 0; m < ARRAY_SIZE(masks); ++m) {317317+ for (i = 0; i < hdr->e_shnum; ++i) {318318+ Elf_Shdr *s = &sechdrs[i];319319+320320+ // || strncmp(secstrings + s->sh_name, ".init", 5) == 0)321321+ if ((s->sh_flags & masks[m][0]) != masks[m][0]322322+ || (s->sh_flags & masks[m][1])323323+ || s->sh_entsize != ~0UL)324324+ continue;325325+ s->sh_entsize = get_offset(&mod->core_size, s);326326+ }327327+328328+ if (m == 0)329329+ mod->core_text_size = mod->core_size;330330+331331+ }332332+}333333+334334+335335+/* from module-elf32.c, but subverted a little */336336+337337+struct mips_hi16 {338338+ struct mips_hi16 *next;339339+ Elf32_Addr *addr;340340+ Elf32_Addr value;341341+};342342+343343+static struct mips_hi16 *mips_hi16_list;344344+static unsigned int gp_offs, gp_addr;345345+346346+static int apply_r_mips_none(struct module *me, uint32_t *location,347347+ Elf32_Addr v)348348+{349349+ return 0;350350+}351351+352352+static int apply_r_mips_gprel16(struct module *me, uint32_t *location,353353+ Elf32_Addr v)354354+{355355+ int rel;356356+357357+ if( !(*location & 0xffff) ) {358358+ rel = (int)v - gp_addr;359359+ }360360+ else {361361+ /* .sbss + gp(relative) + offset */362362+ /* kludge! */363363+ rel = (int)(short)((int)v + gp_offs +364364+ (int)(short)(*location & 0xffff) - gp_addr);365365+ }366366+367367+ if( (rel > 32768) || (rel < -32768) ) {368368+ printk(KERN_ERR369369+ "apply_r_mips_gprel16: relative address out of range 0x%x %d\n",370370+ rel, rel);371371+ return -ENOEXEC;372372+ }373373+374374+ *location = (*location & 0xffff0000) | (rel & 0xffff);375375+376376+ return 0;377377+}378378+379379+static int apply_r_mips_pc16(struct module *me, uint32_t *location,380380+ Elf32_Addr v)381381+{382382+ int rel;383383+ rel = (((unsigned int)v - (unsigned int)location));384384+ rel >>= 2; // because the offset is in _instructions_ not bytes.385385+ rel -= 1; // and one instruction less due to the branch delay slot.386386+387387+ if( (rel > 32768) || (rel < -32768) ) {388388+ printk(KERN_ERR389389+ "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);390390+ return -ENOEXEC;391391+ }392392+393393+ *location = (*location & 0xffff0000) | (rel & 0xffff);394394+395395+ return 0;396396+}397397+398398+static int apply_r_mips_32(struct module *me, uint32_t *location,399399+ Elf32_Addr v)400400+{401401+ *location += v;402402+403403+ return 0;404404+}405405+406406+static int apply_r_mips_26(struct module *me, uint32_t *location,407407+ Elf32_Addr v)408408+{409409+ if (v % 4) {410410+ printk(KERN_ERR "module %s: dangerous relocation mod4\n", me->name);411411+ return -ENOEXEC;412412+ }413413+414414+/* Not desperately convinced this is a good check of an overflow condition415415+ anyway. But it gets in the way of handling undefined weak symbols which416416+ we want to set to zero.417417+ if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {418418+ printk(KERN_ERR419419+ "module %s: relocation overflow\n",420420+ me->name);421421+ return -ENOEXEC;422422+ }423423+*/424424+425425+ *location = (*location & ~0x03ffffff) |426426+ ((*location + (v >> 2)) & 0x03ffffff);427427+ return 0;428428+}429429+430430+static int apply_r_mips_hi16(struct module *me, uint32_t *location,431431+ Elf32_Addr v)432432+{433433+ struct mips_hi16 *n;434434+435435+ /*436436+ * We cannot relocate this one now because we don't know the value of437437+ * the carry we need to add. Save the information, and let LO16 do the438438+ * actual relocation.439439+ */440440+ n = kmalloc(sizeof *n, GFP_KERNEL);441441+ if (!n)442442+ return -ENOMEM;443443+444444+ n->addr = location;445445+ n->value = v;446446+ n->next = mips_hi16_list;447447+ mips_hi16_list = n;448448+449449+ return 0;450450+}451451+452452+static int apply_r_mips_lo16(struct module *me, uint32_t *location,453453+ Elf32_Addr v)454454+{455455+ unsigned long insnlo = *location;456456+ Elf32_Addr val, vallo;457457+458458+ /* Sign extend the addend we extract from the lo insn. */459459+ vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;460460+461461+ if (mips_hi16_list != NULL) {462462+ struct mips_hi16 *l;463463+464464+ l = mips_hi16_list;465465+ while (l != NULL) {466466+ struct mips_hi16 *next;467467+ unsigned long insn;468468+469469+ /*470470+ * The value for the HI16 had best be the same.471471+ */472472+ if (v != l->value) {473473+ printk("%d != %d\n", v, l->value);474474+ goto out_danger;475475+ }476476+477477+478478+ /*479479+ * Do the HI16 relocation. Note that we actually don't480480+ * need to know anything about the LO16 itself, except481481+ * where to find the low 16 bits of the addend needed482482+ * by the LO16.483483+ */484484+ insn = *l->addr;485485+ val = ((insn & 0xffff) << 16) + vallo;486486+ val += v;487487+488488+ /*489489+ * Account for the sign extension that will happen in490490+ * the low bits.491491+ */492492+ val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;493493+494494+ insn = (insn & ~0xffff) | val;495495+ *l->addr = insn;496496+497497+ next = l->next;498498+ kfree(l);499499+ l = next;500500+ }501501+502502+ mips_hi16_list = NULL;503503+ }504504+505505+ /*506506+ * Ok, we're done with the HI16 relocs. Now deal with the LO16.507507+ */508508+ val = v + vallo;509509+ insnlo = (insnlo & ~0xffff) | (val & 0xffff);510510+ *location = insnlo;511511+512512+ return 0;513513+514514+out_danger:515515+ printk(KERN_ERR "module %s: dangerous " "relocation\n", me->name);516516+517517+ return -ENOEXEC;518518+}519519+520520+static int (*reloc_handlers[]) (struct module *me, uint32_t *location,521521+ Elf32_Addr v) = {522522+ [R_MIPS_NONE] = apply_r_mips_none,523523+ [R_MIPS_32] = apply_r_mips_32,524524+ [R_MIPS_26] = apply_r_mips_26,525525+ [R_MIPS_HI16] = apply_r_mips_hi16,526526+ [R_MIPS_LO16] = apply_r_mips_lo16,527527+ [R_MIPS_GPREL16] = apply_r_mips_gprel16,528528+ [R_MIPS_PC16] = apply_r_mips_pc16529529+};530530+531531+532532+int apply_relocations(Elf32_Shdr *sechdrs,533533+ const char *strtab,534534+ unsigned int symindex,535535+ unsigned int relsec,536536+ struct module *me)537537+{538538+ Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;539539+ Elf32_Sym *sym;540540+ uint32_t *location;541541+ unsigned int i;542542+ Elf32_Addr v;543543+ int res;544544+545545+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {546546+ Elf32_Word r_info = rel[i].r_info;547547+548548+ /* This is where to make the change */549549+ location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr550550+ + rel[i].r_offset;551551+ /* This is the symbol it is referring to */552552+ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr553553+ + ELF32_R_SYM(r_info);554554+555555+ if (!sym->st_value) {556556+ printk(KERN_DEBUG "%s: undefined weak symbol %s\n",557557+ me->name, strtab + sym->st_name);558558+ /* just print the warning, dont barf */559559+ }560560+561561+ v = sym->st_value;562562+563563+ res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);564564+ if( res ) {565565+ printk(KERN_DEBUG566566+ "relocation error 0x%x sym refer <%s> value 0x%x "567567+ "type 0x%x r_info 0x%x\n",568568+ (unsigned int)location, strtab + sym->st_name, v,569569+ r_info, ELF32_R_TYPE(r_info));570570+ }571571+572572+ if (res)573573+ return res;574574+ }575575+576576+ return 0;577577+}578578+579579+void save_gp_address(unsigned int secbase, unsigned int rel)580580+{581581+ gp_addr = secbase + rel;582582+ gp_offs = gp_addr - (secbase & 0xffff0000);583583+}584584+/* end module-elf32.c */585585+586586+587587+588588+/* Change all symbols so that sh_value encodes the pointer directly. */589589+static int simplify_symbols(Elf_Shdr * sechdrs,590590+ unsigned int symindex,591591+ const char *strtab,592592+ const char *secstrings,593593+ unsigned int nsecs, struct module *mod)594594+{595595+ Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;596596+ unsigned long secbase, bssbase = 0;597597+ unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);598598+ int ret = 0, size;599599+600600+ /* find the .bss section for COMMON symbols */601601+ for (i = 0; i < nsecs; i++) {602602+ if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0)603603+ bssbase = sechdrs[i].sh_addr;604604+ }605605+606606+ for (i = 1; i < n; i++) {607607+ switch (sym[i].st_shndx) {608608+ case SHN_COMMON:609609+ /* Allocate space for the symbol in the .bss section. st_value is currently size.610610+ We want it to have the address of the symbol. */611611+612612+ size = sym[i].st_value;613613+ sym[i].st_value = bssbase;614614+615615+ bssbase += size;616616+ break;617617+618618+ case SHN_ABS:619619+ /* Don't need to do anything */620620+ break;621621+622622+ case SHN_UNDEF:623623+ /* ret = -ENOENT; */624624+ break;625625+626626+ case SHN_MIPS_SCOMMON:627627+628628+ printk(KERN_DEBUG629629+ "simplify_symbols: ignoring SHN_MIPS_SCOMMON symbol <%s> st_shndx %d\n",630630+ strtab + sym[i].st_name, sym[i].st_shndx);631631+632632+ // .sbss section633633+ break;634634+635635+ default:636636+ secbase = sechdrs[sym[i].st_shndx].sh_addr;637637+638638+ if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {639639+ save_gp_address(secbase, sym[i].st_value);640640+ }641641+642642+ sym[i].st_value += secbase;643643+ break;644644+ }645645+646646+ }647647+648648+ return ret;649649+}650650+651651+#ifdef DEBUG_ELFLOADER652652+static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,653653+ const char *strtab, struct module *mod)654654+{655655+ Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;656656+ unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);657657+658658+ printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);659659+ for (i = 1; i < n; i++) {660660+ printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,661661+ strtab + sym[i].st_name, sym[i].st_value);662662+ }663663+}664664+#endif665665+666666+static void dump_tc(struct tc *t)667667+{668668+ printk(KERN_WARNING "VPE: TC index %d TCStatus 0x%lx halt 0x%lx\n",669669+ t->index, read_tc_c0_tcstatus(), read_tc_c0_tchalt());670670+ printk(KERN_WARNING "VPE: tcrestart 0x%lx\n", read_tc_c0_tcrestart());671671+}672672+673673+static void dump_tclist(void)674674+{675675+ struct tc *t;676676+677677+ list_for_each_entry(t, &vpecontrol.tc_list, list) {678678+ dump_tc(t);679679+ }680680+}681681+682682+/* We are prepared so configure and start the VPE... */683683+int vpe_run(vpe_t * v)684684+{685685+ unsigned long val;686686+ struct tc *t;687687+688688+ /* check we are the Master VPE */689689+ val = read_c0_vpeconf0();690690+ if (!(val & VPECONF0_MVP)) {691691+ printk(KERN_WARNING692692+ "VPE: only Master VPE's are allowed to configure MT\n");693693+ return -1;694694+ }695695+696696+ /* disable MT (using dvpe) */697697+ dvpe();698698+699699+ /* Put MVPE's into 'configuration state' */700700+ write_c0_mvpcontrol(read_c0_mvpcontrol() | MVPCONTROL_VPC);701701+702702+ if (!list_empty(&v->tc)) {703703+ if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {704704+ printk(KERN_WARNING "VPE: TC %d is already in use.\n",705705+ t->index);706706+ return -ENOEXEC;707707+ }708708+ } else {709709+ printk(KERN_WARNING "VPE: No TC's associated with VPE %d\n",710710+ v->minor);711711+ return -ENOEXEC;712712+ }713713+714714+ settc(t->index);715715+716716+ val = read_vpe_c0_vpeconf0();717717+718718+ /* should check it is halted, and not activated */719719+ if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {720720+ printk(KERN_WARNING "VPE: TC %d is already doing something!\n",721721+ t->index);722722+723723+ dump_tclist();724724+ return -ENOEXEC;725725+ }726726+727727+ /* Write the address we want it to start running from in the TCPC register. */728728+ write_tc_c0_tcrestart((unsigned long)v->__start);729729+730730+ /* write the sivc_info address to tccontext */731731+ write_tc_c0_tccontext((unsigned long)0);732732+733733+ /* Set up the XTC bit in vpeconf0 to point at our tc */734734+ write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | (t->index << VPECONF0_XTC_SHIFT));735735+736736+ /* mark the TC as activated, not interrupt exempt and not dynamically allocatable */737737+ val = read_tc_c0_tcstatus();738738+ val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;739739+ write_tc_c0_tcstatus(val);740740+741741+ write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);742742+743743+ /* set up VPE1 */744744+ write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); // no multiple TC's745745+ write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); // enable this VPE746746+747747+ /*748748+ * The sde-kit passes 'memsize' to __start in $a3, so set something749749+ * here...750750+ * Or set $a3 (register 7) to zero and define DFLT_STACK_SIZE and751751+ * DFLT_HEAP_SIZE when you compile your program752752+ */753753+754754+ mttgpr(7, 0);755755+756756+ /* set config to be the same as vpe0, particularly kseg0 coherency alg */757757+ write_vpe_c0_config(read_c0_config());758758+759759+ /* clear out any left overs from a previous program */760760+ write_vpe_c0_cause(0);761761+762762+ /* take system out of configuration state */763763+ write_c0_mvpcontrol(read_c0_mvpcontrol() & ~MVPCONTROL_VPC);764764+765765+ /* clear interrupts enabled IE, ERL, EXL, and KSU from c0 status */766766+ write_vpe_c0_status(read_vpe_c0_status() & ~(ST0_ERL | ST0_KSU | ST0_IE | ST0_EXL));767767+768768+ /* set it running */769769+ evpe(EVPE_ENABLE);770770+771771+ return 0;772772+}773773+774774+static unsigned long find_vpe_symbols(vpe_t * v, Elf_Shdr * sechdrs,775775+ unsigned int symindex, const char *strtab,776776+ struct module *mod)777777+{778778+ Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;779779+ unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);780780+781781+ for (i = 1; i < n; i++) {782782+ if (strcmp(strtab + sym[i].st_name, "__start") == 0) {783783+ v->__start = sym[i].st_value;784784+ }785785+786786+ if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {787787+ v->shared_ptr = (void *)sym[i].st_value;788788+ }789789+ }790790+791791+ return 0;792792+}793793+794794+/* Allocates a VPE with some program code space(the load address), copies the contents795795+ of the program (p)buffer performing relocatations/etc, free's it when finished.796796+*/797797+int vpe_elfload(vpe_t * v)798798+{799799+ Elf_Ehdr *hdr;800800+ Elf_Shdr *sechdrs;801801+ long err = 0;802802+ char *secstrings, *strtab = NULL;803803+ unsigned int len, i, symindex = 0, strindex = 0;804804+805805+ struct module mod; // so we can re-use the relocations code806806+807807+ memset(&mod, 0, sizeof(struct module));808808+ strcpy(mod.name, "VPE dummy prog module");809809+810810+ hdr = (Elf_Ehdr *) v->pbuffer;811811+ len = v->plen;812812+813813+ /* Sanity checks against insmoding binaries or wrong arch,814814+ weird elf version */815815+ if (memcmp(hdr->e_ident, ELFMAG, 4) != 0816816+ || hdr->e_type != ET_REL || !elf_check_arch(hdr)817817+ || hdr->e_shentsize != sizeof(*sechdrs)) {818818+ printk(KERN_WARNING819819+ "VPE program, wrong arch or weird elf version\n");820820+821821+ return -ENOEXEC;822822+ }823823+824824+ if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {825825+ printk(KERN_ERR "VPE program length %u truncated\n", len);826826+ return -ENOEXEC;827827+ }828828+829829+ /* Convenience variables */830830+ sechdrs = (void *)hdr + hdr->e_shoff;831831+ secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;832832+ sechdrs[0].sh_addr = 0;833833+834834+ /* And these should exist, but gcc whinges if we don't init them */835835+ symindex = strindex = 0;836836+837837+ for (i = 1; i < hdr->e_shnum; i++) {838838+839839+ if (sechdrs[i].sh_type != SHT_NOBITS840840+ && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {841841+ printk(KERN_ERR "VPE program length %u truncated\n",842842+ len);843843+ return -ENOEXEC;844844+ }845845+846846+ /* Mark all sections sh_addr with their address in the847847+ temporary image. */848848+ sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;849849+850850+ /* Internal symbols and strings. */851851+ if (sechdrs[i].sh_type == SHT_SYMTAB) {852852+ symindex = i;853853+ strindex = sechdrs[i].sh_link;854854+ strtab = (char *)hdr + sechdrs[strindex].sh_offset;855855+ }856856+ }857857+858858+ layout_sections(&mod, hdr, sechdrs, secstrings);859859+860860+ v->load_addr = alloc_progmem(mod.core_size);861861+ memset(v->load_addr, 0, mod.core_size);862862+863863+ printk("VPE elf_loader: loading to %p\n", v->load_addr);864864+865865+ for (i = 0; i < hdr->e_shnum; i++) {866866+ void *dest;867867+868868+ if (!(sechdrs[i].sh_flags & SHF_ALLOC))869869+ continue;870870+871871+ dest = v->load_addr + sechdrs[i].sh_entsize;872872+873873+ if (sechdrs[i].sh_type != SHT_NOBITS)874874+ memcpy(dest, (void *)sechdrs[i].sh_addr,875875+ sechdrs[i].sh_size);876876+ /* Update sh_addr to point to copy in image. */877877+ sechdrs[i].sh_addr = (unsigned long)dest;878878+ }879879+880880+ /* Fix up syms, so that st_value is a pointer to location. */881881+ err =882882+ simplify_symbols(sechdrs, symindex, strtab, secstrings,883883+ hdr->e_shnum, &mod);884884+ if (err < 0) {885885+ printk(KERN_WARNING "VPE: unable to simplify symbols\n");886886+ goto cleanup;887887+ }888888+889889+ /* Now do relocations. */890890+ for (i = 1; i < hdr->e_shnum; i++) {891891+ const char *strtab = (char *)sechdrs[strindex].sh_addr;892892+ unsigned int info = sechdrs[i].sh_info;893893+894894+ /* Not a valid relocation section? */895895+ if (info >= hdr->e_shnum)896896+ continue;897897+898898+ /* Don't bother with non-allocated sections */899899+ if (!(sechdrs[info].sh_flags & SHF_ALLOC))900900+ continue;901901+902902+ if (sechdrs[i].sh_type == SHT_REL)903903+ err =904904+ apply_relocations(sechdrs, strtab, symindex, i, &mod);905905+ else if (sechdrs[i].sh_type == SHT_RELA)906906+ err = apply_relocate_add(sechdrs, strtab, symindex, i,907907+ &mod);908908+ if (err < 0) {909909+ printk(KERN_WARNING910910+ "vpe_elfload: error in relocations err %ld\n",911911+ err);912912+ goto cleanup;913913+ }914914+ }915915+916916+ /* make sure it's physically written out */917917+ flush_icache_range((unsigned long)v->load_addr,918918+ (unsigned long)v->load_addr + v->len);919919+920920+ if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {921921+922922+ printk(KERN_WARNING923923+ "VPE: program doesn't contain __start or vpe_shared symbols\n");924924+ err = -ENOEXEC;925925+ }926926+927927+ printk(" elf loaded\n");928928+929929+cleanup:930930+ return err;931931+}932932+933933+static void dump_vpe(vpe_t * v)934934+{935935+ struct tc *t;936936+937937+ printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol());938938+ printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0());939939+940940+ list_for_each_entry(t, &vpecontrol.tc_list, list) {941941+ dump_tc(t);942942+ }943943+}944944+945945+/* checks for VPE is unused and gets ready to load program */946946+static int vpe_open(struct inode *inode, struct file *filp)947947+{948948+ int minor;949949+ vpe_t *v;950950+951951+ /* assume only 1 device at the mo. */952952+ if ((minor = MINOR(inode->i_rdev)) != 1) {953953+ printk(KERN_WARNING "VPE: only vpe1 is supported\n");954954+ return -ENODEV;955955+ }956956+957957+ if ((v = get_vpe(minor)) == NULL) {958958+ printk(KERN_WARNING "VPE: unable to get vpe\n");959959+ return -ENODEV;960960+ }961961+962962+ if (v->state != VPE_STATE_UNUSED) {963963+ unsigned long tmp;964964+ struct tc *t;965965+966966+ printk(KERN_WARNING "VPE: device %d already in use\n", minor);967967+968968+ dvpe();969969+ dump_vpe(v);970970+971971+ printk(KERN_WARNING "VPE: re-initialising %d\n", minor);972972+973973+ release_progmem(v->load_addr);974974+975975+ t = get_tc(minor);976976+ settc(minor);977977+ tmp = read_tc_c0_tcstatus();978978+979979+ /* mark not allocated and not dynamically allocatable */980980+ tmp &= ~(TCSTATUS_A | TCSTATUS_DA);981981+ tmp |= TCSTATUS_IXMT; /* interrupt exempt */982982+ write_tc_c0_tcstatus(tmp);983983+984984+ write_tc_c0_tchalt(TCHALT_H);985985+986986+ }987987+988988+ // allocate it so when we get write ops we know it's expected.989989+ v->state = VPE_STATE_INUSE;990990+991991+ /* this of-course trashes what was there before... */992992+ v->pbuffer = vmalloc(P_SIZE);993993+ v->plen = P_SIZE;994994+ v->load_addr = NULL;995995+ v->len = 0;996996+997997+ return 0;998998+}999999+10001000+static int vpe_release(struct inode *inode, struct file *filp)10011001+{10021002+ int minor, ret = 0;10031003+ vpe_t *v;10041004+ Elf_Ehdr *hdr;10051005+10061006+ minor = MINOR(inode->i_rdev);10071007+ if ((v = get_vpe(minor)) == NULL)10081008+ return -ENODEV;10091009+10101010+ // simple case of fire and forget, so tell the VPE to run...10111011+10121012+ hdr = (Elf_Ehdr *) v->pbuffer;10131013+ if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) {10141014+ if (vpe_elfload(v) >= 0)10151015+ vpe_run(v);10161016+ else {10171017+ printk(KERN_WARNING "VPE: ELF load failed.\n");10181018+ ret = -ENOEXEC;10191019+ }10201020+ } else {10211021+ printk(KERN_WARNING "VPE: only elf files are supported\n");10221022+ ret = -ENOEXEC;10231023+ }10241024+10251025+ // cleanup any temp buffers10261026+ if (v->pbuffer)10271027+ vfree(v->pbuffer);10281028+ v->plen = 0;10291029+ return ret;10301030+}10311031+10321032+static ssize_t vpe_write(struct file *file, const char __user * buffer,10331033+ size_t count, loff_t * ppos)10341034+{10351035+ int minor;10361036+ size_t ret = count;10371037+ vpe_t *v;10381038+10391039+ minor = MINOR(file->f_dentry->d_inode->i_rdev);10401040+ if ((v = get_vpe(minor)) == NULL)10411041+ return -ENODEV;10421042+10431043+ if (v->pbuffer == NULL) {10441044+ printk(KERN_ERR "vpe_write: no pbuffer\n");10451045+ return -ENOMEM;10461046+ }10471047+10481048+ if ((count + v->len) > v->plen) {10491049+ printk(KERN_WARNING10501050+ "VPE Loader: elf size too big. Perhaps strip uneeded symbols\n");10511051+ return -ENOMEM;10521052+ }10531053+10541054+ count -= copy_from_user(v->pbuffer + v->len, buffer, count);10551055+ if (!count) {10561056+ printk("vpe_write: copy_to_user failed\n");10571057+ return -EFAULT;10581058+ }10591059+10601060+ v->len += count;10611061+ return ret;10621062+}10631063+10641064+static struct file_operations vpe_fops = {10651065+ .owner = THIS_MODULE,10661066+ .open = vpe_open,10671067+ .release = vpe_release,10681068+ .write = vpe_write10691069+};10701070+10711071+/* module wrapper entry points */10721072+/* give me a vpe */10731073+vpe_handle vpe_alloc(void)10741074+{10751075+ int i;10761076+ struct vpe *v;10771077+10781078+ /* find a vpe */10791079+ for (i = 1; i < MAX_VPES; i++) {10801080+ if ((v = get_vpe(i)) != NULL) {10811081+ v->state = VPE_STATE_INUSE;10821082+ return v;10831083+ }10841084+ }10851085+ return NULL;10861086+}10871087+10881088+EXPORT_SYMBOL(vpe_alloc);10891089+10901090+/* start running from here */10911091+int vpe_start(vpe_handle vpe, unsigned long start)10921092+{10931093+ struct vpe *v = vpe;10941094+10951095+ v->__start = start;10961096+ return vpe_run(v);10971097+}10981098+10991099+EXPORT_SYMBOL(vpe_start);11001100+11011101+/* halt it for now */11021102+int vpe_stop(vpe_handle vpe)11031103+{11041104+ struct vpe *v = vpe;11051105+ struct tc *t;11061106+ unsigned int evpe_flags;11071107+11081108+ evpe_flags = dvpe();11091109+11101110+ if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {11111111+11121112+ settc(t->index);11131113+ write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);11141114+ }11151115+11161116+ evpe(evpe_flags);11171117+11181118+ return 0;11191119+}11201120+11211121+EXPORT_SYMBOL(vpe_stop);11221122+11231123+/* I've done with it thank you */11241124+int vpe_free(vpe_handle vpe)11251125+{11261126+ struct vpe *v = vpe;11271127+ struct tc *t;11281128+ unsigned int evpe_flags;11291129+11301130+ if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {11311131+ return -ENOEXEC;11321132+ }11331133+11341134+ evpe_flags = dvpe();11351135+11361136+ /* Put MVPE's into 'configuration state' */11371137+ write_c0_mvpcontrol(read_c0_mvpcontrol() | MVPCONTROL_VPC);11381138+11391139+ settc(t->index);11401140+ write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);11411141+11421142+ /* mark the TC unallocated and halt'ed */11431143+ write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);11441144+ write_tc_c0_tchalt(TCHALT_H);11451145+11461146+ v->state = VPE_STATE_UNUSED;11471147+11481148+ write_c0_mvpcontrol(read_c0_mvpcontrol() & ~MVPCONTROL_VPC);11491149+ evpe(evpe_flags);11501150+11511151+ return 0;11521152+}11531153+11541154+EXPORT_SYMBOL(vpe_free);11551155+11561156+void *vpe_get_shared(int index)11571157+{11581158+ struct vpe *v;11591159+11601160+ if ((v = get_vpe(index)) == NULL) {11611161+ printk(KERN_WARNING "vpe: invalid vpe index %d\n", index);11621162+ return NULL;11631163+ }11641164+11651165+ return v->shared_ptr;11661166+}11671167+11681168+EXPORT_SYMBOL(vpe_get_shared);11691169+11701170+static int __init vpe_module_init(void)11711171+{11721172+ struct vpe *v = NULL;11731173+ struct tc *t;11741174+ unsigned long val;11751175+ int i;11761176+11771177+ if (!cpu_has_mipsmt) {11781178+ printk("VPE loader: not a MIPS MT capable processor\n");11791179+ return -ENODEV;11801180+ }11811181+11821182+ if ((major = register_chrdev(VPE_MAJOR, module_name, &vpe_fops) < 0)) {11831183+ printk("VPE loader: unable to register character device\n");11841184+ return -EBUSY;11851185+ }11861186+11871187+ if (major == 0)11881188+ major = VPE_MAJOR;11891189+11901190+ dmt();11911191+ dvpe();11921192+11931193+ /* Put MVPE's into 'configuration state' */11941194+ write_c0_mvpcontrol(read_c0_mvpcontrol() | MVPCONTROL_VPC);11951195+11961196+ /* dump_mtregs(); */11971197+11981198+ INIT_LIST_HEAD(&vpecontrol.vpe_list);11991199+ INIT_LIST_HEAD(&vpecontrol.tc_list);12001200+12011201+ val = read_c0_mvpconf0();12021202+ for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) {12031203+ t = alloc_tc(i);12041204+12051205+ /* VPE's */12061206+ if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) {12071207+ settc(i);12081208+12091209+ if ((v = alloc_vpe(i)) == NULL) {12101210+ printk(KERN_WARNING "VPE: unable to allocate VPE\n");12111211+ return -ENODEV;12121212+ }12131213+12141214+ list_add(&t->tc, &v->tc); /* add the tc to the list of this vpe's tc's. */12151215+12161216+ /* deactivate all but vpe0 */12171217+ if (i != 0) {12181218+ unsigned long tmp = read_vpe_c0_vpeconf0();12191219+12201220+ tmp &= ~VPECONF0_VPA;12211221+12221222+ /* master VPE */12231223+ tmp |= VPECONF0_MVP;12241224+ write_vpe_c0_vpeconf0(tmp);12251225+ }12261226+12271227+ /* disable multi-threading with TC's */12281228+ write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);12291229+12301230+ if (i != 0) {12311231+ write_vpe_c0_status((read_c0_status() &12321232+ ~(ST0_IM | ST0_IE | ST0_KSU))12331233+ | ST0_CU0);12341234+12351235+ /* set config to be the same as vpe0, particularly kseg0 coherency alg */12361236+ write_vpe_c0_config(read_c0_config());12371237+ }12381238+12391239+ }12401240+12411241+ /* TC's */12421242+ t->pvpe = v; /* set the parent vpe */12431243+12441244+ if (i != 0) {12451245+ unsigned long tmp;12461246+12471247+ /* tc 0 will of course be running.... */12481248+ if (i == 0)12491249+ t->state = TC_STATE_RUNNING;12501250+12511251+ settc(i);12521252+12531253+ /* bind a TC to each VPE, May as well put all excess TC's12541254+ on the last VPE */12551255+ if (i >= (((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1))12561256+ write_tc_c0_tcbind(read_tc_c0_tcbind() |12571257+ ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT));12581258+ else12591259+ write_tc_c0_tcbind(read_tc_c0_tcbind() | i);12601260+12611261+ tmp = read_tc_c0_tcstatus();12621262+12631263+ /* mark not allocated and not dynamically allocatable */12641264+ tmp &= ~(TCSTATUS_A | TCSTATUS_DA);12651265+ tmp |= TCSTATUS_IXMT; /* interrupt exempt */12661266+ write_tc_c0_tcstatus(tmp);12671267+12681268+ write_tc_c0_tchalt(TCHALT_H);12691269+ }12701270+ }12711271+12721272+ /* release config state */12731273+ write_c0_mvpcontrol(read_c0_mvpcontrol() & ~MVPCONTROL_VPC);12741274+12751275+ return 0;12761276+}12771277+12781278+static void __exit vpe_module_exit(void)12791279+{12801280+ struct vpe *v, *n;12811281+12821282+ list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {12831283+ if (v->state != VPE_STATE_UNUSED) {12841284+ release_vpe(v);12851285+ }12861286+ }12871287+12881288+ unregister_chrdev(major, module_name);12891289+}12901290+12911291+module_init(vpe_module_init);12921292+module_exit(vpe_module_exit);12931293+MODULE_DESCRIPTION("MIPS VPE Loader");12941294+MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc");12951295+MODULE_LICENSE("GPL");