xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision be6e1437)
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 
31e01402b1SRalf Baechle #include <linux/kernel.h>
32e01402b1SRalf Baechle #include <linux/module.h>
33e01402b1SRalf Baechle #include <linux/fs.h>
34e01402b1SRalf Baechle #include <linux/init.h>
35e01402b1SRalf Baechle #include <asm/uaccess.h>
36e01402b1SRalf Baechle #include <linux/slab.h>
37e01402b1SRalf Baechle #include <linux/list.h>
38e01402b1SRalf Baechle #include <linux/vmalloc.h>
39e01402b1SRalf Baechle #include <linux/elf.h>
40e01402b1SRalf Baechle #include <linux/seq_file.h>
41e01402b1SRalf Baechle #include <linux/syscalls.h>
42e01402b1SRalf Baechle #include <linux/moduleloader.h>
43e01402b1SRalf Baechle #include <linux/interrupt.h>
44e01402b1SRalf Baechle #include <linux/poll.h>
45e01402b1SRalf Baechle #include <linux/bootmem.h>
46e01402b1SRalf Baechle #include <asm/mipsregs.h>
47340ee4b9SRalf Baechle #include <asm/mipsmtregs.h>
48e01402b1SRalf Baechle #include <asm/cacheflush.h>
49e01402b1SRalf Baechle #include <asm/atomic.h>
50e01402b1SRalf Baechle #include <asm/cpu.h>
51e01402b1SRalf Baechle #include <asm/processor.h>
52e01402b1SRalf Baechle #include <asm/system.h>
532600990eSRalf Baechle #include <asm/vpe.h>
542600990eSRalf Baechle #include <asm/kspd.h>
55e01402b1SRalf Baechle 
56e01402b1SRalf Baechle typedef void *vpe_handle;
57e01402b1SRalf Baechle 
58e01402b1SRalf Baechle #ifndef ARCH_SHF_SMALL
59e01402b1SRalf Baechle #define ARCH_SHF_SMALL 0
60e01402b1SRalf Baechle #endif
61e01402b1SRalf Baechle 
62e01402b1SRalf Baechle /* If this is set, the section belongs in the init part of the module */
63e01402b1SRalf Baechle #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
64e01402b1SRalf Baechle 
65e01402b1SRalf Baechle static char module_name[] = "vpe";
66307bd284SRalf Baechle static int major;
67e01402b1SRalf Baechle 
682600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
692600990eSRalf Baechle  static struct kspd_notifications kspd_events;
702600990eSRalf Baechle static int kspd_events_reqd = 0;
712600990eSRalf Baechle #endif
722600990eSRalf Baechle 
73e01402b1SRalf Baechle /* grab the likely amount of memory we will need. */
74e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
75e01402b1SRalf Baechle #define P_SIZE (2 * 1024 * 1024)
76e01402b1SRalf Baechle #else
77e01402b1SRalf Baechle /* add an overhead to the max kmalloc size for non-striped symbols/etc */
78e01402b1SRalf Baechle #define P_SIZE (256 * 1024)
79e01402b1SRalf Baechle #endif
80e01402b1SRalf Baechle 
812600990eSRalf Baechle extern unsigned long physical_memsize;
822600990eSRalf Baechle 
83e01402b1SRalf Baechle #define MAX_VPES 16
842600990eSRalf Baechle #define VPE_PATH_MAX 256
85e01402b1SRalf Baechle 
86e01402b1SRalf Baechle enum vpe_state {
87e01402b1SRalf Baechle 	VPE_STATE_UNUSED = 0,
88e01402b1SRalf Baechle 	VPE_STATE_INUSE,
89e01402b1SRalf Baechle 	VPE_STATE_RUNNING
90e01402b1SRalf Baechle };
91e01402b1SRalf Baechle 
92e01402b1SRalf Baechle enum tc_state {
93e01402b1SRalf Baechle 	TC_STATE_UNUSED = 0,
94e01402b1SRalf Baechle 	TC_STATE_INUSE,
95e01402b1SRalf Baechle 	TC_STATE_RUNNING,
96e01402b1SRalf Baechle 	TC_STATE_DYNAMIC
97e01402b1SRalf Baechle };
98e01402b1SRalf Baechle 
99307bd284SRalf Baechle struct vpe {
100e01402b1SRalf Baechle 	enum vpe_state state;
101e01402b1SRalf Baechle 
102e01402b1SRalf Baechle 	/* (device) minor associated with this vpe */
103e01402b1SRalf Baechle 	int minor;
104e01402b1SRalf Baechle 
105e01402b1SRalf Baechle 	/* elfloader stuff */
106e01402b1SRalf Baechle 	void *load_addr;
107571e0bedSRalf Baechle 	unsigned long len;
108e01402b1SRalf Baechle 	char *pbuffer;
109571e0bedSRalf Baechle 	unsigned long plen;
1102600990eSRalf Baechle 	unsigned int uid, gid;
1112600990eSRalf Baechle 	char cwd[VPE_PATH_MAX];
112e01402b1SRalf Baechle 
113e01402b1SRalf Baechle 	unsigned long __start;
114e01402b1SRalf Baechle 
115e01402b1SRalf Baechle 	/* tc's associated with this vpe */
116e01402b1SRalf Baechle 	struct list_head tc;
117e01402b1SRalf Baechle 
118e01402b1SRalf Baechle 	/* The list of vpe's */
119e01402b1SRalf Baechle 	struct list_head list;
120e01402b1SRalf Baechle 
121e01402b1SRalf Baechle 	/* shared symbol address */
122e01402b1SRalf Baechle 	void *shared_ptr;
1232600990eSRalf Baechle 
1242600990eSRalf Baechle 	/* the list of who wants to know when something major happens */
1252600990eSRalf Baechle 	struct list_head notify;
126307bd284SRalf Baechle };
127307bd284SRalf Baechle 
128307bd284SRalf Baechle struct tc {
129307bd284SRalf Baechle 	enum tc_state state;
130307bd284SRalf Baechle 	int index;
131307bd284SRalf Baechle 
132307bd284SRalf Baechle 	/* parent VPE */
133307bd284SRalf Baechle 	struct vpe *pvpe;
134307bd284SRalf Baechle 
135307bd284SRalf Baechle 	/* The list of TC's with this VPE */
136307bd284SRalf Baechle 	struct list_head tc;
137307bd284SRalf Baechle 
138307bd284SRalf Baechle 	/* The global list of tc's */
139307bd284SRalf Baechle 	struct list_head list;
140307bd284SRalf Baechle };
141e01402b1SRalf Baechle 
1429cfdf6f1SRalf Baechle struct {
143e01402b1SRalf Baechle 	/* Virtual processing elements */
144e01402b1SRalf Baechle 	struct list_head vpe_list;
145e01402b1SRalf Baechle 
146e01402b1SRalf Baechle 	/* Thread contexts */
147e01402b1SRalf Baechle 	struct list_head tc_list;
1489cfdf6f1SRalf Baechle } vpecontrol = {
1499cfdf6f1SRalf Baechle 	.vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list),
1509cfdf6f1SRalf Baechle 	.tc_list = LIST_HEAD_INIT(vpecontrol.tc_list)
1519cfdf6f1SRalf Baechle };
152e01402b1SRalf Baechle 
153e01402b1SRalf Baechle static void release_progmem(void *ptr);
1542600990eSRalf Baechle /* static __attribute_used__ void dump_vpe(struct vpe * v); */
155e01402b1SRalf Baechle extern void save_gp_address(unsigned int secbase, unsigned int rel);
156e01402b1SRalf Baechle 
157e01402b1SRalf Baechle /* get the vpe associated with this minor */
158e01402b1SRalf Baechle struct vpe *get_vpe(int minor)
159e01402b1SRalf Baechle {
160e01402b1SRalf Baechle 	struct vpe *v;
161e01402b1SRalf Baechle 
1622600990eSRalf Baechle 	if (!cpu_has_mipsmt)
1632600990eSRalf Baechle 		return NULL;
1642600990eSRalf Baechle 
165e01402b1SRalf Baechle 	list_for_each_entry(v, &vpecontrol.vpe_list, list) {
166e01402b1SRalf Baechle 		if (v->minor == minor)
167e01402b1SRalf Baechle 			return v;
168e01402b1SRalf Baechle 	}
169e01402b1SRalf Baechle 
170e01402b1SRalf Baechle 	return NULL;
171e01402b1SRalf Baechle }
172e01402b1SRalf Baechle 
173e01402b1SRalf Baechle /* get the vpe associated with this minor */
174e01402b1SRalf Baechle struct tc *get_tc(int index)
175e01402b1SRalf Baechle {
176e01402b1SRalf Baechle 	struct tc *t;
177e01402b1SRalf Baechle 
178e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
179e01402b1SRalf Baechle 		if (t->index == index)
180e01402b1SRalf Baechle 			return t;
181e01402b1SRalf Baechle 	}
182e01402b1SRalf Baechle 
183e01402b1SRalf Baechle 	return NULL;
184e01402b1SRalf Baechle }
185e01402b1SRalf Baechle 
186e01402b1SRalf Baechle struct tc *get_tc_unused(void)
187e01402b1SRalf Baechle {
188e01402b1SRalf Baechle 	struct tc *t;
189e01402b1SRalf Baechle 
190e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
191e01402b1SRalf Baechle 		if (t->state == TC_STATE_UNUSED)
192e01402b1SRalf Baechle 			return t;
193e01402b1SRalf Baechle 	}
194e01402b1SRalf Baechle 
195e01402b1SRalf Baechle 	return NULL;
196e01402b1SRalf Baechle }
197e01402b1SRalf Baechle 
198e01402b1SRalf Baechle /* allocate a vpe and associate it with this minor (or index) */
199e01402b1SRalf Baechle struct vpe *alloc_vpe(int minor)
200e01402b1SRalf Baechle {
201e01402b1SRalf Baechle 	struct vpe *v;
202e01402b1SRalf Baechle 
203307bd284SRalf Baechle 	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {
204e01402b1SRalf Baechle 		return NULL;
205e01402b1SRalf Baechle 	}
206e01402b1SRalf Baechle 
207e01402b1SRalf Baechle 	INIT_LIST_HEAD(&v->tc);
208e01402b1SRalf Baechle 	list_add_tail(&v->list, &vpecontrol.vpe_list);
209e01402b1SRalf Baechle 
2102600990eSRalf Baechle 	INIT_LIST_HEAD(&v->notify);
211e01402b1SRalf Baechle 	v->minor = minor;
212e01402b1SRalf Baechle 	return v;
213e01402b1SRalf Baechle }
214e01402b1SRalf Baechle 
215e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */
216e01402b1SRalf Baechle struct tc *alloc_tc(int index)
217e01402b1SRalf Baechle {
218e01402b1SRalf Baechle 	struct tc *t;
219e01402b1SRalf Baechle 
220307bd284SRalf Baechle 	if ((t = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) {
221e01402b1SRalf Baechle 		return NULL;
222e01402b1SRalf Baechle 	}
223e01402b1SRalf Baechle 
224e01402b1SRalf Baechle 	INIT_LIST_HEAD(&t->tc);
225e01402b1SRalf Baechle 	list_add_tail(&t->list, &vpecontrol.tc_list);
226e01402b1SRalf Baechle 
227e01402b1SRalf Baechle 	t->index = index;
228e01402b1SRalf Baechle 
229e01402b1SRalf Baechle 	return t;
230e01402b1SRalf Baechle }
231e01402b1SRalf Baechle 
232e01402b1SRalf Baechle /* clean up and free everything */
233e01402b1SRalf Baechle void release_vpe(struct vpe *v)
234e01402b1SRalf Baechle {
235e01402b1SRalf Baechle 	list_del(&v->list);
236e01402b1SRalf Baechle 	if (v->load_addr)
237e01402b1SRalf Baechle 		release_progmem(v);
238e01402b1SRalf Baechle 	kfree(v);
239e01402b1SRalf Baechle }
240e01402b1SRalf Baechle 
241e01402b1SRalf Baechle void dump_mtregs(void)
242e01402b1SRalf Baechle {
243e01402b1SRalf Baechle 	unsigned long val;
244e01402b1SRalf Baechle 
245e01402b1SRalf Baechle 	val = read_c0_config3();
246e01402b1SRalf Baechle 	printk("config3 0x%lx MT %ld\n", val,
247e01402b1SRalf Baechle 	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
248e01402b1SRalf Baechle 
249e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
250e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
251e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
252e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
253e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
254e01402b1SRalf Baechle 
2552600990eSRalf Baechle 	val = read_c0_mvpconf0();
2562600990eSRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
2572600990eSRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
2582600990eSRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
259e01402b1SRalf Baechle }
260e01402b1SRalf Baechle 
261e01402b1SRalf Baechle /* Find some VPE program space  */
262571e0bedSRalf Baechle static void *alloc_progmem(unsigned long len)
263e01402b1SRalf Baechle {
264e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
265e01402b1SRalf Baechle 	/* this means you must tell linux to use less memory than you physically have */
266571e0bedSRalf Baechle 	return pfn_to_kaddr(max_pfn);
267e01402b1SRalf Baechle #else
268e01402b1SRalf Baechle 	// simple grab some mem for now
269e01402b1SRalf Baechle 	return kmalloc(len, GFP_KERNEL);
270e01402b1SRalf Baechle #endif
271e01402b1SRalf Baechle }
272e01402b1SRalf Baechle 
273e01402b1SRalf Baechle static void release_progmem(void *ptr)
274e01402b1SRalf Baechle {
275e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
276e01402b1SRalf Baechle 	kfree(ptr);
277e01402b1SRalf Baechle #endif
278e01402b1SRalf Baechle }
279e01402b1SRalf Baechle 
280e01402b1SRalf Baechle /* Update size with this section: return offset. */
281e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
282e01402b1SRalf Baechle {
283e01402b1SRalf Baechle 	long ret;
284e01402b1SRalf Baechle 
285e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
286e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
287e01402b1SRalf Baechle 	return ret;
288e01402b1SRalf Baechle }
289e01402b1SRalf Baechle 
290e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
291e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
292e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
293e01402b1SRalf Baechle    belongs in init. */
294e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
295e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
296e01402b1SRalf Baechle {
297e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
298e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
299e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
300e01402b1SRalf Baechle 		 * finder in the two loops below */
301e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
302e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
303e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
304e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
305e01402b1SRalf Baechle 	};
306e01402b1SRalf Baechle 	unsigned int m, i;
307e01402b1SRalf Baechle 
308e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
309e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
310e01402b1SRalf Baechle 
311e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
312e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
313e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
314e01402b1SRalf Baechle 
315e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
316e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
317e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
318e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
319e01402b1SRalf Baechle 				continue;
320e01402b1SRalf Baechle 			s->sh_entsize = get_offset(&mod->core_size, s);
321e01402b1SRalf Baechle 		}
322e01402b1SRalf Baechle 
323e01402b1SRalf Baechle 		if (m == 0)
324e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
325e01402b1SRalf Baechle 
326e01402b1SRalf Baechle 	}
327e01402b1SRalf Baechle }
328e01402b1SRalf Baechle 
329e01402b1SRalf Baechle 
330e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
331e01402b1SRalf Baechle 
332e01402b1SRalf Baechle struct mips_hi16 {
333e01402b1SRalf Baechle 	struct mips_hi16 *next;
334e01402b1SRalf Baechle 	Elf32_Addr *addr;
335e01402b1SRalf Baechle 	Elf32_Addr value;
336e01402b1SRalf Baechle };
337e01402b1SRalf Baechle 
338e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
339e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
340e01402b1SRalf Baechle 
341e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
342e01402b1SRalf Baechle 			     Elf32_Addr v)
343e01402b1SRalf Baechle {
344e01402b1SRalf Baechle 	return 0;
345e01402b1SRalf Baechle }
346e01402b1SRalf Baechle 
347e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
348e01402b1SRalf Baechle 				Elf32_Addr v)
349e01402b1SRalf Baechle {
350e01402b1SRalf Baechle 	int rel;
351e01402b1SRalf Baechle 
352e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
353e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
354e01402b1SRalf Baechle 	}
355e01402b1SRalf Baechle 	else {
356e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
357e01402b1SRalf Baechle 		/* kludge! */
358e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
359e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
360e01402b1SRalf Baechle 	}
361e01402b1SRalf Baechle 
362e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3632600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3642600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3652600990eSRalf Baechle 		       rel);
366e01402b1SRalf Baechle 		return -ENOEXEC;
367e01402b1SRalf Baechle 	}
368e01402b1SRalf Baechle 
369e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
370e01402b1SRalf Baechle 
371e01402b1SRalf Baechle 	return 0;
372e01402b1SRalf Baechle }
373e01402b1SRalf Baechle 
374e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
375e01402b1SRalf Baechle 			     Elf32_Addr v)
376e01402b1SRalf Baechle {
377e01402b1SRalf Baechle 	int rel;
378e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
379e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
380e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
381e01402b1SRalf Baechle 
382e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3832600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
384e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
385e01402b1SRalf Baechle 		return -ENOEXEC;
386e01402b1SRalf Baechle 	}
387e01402b1SRalf Baechle 
388e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
389e01402b1SRalf Baechle 
390e01402b1SRalf Baechle 	return 0;
391e01402b1SRalf Baechle }
392e01402b1SRalf Baechle 
393e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
394e01402b1SRalf Baechle 			   Elf32_Addr v)
395e01402b1SRalf Baechle {
396e01402b1SRalf Baechle 	*location += v;
397e01402b1SRalf Baechle 
398e01402b1SRalf Baechle 	return 0;
399e01402b1SRalf Baechle }
400e01402b1SRalf Baechle 
401e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
402e01402b1SRalf Baechle 			   Elf32_Addr v)
403e01402b1SRalf Baechle {
404e01402b1SRalf Baechle 	if (v % 4) {
4052600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4062600990eSRalf Baechle 		       " unaligned relocation\n");
407e01402b1SRalf Baechle 		return -ENOEXEC;
408e01402b1SRalf Baechle 	}
409e01402b1SRalf Baechle 
410307bd284SRalf Baechle /*
411307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
412307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
413307bd284SRalf Baechle  * we want to set to zero.
414307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
415307bd284SRalf Baechle  * printk(KERN_ERR
416307bd284SRalf Baechle  * "module %s: relocation overflow\n",
417307bd284SRalf Baechle  * me->name);
418307bd284SRalf Baechle  * return -ENOEXEC;
419307bd284SRalf Baechle  * }
420e01402b1SRalf Baechle  */
421e01402b1SRalf Baechle 
422e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
423e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
424e01402b1SRalf Baechle 	return 0;
425e01402b1SRalf Baechle }
426e01402b1SRalf Baechle 
427e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
428e01402b1SRalf Baechle 			     Elf32_Addr v)
429e01402b1SRalf Baechle {
430e01402b1SRalf Baechle 	struct mips_hi16 *n;
431e01402b1SRalf Baechle 
432e01402b1SRalf Baechle 	/*
433e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
434e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
435e01402b1SRalf Baechle 	 * actual relocation.
436e01402b1SRalf Baechle 	 */
437e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
438e01402b1SRalf Baechle 	if (!n)
439e01402b1SRalf Baechle 		return -ENOMEM;
440e01402b1SRalf Baechle 
441e01402b1SRalf Baechle 	n->addr = location;
442e01402b1SRalf Baechle 	n->value = v;
443e01402b1SRalf Baechle 	n->next = mips_hi16_list;
444e01402b1SRalf Baechle 	mips_hi16_list = n;
445e01402b1SRalf Baechle 
446e01402b1SRalf Baechle 	return 0;
447e01402b1SRalf Baechle }
448e01402b1SRalf Baechle 
449e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
450e01402b1SRalf Baechle 			     Elf32_Addr v)
451e01402b1SRalf Baechle {
452e01402b1SRalf Baechle 	unsigned long insnlo = *location;
453e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
454e01402b1SRalf Baechle 
455e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
456e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
457e01402b1SRalf Baechle 
458e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
459e01402b1SRalf Baechle 		struct mips_hi16 *l;
460e01402b1SRalf Baechle 
461e01402b1SRalf Baechle 		l = mips_hi16_list;
462e01402b1SRalf Baechle 		while (l != NULL) {
463e01402b1SRalf Baechle 			struct mips_hi16 *next;
464e01402b1SRalf Baechle 			unsigned long insn;
465e01402b1SRalf Baechle 
466e01402b1SRalf Baechle 			/*
467e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
468e01402b1SRalf Baechle 			 */
469e01402b1SRalf Baechle  			if (v != l->value) {
4702600990eSRalf Baechle 				printk(KERN_DEBUG "VPE loader: "
4712600990eSRalf Baechle 				       "apply_r_mips_lo16/hi16: 	"
4722600990eSRalf Baechle 				       "inconsistent value information\n");
4732600990eSRalf Baechle 				return -ENOEXEC;
474e01402b1SRalf Baechle 			}
475e01402b1SRalf Baechle 
476e01402b1SRalf Baechle 			/*
477e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
478e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
479e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
480e01402b1SRalf Baechle 			 * by the LO16.
481e01402b1SRalf Baechle 			 */
482e01402b1SRalf Baechle 			insn = *l->addr;
483e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
484e01402b1SRalf Baechle 			val += v;
485e01402b1SRalf Baechle 
486e01402b1SRalf Baechle 			/*
487e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
488e01402b1SRalf Baechle 			 * the low bits.
489e01402b1SRalf Baechle 			 */
490e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
491e01402b1SRalf Baechle 
492e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
493e01402b1SRalf Baechle 			*l->addr = insn;
494e01402b1SRalf Baechle 
495e01402b1SRalf Baechle 			next = l->next;
496e01402b1SRalf Baechle 			kfree(l);
497e01402b1SRalf Baechle 			l = next;
498e01402b1SRalf Baechle 		}
499e01402b1SRalf Baechle 
500e01402b1SRalf Baechle 		mips_hi16_list = NULL;
501e01402b1SRalf Baechle 	}
502e01402b1SRalf Baechle 
503e01402b1SRalf Baechle 	/*
504e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
505e01402b1SRalf Baechle 	 */
506e01402b1SRalf Baechle 	val = v + vallo;
507e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
508e01402b1SRalf Baechle 	*location = insnlo;
509e01402b1SRalf Baechle 
510e01402b1SRalf Baechle 	return 0;
511e01402b1SRalf Baechle }
512e01402b1SRalf Baechle 
513e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
514e01402b1SRalf Baechle 				Elf32_Addr v) = {
515e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
516e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
517e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
518e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
519e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
520e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
521e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
522e01402b1SRalf Baechle };
523e01402b1SRalf Baechle 
5242600990eSRalf Baechle static char *rstrs[] = {
5252600990eSRalf Baechle     	[R_MIPS_NONE]	= "MIPS_NONE",
5262600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5272600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5282600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5292600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5302600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5312600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5322600990eSRalf Baechle };
533e01402b1SRalf Baechle 
534e01402b1SRalf Baechle int apply_relocations(Elf32_Shdr *sechdrs,
535e01402b1SRalf Baechle 		      const char *strtab,
536e01402b1SRalf Baechle 		      unsigned int symindex,
537e01402b1SRalf Baechle 		      unsigned int relsec,
538e01402b1SRalf Baechle 		      struct module *me)
539e01402b1SRalf Baechle {
540e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
541e01402b1SRalf Baechle 	Elf32_Sym *sym;
542e01402b1SRalf Baechle 	uint32_t *location;
543e01402b1SRalf Baechle 	unsigned int i;
544e01402b1SRalf Baechle 	Elf32_Addr v;
545e01402b1SRalf Baechle 	int res;
546e01402b1SRalf Baechle 
547e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
548e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
549e01402b1SRalf Baechle 
550e01402b1SRalf Baechle 		/* This is where to make the change */
551e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
552e01402b1SRalf Baechle 			+ rel[i].r_offset;
553e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
554e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
555e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
556e01402b1SRalf Baechle 
557e01402b1SRalf Baechle 		if (!sym->st_value) {
558e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
559e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
560e01402b1SRalf Baechle 			/* just print the warning, dont barf */
561e01402b1SRalf Baechle 		}
562e01402b1SRalf Baechle 
563e01402b1SRalf Baechle 		v = sym->st_value;
564e01402b1SRalf Baechle 
565e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
566e01402b1SRalf Baechle 		if( res ) {
5672600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5682600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5692600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5702600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5712600990eSRalf Baechle 			       strtab + sym->st_name);
572e01402b1SRalf Baechle 			return res;
573e01402b1SRalf Baechle 		}
5742600990eSRalf Baechle 	}
575e01402b1SRalf Baechle 
576e01402b1SRalf Baechle 	return 0;
577e01402b1SRalf Baechle }
578e01402b1SRalf Baechle 
579e01402b1SRalf Baechle void save_gp_address(unsigned int secbase, unsigned int rel)
580e01402b1SRalf Baechle {
581e01402b1SRalf Baechle 	gp_addr = secbase + rel;
582e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
583e01402b1SRalf Baechle }
584e01402b1SRalf Baechle /* end module-elf32.c */
585e01402b1SRalf Baechle 
586e01402b1SRalf Baechle 
587e01402b1SRalf Baechle 
588e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
5892600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
590e01402b1SRalf Baechle 			    unsigned int symindex,
591e01402b1SRalf Baechle 			    const char *strtab,
592e01402b1SRalf Baechle 			    const char *secstrings,
593e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
594e01402b1SRalf Baechle {
595e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
596e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
597e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
5982600990eSRalf Baechle 	int size;
599e01402b1SRalf Baechle 
600e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
601e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6022600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
603e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6042600990eSRalf Baechle 			break;
6052600990eSRalf Baechle 		}
606e01402b1SRalf Baechle 	}
607e01402b1SRalf Baechle 
608e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
609e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
610e01402b1SRalf Baechle 		case SHN_COMMON:
6112600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6122600990eSRalf Baechle 			   st_value is currently size.
613e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
614e01402b1SRalf Baechle 
615e01402b1SRalf Baechle 			size = sym[i].st_value;
616e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
617e01402b1SRalf Baechle 
618e01402b1SRalf Baechle 			bssbase += size;
619e01402b1SRalf Baechle 			break;
620e01402b1SRalf Baechle 
621e01402b1SRalf Baechle 		case SHN_ABS:
622e01402b1SRalf Baechle 			/* Don't need to do anything */
623e01402b1SRalf Baechle 			break;
624e01402b1SRalf Baechle 
625e01402b1SRalf Baechle 		case SHN_UNDEF:
626e01402b1SRalf Baechle 			/* ret = -ENOENT; */
627e01402b1SRalf Baechle 			break;
628e01402b1SRalf Baechle 
629e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6302600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON"
6312600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6322600990eSRalf Baechle 			       sym[i].st_shndx);
633e01402b1SRalf Baechle 			// .sbss section
634e01402b1SRalf Baechle 			break;
635e01402b1SRalf Baechle 
636e01402b1SRalf Baechle 		default:
637e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
638e01402b1SRalf Baechle 
639e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
640e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
641e01402b1SRalf Baechle 			}
642e01402b1SRalf Baechle 
643e01402b1SRalf Baechle 			sym[i].st_value += secbase;
644e01402b1SRalf Baechle 			break;
645e01402b1SRalf Baechle 		}
646e01402b1SRalf Baechle 	}
647e01402b1SRalf Baechle }
648e01402b1SRalf Baechle 
649e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
650e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
651e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
652e01402b1SRalf Baechle {
653e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
654e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
655e01402b1SRalf Baechle 
656e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
657e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
658e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
659e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
660e01402b1SRalf Baechle 	}
661e01402b1SRalf Baechle }
662e01402b1SRalf Baechle #endif
663e01402b1SRalf Baechle 
664e01402b1SRalf Baechle static void dump_tc(struct tc *t)
665e01402b1SRalf Baechle {
6662600990eSRalf Baechle   	unsigned long val;
6672600990eSRalf Baechle 
6682600990eSRalf Baechle   	settc(t->index);
6692600990eSRalf Baechle  	printk(KERN_DEBUG "VPE loader: TC index %d targtc %ld "
6702600990eSRalf Baechle  	       "TCStatus 0x%lx halt 0x%lx\n",
6712600990eSRalf Baechle   	       t->index, read_c0_vpecontrol() & VPECONTROL_TARGTC,
6722600990eSRalf Baechle   	       read_tc_c0_tcstatus(), read_tc_c0_tchalt());
6732600990eSRalf Baechle 
6742600990eSRalf Baechle  	printk(KERN_DEBUG " tcrestart 0x%lx\n", read_tc_c0_tcrestart());
6752600990eSRalf Baechle  	printk(KERN_DEBUG " tcbind 0x%lx\n", read_tc_c0_tcbind());
6762600990eSRalf Baechle 
6772600990eSRalf Baechle   	val = read_c0_vpeconf0();
6782600990eSRalf Baechle  	printk(KERN_DEBUG " VPEConf0 0x%lx MVP %ld\n", val,
6792600990eSRalf Baechle   	       (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT);
6802600990eSRalf Baechle 
6812600990eSRalf Baechle  	printk(KERN_DEBUG " c0 status 0x%lx\n", read_vpe_c0_status());
6822600990eSRalf Baechle  	printk(KERN_DEBUG " c0 cause 0x%lx\n", read_vpe_c0_cause());
6832600990eSRalf Baechle 
6842600990eSRalf Baechle  	printk(KERN_DEBUG " c0 badvaddr 0x%lx\n", read_vpe_c0_badvaddr());
6852600990eSRalf Baechle  	printk(KERN_DEBUG " c0 epc 0x%lx\n", read_vpe_c0_epc());
686e01402b1SRalf Baechle }
687e01402b1SRalf Baechle 
688e01402b1SRalf Baechle static void dump_tclist(void)
689e01402b1SRalf Baechle {
690e01402b1SRalf Baechle 	struct tc *t;
691e01402b1SRalf Baechle 
692e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
693e01402b1SRalf Baechle 		dump_tc(t);
694e01402b1SRalf Baechle 	}
695e01402b1SRalf Baechle }
696e01402b1SRalf Baechle 
697e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
698be6e1437SRalf Baechle static int vpe_run(struct vpe * v)
699e01402b1SRalf Baechle {
7002600990eSRalf Baechle 	struct vpe_notifications *n;
7012600990eSRalf Baechle 	unsigned long val, dmt_flag;
702e01402b1SRalf Baechle 	struct tc *t;
703e01402b1SRalf Baechle 
704e01402b1SRalf Baechle 	/* check we are the Master VPE */
705e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
706e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
707e01402b1SRalf Baechle 		printk(KERN_WARNING
7082600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
709e01402b1SRalf Baechle 		return -1;
710e01402b1SRalf Baechle 	}
711e01402b1SRalf Baechle 
712e01402b1SRalf Baechle 	/* disable MT (using dvpe) */
713e01402b1SRalf Baechle 	dvpe();
714e01402b1SRalf Baechle 
715e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
716e01402b1SRalf Baechle                 if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
7172600990eSRalf Baechle                         printk(KERN_WARNING "VPE loader: TC %d is already in use.\n",
718e01402b1SRalf Baechle                                t->index);
719e01402b1SRalf Baechle                         return -ENOEXEC;
720e01402b1SRalf Baechle                 }
721e01402b1SRalf Baechle         } else {
7222600990eSRalf Baechle                 printk(KERN_WARNING "VPE loader: No TC's associated with VPE %d\n",
723e01402b1SRalf Baechle                        v->minor);
724e01402b1SRalf Baechle                 return -ENOEXEC;
725e01402b1SRalf Baechle         }
726e01402b1SRalf Baechle 
7272600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7282600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
729e01402b1SRalf Baechle 
7302600990eSRalf Baechle 	settc(t->index);
731e01402b1SRalf Baechle 
732e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
733e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
7342600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already doing something!\n",
735e01402b1SRalf Baechle 		       t->index);
736e01402b1SRalf Baechle 		dump_tclist();
737e01402b1SRalf Baechle 		return -ENOEXEC;
738e01402b1SRalf Baechle 	}
739e01402b1SRalf Baechle 
7402600990eSRalf Baechle 	/*
7412600990eSRalf Baechle 	 * Disable multi-threaded execution whilst we activate, clear the
7422600990eSRalf Baechle 	 * halt bit and bound the tc to the other VPE...
7432600990eSRalf Baechle 	 */
7442600990eSRalf Baechle 	dmt_flag = dmt();
7452600990eSRalf Baechle 
746e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
747e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
748e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
7492600990eSRalf Baechle 	/*
7502600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7512600990eSRalf Baechle 	 * allocatable
7522600990eSRalf Baechle 	 */
753e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
754e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
755e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
756e01402b1SRalf Baechle 
757e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
758e01402b1SRalf Baechle 
759e01402b1SRalf Baechle 	/*
760e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7612600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
762e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
763e01402b1SRalf Baechle 	 */
7642600990eSRalf Baechle  	mttgpr(7, physical_memsize);
765e01402b1SRalf Baechle 
766e01402b1SRalf Baechle 
7672600990eSRalf Baechle 	/* set up VPE1 */
7682600990eSRalf Baechle 	/*
7692600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7702600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7712600990eSRalf Baechle 	 */
7722600990eSRalf Baechle  	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | v->minor);
7732600990eSRalf Baechle 
774a94d7020SElizabeth Oldham 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
775a94d7020SElizabeth Oldham 
776a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
777a94d7020SElizabeth Oldham 
7782600990eSRalf Baechle         /* Set up the XTC bit in vpeconf0 to point at our tc */
7792600990eSRalf Baechle         write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7802600990eSRalf Baechle                                | (t->index << VPECONF0_XTC_SHIFT));
7812600990eSRalf Baechle 
782a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
783a94d7020SElizabeth Oldham 
7842600990eSRalf Baechle         /* enable this VPE */
7852600990eSRalf Baechle         write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
786e01402b1SRalf Baechle 
787e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7882600990eSRalf Baechle 	write_vpe_c0_status(0);
789e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
790e01402b1SRalf Baechle 
791e01402b1SRalf Baechle 	/* take system out of configuration state */
792340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
793e01402b1SRalf Baechle 
7942600990eSRalf Baechle 	/* now safe to re-enable multi-threading */
7952600990eSRalf Baechle 	emt(dmt_flag);
796e01402b1SRalf Baechle 
797e01402b1SRalf Baechle 	/* set it running */
798e01402b1SRalf Baechle 	evpe(EVPE_ENABLE);
799e01402b1SRalf Baechle 
8002600990eSRalf Baechle 	list_for_each_entry(n, &v->notify, list) {
8012600990eSRalf Baechle 		n->start(v->minor);
8022600990eSRalf Baechle 	}
8032600990eSRalf Baechle 
804e01402b1SRalf Baechle 	return 0;
805e01402b1SRalf Baechle }
806e01402b1SRalf Baechle 
8072600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
808e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
809e01402b1SRalf Baechle 				      struct module *mod)
810e01402b1SRalf Baechle {
811e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
812e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
813e01402b1SRalf Baechle 
814e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
815e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
816e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
817e01402b1SRalf Baechle 		}
818e01402b1SRalf Baechle 
819e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
820e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
821e01402b1SRalf Baechle 		}
822e01402b1SRalf Baechle 	}
823e01402b1SRalf Baechle 
8242600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8252600990eSRalf Baechle 		return -1;
8262600990eSRalf Baechle 
827e01402b1SRalf Baechle 	return 0;
828e01402b1SRalf Baechle }
829e01402b1SRalf Baechle 
830307bd284SRalf Baechle /*
8312600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8322600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8332600990eSRalf Baechle  * when finished.
834e01402b1SRalf Baechle  */
835be6e1437SRalf Baechle static int vpe_elfload(struct vpe * v)
836e01402b1SRalf Baechle {
837e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
838e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
839e01402b1SRalf Baechle 	long err = 0;
840e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8412600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
842e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
843e01402b1SRalf Baechle 
844e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8452600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
846e01402b1SRalf Baechle 
847e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
848e01402b1SRalf Baechle 	len = v->plen;
849e01402b1SRalf Baechle 
850e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
851e01402b1SRalf Baechle 	   weird elf version */
852e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
8532600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8542600990eSRalf Baechle 	    || !elf_check_arch(hdr)
855e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
856e01402b1SRalf Baechle 		printk(KERN_WARNING
8572600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
858e01402b1SRalf Baechle 
859e01402b1SRalf Baechle 		return -ENOEXEC;
860e01402b1SRalf Baechle 	}
861e01402b1SRalf Baechle 
8622600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8632600990eSRalf Baechle 		relocate = 1;
8642600990eSRalf Baechle 
865e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8662600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8672600990eSRalf Baechle 		       len);
8682600990eSRalf Baechle 
869e01402b1SRalf Baechle 		return -ENOEXEC;
870e01402b1SRalf Baechle 	}
871e01402b1SRalf Baechle 
872e01402b1SRalf Baechle 	/* Convenience variables */
873e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
874e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
875e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
876e01402b1SRalf Baechle 
877e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
878e01402b1SRalf Baechle 	symindex = strindex = 0;
879e01402b1SRalf Baechle 
8802600990eSRalf Baechle 	if (relocate) {
881e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
882e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
883e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
884e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
885e01402b1SRalf Baechle 				       len);
886e01402b1SRalf Baechle 				return -ENOEXEC;
887e01402b1SRalf Baechle 			}
888e01402b1SRalf Baechle 
889e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
890e01402b1SRalf Baechle 			   temporary image. */
891e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
892e01402b1SRalf Baechle 
893e01402b1SRalf Baechle 			/* Internal symbols and strings. */
894e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
895e01402b1SRalf Baechle 				symindex = i;
896e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
897e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
898e01402b1SRalf Baechle 			}
899e01402b1SRalf Baechle 		}
900e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
9012600990eSRalf Baechle 	}
902e01402b1SRalf Baechle 
903e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
904e01402b1SRalf Baechle 	memset(v->load_addr, 0, mod.core_size);
905e01402b1SRalf Baechle 
9062600990eSRalf Baechle 	printk("VPE loader: loading to %p\n", v->load_addr);
907e01402b1SRalf Baechle 
9082600990eSRalf Baechle 	if (relocate) {
909e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
910e01402b1SRalf Baechle 			void *dest;
911e01402b1SRalf Baechle 
912e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
913e01402b1SRalf Baechle 				continue;
914e01402b1SRalf Baechle 
915e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
916e01402b1SRalf Baechle 
917e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
918e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
919e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
920e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
921e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9222600990eSRalf Baechle 
9232600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9242600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
925e01402b1SRalf Baechle 		}
926e01402b1SRalf Baechle 
927e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
928e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
929e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
930e01402b1SRalf Baechle 
931e01402b1SRalf Baechle  		/* Now do relocations. */
932e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
933e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
934e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
935e01402b1SRalf Baechle 
936e01402b1SRalf Baechle  			/* Not a valid relocation section? */
937e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
938e01402b1SRalf Baechle  				continue;
939e01402b1SRalf Baechle 
940e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
941e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
942e01402b1SRalf Baechle  				continue;
943e01402b1SRalf Baechle 
944e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9452600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9462600990eSRalf Baechle  							&mod);
947e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
948e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
949e01402b1SRalf Baechle  							 &mod);
9502600990eSRalf Baechle  			if (err < 0)
9512600990eSRalf Baechle  				return err;
9522600990eSRalf Baechle 
9532600990eSRalf Baechle   		}
9542600990eSRalf Baechle   	} else {
9552600990eSRalf Baechle   		for (i = 0; i < hdr->e_shnum; i++) {
9562600990eSRalf Baechle 
9572600990eSRalf Baechle  			/* Internal symbols and strings. */
9582600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9592600990eSRalf Baechle  				symindex = i;
9602600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9612600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9622600990eSRalf Baechle 
9632600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9642600990eSRalf Baechle  				   magic symbols */
9652600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9662600990eSRalf Baechle  			}
9672600990eSRalf Baechle 
9682600990eSRalf Baechle  			/* filter sections we dont want in the final image */
9692600990eSRalf Baechle  			if (!(sechdrs[i].sh_flags & SHF_ALLOC) ||
9702600990eSRalf Baechle  			    (sechdrs[i].sh_type == SHT_MIPS_REGINFO)) {
9712600990eSRalf Baechle  				printk( KERN_DEBUG " ignoring section, "
9722600990eSRalf Baechle  					"name %s type %x address 0x%x \n",
9732600990eSRalf Baechle  					secstrings + sechdrs[i].sh_name,
9742600990eSRalf Baechle  					sechdrs[i].sh_type, sechdrs[i].sh_addr);
9752600990eSRalf Baechle  				continue;
9762600990eSRalf Baechle  			}
9772600990eSRalf Baechle 
9782600990eSRalf Baechle   			if (sechdrs[i].sh_addr < (unsigned int)v->load_addr) {
9792600990eSRalf Baechle  				printk( KERN_WARNING "VPE loader: "
9802600990eSRalf Baechle  					"fully linked image has invalid section, "
9812600990eSRalf Baechle  					"name %s type %x address 0x%x, before load "
9822600990eSRalf Baechle  					"address of 0x%x\n",
9832600990eSRalf Baechle  					secstrings + sechdrs[i].sh_name,
9842600990eSRalf Baechle  					sechdrs[i].sh_type, sechdrs[i].sh_addr,
9852600990eSRalf Baechle  					(unsigned int)v->load_addr);
9862600990eSRalf Baechle   				return -ENOEXEC;
9872600990eSRalf Baechle   			}
9882600990eSRalf Baechle 
9892600990eSRalf Baechle  			printk(KERN_DEBUG " copying section sh_name %s, sh_addr 0x%x "
9902600990eSRalf Baechle 			       "size 0x%x0 from x%p\n",
9912600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr,
9922600990eSRalf Baechle 			       sechdrs[i].sh_size, hdr + sechdrs[i].sh_offset);
9932600990eSRalf Baechle 
9942600990eSRalf Baechle   			if (sechdrs[i].sh_type != SHT_NOBITS)
9952600990eSRalf Baechle 				memcpy((void *)sechdrs[i].sh_addr,
9962600990eSRalf Baechle 				       (char *)hdr + sechdrs[i].sh_offset,
9972600990eSRalf Baechle  				       sechdrs[i].sh_size);
9982600990eSRalf Baechle 			else
9992600990eSRalf Baechle 				memset((void *)sechdrs[i].sh_addr, 0, sechdrs[i].sh_size);
1000e01402b1SRalf Baechle 		}
1001e01402b1SRalf Baechle 	}
1002e01402b1SRalf Baechle 
1003e01402b1SRalf Baechle 	/* make sure it's physically written out */
1004e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
1005e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
1006e01402b1SRalf Baechle 
1007e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
10082600990eSRalf Baechle 		if (v->__start == 0) {
10092600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
10102600990eSRalf Baechle 			       "a __start symbol\n");
10112600990eSRalf Baechle 			return -ENOEXEC;
10122600990eSRalf Baechle 		}
1013e01402b1SRalf Baechle 
10142600990eSRalf Baechle 		if (v->shared_ptr == NULL)
10152600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
10162600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
10172600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
1018e01402b1SRalf Baechle 	}
1019e01402b1SRalf Baechle 
1020e01402b1SRalf Baechle 	printk(" elf loaded\n");
10212600990eSRalf Baechle 	return 0;
1022e01402b1SRalf Baechle }
1023e01402b1SRalf Baechle 
10242600990eSRalf Baechle __attribute_used__ void dump_vpe(struct vpe * v)
1025e01402b1SRalf Baechle {
1026e01402b1SRalf Baechle 	struct tc *t;
1027e01402b1SRalf Baechle 
10282600990eSRalf Baechle 	settc(v->minor);
10292600990eSRalf Baechle 
1030e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol());
1031e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0());
1032e01402b1SRalf Baechle 
10332600990eSRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list)
1034e01402b1SRalf Baechle 		dump_tc(t);
1035e01402b1SRalf Baechle }
1036e01402b1SRalf Baechle 
10372600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
1038e01402b1SRalf Baechle {
10392600990eSRalf Baechle 	int tmp;
1040e01402b1SRalf Baechle 
10412600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
10422600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1043e01402b1SRalf Baechle 
10442600990eSRalf Baechle 	settc(tc->index);
1045e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
1046e01402b1SRalf Baechle 
1047e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1048e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1049e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1050e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1051e01402b1SRalf Baechle 
1052e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
1053e01402b1SRalf Baechle 
10542600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
10552600990eSRalf Baechle 	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10562600990eSRalf Baechle 
10572600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
10582600990eSRalf Baechle }
10592600990eSRalf Baechle 
10602600990eSRalf Baechle static int getcwd(char *buff, int size)
10612600990eSRalf Baechle {
10622600990eSRalf Baechle 	mm_segment_t old_fs;
10632600990eSRalf Baechle 	int ret;
10642600990eSRalf Baechle 
10652600990eSRalf Baechle 	old_fs = get_fs();
10662600990eSRalf Baechle 	set_fs(KERNEL_DS);
10672600990eSRalf Baechle 
10682600990eSRalf Baechle 	ret = sys_getcwd(buff,size);
10692600990eSRalf Baechle 
10702600990eSRalf Baechle 	set_fs(old_fs);
10712600990eSRalf Baechle 
10722600990eSRalf Baechle 	return ret;
10732600990eSRalf Baechle }
10742600990eSRalf Baechle 
10752600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10762600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10772600990eSRalf Baechle {
10782600990eSRalf Baechle 	int minor, ret;
10792600990eSRalf Baechle 	struct vpe *v;
10802600990eSRalf Baechle 	struct vpe_notifications *not;
10812600990eSRalf Baechle 
10822600990eSRalf Baechle 	/* assume only 1 device at the mo. */
108379e55bcfSRalf Baechle 	if ((minor = iminor(inode)) != 1) {
10842600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
10852600990eSRalf Baechle 		return -ENODEV;
10862600990eSRalf Baechle 	}
10872600990eSRalf Baechle 
10882600990eSRalf Baechle 	if ((v = get_vpe(minor)) == NULL) {
10892600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: unable to get vpe\n");
10902600990eSRalf Baechle 		return -ENODEV;
10912600990eSRalf Baechle 	}
10922600990eSRalf Baechle 
10932600990eSRalf Baechle 	if (v->state != VPE_STATE_UNUSED) {
10942600990eSRalf Baechle 		dvpe();
10952600990eSRalf Baechle 
10962600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
10972600990eSRalf Baechle 
10982600990eSRalf Baechle 		dump_tc(get_tc(minor));
10992600990eSRalf Baechle 
11002600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
11012600990eSRalf Baechle 			not->stop(minor);
11022600990eSRalf Baechle 		}
11032600990eSRalf Baechle 
11042600990eSRalf Baechle 		release_progmem(v->load_addr);
11052600990eSRalf Baechle 		cleanup_tc(get_tc(minor));
1106e01402b1SRalf Baechle 	}
1107e01402b1SRalf Baechle 
1108e01402b1SRalf Baechle 	// allocate it so when we get write ops we know it's expected.
1109e01402b1SRalf Baechle 	v->state = VPE_STATE_INUSE;
1110e01402b1SRalf Baechle 
1111e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1112e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1113e01402b1SRalf Baechle 	v->plen = P_SIZE;
1114e01402b1SRalf Baechle 	v->load_addr = NULL;
1115e01402b1SRalf Baechle 	v->len = 0;
1116e01402b1SRalf Baechle 
11172600990eSRalf Baechle 	v->uid = filp->f_uid;
11182600990eSRalf Baechle 	v->gid = filp->f_gid;
11192600990eSRalf Baechle 
11202600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
11212600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
11222600990eSRalf Baechle 	if (!kspd_events_reqd) {
11232600990eSRalf Baechle 		kspd_notify(&kspd_events);
11242600990eSRalf Baechle 		kspd_events_reqd++;
11252600990eSRalf Baechle 	}
11262600990eSRalf Baechle #endif
11272600990eSRalf Baechle 
11282600990eSRalf Baechle 	v->cwd[0] = 0;
11292600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
11302600990eSRalf Baechle 	if (ret < 0)
11312600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
11322600990eSRalf Baechle 
11332600990eSRalf Baechle 	v->shared_ptr = NULL;
11342600990eSRalf Baechle 	v->__start = 0;
1135e01402b1SRalf Baechle 	return 0;
1136e01402b1SRalf Baechle }
1137e01402b1SRalf Baechle 
1138e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1139e01402b1SRalf Baechle {
1140e01402b1SRalf Baechle 	int minor, ret = 0;
1141307bd284SRalf Baechle 	struct vpe *v;
1142e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
1143e01402b1SRalf Baechle 
114479e55bcfSRalf Baechle 	minor = iminor(inode);
1145e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL)
1146e01402b1SRalf Baechle 		return -ENODEV;
1147e01402b1SRalf Baechle 
1148e01402b1SRalf Baechle 	// simple case of fire and forget, so tell the VPE to run...
1149e01402b1SRalf Baechle 
1150e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1151e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) {
1152e01402b1SRalf Baechle 		if (vpe_elfload(v) >= 0)
1153e01402b1SRalf Baechle 			vpe_run(v);
1154e01402b1SRalf Baechle 		else {
11552600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1156e01402b1SRalf Baechle 			ret = -ENOEXEC;
1157e01402b1SRalf Baechle 		}
1158e01402b1SRalf Baechle 	} else {
11592600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1160e01402b1SRalf Baechle 		ret = -ENOEXEC;
1161e01402b1SRalf Baechle 	}
1162e01402b1SRalf Baechle 
11632600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11642600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11652600990eSRalf Baechle 	   loose what has happened. So perhaps if garbage is sent to the vpe
11662600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11672600990eSRalf Baechle 	   executable will be along shortly. */
11682600990eSRalf Baechle 	if (ret < 0)
11692600990eSRalf Baechle 		v->shared_ptr = NULL;
11702600990eSRalf Baechle 
1171e01402b1SRalf Baechle 	// cleanup any temp buffers
1172e01402b1SRalf Baechle 	if (v->pbuffer)
1173e01402b1SRalf Baechle 		vfree(v->pbuffer);
1174e01402b1SRalf Baechle 	v->plen = 0;
1175e01402b1SRalf Baechle 	return ret;
1176e01402b1SRalf Baechle }
1177e01402b1SRalf Baechle 
1178e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1179e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1180e01402b1SRalf Baechle {
1181e01402b1SRalf Baechle 	int minor;
1182e01402b1SRalf Baechle 	size_t ret = count;
1183307bd284SRalf Baechle 	struct vpe *v;
1184e01402b1SRalf Baechle 
11851b04fe9aSJosef Sipek 	minor = iminor(file->f_path.dentry->d_inode);
1186e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL)
1187e01402b1SRalf Baechle 		return -ENODEV;
1188e01402b1SRalf Baechle 
1189e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
11902600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: no buffer for program\n");
1191e01402b1SRalf Baechle 		return -ENOMEM;
1192e01402b1SRalf Baechle 	}
1193e01402b1SRalf Baechle 
1194e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1195e01402b1SRalf Baechle 		printk(KERN_WARNING
11962600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1197e01402b1SRalf Baechle 		return -ENOMEM;
1198e01402b1SRalf Baechle 	}
1199e01402b1SRalf Baechle 
1200e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
12012600990eSRalf Baechle 	if (!count)
1202e01402b1SRalf Baechle 		return -EFAULT;
1203e01402b1SRalf Baechle 
1204e01402b1SRalf Baechle 	v->len += count;
1205e01402b1SRalf Baechle 	return ret;
1206e01402b1SRalf Baechle }
1207e01402b1SRalf Baechle 
1208e01402b1SRalf Baechle static struct file_operations vpe_fops = {
1209e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1210e01402b1SRalf Baechle 	.open = vpe_open,
1211e01402b1SRalf Baechle 	.release = vpe_release,
1212e01402b1SRalf Baechle 	.write = vpe_write
1213e01402b1SRalf Baechle };
1214e01402b1SRalf Baechle 
1215e01402b1SRalf Baechle /* module wrapper entry points */
1216e01402b1SRalf Baechle /* give me a vpe */
1217e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1218e01402b1SRalf Baechle {
1219e01402b1SRalf Baechle 	int i;
1220e01402b1SRalf Baechle 	struct vpe *v;
1221e01402b1SRalf Baechle 
1222e01402b1SRalf Baechle 	/* find a vpe */
1223e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1224e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1225e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1226e01402b1SRalf Baechle 			return v;
1227e01402b1SRalf Baechle 		}
1228e01402b1SRalf Baechle 	}
1229e01402b1SRalf Baechle 	return NULL;
1230e01402b1SRalf Baechle }
1231e01402b1SRalf Baechle 
1232e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1233e01402b1SRalf Baechle 
1234e01402b1SRalf Baechle /* start running from here */
1235e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1236e01402b1SRalf Baechle {
1237e01402b1SRalf Baechle 	struct vpe *v = vpe;
1238e01402b1SRalf Baechle 
1239e01402b1SRalf Baechle 	v->__start = start;
1240e01402b1SRalf Baechle 	return vpe_run(v);
1241e01402b1SRalf Baechle }
1242e01402b1SRalf Baechle 
1243e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1244e01402b1SRalf Baechle 
1245e01402b1SRalf Baechle /* halt it for now */
1246e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1247e01402b1SRalf Baechle {
1248e01402b1SRalf Baechle 	struct vpe *v = vpe;
1249e01402b1SRalf Baechle 	struct tc *t;
1250e01402b1SRalf Baechle 	unsigned int evpe_flags;
1251e01402b1SRalf Baechle 
1252e01402b1SRalf Baechle 	evpe_flags = dvpe();
1253e01402b1SRalf Baechle 
1254e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1255e01402b1SRalf Baechle 
1256e01402b1SRalf Baechle 		settc(t->index);
1257e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1258e01402b1SRalf Baechle 	}
1259e01402b1SRalf Baechle 
1260e01402b1SRalf Baechle 	evpe(evpe_flags);
1261e01402b1SRalf Baechle 
1262e01402b1SRalf Baechle 	return 0;
1263e01402b1SRalf Baechle }
1264e01402b1SRalf Baechle 
1265e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1266e01402b1SRalf Baechle 
1267e01402b1SRalf Baechle /* I've done with it thank you */
1268e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1269e01402b1SRalf Baechle {
1270e01402b1SRalf Baechle 	struct vpe *v = vpe;
1271e01402b1SRalf Baechle 	struct tc *t;
1272e01402b1SRalf Baechle 	unsigned int evpe_flags;
1273e01402b1SRalf Baechle 
1274e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1275e01402b1SRalf Baechle 		return -ENOEXEC;
1276e01402b1SRalf Baechle 	}
1277e01402b1SRalf Baechle 
1278e01402b1SRalf Baechle 	evpe_flags = dvpe();
1279e01402b1SRalf Baechle 
1280e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1281340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1282e01402b1SRalf Baechle 
1283e01402b1SRalf Baechle 	settc(t->index);
1284e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1285e01402b1SRalf Baechle 
1286e01402b1SRalf Baechle 	/* mark the TC unallocated and halt'ed */
1287e01402b1SRalf Baechle 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1288e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
1289e01402b1SRalf Baechle 
1290e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1291e01402b1SRalf Baechle 
1292340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1293e01402b1SRalf Baechle 	evpe(evpe_flags);
1294e01402b1SRalf Baechle 
1295e01402b1SRalf Baechle 	return 0;
1296e01402b1SRalf Baechle }
1297e01402b1SRalf Baechle 
1298e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1299e01402b1SRalf Baechle 
1300e01402b1SRalf Baechle void *vpe_get_shared(int index)
1301e01402b1SRalf Baechle {
1302e01402b1SRalf Baechle 	struct vpe *v;
1303e01402b1SRalf Baechle 
13042600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1305e01402b1SRalf Baechle 		return NULL;
1306e01402b1SRalf Baechle 
1307e01402b1SRalf Baechle 	return v->shared_ptr;
1308e01402b1SRalf Baechle }
1309e01402b1SRalf Baechle 
1310e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1311e01402b1SRalf Baechle 
13122600990eSRalf Baechle int vpe_getuid(int index)
13132600990eSRalf Baechle {
13142600990eSRalf Baechle 	struct vpe *v;
13152600990eSRalf Baechle 
13162600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13172600990eSRalf Baechle 		return -1;
13182600990eSRalf Baechle 
13192600990eSRalf Baechle 	return v->uid;
13202600990eSRalf Baechle }
13212600990eSRalf Baechle 
13222600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
13232600990eSRalf Baechle 
13242600990eSRalf Baechle int vpe_getgid(int index)
13252600990eSRalf Baechle {
13262600990eSRalf Baechle 	struct vpe *v;
13272600990eSRalf Baechle 
13282600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13292600990eSRalf Baechle 		return -1;
13302600990eSRalf Baechle 
13312600990eSRalf Baechle 	return v->gid;
13322600990eSRalf Baechle }
13332600990eSRalf Baechle 
13342600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
13352600990eSRalf Baechle 
13362600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
13372600990eSRalf Baechle {
13382600990eSRalf Baechle 	struct vpe *v;
13392600990eSRalf Baechle 
13402600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13412600990eSRalf Baechle 		return -1;
13422600990eSRalf Baechle 
13432600990eSRalf Baechle 	list_add(&notify->list, &v->notify);
13442600990eSRalf Baechle 	return 0;
13452600990eSRalf Baechle }
13462600990eSRalf Baechle 
13472600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13482600990eSRalf Baechle 
13492600990eSRalf Baechle char *vpe_getcwd(int index)
13502600990eSRalf Baechle {
13512600990eSRalf Baechle 	struct vpe *v;
13522600990eSRalf Baechle 
13532600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13542600990eSRalf Baechle 		return NULL;
13552600990eSRalf Baechle 
13562600990eSRalf Baechle 	return v->cwd;
13572600990eSRalf Baechle }
13582600990eSRalf Baechle 
13592600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13602600990eSRalf Baechle 
13612600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13622600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13632600990eSRalf Baechle {
13642600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13652600990eSRalf Baechle }
13662600990eSRalf Baechle #endif
13672600990eSRalf Baechle 
1368e01402b1SRalf Baechle static int __init vpe_module_init(void)
1369e01402b1SRalf Baechle {
1370e01402b1SRalf Baechle 	struct vpe *v = NULL;
1371e01402b1SRalf Baechle 	struct tc *t;
1372e01402b1SRalf Baechle 	unsigned long val;
1373e01402b1SRalf Baechle 	int i;
1374e01402b1SRalf Baechle 
1375e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1376e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1377e01402b1SRalf Baechle 		return -ENODEV;
1378e01402b1SRalf Baechle 	}
1379e01402b1SRalf Baechle 
1380682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1381682e852eSAlexey Dobriyan 	if (major < 0) {
1382e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1383307bd284SRalf Baechle 		return major;
1384e01402b1SRalf Baechle 	}
1385e01402b1SRalf Baechle 
1386e01402b1SRalf Baechle 	dmt();
1387e01402b1SRalf Baechle 	dvpe();
1388e01402b1SRalf Baechle 
1389e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1390340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1391e01402b1SRalf Baechle 
1392e01402b1SRalf Baechle 	/* dump_mtregs(); */
1393e01402b1SRalf Baechle 
1394e01402b1SRalf Baechle 
1395e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
1396e01402b1SRalf Baechle 	for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) {
1397e01402b1SRalf Baechle 		t = alloc_tc(i);
1398e01402b1SRalf Baechle 
1399e01402b1SRalf Baechle 		/* VPE's */
1400e01402b1SRalf Baechle 		if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) {
1401e01402b1SRalf Baechle 			settc(i);
1402e01402b1SRalf Baechle 
1403e01402b1SRalf Baechle 			if ((v = alloc_vpe(i)) == NULL) {
1404e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
1405e01402b1SRalf Baechle 				return -ENODEV;
1406e01402b1SRalf Baechle 			}
1407e01402b1SRalf Baechle 
14082600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
14092600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1410e01402b1SRalf Baechle 
1411e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
1412e01402b1SRalf Baechle 			if (i != 0) {
1413e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1414e01402b1SRalf Baechle 
1415e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1416e01402b1SRalf Baechle 
1417e01402b1SRalf Baechle 				/* master VPE */
1418e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1419e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1420e01402b1SRalf Baechle 			}
1421e01402b1SRalf Baechle 
1422e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1423e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1424e01402b1SRalf Baechle 
1425e01402b1SRalf Baechle 			if (i != 0) {
1426e01402b1SRalf Baechle 				write_vpe_c0_status((read_c0_status() &
1427e01402b1SRalf Baechle 						     ~(ST0_IM | ST0_IE | ST0_KSU))
1428e01402b1SRalf Baechle 						    | ST0_CU0);
1429e01402b1SRalf Baechle 
14302600990eSRalf Baechle 				/*
14312600990eSRalf Baechle 				 * Set config to be the same as vpe0,
14322600990eSRalf Baechle 				 * particularly kseg0 coherency alg
14332600990eSRalf Baechle 				 */
1434e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1435e01402b1SRalf Baechle 			}
1436e01402b1SRalf Baechle 		}
1437e01402b1SRalf Baechle 
1438e01402b1SRalf Baechle 		/* TC's */
1439e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1440e01402b1SRalf Baechle 
1441e01402b1SRalf Baechle 		if (i != 0) {
1442e01402b1SRalf Baechle 			unsigned long tmp;
1443e01402b1SRalf Baechle 
1444e01402b1SRalf Baechle 			settc(i);
1445e01402b1SRalf Baechle 
14462600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
14472600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
14482600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
14492600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
14502600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
14512600990eSRalf Baechle 
14522600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
14532600990eSRalf Baechle 			   notify GDB...
14542600990eSRalf Baechle 			*/
14552600990eSRalf Baechle 
14562600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
14572600990eSRalf Baechle 				/* tc is bound >vpe0 */
14582600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
14592600990eSRalf Baechle 
14602600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
14612600990eSRalf Baechle 			}
1462e01402b1SRalf Baechle 
1463e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1464e01402b1SRalf Baechle 
14652600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1466e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1467e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1468e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1469e01402b1SRalf Baechle 
1470e01402b1SRalf Baechle 			write_tc_c0_tchalt(TCHALT_H);
1471e01402b1SRalf Baechle 		}
1472e01402b1SRalf Baechle 	}
1473e01402b1SRalf Baechle 
1474e01402b1SRalf Baechle 	/* release config state */
1475340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1476e01402b1SRalf Baechle 
14772600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
14782600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
14792600990eSRalf Baechle #endif
1480e01402b1SRalf Baechle 	return 0;
1481e01402b1SRalf Baechle }
1482e01402b1SRalf Baechle 
1483e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1484e01402b1SRalf Baechle {
1485e01402b1SRalf Baechle 	struct vpe *v, *n;
1486e01402b1SRalf Baechle 
1487e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1488e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1489e01402b1SRalf Baechle 			release_vpe(v);
1490e01402b1SRalf Baechle 		}
1491e01402b1SRalf Baechle 	}
1492e01402b1SRalf Baechle 
1493e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1494e01402b1SRalf Baechle }
1495e01402b1SRalf Baechle 
1496e01402b1SRalf Baechle module_init(vpe_module_init);
1497e01402b1SRalf Baechle module_exit(vpe_module_exit);
1498e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
14992600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1500e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1501