xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision 2600990e)
1e01402b1SRalf Baechle /*
2e01402b1SRalf Baechle  * Copyright (C) 2004, 2005 MIPS Technologies, Inc.  All rights reserved.
3e01402b1SRalf Baechle  *
4e01402b1SRalf Baechle  *  This program is free software; you can distribute it and/or modify it
5e01402b1SRalf Baechle  *  under the terms of the GNU General Public License (Version 2) as
6e01402b1SRalf Baechle  *  published by the Free Software Foundation.
7e01402b1SRalf Baechle  *
8e01402b1SRalf Baechle  *  This program is distributed in the hope it will be useful, but WITHOUT
9e01402b1SRalf Baechle  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10e01402b1SRalf Baechle  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11e01402b1SRalf Baechle  *  for more details.
12e01402b1SRalf Baechle  *
13e01402b1SRalf Baechle  *  You should have received a copy of the GNU General Public License along
14e01402b1SRalf Baechle  *  with this program; if not, write to the Free Software Foundation, Inc.,
15e01402b1SRalf Baechle  *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
16e01402b1SRalf Baechle  */
17e01402b1SRalf Baechle 
18e01402b1SRalf Baechle /*
19e01402b1SRalf Baechle  * VPE support module
20e01402b1SRalf Baechle  *
21e01402b1SRalf Baechle  * Provides support for loading a MIPS SP program on VPE1.
22e01402b1SRalf Baechle  * The SP enviroment is rather simple, no tlb's.  It needs to be relocatable
23e01402b1SRalf Baechle  * (or partially linked). You should initialise your stack in the startup
24e01402b1SRalf Baechle  * code. This loader looks for the symbol __start and sets up
25e01402b1SRalf Baechle  * execution to resume from there. The MIPS SDE kit contains suitable examples.
26e01402b1SRalf Baechle  *
27e01402b1SRalf Baechle  * To load and run, simply cat a SP 'program file' to /dev/vpe1.
28e01402b1SRalf Baechle  * i.e cat spapp >/dev/vpe1.
29e01402b1SRalf Baechle  */
302600990eSRalf Baechle 
31340ee4b9SRalf Baechle #include <linux/config.h>
32e01402b1SRalf Baechle #include <linux/kernel.h>
33e01402b1SRalf Baechle #include <linux/module.h>
34e01402b1SRalf Baechle #include <linux/fs.h>
35e01402b1SRalf Baechle #include <linux/init.h>
36e01402b1SRalf Baechle #include <asm/uaccess.h>
37e01402b1SRalf Baechle #include <linux/slab.h>
38e01402b1SRalf Baechle #include <linux/list.h>
39e01402b1SRalf Baechle #include <linux/vmalloc.h>
40e01402b1SRalf Baechle #include <linux/elf.h>
41e01402b1SRalf Baechle #include <linux/seq_file.h>
42e01402b1SRalf Baechle #include <linux/syscalls.h>
43e01402b1SRalf Baechle #include <linux/moduleloader.h>
44e01402b1SRalf Baechle #include <linux/interrupt.h>
45e01402b1SRalf Baechle #include <linux/poll.h>
46e01402b1SRalf Baechle #include <linux/bootmem.h>
47e01402b1SRalf Baechle #include <asm/mipsregs.h>
48340ee4b9SRalf Baechle #include <asm/mipsmtregs.h>
49e01402b1SRalf Baechle #include <asm/cacheflush.h>
50e01402b1SRalf Baechle #include <asm/atomic.h>
51e01402b1SRalf Baechle #include <asm/cpu.h>
52e01402b1SRalf Baechle #include <asm/processor.h>
53e01402b1SRalf Baechle #include <asm/system.h>
542600990eSRalf Baechle #include <asm/vpe.h>
552600990eSRalf Baechle #include <asm/kspd.h>
56e01402b1SRalf Baechle 
57e01402b1SRalf Baechle typedef void *vpe_handle;
58e01402b1SRalf Baechle 
59e01402b1SRalf Baechle #ifndef ARCH_SHF_SMALL
60e01402b1SRalf Baechle #define ARCH_SHF_SMALL 0
61e01402b1SRalf Baechle #endif
62e01402b1SRalf Baechle 
63e01402b1SRalf Baechle /* If this is set, the section belongs in the init part of the module */
64e01402b1SRalf Baechle #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
65e01402b1SRalf Baechle 
66e01402b1SRalf Baechle static char module_name[] = "vpe";
67307bd284SRalf Baechle static int major;
68e01402b1SRalf Baechle 
692600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
702600990eSRalf Baechle  static struct kspd_notifications kspd_events;
712600990eSRalf Baechle static int kspd_events_reqd = 0;
722600990eSRalf Baechle #endif
732600990eSRalf Baechle 
74e01402b1SRalf Baechle /* grab the likely amount of memory we will need. */
75e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
76e01402b1SRalf Baechle #define P_SIZE (2 * 1024 * 1024)
77e01402b1SRalf Baechle #else
78e01402b1SRalf Baechle /* add an overhead to the max kmalloc size for non-striped symbols/etc */
79e01402b1SRalf Baechle #define P_SIZE (256 * 1024)
80e01402b1SRalf Baechle #endif
81e01402b1SRalf Baechle 
822600990eSRalf Baechle extern unsigned long physical_memsize;
832600990eSRalf Baechle 
84e01402b1SRalf Baechle #define MAX_VPES 16
852600990eSRalf Baechle #define VPE_PATH_MAX 256
86e01402b1SRalf Baechle 
87e01402b1SRalf Baechle enum vpe_state {
88e01402b1SRalf Baechle 	VPE_STATE_UNUSED = 0,
89e01402b1SRalf Baechle 	VPE_STATE_INUSE,
90e01402b1SRalf Baechle 	VPE_STATE_RUNNING
91e01402b1SRalf Baechle };
92e01402b1SRalf Baechle 
93e01402b1SRalf Baechle enum tc_state {
94e01402b1SRalf Baechle 	TC_STATE_UNUSED = 0,
95e01402b1SRalf Baechle 	TC_STATE_INUSE,
96e01402b1SRalf Baechle 	TC_STATE_RUNNING,
97e01402b1SRalf Baechle 	TC_STATE_DYNAMIC
98e01402b1SRalf Baechle };
99e01402b1SRalf Baechle 
100307bd284SRalf Baechle struct vpe {
101e01402b1SRalf Baechle 	enum vpe_state state;
102e01402b1SRalf Baechle 
103e01402b1SRalf Baechle 	/* (device) minor associated with this vpe */
104e01402b1SRalf Baechle 	int minor;
105e01402b1SRalf Baechle 
106e01402b1SRalf Baechle 	/* elfloader stuff */
107e01402b1SRalf Baechle 	void *load_addr;
108571e0bedSRalf Baechle 	unsigned long len;
109e01402b1SRalf Baechle 	char *pbuffer;
110571e0bedSRalf Baechle 	unsigned long plen;
1112600990eSRalf Baechle 	unsigned int uid, gid;
1122600990eSRalf Baechle 	char cwd[VPE_PATH_MAX];
113e01402b1SRalf Baechle 
114e01402b1SRalf Baechle 	unsigned long __start;
115e01402b1SRalf Baechle 
116e01402b1SRalf Baechle 	/* tc's associated with this vpe */
117e01402b1SRalf Baechle 	struct list_head tc;
118e01402b1SRalf Baechle 
119e01402b1SRalf Baechle 	/* The list of vpe's */
120e01402b1SRalf Baechle 	struct list_head list;
121e01402b1SRalf Baechle 
122e01402b1SRalf Baechle 	/* shared symbol address */
123e01402b1SRalf Baechle 	void *shared_ptr;
1242600990eSRalf Baechle 
1252600990eSRalf Baechle 	/* the list of who wants to know when something major happens */
1262600990eSRalf Baechle 	struct list_head notify;
127307bd284SRalf Baechle };
128307bd284SRalf Baechle 
129307bd284SRalf Baechle struct tc {
130307bd284SRalf Baechle 	enum tc_state state;
131307bd284SRalf Baechle 	int index;
132307bd284SRalf Baechle 
133307bd284SRalf Baechle 	/* parent VPE */
134307bd284SRalf Baechle 	struct vpe *pvpe;
135307bd284SRalf Baechle 
136307bd284SRalf Baechle 	/* The list of TC's with this VPE */
137307bd284SRalf Baechle 	struct list_head tc;
138307bd284SRalf Baechle 
139307bd284SRalf Baechle 	/* The global list of tc's */
140307bd284SRalf Baechle 	struct list_head list;
141307bd284SRalf Baechle };
142e01402b1SRalf Baechle 
143e01402b1SRalf Baechle struct vpecontrol_ {
144e01402b1SRalf Baechle 	/* Virtual processing elements */
145e01402b1SRalf Baechle 	struct list_head vpe_list;
146e01402b1SRalf Baechle 
147e01402b1SRalf Baechle 	/* Thread contexts */
148e01402b1SRalf Baechle 	struct list_head tc_list;
149e01402b1SRalf Baechle } vpecontrol;
150e01402b1SRalf Baechle 
151e01402b1SRalf Baechle static void release_progmem(void *ptr);
1522600990eSRalf Baechle /* static __attribute_used__ void dump_vpe(struct vpe * v); */
153e01402b1SRalf Baechle extern void save_gp_address(unsigned int secbase, unsigned int rel);
154e01402b1SRalf Baechle 
155e01402b1SRalf Baechle /* get the vpe associated with this minor */
156e01402b1SRalf Baechle struct vpe *get_vpe(int minor)
157e01402b1SRalf Baechle {
158e01402b1SRalf Baechle 	struct vpe *v;
159e01402b1SRalf Baechle 
1602600990eSRalf Baechle 	if (!cpu_has_mipsmt)
1612600990eSRalf Baechle 		return NULL;
1622600990eSRalf Baechle 
163e01402b1SRalf Baechle 	list_for_each_entry(v, &vpecontrol.vpe_list, list) {
164e01402b1SRalf Baechle 		if (v->minor == minor)
165e01402b1SRalf Baechle 			return v;
166e01402b1SRalf Baechle 	}
167e01402b1SRalf Baechle 
168e01402b1SRalf Baechle 	return NULL;
169e01402b1SRalf Baechle }
170e01402b1SRalf Baechle 
171e01402b1SRalf Baechle /* get the vpe associated with this minor */
172e01402b1SRalf Baechle struct tc *get_tc(int index)
173e01402b1SRalf Baechle {
174e01402b1SRalf Baechle 	struct tc *t;
175e01402b1SRalf Baechle 
176e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
177e01402b1SRalf Baechle 		if (t->index == index)
178e01402b1SRalf Baechle 			return t;
179e01402b1SRalf Baechle 	}
180e01402b1SRalf Baechle 
181e01402b1SRalf Baechle 	return NULL;
182e01402b1SRalf Baechle }
183e01402b1SRalf Baechle 
184e01402b1SRalf Baechle struct tc *get_tc_unused(void)
185e01402b1SRalf Baechle {
186e01402b1SRalf Baechle 	struct tc *t;
187e01402b1SRalf Baechle 
188e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
189e01402b1SRalf Baechle 		if (t->state == TC_STATE_UNUSED)
190e01402b1SRalf Baechle 			return t;
191e01402b1SRalf Baechle 	}
192e01402b1SRalf Baechle 
193e01402b1SRalf Baechle 	return NULL;
194e01402b1SRalf Baechle }
195e01402b1SRalf Baechle 
196e01402b1SRalf Baechle /* allocate a vpe and associate it with this minor (or index) */
197e01402b1SRalf Baechle struct vpe *alloc_vpe(int minor)
198e01402b1SRalf Baechle {
199e01402b1SRalf Baechle 	struct vpe *v;
200e01402b1SRalf Baechle 
201307bd284SRalf Baechle 	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {
202e01402b1SRalf Baechle 		return NULL;
203e01402b1SRalf Baechle 	}
204e01402b1SRalf Baechle 
205e01402b1SRalf Baechle 	INIT_LIST_HEAD(&v->tc);
206e01402b1SRalf Baechle 	list_add_tail(&v->list, &vpecontrol.vpe_list);
207e01402b1SRalf Baechle 
2082600990eSRalf Baechle 	INIT_LIST_HEAD(&v->notify);
209e01402b1SRalf Baechle 	v->minor = minor;
210e01402b1SRalf Baechle 	return v;
211e01402b1SRalf Baechle }
212e01402b1SRalf Baechle 
213e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */
214e01402b1SRalf Baechle struct tc *alloc_tc(int index)
215e01402b1SRalf Baechle {
216e01402b1SRalf Baechle 	struct tc *t;
217e01402b1SRalf Baechle 
218307bd284SRalf Baechle 	if ((t = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) {
219e01402b1SRalf Baechle 		return NULL;
220e01402b1SRalf Baechle 	}
221e01402b1SRalf Baechle 
222e01402b1SRalf Baechle 	INIT_LIST_HEAD(&t->tc);
223e01402b1SRalf Baechle 	list_add_tail(&t->list, &vpecontrol.tc_list);
224e01402b1SRalf Baechle 
225e01402b1SRalf Baechle 	t->index = index;
226e01402b1SRalf Baechle 
227e01402b1SRalf Baechle 	return t;
228e01402b1SRalf Baechle }
229e01402b1SRalf Baechle 
230e01402b1SRalf Baechle /* clean up and free everything */
231e01402b1SRalf Baechle void release_vpe(struct vpe *v)
232e01402b1SRalf Baechle {
233e01402b1SRalf Baechle 	list_del(&v->list);
234e01402b1SRalf Baechle 	if (v->load_addr)
235e01402b1SRalf Baechle 		release_progmem(v);
236e01402b1SRalf Baechle 	kfree(v);
237e01402b1SRalf Baechle }
238e01402b1SRalf Baechle 
239e01402b1SRalf Baechle void dump_mtregs(void)
240e01402b1SRalf Baechle {
241e01402b1SRalf Baechle 	unsigned long val;
242e01402b1SRalf Baechle 
243e01402b1SRalf Baechle 	val = read_c0_config3();
244e01402b1SRalf Baechle 	printk("config3 0x%lx MT %ld\n", val,
245e01402b1SRalf Baechle 	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
246e01402b1SRalf Baechle 
247e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
248e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
249e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
250e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
251e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
252e01402b1SRalf Baechle 
2532600990eSRalf Baechle 	val = read_c0_mvpconf0();
2542600990eSRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
2552600990eSRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
2562600990eSRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
257e01402b1SRalf Baechle }
258e01402b1SRalf Baechle 
259e01402b1SRalf Baechle /* Find some VPE program space  */
260571e0bedSRalf Baechle static void *alloc_progmem(unsigned long len)
261e01402b1SRalf Baechle {
262e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
263e01402b1SRalf Baechle 	/* this means you must tell linux to use less memory than you physically have */
264571e0bedSRalf Baechle 	return pfn_to_kaddr(max_pfn);
265e01402b1SRalf Baechle #else
266e01402b1SRalf Baechle 	// simple grab some mem for now
267e01402b1SRalf Baechle 	return kmalloc(len, GFP_KERNEL);
268e01402b1SRalf Baechle #endif
269e01402b1SRalf Baechle }
270e01402b1SRalf Baechle 
271e01402b1SRalf Baechle static void release_progmem(void *ptr)
272e01402b1SRalf Baechle {
273e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
274e01402b1SRalf Baechle 	kfree(ptr);
275e01402b1SRalf Baechle #endif
276e01402b1SRalf Baechle }
277e01402b1SRalf Baechle 
278e01402b1SRalf Baechle /* Update size with this section: return offset. */
279e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
280e01402b1SRalf Baechle {
281e01402b1SRalf Baechle 	long ret;
282e01402b1SRalf Baechle 
283e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
284e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
285e01402b1SRalf Baechle 	return ret;
286e01402b1SRalf Baechle }
287e01402b1SRalf Baechle 
288e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
289e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
290e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
291e01402b1SRalf Baechle    belongs in init. */
292e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
293e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
294e01402b1SRalf Baechle {
295e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
296e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
297e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
298e01402b1SRalf Baechle 		 * finder in the two loops below */
299e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
300e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
301e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
302e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
303e01402b1SRalf Baechle 	};
304e01402b1SRalf Baechle 	unsigned int m, i;
305e01402b1SRalf Baechle 
306e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
307e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
308e01402b1SRalf Baechle 
309e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
310e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
311e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
312e01402b1SRalf Baechle 
313e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
314e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
315e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
316e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
317e01402b1SRalf Baechle 				continue;
318e01402b1SRalf Baechle 			s->sh_entsize = get_offset(&mod->core_size, s);
319e01402b1SRalf Baechle 		}
320e01402b1SRalf Baechle 
321e01402b1SRalf Baechle 		if (m == 0)
322e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
323e01402b1SRalf Baechle 
324e01402b1SRalf Baechle 	}
325e01402b1SRalf Baechle }
326e01402b1SRalf Baechle 
327e01402b1SRalf Baechle 
328e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
329e01402b1SRalf Baechle 
330e01402b1SRalf Baechle struct mips_hi16 {
331e01402b1SRalf Baechle 	struct mips_hi16 *next;
332e01402b1SRalf Baechle 	Elf32_Addr *addr;
333e01402b1SRalf Baechle 	Elf32_Addr value;
334e01402b1SRalf Baechle };
335e01402b1SRalf Baechle 
336e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
337e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
338e01402b1SRalf Baechle 
339e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
340e01402b1SRalf Baechle 			     Elf32_Addr v)
341e01402b1SRalf Baechle {
342e01402b1SRalf Baechle 	return 0;
343e01402b1SRalf Baechle }
344e01402b1SRalf Baechle 
345e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
346e01402b1SRalf Baechle 				Elf32_Addr v)
347e01402b1SRalf Baechle {
348e01402b1SRalf Baechle 	int rel;
349e01402b1SRalf Baechle 
350e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
351e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
352e01402b1SRalf Baechle 	}
353e01402b1SRalf Baechle 	else {
354e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
355e01402b1SRalf Baechle 		/* kludge! */
356e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
357e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
358e01402b1SRalf Baechle 	}
359e01402b1SRalf Baechle 
360e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3612600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3622600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3632600990eSRalf Baechle 		       rel);
364e01402b1SRalf Baechle 		return -ENOEXEC;
365e01402b1SRalf Baechle 	}
366e01402b1SRalf Baechle 
367e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
368e01402b1SRalf Baechle 
369e01402b1SRalf Baechle 	return 0;
370e01402b1SRalf Baechle }
371e01402b1SRalf Baechle 
372e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
373e01402b1SRalf Baechle 			     Elf32_Addr v)
374e01402b1SRalf Baechle {
375e01402b1SRalf Baechle 	int rel;
376e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
377e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
378e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
379e01402b1SRalf Baechle 
380e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3812600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
382e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
383e01402b1SRalf Baechle 		return -ENOEXEC;
384e01402b1SRalf Baechle 	}
385e01402b1SRalf Baechle 
386e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
387e01402b1SRalf Baechle 
388e01402b1SRalf Baechle 	return 0;
389e01402b1SRalf Baechle }
390e01402b1SRalf Baechle 
391e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
392e01402b1SRalf Baechle 			   Elf32_Addr v)
393e01402b1SRalf Baechle {
394e01402b1SRalf Baechle 	*location += v;
395e01402b1SRalf Baechle 
396e01402b1SRalf Baechle 	return 0;
397e01402b1SRalf Baechle }
398e01402b1SRalf Baechle 
399e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
400e01402b1SRalf Baechle 			   Elf32_Addr v)
401e01402b1SRalf Baechle {
402e01402b1SRalf Baechle 	if (v % 4) {
4032600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4042600990eSRalf Baechle 		       " unaligned relocation\n");
405e01402b1SRalf Baechle 		return -ENOEXEC;
406e01402b1SRalf Baechle 	}
407e01402b1SRalf Baechle 
408307bd284SRalf Baechle /*
409307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
410307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
411307bd284SRalf Baechle  * we want to set to zero.
412307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
413307bd284SRalf Baechle  * printk(KERN_ERR
414307bd284SRalf Baechle  * "module %s: relocation overflow\n",
415307bd284SRalf Baechle  * me->name);
416307bd284SRalf Baechle  * return -ENOEXEC;
417307bd284SRalf Baechle  * }
418e01402b1SRalf Baechle  */
419e01402b1SRalf Baechle 
420e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
421e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
422e01402b1SRalf Baechle 	return 0;
423e01402b1SRalf Baechle }
424e01402b1SRalf Baechle 
425e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
426e01402b1SRalf Baechle 			     Elf32_Addr v)
427e01402b1SRalf Baechle {
428e01402b1SRalf Baechle 	struct mips_hi16 *n;
429e01402b1SRalf Baechle 
430e01402b1SRalf Baechle 	/*
431e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
432e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
433e01402b1SRalf Baechle 	 * actual relocation.
434e01402b1SRalf Baechle 	 */
435e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
436e01402b1SRalf Baechle 	if (!n)
437e01402b1SRalf Baechle 		return -ENOMEM;
438e01402b1SRalf Baechle 
439e01402b1SRalf Baechle 	n->addr = location;
440e01402b1SRalf Baechle 	n->value = v;
441e01402b1SRalf Baechle 	n->next = mips_hi16_list;
442e01402b1SRalf Baechle 	mips_hi16_list = n;
443e01402b1SRalf Baechle 
444e01402b1SRalf Baechle 	return 0;
445e01402b1SRalf Baechle }
446e01402b1SRalf Baechle 
447e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
448e01402b1SRalf Baechle 			     Elf32_Addr v)
449e01402b1SRalf Baechle {
450e01402b1SRalf Baechle 	unsigned long insnlo = *location;
451e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
452e01402b1SRalf Baechle 
453e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
454e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
455e01402b1SRalf Baechle 
456e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
457e01402b1SRalf Baechle 		struct mips_hi16 *l;
458e01402b1SRalf Baechle 
459e01402b1SRalf Baechle 		l = mips_hi16_list;
460e01402b1SRalf Baechle 		while (l != NULL) {
461e01402b1SRalf Baechle 			struct mips_hi16 *next;
462e01402b1SRalf Baechle 			unsigned long insn;
463e01402b1SRalf Baechle 
464e01402b1SRalf Baechle 			/*
465e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
466e01402b1SRalf Baechle 			 */
467e01402b1SRalf Baechle  			if (v != l->value) {
4682600990eSRalf Baechle 				printk(KERN_DEBUG "VPE loader: "
4692600990eSRalf Baechle 				       "apply_r_mips_lo16/hi16: 	"
4702600990eSRalf Baechle 				       "inconsistent value information\n");
4712600990eSRalf Baechle 				return -ENOEXEC;
472e01402b1SRalf Baechle 			}
473e01402b1SRalf Baechle 
474e01402b1SRalf Baechle 			/*
475e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
476e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
477e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
478e01402b1SRalf Baechle 			 * by the LO16.
479e01402b1SRalf Baechle 			 */
480e01402b1SRalf Baechle 			insn = *l->addr;
481e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
482e01402b1SRalf Baechle 			val += v;
483e01402b1SRalf Baechle 
484e01402b1SRalf Baechle 			/*
485e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
486e01402b1SRalf Baechle 			 * the low bits.
487e01402b1SRalf Baechle 			 */
488e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
489e01402b1SRalf Baechle 
490e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
491e01402b1SRalf Baechle 			*l->addr = insn;
492e01402b1SRalf Baechle 
493e01402b1SRalf Baechle 			next = l->next;
494e01402b1SRalf Baechle 			kfree(l);
495e01402b1SRalf Baechle 			l = next;
496e01402b1SRalf Baechle 		}
497e01402b1SRalf Baechle 
498e01402b1SRalf Baechle 		mips_hi16_list = NULL;
499e01402b1SRalf Baechle 	}
500e01402b1SRalf Baechle 
501e01402b1SRalf Baechle 	/*
502e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
503e01402b1SRalf Baechle 	 */
504e01402b1SRalf Baechle 	val = v + vallo;
505e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
506e01402b1SRalf Baechle 	*location = insnlo;
507e01402b1SRalf Baechle 
508e01402b1SRalf Baechle 	return 0;
509e01402b1SRalf Baechle }
510e01402b1SRalf Baechle 
511e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
512e01402b1SRalf Baechle 				Elf32_Addr v) = {
513e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
514e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
515e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
516e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
517e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
518e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
519e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
520e01402b1SRalf Baechle };
521e01402b1SRalf Baechle 
5222600990eSRalf Baechle static char *rstrs[] = {
5232600990eSRalf Baechle     	[R_MIPS_NONE]	= "MIPS_NONE",
5242600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5252600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5262600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5272600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5282600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5292600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5302600990eSRalf Baechle };
531e01402b1SRalf Baechle 
532e01402b1SRalf Baechle int apply_relocations(Elf32_Shdr *sechdrs,
533e01402b1SRalf Baechle 		      const char *strtab,
534e01402b1SRalf Baechle 		      unsigned int symindex,
535e01402b1SRalf Baechle 		      unsigned int relsec,
536e01402b1SRalf Baechle 		      struct module *me)
537e01402b1SRalf Baechle {
538e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
539e01402b1SRalf Baechle 	Elf32_Sym *sym;
540e01402b1SRalf Baechle 	uint32_t *location;
541e01402b1SRalf Baechle 	unsigned int i;
542e01402b1SRalf Baechle 	Elf32_Addr v;
543e01402b1SRalf Baechle 	int res;
544e01402b1SRalf Baechle 
545e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
546e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
547e01402b1SRalf Baechle 
548e01402b1SRalf Baechle 		/* This is where to make the change */
549e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
550e01402b1SRalf Baechle 			+ rel[i].r_offset;
551e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
552e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
553e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
554e01402b1SRalf Baechle 
555e01402b1SRalf Baechle 		if (!sym->st_value) {
556e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
557e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
558e01402b1SRalf Baechle 			/* just print the warning, dont barf */
559e01402b1SRalf Baechle 		}
560e01402b1SRalf Baechle 
561e01402b1SRalf Baechle 		v = sym->st_value;
562e01402b1SRalf Baechle 
563e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
564e01402b1SRalf Baechle 		if( res ) {
5652600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5662600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5672600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5682600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5692600990eSRalf Baechle 			       strtab + sym->st_name);
570e01402b1SRalf Baechle 			return res;
571e01402b1SRalf Baechle 		}
5722600990eSRalf Baechle 	}
573e01402b1SRalf Baechle 
574e01402b1SRalf Baechle 	return 0;
575e01402b1SRalf Baechle }
576e01402b1SRalf Baechle 
577e01402b1SRalf Baechle void save_gp_address(unsigned int secbase, unsigned int rel)
578e01402b1SRalf Baechle {
579e01402b1SRalf Baechle 	gp_addr = secbase + rel;
580e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
581e01402b1SRalf Baechle }
582e01402b1SRalf Baechle /* end module-elf32.c */
583e01402b1SRalf Baechle 
584e01402b1SRalf Baechle 
585e01402b1SRalf Baechle 
586e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
5872600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
588e01402b1SRalf Baechle 			    unsigned int symindex,
589e01402b1SRalf Baechle 			    const char *strtab,
590e01402b1SRalf Baechle 			    const char *secstrings,
591e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
592e01402b1SRalf Baechle {
593e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
594e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
595e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
5962600990eSRalf Baechle 	int size;
597e01402b1SRalf Baechle 
598e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
599e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6002600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
601e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6022600990eSRalf Baechle 			break;
6032600990eSRalf Baechle 		}
604e01402b1SRalf Baechle 	}
605e01402b1SRalf Baechle 
606e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
607e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
608e01402b1SRalf Baechle 		case SHN_COMMON:
6092600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6102600990eSRalf Baechle 			   st_value is currently size.
611e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
612e01402b1SRalf Baechle 
613e01402b1SRalf Baechle 			size = sym[i].st_value;
614e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
615e01402b1SRalf Baechle 
616e01402b1SRalf Baechle 			bssbase += size;
617e01402b1SRalf Baechle 			break;
618e01402b1SRalf Baechle 
619e01402b1SRalf Baechle 		case SHN_ABS:
620e01402b1SRalf Baechle 			/* Don't need to do anything */
621e01402b1SRalf Baechle 			break;
622e01402b1SRalf Baechle 
623e01402b1SRalf Baechle 		case SHN_UNDEF:
624e01402b1SRalf Baechle 			/* ret = -ENOENT; */
625e01402b1SRalf Baechle 			break;
626e01402b1SRalf Baechle 
627e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6282600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON"
6292600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6302600990eSRalf Baechle 			       sym[i].st_shndx);
631e01402b1SRalf Baechle 			// .sbss section
632e01402b1SRalf Baechle 			break;
633e01402b1SRalf Baechle 
634e01402b1SRalf Baechle 		default:
635e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
636e01402b1SRalf Baechle 
637e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
638e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
639e01402b1SRalf Baechle 			}
640e01402b1SRalf Baechle 
641e01402b1SRalf Baechle 			sym[i].st_value += secbase;
642e01402b1SRalf Baechle 			break;
643e01402b1SRalf Baechle 		}
644e01402b1SRalf Baechle 	}
645e01402b1SRalf Baechle }
646e01402b1SRalf Baechle 
647e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
648e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
649e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
650e01402b1SRalf Baechle {
651e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
652e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
653e01402b1SRalf Baechle 
654e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
655e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
656e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
657e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
658e01402b1SRalf Baechle 	}
659e01402b1SRalf Baechle }
660e01402b1SRalf Baechle #endif
661e01402b1SRalf Baechle 
662e01402b1SRalf Baechle static void dump_tc(struct tc *t)
663e01402b1SRalf Baechle {
6642600990eSRalf Baechle   	unsigned long val;
6652600990eSRalf Baechle 
6662600990eSRalf Baechle   	settc(t->index);
6672600990eSRalf Baechle  	printk(KERN_DEBUG "VPE loader: TC index %d targtc %ld "
6682600990eSRalf Baechle  	       "TCStatus 0x%lx halt 0x%lx\n",
6692600990eSRalf Baechle   	       t->index, read_c0_vpecontrol() & VPECONTROL_TARGTC,
6702600990eSRalf Baechle   	       read_tc_c0_tcstatus(), read_tc_c0_tchalt());
6712600990eSRalf Baechle 
6722600990eSRalf Baechle  	printk(KERN_DEBUG " tcrestart 0x%lx\n", read_tc_c0_tcrestart());
6732600990eSRalf Baechle  	printk(KERN_DEBUG " tcbind 0x%lx\n", read_tc_c0_tcbind());
6742600990eSRalf Baechle 
6752600990eSRalf Baechle   	val = read_c0_vpeconf0();
6762600990eSRalf Baechle  	printk(KERN_DEBUG " VPEConf0 0x%lx MVP %ld\n", val,
6772600990eSRalf Baechle   	       (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT);
6782600990eSRalf Baechle 
6792600990eSRalf Baechle  	printk(KERN_DEBUG " c0 status 0x%lx\n", read_vpe_c0_status());
6802600990eSRalf Baechle  	printk(KERN_DEBUG " c0 cause 0x%lx\n", read_vpe_c0_cause());
6812600990eSRalf Baechle 
6822600990eSRalf Baechle  	printk(KERN_DEBUG " c0 badvaddr 0x%lx\n", read_vpe_c0_badvaddr());
6832600990eSRalf Baechle  	printk(KERN_DEBUG " c0 epc 0x%lx\n", read_vpe_c0_epc());
684e01402b1SRalf Baechle }
685e01402b1SRalf Baechle 
686e01402b1SRalf Baechle static void dump_tclist(void)
687e01402b1SRalf Baechle {
688e01402b1SRalf Baechle 	struct tc *t;
689e01402b1SRalf Baechle 
690e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
691e01402b1SRalf Baechle 		dump_tc(t);
692e01402b1SRalf Baechle 	}
693e01402b1SRalf Baechle }
694e01402b1SRalf Baechle 
695e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
696307bd284SRalf Baechle int vpe_run(struct vpe * v)
697e01402b1SRalf Baechle {
6982600990eSRalf Baechle 	struct vpe_notifications *n;
6992600990eSRalf Baechle 	unsigned long val, dmt_flag;
700e01402b1SRalf Baechle 	struct tc *t;
701e01402b1SRalf Baechle 
702e01402b1SRalf Baechle 	/* check we are the Master VPE */
703e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
704e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
705e01402b1SRalf Baechle 		printk(KERN_WARNING
7062600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
707e01402b1SRalf Baechle 		return -1;
708e01402b1SRalf Baechle 	}
709e01402b1SRalf Baechle 
710e01402b1SRalf Baechle 	/* disable MT (using dvpe) */
711e01402b1SRalf Baechle 	dvpe();
712e01402b1SRalf Baechle 
713e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
714e01402b1SRalf Baechle                 if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
7152600990eSRalf Baechle                         printk(KERN_WARNING "VPE loader: TC %d is already in use.\n",
716e01402b1SRalf Baechle                                t->index);
717e01402b1SRalf Baechle                         return -ENOEXEC;
718e01402b1SRalf Baechle                 }
719e01402b1SRalf Baechle         } else {
7202600990eSRalf Baechle                 printk(KERN_WARNING "VPE loader: No TC's associated with VPE %d\n",
721e01402b1SRalf Baechle                        v->minor);
722e01402b1SRalf Baechle                 return -ENOEXEC;
723e01402b1SRalf Baechle         }
724e01402b1SRalf Baechle 
7252600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7262600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
727e01402b1SRalf Baechle 
7282600990eSRalf Baechle 	settc(t->index);
729e01402b1SRalf Baechle 
730e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
731e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
7322600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already doing something!\n",
733e01402b1SRalf Baechle 		       t->index);
734e01402b1SRalf Baechle 		dump_tclist();
735e01402b1SRalf Baechle 		return -ENOEXEC;
736e01402b1SRalf Baechle 	}
737e01402b1SRalf Baechle 
7382600990eSRalf Baechle 	/*
7392600990eSRalf Baechle 	 * Disable multi-threaded execution whilst we activate, clear the
7402600990eSRalf Baechle 	 * halt bit and bound the tc to the other VPE...
7412600990eSRalf Baechle 	 */
7422600990eSRalf Baechle 	dmt_flag = dmt();
7432600990eSRalf Baechle 
744e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
745e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
746e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
7472600990eSRalf Baechle 	/*
7482600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7492600990eSRalf Baechle 	 * allocatable
7502600990eSRalf Baechle 	 */
751e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
752e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
753e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
754e01402b1SRalf Baechle 
755e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
756e01402b1SRalf Baechle 
757e01402b1SRalf Baechle 	/*
758e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7592600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
760e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
761e01402b1SRalf Baechle 	 */
7622600990eSRalf Baechle  	mttgpr(7, physical_memsize);
763e01402b1SRalf Baechle 
764e01402b1SRalf Baechle 
7652600990eSRalf Baechle 	/* set up VPE1 */
7662600990eSRalf Baechle 	/*
7672600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7682600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7692600990eSRalf Baechle 	 */
7702600990eSRalf Baechle  	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | v->minor);
7712600990eSRalf Baechle 
7722600990eSRalf Baechle         /* Set up the XTC bit in vpeconf0 to point at our tc */
7732600990eSRalf Baechle         write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7742600990eSRalf Baechle                                | (t->index << VPECONF0_XTC_SHIFT));
7752600990eSRalf Baechle 
7762600990eSRalf Baechle         /* enable this VPE */
7772600990eSRalf Baechle         write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
778e01402b1SRalf Baechle 
779e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7802600990eSRalf Baechle 	write_vpe_c0_status(0);
781e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
782e01402b1SRalf Baechle 
783e01402b1SRalf Baechle 	/* take system out of configuration state */
784340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
785e01402b1SRalf Baechle 
7862600990eSRalf Baechle 	/* now safe to re-enable multi-threading */
7872600990eSRalf Baechle 	emt(dmt_flag);
788e01402b1SRalf Baechle 
789e01402b1SRalf Baechle 	/* set it running */
790e01402b1SRalf Baechle 	evpe(EVPE_ENABLE);
791e01402b1SRalf Baechle 
7922600990eSRalf Baechle 	list_for_each_entry(n, &v->notify, list) {
7932600990eSRalf Baechle 		n->start(v->minor);
7942600990eSRalf Baechle 	}
7952600990eSRalf Baechle 
796e01402b1SRalf Baechle 	return 0;
797e01402b1SRalf Baechle }
798e01402b1SRalf Baechle 
7992600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
800e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
801e01402b1SRalf Baechle 				      struct module *mod)
802e01402b1SRalf Baechle {
803e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
804e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
805e01402b1SRalf Baechle 
806e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
807e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
808e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
809e01402b1SRalf Baechle 		}
810e01402b1SRalf Baechle 
811e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
812e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
813e01402b1SRalf Baechle 		}
814e01402b1SRalf Baechle 	}
815e01402b1SRalf Baechle 
8162600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8172600990eSRalf Baechle 		return -1;
8182600990eSRalf Baechle 
819e01402b1SRalf Baechle 	return 0;
820e01402b1SRalf Baechle }
821e01402b1SRalf Baechle 
822307bd284SRalf Baechle /*
8232600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8242600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8252600990eSRalf Baechle  * when finished.
826e01402b1SRalf Baechle  */
827307bd284SRalf Baechle int vpe_elfload(struct vpe * v)
828e01402b1SRalf Baechle {
829e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
830e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
831e01402b1SRalf Baechle 	long err = 0;
832e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8332600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
834e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
835e01402b1SRalf Baechle 
836e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8372600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
838e01402b1SRalf Baechle 
839e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
840e01402b1SRalf Baechle 	len = v->plen;
841e01402b1SRalf Baechle 
842e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
843e01402b1SRalf Baechle 	   weird elf version */
844e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
8452600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8462600990eSRalf Baechle 	    || !elf_check_arch(hdr)
847e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
848e01402b1SRalf Baechle 		printk(KERN_WARNING
8492600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
850e01402b1SRalf Baechle 
851e01402b1SRalf Baechle 		return -ENOEXEC;
852e01402b1SRalf Baechle 	}
853e01402b1SRalf Baechle 
8542600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8552600990eSRalf Baechle 		relocate = 1;
8562600990eSRalf Baechle 
857e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8582600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8592600990eSRalf Baechle 		       len);
8602600990eSRalf Baechle 
861e01402b1SRalf Baechle 		return -ENOEXEC;
862e01402b1SRalf Baechle 	}
863e01402b1SRalf Baechle 
864e01402b1SRalf Baechle 	/* Convenience variables */
865e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
866e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
867e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
868e01402b1SRalf Baechle 
869e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
870e01402b1SRalf Baechle 	symindex = strindex = 0;
871e01402b1SRalf Baechle 
8722600990eSRalf Baechle 	if (relocate) {
873e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
874e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
875e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
876e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
877e01402b1SRalf Baechle 				       len);
878e01402b1SRalf Baechle 				return -ENOEXEC;
879e01402b1SRalf Baechle 			}
880e01402b1SRalf Baechle 
881e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
882e01402b1SRalf Baechle 			   temporary image. */
883e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
884e01402b1SRalf Baechle 
885e01402b1SRalf Baechle 			/* Internal symbols and strings. */
886e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
887e01402b1SRalf Baechle 				symindex = i;
888e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
889e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
890e01402b1SRalf Baechle 			}
891e01402b1SRalf Baechle 		}
892e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
8932600990eSRalf Baechle 	}
894e01402b1SRalf Baechle 
895e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
896e01402b1SRalf Baechle 	memset(v->load_addr, 0, mod.core_size);
897e01402b1SRalf Baechle 
8982600990eSRalf Baechle 	printk("VPE loader: loading to %p\n", v->load_addr);
899e01402b1SRalf Baechle 
9002600990eSRalf Baechle 	if (relocate) {
901e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
902e01402b1SRalf Baechle 			void *dest;
903e01402b1SRalf Baechle 
904e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
905e01402b1SRalf Baechle 				continue;
906e01402b1SRalf Baechle 
907e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
908e01402b1SRalf Baechle 
909e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
910e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
911e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
912e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
913e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9142600990eSRalf Baechle 
9152600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9162600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
917e01402b1SRalf Baechle 		}
918e01402b1SRalf Baechle 
919e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
920e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
921e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
922e01402b1SRalf Baechle 
923e01402b1SRalf Baechle  		/* Now do relocations. */
924e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
925e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
926e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
927e01402b1SRalf Baechle 
928e01402b1SRalf Baechle  			/* Not a valid relocation section? */
929e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
930e01402b1SRalf Baechle  				continue;
931e01402b1SRalf Baechle 
932e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
933e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
934e01402b1SRalf Baechle  				continue;
935e01402b1SRalf Baechle 
936e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9372600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9382600990eSRalf Baechle  							&mod);
939e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
940e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
941e01402b1SRalf Baechle  							 &mod);
9422600990eSRalf Baechle  			if (err < 0)
9432600990eSRalf Baechle  				return err;
9442600990eSRalf Baechle 
9452600990eSRalf Baechle   		}
9462600990eSRalf Baechle   	} else {
9472600990eSRalf Baechle   		for (i = 0; i < hdr->e_shnum; i++) {
9482600990eSRalf Baechle 
9492600990eSRalf Baechle  			/* Internal symbols and strings. */
9502600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9512600990eSRalf Baechle  				symindex = i;
9522600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9532600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9542600990eSRalf Baechle 
9552600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9562600990eSRalf Baechle  				   magic symbols */
9572600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9582600990eSRalf Baechle  			}
9592600990eSRalf Baechle 
9602600990eSRalf Baechle  			/* filter sections we dont want in the final image */
9612600990eSRalf Baechle  			if (!(sechdrs[i].sh_flags & SHF_ALLOC) ||
9622600990eSRalf Baechle  			    (sechdrs[i].sh_type == SHT_MIPS_REGINFO)) {
9632600990eSRalf Baechle  				printk( KERN_DEBUG " ignoring section, "
9642600990eSRalf Baechle  					"name %s type %x address 0x%x \n",
9652600990eSRalf Baechle  					secstrings + sechdrs[i].sh_name,
9662600990eSRalf Baechle  					sechdrs[i].sh_type, sechdrs[i].sh_addr);
9672600990eSRalf Baechle  				continue;
9682600990eSRalf Baechle  			}
9692600990eSRalf Baechle 
9702600990eSRalf Baechle   			if (sechdrs[i].sh_addr < (unsigned int)v->load_addr) {
9712600990eSRalf Baechle  				printk( KERN_WARNING "VPE loader: "
9722600990eSRalf Baechle  					"fully linked image has invalid section, "
9732600990eSRalf Baechle  					"name %s type %x address 0x%x, before load "
9742600990eSRalf Baechle  					"address of 0x%x\n",
9752600990eSRalf Baechle  					secstrings + sechdrs[i].sh_name,
9762600990eSRalf Baechle  					sechdrs[i].sh_type, sechdrs[i].sh_addr,
9772600990eSRalf Baechle  					(unsigned int)v->load_addr);
9782600990eSRalf Baechle   				return -ENOEXEC;
9792600990eSRalf Baechle   			}
9802600990eSRalf Baechle 
9812600990eSRalf Baechle  			printk(KERN_DEBUG " copying section sh_name %s, sh_addr 0x%x "
9822600990eSRalf Baechle 			       "size 0x%x0 from x%p\n",
9832600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr,
9842600990eSRalf Baechle 			       sechdrs[i].sh_size, hdr + sechdrs[i].sh_offset);
9852600990eSRalf Baechle 
9862600990eSRalf Baechle   			if (sechdrs[i].sh_type != SHT_NOBITS)
9872600990eSRalf Baechle 				memcpy((void *)sechdrs[i].sh_addr,
9882600990eSRalf Baechle 				       (char *)hdr + sechdrs[i].sh_offset,
9892600990eSRalf Baechle  				       sechdrs[i].sh_size);
9902600990eSRalf Baechle 			else
9912600990eSRalf Baechle 				memset((void *)sechdrs[i].sh_addr, 0, sechdrs[i].sh_size);
992e01402b1SRalf Baechle 		}
993e01402b1SRalf Baechle 	}
994e01402b1SRalf Baechle 
995e01402b1SRalf Baechle 	/* make sure it's physically written out */
996e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
997e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
998e01402b1SRalf Baechle 
999e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
10002600990eSRalf Baechle 		if (v->__start == 0) {
10012600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
10022600990eSRalf Baechle 			       "a __start symbol\n");
10032600990eSRalf Baechle 			return -ENOEXEC;
10042600990eSRalf Baechle 		}
1005e01402b1SRalf Baechle 
10062600990eSRalf Baechle 		if (v->shared_ptr == NULL)
10072600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
10082600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
10092600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
1010e01402b1SRalf Baechle 	}
1011e01402b1SRalf Baechle 
1012e01402b1SRalf Baechle 	printk(" elf loaded\n");
10132600990eSRalf Baechle 	return 0;
1014e01402b1SRalf Baechle }
1015e01402b1SRalf Baechle 
10162600990eSRalf Baechle __attribute_used__ void dump_vpe(struct vpe * v)
1017e01402b1SRalf Baechle {
1018e01402b1SRalf Baechle 	struct tc *t;
1019e01402b1SRalf Baechle 
10202600990eSRalf Baechle 	settc(v->minor);
10212600990eSRalf Baechle 
1022e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol());
1023e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0());
1024e01402b1SRalf Baechle 
10252600990eSRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list)
1026e01402b1SRalf Baechle 		dump_tc(t);
1027e01402b1SRalf Baechle }
1028e01402b1SRalf Baechle 
10292600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
1030e01402b1SRalf Baechle {
10312600990eSRalf Baechle 	int tmp;
1032e01402b1SRalf Baechle 
10332600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
10342600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1035e01402b1SRalf Baechle 
10362600990eSRalf Baechle 	settc(tc->index);
1037e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
1038e01402b1SRalf Baechle 
1039e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1040e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1041e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1042e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1043e01402b1SRalf Baechle 
1044e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
1045e01402b1SRalf Baechle 
10462600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
10472600990eSRalf Baechle 	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10482600990eSRalf Baechle 
10492600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
10502600990eSRalf Baechle }
10512600990eSRalf Baechle 
10522600990eSRalf Baechle static int getcwd(char *buff, int size)
10532600990eSRalf Baechle {
10542600990eSRalf Baechle 	mm_segment_t old_fs;
10552600990eSRalf Baechle 	int ret;
10562600990eSRalf Baechle 
10572600990eSRalf Baechle 	old_fs = get_fs();
10582600990eSRalf Baechle 	set_fs(KERNEL_DS);
10592600990eSRalf Baechle 
10602600990eSRalf Baechle 	ret = sys_getcwd(buff,size);
10612600990eSRalf Baechle 
10622600990eSRalf Baechle 	set_fs(old_fs);
10632600990eSRalf Baechle 
10642600990eSRalf Baechle 	return ret;
10652600990eSRalf Baechle }
10662600990eSRalf Baechle 
10672600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10682600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10692600990eSRalf Baechle {
10702600990eSRalf Baechle 	int minor, ret;
10712600990eSRalf Baechle 	struct vpe *v;
10722600990eSRalf Baechle 	struct vpe_notifications *not;
10732600990eSRalf Baechle 
10742600990eSRalf Baechle 	/* assume only 1 device at the mo. */
10752600990eSRalf Baechle 	if ((minor = MINOR(inode->i_rdev)) != 1) {
10762600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
10772600990eSRalf Baechle 		return -ENODEV;
10782600990eSRalf Baechle 	}
10792600990eSRalf Baechle 
10802600990eSRalf Baechle 	if ((v = get_vpe(minor)) == NULL) {
10812600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: unable to get vpe\n");
10822600990eSRalf Baechle 		return -ENODEV;
10832600990eSRalf Baechle 	}
10842600990eSRalf Baechle 
10852600990eSRalf Baechle 	if (v->state != VPE_STATE_UNUSED) {
10862600990eSRalf Baechle 		dvpe();
10872600990eSRalf Baechle 
10882600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
10892600990eSRalf Baechle 
10902600990eSRalf Baechle 		dump_tc(get_tc(minor));
10912600990eSRalf Baechle 
10922600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
10932600990eSRalf Baechle 			not->stop(minor);
10942600990eSRalf Baechle 		}
10952600990eSRalf Baechle 
10962600990eSRalf Baechle 		release_progmem(v->load_addr);
10972600990eSRalf Baechle 		cleanup_tc(get_tc(minor));
1098e01402b1SRalf Baechle 	}
1099e01402b1SRalf Baechle 
1100e01402b1SRalf Baechle 	// allocate it so when we get write ops we know it's expected.
1101e01402b1SRalf Baechle 	v->state = VPE_STATE_INUSE;
1102e01402b1SRalf Baechle 
1103e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1104e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1105e01402b1SRalf Baechle 	v->plen = P_SIZE;
1106e01402b1SRalf Baechle 	v->load_addr = NULL;
1107e01402b1SRalf Baechle 	v->len = 0;
1108e01402b1SRalf Baechle 
11092600990eSRalf Baechle 	v->uid = filp->f_uid;
11102600990eSRalf Baechle 	v->gid = filp->f_gid;
11112600990eSRalf Baechle 
11122600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
11132600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
11142600990eSRalf Baechle 	if (!kspd_events_reqd) {
11152600990eSRalf Baechle 		kspd_notify(&kspd_events);
11162600990eSRalf Baechle 		kspd_events_reqd++;
11172600990eSRalf Baechle 	}
11182600990eSRalf Baechle #endif
11192600990eSRalf Baechle 
11202600990eSRalf Baechle 	v->cwd[0] = 0;
11212600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
11222600990eSRalf Baechle 	if (ret < 0)
11232600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
11242600990eSRalf Baechle 
11252600990eSRalf Baechle 	v->shared_ptr = NULL;
11262600990eSRalf Baechle 	v->__start = 0;
1127e01402b1SRalf Baechle 	return 0;
1128e01402b1SRalf Baechle }
1129e01402b1SRalf Baechle 
1130e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1131e01402b1SRalf Baechle {
1132e01402b1SRalf Baechle 	int minor, ret = 0;
1133307bd284SRalf Baechle 	struct vpe *v;
1134e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
1135e01402b1SRalf Baechle 
1136e01402b1SRalf Baechle 	minor = MINOR(inode->i_rdev);
1137e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL)
1138e01402b1SRalf Baechle 		return -ENODEV;
1139e01402b1SRalf Baechle 
1140e01402b1SRalf Baechle 	// simple case of fire and forget, so tell the VPE to run...
1141e01402b1SRalf Baechle 
1142e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1143e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) {
1144e01402b1SRalf Baechle 		if (vpe_elfload(v) >= 0)
1145e01402b1SRalf Baechle 			vpe_run(v);
1146e01402b1SRalf Baechle 		else {
11472600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1148e01402b1SRalf Baechle 			ret = -ENOEXEC;
1149e01402b1SRalf Baechle 		}
1150e01402b1SRalf Baechle 	} else {
11512600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1152e01402b1SRalf Baechle 		ret = -ENOEXEC;
1153e01402b1SRalf Baechle 	}
1154e01402b1SRalf Baechle 
11552600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11562600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11572600990eSRalf Baechle 	   loose what has happened. So perhaps if garbage is sent to the vpe
11582600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11592600990eSRalf Baechle 	   executable will be along shortly. */
11602600990eSRalf Baechle 	if (ret < 0)
11612600990eSRalf Baechle 		v->shared_ptr = NULL;
11622600990eSRalf Baechle 
1163e01402b1SRalf Baechle 	// cleanup any temp buffers
1164e01402b1SRalf Baechle 	if (v->pbuffer)
1165e01402b1SRalf Baechle 		vfree(v->pbuffer);
1166e01402b1SRalf Baechle 	v->plen = 0;
1167e01402b1SRalf Baechle 	return ret;
1168e01402b1SRalf Baechle }
1169e01402b1SRalf Baechle 
1170e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1171e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1172e01402b1SRalf Baechle {
1173e01402b1SRalf Baechle 	int minor;
1174e01402b1SRalf Baechle 	size_t ret = count;
1175307bd284SRalf Baechle 	struct vpe *v;
1176e01402b1SRalf Baechle 
1177e01402b1SRalf Baechle 	minor = MINOR(file->f_dentry->d_inode->i_rdev);
1178e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL)
1179e01402b1SRalf Baechle 		return -ENODEV;
1180e01402b1SRalf Baechle 
1181e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
11822600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: no buffer for program\n");
1183e01402b1SRalf Baechle 		return -ENOMEM;
1184e01402b1SRalf Baechle 	}
1185e01402b1SRalf Baechle 
1186e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1187e01402b1SRalf Baechle 		printk(KERN_WARNING
11882600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1189e01402b1SRalf Baechle 		return -ENOMEM;
1190e01402b1SRalf Baechle 	}
1191e01402b1SRalf Baechle 
1192e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
11932600990eSRalf Baechle 	if (!count)
1194e01402b1SRalf Baechle 		return -EFAULT;
1195e01402b1SRalf Baechle 
1196e01402b1SRalf Baechle 	v->len += count;
1197e01402b1SRalf Baechle 	return ret;
1198e01402b1SRalf Baechle }
1199e01402b1SRalf Baechle 
1200e01402b1SRalf Baechle static struct file_operations vpe_fops = {
1201e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1202e01402b1SRalf Baechle 	.open = vpe_open,
1203e01402b1SRalf Baechle 	.release = vpe_release,
1204e01402b1SRalf Baechle 	.write = vpe_write
1205e01402b1SRalf Baechle };
1206e01402b1SRalf Baechle 
1207e01402b1SRalf Baechle /* module wrapper entry points */
1208e01402b1SRalf Baechle /* give me a vpe */
1209e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1210e01402b1SRalf Baechle {
1211e01402b1SRalf Baechle 	int i;
1212e01402b1SRalf Baechle 	struct vpe *v;
1213e01402b1SRalf Baechle 
1214e01402b1SRalf Baechle 	/* find a vpe */
1215e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1216e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1217e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1218e01402b1SRalf Baechle 			return v;
1219e01402b1SRalf Baechle 		}
1220e01402b1SRalf Baechle 	}
1221e01402b1SRalf Baechle 	return NULL;
1222e01402b1SRalf Baechle }
1223e01402b1SRalf Baechle 
1224e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1225e01402b1SRalf Baechle 
1226e01402b1SRalf Baechle /* start running from here */
1227e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1228e01402b1SRalf Baechle {
1229e01402b1SRalf Baechle 	struct vpe *v = vpe;
1230e01402b1SRalf Baechle 
1231e01402b1SRalf Baechle 	v->__start = start;
1232e01402b1SRalf Baechle 	return vpe_run(v);
1233e01402b1SRalf Baechle }
1234e01402b1SRalf Baechle 
1235e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1236e01402b1SRalf Baechle 
1237e01402b1SRalf Baechle /* halt it for now */
1238e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1239e01402b1SRalf Baechle {
1240e01402b1SRalf Baechle 	struct vpe *v = vpe;
1241e01402b1SRalf Baechle 	struct tc *t;
1242e01402b1SRalf Baechle 	unsigned int evpe_flags;
1243e01402b1SRalf Baechle 
1244e01402b1SRalf Baechle 	evpe_flags = dvpe();
1245e01402b1SRalf Baechle 
1246e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1247e01402b1SRalf Baechle 
1248e01402b1SRalf Baechle 		settc(t->index);
1249e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1250e01402b1SRalf Baechle 	}
1251e01402b1SRalf Baechle 
1252e01402b1SRalf Baechle 	evpe(evpe_flags);
1253e01402b1SRalf Baechle 
1254e01402b1SRalf Baechle 	return 0;
1255e01402b1SRalf Baechle }
1256e01402b1SRalf Baechle 
1257e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1258e01402b1SRalf Baechle 
1259e01402b1SRalf Baechle /* I've done with it thank you */
1260e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1261e01402b1SRalf Baechle {
1262e01402b1SRalf Baechle 	struct vpe *v = vpe;
1263e01402b1SRalf Baechle 	struct tc *t;
1264e01402b1SRalf Baechle 	unsigned int evpe_flags;
1265e01402b1SRalf Baechle 
1266e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1267e01402b1SRalf Baechle 		return -ENOEXEC;
1268e01402b1SRalf Baechle 	}
1269e01402b1SRalf Baechle 
1270e01402b1SRalf Baechle 	evpe_flags = dvpe();
1271e01402b1SRalf Baechle 
1272e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1273340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1274e01402b1SRalf Baechle 
1275e01402b1SRalf Baechle 	settc(t->index);
1276e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1277e01402b1SRalf Baechle 
1278e01402b1SRalf Baechle 	/* mark the TC unallocated and halt'ed */
1279e01402b1SRalf Baechle 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1280e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
1281e01402b1SRalf Baechle 
1282e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1283e01402b1SRalf Baechle 
1284340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1285e01402b1SRalf Baechle 	evpe(evpe_flags);
1286e01402b1SRalf Baechle 
1287e01402b1SRalf Baechle 	return 0;
1288e01402b1SRalf Baechle }
1289e01402b1SRalf Baechle 
1290e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1291e01402b1SRalf Baechle 
1292e01402b1SRalf Baechle void *vpe_get_shared(int index)
1293e01402b1SRalf Baechle {
1294e01402b1SRalf Baechle 	struct vpe *v;
1295e01402b1SRalf Baechle 
12962600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1297e01402b1SRalf Baechle 		return NULL;
1298e01402b1SRalf Baechle 
1299e01402b1SRalf Baechle 	return v->shared_ptr;
1300e01402b1SRalf Baechle }
1301e01402b1SRalf Baechle 
1302e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1303e01402b1SRalf Baechle 
13042600990eSRalf Baechle int vpe_getuid(int index)
13052600990eSRalf Baechle {
13062600990eSRalf Baechle 	struct vpe *v;
13072600990eSRalf Baechle 
13082600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13092600990eSRalf Baechle 		return -1;
13102600990eSRalf Baechle 
13112600990eSRalf Baechle 	return v->uid;
13122600990eSRalf Baechle }
13132600990eSRalf Baechle 
13142600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
13152600990eSRalf Baechle 
13162600990eSRalf Baechle int vpe_getgid(int index)
13172600990eSRalf Baechle {
13182600990eSRalf Baechle 	struct vpe *v;
13192600990eSRalf Baechle 
13202600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13212600990eSRalf Baechle 		return -1;
13222600990eSRalf Baechle 
13232600990eSRalf Baechle 	return v->gid;
13242600990eSRalf Baechle }
13252600990eSRalf Baechle 
13262600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
13272600990eSRalf Baechle 
13282600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
13292600990eSRalf Baechle {
13302600990eSRalf Baechle 	struct vpe *v;
13312600990eSRalf Baechle 
13322600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13332600990eSRalf Baechle 		return -1;
13342600990eSRalf Baechle 
13352600990eSRalf Baechle 	list_add(&notify->list, &v->notify);
13362600990eSRalf Baechle 	return 0;
13372600990eSRalf Baechle }
13382600990eSRalf Baechle 
13392600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13402600990eSRalf Baechle 
13412600990eSRalf Baechle char *vpe_getcwd(int index)
13422600990eSRalf Baechle {
13432600990eSRalf Baechle 	struct vpe *v;
13442600990eSRalf Baechle 
13452600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13462600990eSRalf Baechle 		return NULL;
13472600990eSRalf Baechle 
13482600990eSRalf Baechle 	return v->cwd;
13492600990eSRalf Baechle }
13502600990eSRalf Baechle 
13512600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13522600990eSRalf Baechle 
13532600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13542600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13552600990eSRalf Baechle {
13562600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13572600990eSRalf Baechle }
13582600990eSRalf Baechle #endif
13592600990eSRalf Baechle 
1360e01402b1SRalf Baechle static int __init vpe_module_init(void)
1361e01402b1SRalf Baechle {
1362e01402b1SRalf Baechle 	struct vpe *v = NULL;
1363e01402b1SRalf Baechle 	struct tc *t;
1364e01402b1SRalf Baechle 	unsigned long val;
1365e01402b1SRalf Baechle 	int i;
1366e01402b1SRalf Baechle 
1367e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1368e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1369e01402b1SRalf Baechle 		return -ENODEV;
1370e01402b1SRalf Baechle 	}
1371e01402b1SRalf Baechle 
1372682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1373682e852eSAlexey Dobriyan 	if (major < 0) {
1374e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1375307bd284SRalf Baechle 		return major;
1376e01402b1SRalf Baechle 	}
1377e01402b1SRalf Baechle 
1378e01402b1SRalf Baechle 	dmt();
1379e01402b1SRalf Baechle 	dvpe();
1380e01402b1SRalf Baechle 
1381e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1382340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1383e01402b1SRalf Baechle 
1384e01402b1SRalf Baechle 	/* dump_mtregs(); */
1385e01402b1SRalf Baechle 
1386e01402b1SRalf Baechle 	INIT_LIST_HEAD(&vpecontrol.vpe_list);
1387e01402b1SRalf Baechle 	INIT_LIST_HEAD(&vpecontrol.tc_list);
1388e01402b1SRalf Baechle 
1389e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
1390e01402b1SRalf Baechle 	for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) {
1391e01402b1SRalf Baechle 		t = alloc_tc(i);
1392e01402b1SRalf Baechle 
1393e01402b1SRalf Baechle 		/* VPE's */
1394e01402b1SRalf Baechle 		if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) {
1395e01402b1SRalf Baechle 			settc(i);
1396e01402b1SRalf Baechle 
1397e01402b1SRalf Baechle 			if ((v = alloc_vpe(i)) == NULL) {
1398e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
1399e01402b1SRalf Baechle 				return -ENODEV;
1400e01402b1SRalf Baechle 			}
1401e01402b1SRalf Baechle 
14022600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
14032600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1404e01402b1SRalf Baechle 
1405e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
1406e01402b1SRalf Baechle 			if (i != 0) {
1407e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1408e01402b1SRalf Baechle 
1409e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1410e01402b1SRalf Baechle 
1411e01402b1SRalf Baechle 				/* master VPE */
1412e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1413e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1414e01402b1SRalf Baechle 			}
1415e01402b1SRalf Baechle 
1416e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1417e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1418e01402b1SRalf Baechle 
1419e01402b1SRalf Baechle 			if (i != 0) {
1420e01402b1SRalf Baechle 				write_vpe_c0_status((read_c0_status() &
1421e01402b1SRalf Baechle 						     ~(ST0_IM | ST0_IE | ST0_KSU))
1422e01402b1SRalf Baechle 						    | ST0_CU0);
1423e01402b1SRalf Baechle 
14242600990eSRalf Baechle 				/*
14252600990eSRalf Baechle 				 * Set config to be the same as vpe0,
14262600990eSRalf Baechle 				 * particularly kseg0 coherency alg
14272600990eSRalf Baechle 				 */
1428e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1429e01402b1SRalf Baechle 			}
1430e01402b1SRalf Baechle 		}
1431e01402b1SRalf Baechle 
1432e01402b1SRalf Baechle 		/* TC's */
1433e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1434e01402b1SRalf Baechle 
1435e01402b1SRalf Baechle 		if (i != 0) {
1436e01402b1SRalf Baechle 			unsigned long tmp;
1437e01402b1SRalf Baechle 
1438e01402b1SRalf Baechle 			settc(i);
1439e01402b1SRalf Baechle 
14402600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
14412600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
14422600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
14432600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
14442600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
14452600990eSRalf Baechle 
14462600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
14472600990eSRalf Baechle 			   notify GDB...
14482600990eSRalf Baechle 			*/
14492600990eSRalf Baechle 
14502600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
14512600990eSRalf Baechle 				/* tc is bound >vpe0 */
14522600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
14532600990eSRalf Baechle 
14542600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
14552600990eSRalf Baechle 			}
1456e01402b1SRalf Baechle 
1457e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1458e01402b1SRalf Baechle 
14592600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1460e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1461e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1462e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1463e01402b1SRalf Baechle 
1464e01402b1SRalf Baechle 			write_tc_c0_tchalt(TCHALT_H);
1465e01402b1SRalf Baechle 		}
1466e01402b1SRalf Baechle 	}
1467e01402b1SRalf Baechle 
1468e01402b1SRalf Baechle 	/* release config state */
1469340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1470e01402b1SRalf Baechle 
14712600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
14722600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
14732600990eSRalf Baechle #endif
1474e01402b1SRalf Baechle 	return 0;
1475e01402b1SRalf Baechle }
1476e01402b1SRalf Baechle 
1477e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1478e01402b1SRalf Baechle {
1479e01402b1SRalf Baechle 	struct vpe *v, *n;
1480e01402b1SRalf Baechle 
1481e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1482e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1483e01402b1SRalf Baechle 			release_vpe(v);
1484e01402b1SRalf Baechle 		}
1485e01402b1SRalf Baechle 	}
1486e01402b1SRalf Baechle 
1487e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1488e01402b1SRalf Baechle }
1489e01402b1SRalf Baechle 
1490e01402b1SRalf Baechle module_init(vpe_module_init);
1491e01402b1SRalf Baechle module_exit(vpe_module_exit);
1492e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
14932600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1494e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1495