xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision c4c4018b)
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>
3227a3bbafSRalf Baechle #include <linux/device.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>
5227a3bbafSRalf Baechle #include <asm/mips_mt.h>
53e01402b1SRalf Baechle #include <asm/processor.h>
54e01402b1SRalf Baechle #include <asm/system.h>
552600990eSRalf Baechle #include <asm/vpe.h>
562600990eSRalf Baechle #include <asm/kspd.h>
57e01402b1SRalf Baechle 
58e01402b1SRalf Baechle typedef void *vpe_handle;
59e01402b1SRalf Baechle 
60e01402b1SRalf Baechle #ifndef ARCH_SHF_SMALL
61e01402b1SRalf Baechle #define ARCH_SHF_SMALL 0
62e01402b1SRalf Baechle #endif
63e01402b1SRalf Baechle 
64e01402b1SRalf Baechle /* If this is set, the section belongs in the init part of the module */
65e01402b1SRalf Baechle #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
66e01402b1SRalf Baechle 
67e01402b1SRalf Baechle static char module_name[] = "vpe";
68307bd284SRalf Baechle static int major;
6927a3bbafSRalf Baechle static const int minor = 1;	/* fixed for now  */
70e01402b1SRalf Baechle 
712600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
722600990eSRalf Baechle  static struct kspd_notifications kspd_events;
732600990eSRalf Baechle static int kspd_events_reqd = 0;
742600990eSRalf Baechle #endif
752600990eSRalf Baechle 
76e01402b1SRalf Baechle /* grab the likely amount of memory we will need. */
77e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
78e01402b1SRalf Baechle #define P_SIZE (2 * 1024 * 1024)
79e01402b1SRalf Baechle #else
80e01402b1SRalf Baechle /* add an overhead to the max kmalloc size for non-striped symbols/etc */
81e01402b1SRalf Baechle #define P_SIZE (256 * 1024)
82e01402b1SRalf Baechle #endif
83e01402b1SRalf Baechle 
842600990eSRalf Baechle extern unsigned long physical_memsize;
852600990eSRalf Baechle 
86e01402b1SRalf Baechle #define MAX_VPES 16
872600990eSRalf Baechle #define VPE_PATH_MAX 256
88e01402b1SRalf Baechle 
89e01402b1SRalf Baechle enum vpe_state {
90e01402b1SRalf Baechle 	VPE_STATE_UNUSED = 0,
91e01402b1SRalf Baechle 	VPE_STATE_INUSE,
92e01402b1SRalf Baechle 	VPE_STATE_RUNNING
93e01402b1SRalf Baechle };
94e01402b1SRalf Baechle 
95e01402b1SRalf Baechle enum tc_state {
96e01402b1SRalf Baechle 	TC_STATE_UNUSED = 0,
97e01402b1SRalf Baechle 	TC_STATE_INUSE,
98e01402b1SRalf Baechle 	TC_STATE_RUNNING,
99e01402b1SRalf Baechle 	TC_STATE_DYNAMIC
100e01402b1SRalf Baechle };
101e01402b1SRalf Baechle 
102307bd284SRalf Baechle struct vpe {
103e01402b1SRalf Baechle 	enum vpe_state state;
104e01402b1SRalf Baechle 
105e01402b1SRalf Baechle 	/* (device) minor associated with this vpe */
106e01402b1SRalf Baechle 	int minor;
107e01402b1SRalf Baechle 
108e01402b1SRalf Baechle 	/* elfloader stuff */
109e01402b1SRalf Baechle 	void *load_addr;
110571e0bedSRalf Baechle 	unsigned long len;
111e01402b1SRalf Baechle 	char *pbuffer;
112571e0bedSRalf Baechle 	unsigned long plen;
1132600990eSRalf Baechle 	unsigned int uid, gid;
1142600990eSRalf Baechle 	char cwd[VPE_PATH_MAX];
115e01402b1SRalf Baechle 
116e01402b1SRalf Baechle 	unsigned long __start;
117e01402b1SRalf Baechle 
118e01402b1SRalf Baechle 	/* tc's associated with this vpe */
119e01402b1SRalf Baechle 	struct list_head tc;
120e01402b1SRalf Baechle 
121e01402b1SRalf Baechle 	/* The list of vpe's */
122e01402b1SRalf Baechle 	struct list_head list;
123e01402b1SRalf Baechle 
124e01402b1SRalf Baechle 	/* shared symbol address */
125e01402b1SRalf Baechle 	void *shared_ptr;
1262600990eSRalf Baechle 
1272600990eSRalf Baechle 	/* the list of who wants to know when something major happens */
1282600990eSRalf Baechle 	struct list_head notify;
129307bd284SRalf Baechle };
130307bd284SRalf Baechle 
131307bd284SRalf Baechle struct tc {
132307bd284SRalf Baechle 	enum tc_state state;
133307bd284SRalf Baechle 	int index;
134307bd284SRalf Baechle 
135307bd284SRalf Baechle 	/* parent VPE */
136307bd284SRalf Baechle 	struct vpe *pvpe;
137307bd284SRalf Baechle 
138307bd284SRalf Baechle 	/* The list of TC's with this VPE */
139307bd284SRalf Baechle 	struct list_head tc;
140307bd284SRalf Baechle 
141307bd284SRalf Baechle 	/* The global list of tc's */
142307bd284SRalf Baechle 	struct list_head list;
143307bd284SRalf Baechle };
144e01402b1SRalf Baechle 
1459cfdf6f1SRalf Baechle struct {
146e01402b1SRalf Baechle 	/* Virtual processing elements */
147e01402b1SRalf Baechle 	struct list_head vpe_list;
148e01402b1SRalf Baechle 
149e01402b1SRalf Baechle 	/* Thread contexts */
150e01402b1SRalf Baechle 	struct list_head tc_list;
1519cfdf6f1SRalf Baechle } vpecontrol = {
1529cfdf6f1SRalf Baechle 	.vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list),
1539cfdf6f1SRalf Baechle 	.tc_list = LIST_HEAD_INIT(vpecontrol.tc_list)
1549cfdf6f1SRalf Baechle };
155e01402b1SRalf Baechle 
156e01402b1SRalf Baechle static void release_progmem(void *ptr);
1572600990eSRalf Baechle /* static __attribute_used__ void dump_vpe(struct vpe * v); */
158e01402b1SRalf Baechle extern void save_gp_address(unsigned int secbase, unsigned int rel);
159e01402b1SRalf Baechle 
160e01402b1SRalf Baechle /* get the vpe associated with this minor */
161e01402b1SRalf Baechle struct vpe *get_vpe(int minor)
162e01402b1SRalf Baechle {
163e01402b1SRalf Baechle 	struct vpe *v;
164e01402b1SRalf Baechle 
1652600990eSRalf Baechle 	if (!cpu_has_mipsmt)
1662600990eSRalf Baechle 		return NULL;
1672600990eSRalf Baechle 
168e01402b1SRalf Baechle 	list_for_each_entry(v, &vpecontrol.vpe_list, list) {
169e01402b1SRalf Baechle 		if (v->minor == minor)
170e01402b1SRalf Baechle 			return v;
171e01402b1SRalf Baechle 	}
172e01402b1SRalf Baechle 
173e01402b1SRalf Baechle 	return NULL;
174e01402b1SRalf Baechle }
175e01402b1SRalf Baechle 
176e01402b1SRalf Baechle /* get the vpe associated with this minor */
177e01402b1SRalf Baechle struct tc *get_tc(int index)
178e01402b1SRalf Baechle {
179e01402b1SRalf Baechle 	struct tc *t;
180e01402b1SRalf Baechle 
181e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
182e01402b1SRalf Baechle 		if (t->index == index)
183e01402b1SRalf Baechle 			return t;
184e01402b1SRalf Baechle 	}
185e01402b1SRalf Baechle 
186e01402b1SRalf Baechle 	return NULL;
187e01402b1SRalf Baechle }
188e01402b1SRalf Baechle 
189e01402b1SRalf Baechle struct tc *get_tc_unused(void)
190e01402b1SRalf Baechle {
191e01402b1SRalf Baechle 	struct tc *t;
192e01402b1SRalf Baechle 
193e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
194e01402b1SRalf Baechle 		if (t->state == TC_STATE_UNUSED)
195e01402b1SRalf Baechle 			return t;
196e01402b1SRalf Baechle 	}
197e01402b1SRalf Baechle 
198e01402b1SRalf Baechle 	return NULL;
199e01402b1SRalf Baechle }
200e01402b1SRalf Baechle 
201e01402b1SRalf Baechle /* allocate a vpe and associate it with this minor (or index) */
202e01402b1SRalf Baechle struct vpe *alloc_vpe(int minor)
203e01402b1SRalf Baechle {
204e01402b1SRalf Baechle 	struct vpe *v;
205e01402b1SRalf Baechle 
206307bd284SRalf Baechle 	if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {
207e01402b1SRalf Baechle 		return NULL;
208e01402b1SRalf Baechle 	}
209e01402b1SRalf Baechle 
210e01402b1SRalf Baechle 	INIT_LIST_HEAD(&v->tc);
211e01402b1SRalf Baechle 	list_add_tail(&v->list, &vpecontrol.vpe_list);
212e01402b1SRalf Baechle 
2132600990eSRalf Baechle 	INIT_LIST_HEAD(&v->notify);
214e01402b1SRalf Baechle 	v->minor = minor;
215e01402b1SRalf Baechle 	return v;
216e01402b1SRalf Baechle }
217e01402b1SRalf Baechle 
218e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */
219e01402b1SRalf Baechle struct tc *alloc_tc(int index)
220e01402b1SRalf Baechle {
221e01402b1SRalf Baechle 	struct tc *t;
222e01402b1SRalf Baechle 
223307bd284SRalf Baechle 	if ((t = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) {
224e01402b1SRalf Baechle 		return NULL;
225e01402b1SRalf Baechle 	}
226e01402b1SRalf Baechle 
227e01402b1SRalf Baechle 	INIT_LIST_HEAD(&t->tc);
228e01402b1SRalf Baechle 	list_add_tail(&t->list, &vpecontrol.tc_list);
229e01402b1SRalf Baechle 
230e01402b1SRalf Baechle 	t->index = index;
231e01402b1SRalf Baechle 
232e01402b1SRalf Baechle 	return t;
233e01402b1SRalf Baechle }
234e01402b1SRalf Baechle 
235e01402b1SRalf Baechle /* clean up and free everything */
236e01402b1SRalf Baechle void release_vpe(struct vpe *v)
237e01402b1SRalf Baechle {
238e01402b1SRalf Baechle 	list_del(&v->list);
239e01402b1SRalf Baechle 	if (v->load_addr)
240e01402b1SRalf Baechle 		release_progmem(v);
241e01402b1SRalf Baechle 	kfree(v);
242e01402b1SRalf Baechle }
243e01402b1SRalf Baechle 
244e01402b1SRalf Baechle void dump_mtregs(void)
245e01402b1SRalf Baechle {
246e01402b1SRalf Baechle 	unsigned long val;
247e01402b1SRalf Baechle 
248e01402b1SRalf Baechle 	val = read_c0_config3();
249e01402b1SRalf Baechle 	printk("config3 0x%lx MT %ld\n", val,
250e01402b1SRalf Baechle 	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
251e01402b1SRalf Baechle 
252e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
253e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
254e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
255e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
256e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
257e01402b1SRalf Baechle 
2582600990eSRalf Baechle 	val = read_c0_mvpconf0();
2592600990eSRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
2602600990eSRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
2612600990eSRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
262e01402b1SRalf Baechle }
263e01402b1SRalf Baechle 
264e01402b1SRalf Baechle /* Find some VPE program space  */
265571e0bedSRalf Baechle static void *alloc_progmem(unsigned long len)
266e01402b1SRalf Baechle {
267e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
268e01402b1SRalf Baechle 	/* this means you must tell linux to use less memory than you physically have */
269571e0bedSRalf Baechle 	return pfn_to_kaddr(max_pfn);
270e01402b1SRalf Baechle #else
271e01402b1SRalf Baechle 	// simple grab some mem for now
272e01402b1SRalf Baechle 	return kmalloc(len, GFP_KERNEL);
273e01402b1SRalf Baechle #endif
274e01402b1SRalf Baechle }
275e01402b1SRalf Baechle 
276e01402b1SRalf Baechle static void release_progmem(void *ptr)
277e01402b1SRalf Baechle {
278e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
279e01402b1SRalf Baechle 	kfree(ptr);
280e01402b1SRalf Baechle #endif
281e01402b1SRalf Baechle }
282e01402b1SRalf Baechle 
283e01402b1SRalf Baechle /* Update size with this section: return offset. */
284e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
285e01402b1SRalf Baechle {
286e01402b1SRalf Baechle 	long ret;
287e01402b1SRalf Baechle 
288e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
289e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
290e01402b1SRalf Baechle 	return ret;
291e01402b1SRalf Baechle }
292e01402b1SRalf Baechle 
293e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
294e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
295e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
296e01402b1SRalf Baechle    belongs in init. */
297e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
298e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
299e01402b1SRalf Baechle {
300e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
301e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
302e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
303e01402b1SRalf Baechle 		 * finder in the two loops below */
304e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
305e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
306e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
307e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
308e01402b1SRalf Baechle 	};
309e01402b1SRalf Baechle 	unsigned int m, i;
310e01402b1SRalf Baechle 
311e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
312e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
313e01402b1SRalf Baechle 
314e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
315e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
316e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
317e01402b1SRalf Baechle 
318e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
319e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
320e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
321e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
322e01402b1SRalf Baechle 				continue;
323e01402b1SRalf Baechle 			s->sh_entsize = get_offset(&mod->core_size, s);
324e01402b1SRalf Baechle 		}
325e01402b1SRalf Baechle 
326e01402b1SRalf Baechle 		if (m == 0)
327e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
328e01402b1SRalf Baechle 
329e01402b1SRalf Baechle 	}
330e01402b1SRalf Baechle }
331e01402b1SRalf Baechle 
332e01402b1SRalf Baechle 
333e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
334e01402b1SRalf Baechle 
335e01402b1SRalf Baechle struct mips_hi16 {
336e01402b1SRalf Baechle 	struct mips_hi16 *next;
337e01402b1SRalf Baechle 	Elf32_Addr *addr;
338e01402b1SRalf Baechle 	Elf32_Addr value;
339e01402b1SRalf Baechle };
340e01402b1SRalf Baechle 
341e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
342e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
343e01402b1SRalf Baechle 
344e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
345e01402b1SRalf Baechle 			     Elf32_Addr v)
346e01402b1SRalf Baechle {
347e01402b1SRalf Baechle 	return 0;
348e01402b1SRalf Baechle }
349e01402b1SRalf Baechle 
350e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
351e01402b1SRalf Baechle 				Elf32_Addr v)
352e01402b1SRalf Baechle {
353e01402b1SRalf Baechle 	int rel;
354e01402b1SRalf Baechle 
355e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
356e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
357e01402b1SRalf Baechle 	}
358e01402b1SRalf Baechle 	else {
359e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
360e01402b1SRalf Baechle 		/* kludge! */
361e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
362e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
363e01402b1SRalf Baechle 	}
364e01402b1SRalf Baechle 
365e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3662600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3672600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3682600990eSRalf Baechle 		       rel);
369e01402b1SRalf Baechle 		return -ENOEXEC;
370e01402b1SRalf Baechle 	}
371e01402b1SRalf Baechle 
372e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
373e01402b1SRalf Baechle 
374e01402b1SRalf Baechle 	return 0;
375e01402b1SRalf Baechle }
376e01402b1SRalf Baechle 
377e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
378e01402b1SRalf Baechle 			     Elf32_Addr v)
379e01402b1SRalf Baechle {
380e01402b1SRalf Baechle 	int rel;
381e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
382e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
383e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
384e01402b1SRalf Baechle 
385e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3862600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
387e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
388e01402b1SRalf Baechle 		return -ENOEXEC;
389e01402b1SRalf Baechle 	}
390e01402b1SRalf Baechle 
391e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
392e01402b1SRalf Baechle 
393e01402b1SRalf Baechle 	return 0;
394e01402b1SRalf Baechle }
395e01402b1SRalf Baechle 
396e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
397e01402b1SRalf Baechle 			   Elf32_Addr v)
398e01402b1SRalf Baechle {
399e01402b1SRalf Baechle 	*location += v;
400e01402b1SRalf Baechle 
401e01402b1SRalf Baechle 	return 0;
402e01402b1SRalf Baechle }
403e01402b1SRalf Baechle 
404e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
405e01402b1SRalf Baechle 			   Elf32_Addr v)
406e01402b1SRalf Baechle {
407e01402b1SRalf Baechle 	if (v % 4) {
4082600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4092600990eSRalf Baechle 		       " unaligned relocation\n");
410e01402b1SRalf Baechle 		return -ENOEXEC;
411e01402b1SRalf Baechle 	}
412e01402b1SRalf Baechle 
413307bd284SRalf Baechle /*
414307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
415307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
416307bd284SRalf Baechle  * we want to set to zero.
417307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
418307bd284SRalf Baechle  * printk(KERN_ERR
419307bd284SRalf Baechle  * "module %s: relocation overflow\n",
420307bd284SRalf Baechle  * me->name);
421307bd284SRalf Baechle  * return -ENOEXEC;
422307bd284SRalf Baechle  * }
423e01402b1SRalf Baechle  */
424e01402b1SRalf Baechle 
425e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
426e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
427e01402b1SRalf Baechle 	return 0;
428e01402b1SRalf Baechle }
429e01402b1SRalf Baechle 
430e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
431e01402b1SRalf Baechle 			     Elf32_Addr v)
432e01402b1SRalf Baechle {
433e01402b1SRalf Baechle 	struct mips_hi16 *n;
434e01402b1SRalf Baechle 
435e01402b1SRalf Baechle 	/*
436e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
437e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
438e01402b1SRalf Baechle 	 * actual relocation.
439e01402b1SRalf Baechle 	 */
440e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
441e01402b1SRalf Baechle 	if (!n)
442e01402b1SRalf Baechle 		return -ENOMEM;
443e01402b1SRalf Baechle 
444e01402b1SRalf Baechle 	n->addr = location;
445e01402b1SRalf Baechle 	n->value = v;
446e01402b1SRalf Baechle 	n->next = mips_hi16_list;
447e01402b1SRalf Baechle 	mips_hi16_list = n;
448e01402b1SRalf Baechle 
449e01402b1SRalf Baechle 	return 0;
450e01402b1SRalf Baechle }
451e01402b1SRalf Baechle 
452e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
453e01402b1SRalf Baechle 			     Elf32_Addr v)
454e01402b1SRalf Baechle {
455e01402b1SRalf Baechle 	unsigned long insnlo = *location;
456e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
457e01402b1SRalf Baechle 
458e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
459e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
460e01402b1SRalf Baechle 
461e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
462e01402b1SRalf Baechle 		struct mips_hi16 *l;
463e01402b1SRalf Baechle 
464e01402b1SRalf Baechle 		l = mips_hi16_list;
465e01402b1SRalf Baechle 		while (l != NULL) {
466e01402b1SRalf Baechle 			struct mips_hi16 *next;
467e01402b1SRalf Baechle 			unsigned long insn;
468e01402b1SRalf Baechle 
469e01402b1SRalf Baechle 			/*
470e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
471e01402b1SRalf Baechle 			 */
472e01402b1SRalf Baechle  			if (v != l->value) {
4732600990eSRalf Baechle 				printk(KERN_DEBUG "VPE loader: "
4742600990eSRalf Baechle 				       "apply_r_mips_lo16/hi16: 	"
4752600990eSRalf Baechle 				       "inconsistent value information\n");
4762600990eSRalf Baechle 				return -ENOEXEC;
477e01402b1SRalf Baechle 			}
478e01402b1SRalf Baechle 
479e01402b1SRalf Baechle 			/*
480e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
481e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
482e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
483e01402b1SRalf Baechle 			 * by the LO16.
484e01402b1SRalf Baechle 			 */
485e01402b1SRalf Baechle 			insn = *l->addr;
486e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
487e01402b1SRalf Baechle 			val += v;
488e01402b1SRalf Baechle 
489e01402b1SRalf Baechle 			/*
490e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
491e01402b1SRalf Baechle 			 * the low bits.
492e01402b1SRalf Baechle 			 */
493e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
494e01402b1SRalf Baechle 
495e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
496e01402b1SRalf Baechle 			*l->addr = insn;
497e01402b1SRalf Baechle 
498e01402b1SRalf Baechle 			next = l->next;
499e01402b1SRalf Baechle 			kfree(l);
500e01402b1SRalf Baechle 			l = next;
501e01402b1SRalf Baechle 		}
502e01402b1SRalf Baechle 
503e01402b1SRalf Baechle 		mips_hi16_list = NULL;
504e01402b1SRalf Baechle 	}
505e01402b1SRalf Baechle 
506e01402b1SRalf Baechle 	/*
507e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
508e01402b1SRalf Baechle 	 */
509e01402b1SRalf Baechle 	val = v + vallo;
510e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
511e01402b1SRalf Baechle 	*location = insnlo;
512e01402b1SRalf Baechle 
513e01402b1SRalf Baechle 	return 0;
514e01402b1SRalf Baechle }
515e01402b1SRalf Baechle 
516e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
517e01402b1SRalf Baechle 				Elf32_Addr v) = {
518e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
519e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
520e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
521e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
522e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
523e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
524e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
525e01402b1SRalf Baechle };
526e01402b1SRalf Baechle 
5272600990eSRalf Baechle static char *rstrs[] = {
5282600990eSRalf Baechle 	[R_MIPS_NONE]	= "MIPS_NONE",
5292600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5302600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5312600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5322600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5332600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5342600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5352600990eSRalf Baechle };
536e01402b1SRalf Baechle 
537e01402b1SRalf Baechle int apply_relocations(Elf32_Shdr *sechdrs,
538e01402b1SRalf Baechle 		      const char *strtab,
539e01402b1SRalf Baechle 		      unsigned int symindex,
540e01402b1SRalf Baechle 		      unsigned int relsec,
541e01402b1SRalf Baechle 		      struct module *me)
542e01402b1SRalf Baechle {
543e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
544e01402b1SRalf Baechle 	Elf32_Sym *sym;
545e01402b1SRalf Baechle 	uint32_t *location;
546e01402b1SRalf Baechle 	unsigned int i;
547e01402b1SRalf Baechle 	Elf32_Addr v;
548e01402b1SRalf Baechle 	int res;
549e01402b1SRalf Baechle 
550e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
551e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
552e01402b1SRalf Baechle 
553e01402b1SRalf Baechle 		/* This is where to make the change */
554e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
555e01402b1SRalf Baechle 			+ rel[i].r_offset;
556e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
557e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
558e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
559e01402b1SRalf Baechle 
560e01402b1SRalf Baechle 		if (!sym->st_value) {
561e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
562e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
563e01402b1SRalf Baechle 			/* just print the warning, dont barf */
564e01402b1SRalf Baechle 		}
565e01402b1SRalf Baechle 
566e01402b1SRalf Baechle 		v = sym->st_value;
567e01402b1SRalf Baechle 
568e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
569e01402b1SRalf Baechle 		if( res ) {
5702600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5712600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5722600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5732600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5742600990eSRalf Baechle 			       strtab + sym->st_name);
575e01402b1SRalf Baechle 			return res;
576e01402b1SRalf Baechle 		}
5772600990eSRalf Baechle 	}
578e01402b1SRalf Baechle 
579e01402b1SRalf Baechle 	return 0;
580e01402b1SRalf Baechle }
581e01402b1SRalf Baechle 
582e01402b1SRalf Baechle void save_gp_address(unsigned int secbase, unsigned int rel)
583e01402b1SRalf Baechle {
584e01402b1SRalf Baechle 	gp_addr = secbase + rel;
585e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
586e01402b1SRalf Baechle }
587e01402b1SRalf Baechle /* end module-elf32.c */
588e01402b1SRalf Baechle 
589e01402b1SRalf Baechle 
590e01402b1SRalf Baechle 
591e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
5922600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
593e01402b1SRalf Baechle 			    unsigned int symindex,
594e01402b1SRalf Baechle 			    const char *strtab,
595e01402b1SRalf Baechle 			    const char *secstrings,
596e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
597e01402b1SRalf Baechle {
598e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
599e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
600e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
6012600990eSRalf Baechle 	int size;
602e01402b1SRalf Baechle 
603e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
604e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6052600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
606e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6072600990eSRalf Baechle 			break;
6082600990eSRalf Baechle 		}
609e01402b1SRalf Baechle 	}
610e01402b1SRalf Baechle 
611e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
612e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
613e01402b1SRalf Baechle 		case SHN_COMMON:
6142600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6152600990eSRalf Baechle 			   st_value is currently size.
616e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
617e01402b1SRalf Baechle 
618e01402b1SRalf Baechle 			size = sym[i].st_value;
619e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
620e01402b1SRalf Baechle 
621e01402b1SRalf Baechle 			bssbase += size;
622e01402b1SRalf Baechle 			break;
623e01402b1SRalf Baechle 
624e01402b1SRalf Baechle 		case SHN_ABS:
625e01402b1SRalf Baechle 			/* Don't need to do anything */
626e01402b1SRalf Baechle 			break;
627e01402b1SRalf Baechle 
628e01402b1SRalf Baechle 		case SHN_UNDEF:
629e01402b1SRalf Baechle 			/* ret = -ENOENT; */
630e01402b1SRalf Baechle 			break;
631e01402b1SRalf Baechle 
632e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6332600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON"
6342600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6352600990eSRalf Baechle 			       sym[i].st_shndx);
636e01402b1SRalf Baechle 			// .sbss section
637e01402b1SRalf Baechle 			break;
638e01402b1SRalf Baechle 
639e01402b1SRalf Baechle 		default:
640e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
641e01402b1SRalf Baechle 
642e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
643e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
644e01402b1SRalf Baechle 			}
645e01402b1SRalf Baechle 
646e01402b1SRalf Baechle 			sym[i].st_value += secbase;
647e01402b1SRalf Baechle 			break;
648e01402b1SRalf Baechle 		}
649e01402b1SRalf Baechle 	}
650e01402b1SRalf Baechle }
651e01402b1SRalf Baechle 
652e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
653e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
654e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
655e01402b1SRalf Baechle {
656e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
657e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
658e01402b1SRalf Baechle 
659e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
660e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
661e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
662e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
663e01402b1SRalf Baechle 	}
664e01402b1SRalf Baechle }
665e01402b1SRalf Baechle #endif
666e01402b1SRalf Baechle 
667e01402b1SRalf Baechle static void dump_tc(struct tc *t)
668e01402b1SRalf Baechle {
6692600990eSRalf Baechle   	unsigned long val;
6702600990eSRalf Baechle 
6712600990eSRalf Baechle   	settc(t->index);
6722600990eSRalf Baechle  	printk(KERN_DEBUG "VPE loader: TC index %d targtc %ld "
6732600990eSRalf Baechle  	       "TCStatus 0x%lx halt 0x%lx\n",
6742600990eSRalf Baechle   	       t->index, read_c0_vpecontrol() & VPECONTROL_TARGTC,
6752600990eSRalf Baechle   	       read_tc_c0_tcstatus(), read_tc_c0_tchalt());
6762600990eSRalf Baechle 
6772600990eSRalf Baechle  	printk(KERN_DEBUG " tcrestart 0x%lx\n", read_tc_c0_tcrestart());
6782600990eSRalf Baechle  	printk(KERN_DEBUG " tcbind 0x%lx\n", read_tc_c0_tcbind());
6792600990eSRalf Baechle 
6802600990eSRalf Baechle   	val = read_c0_vpeconf0();
6812600990eSRalf Baechle  	printk(KERN_DEBUG " VPEConf0 0x%lx MVP %ld\n", val,
6822600990eSRalf Baechle   	       (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT);
6832600990eSRalf Baechle 
6842600990eSRalf Baechle  	printk(KERN_DEBUG " c0 status 0x%lx\n", read_vpe_c0_status());
6852600990eSRalf Baechle  	printk(KERN_DEBUG " c0 cause 0x%lx\n", read_vpe_c0_cause());
6862600990eSRalf Baechle 
6872600990eSRalf Baechle  	printk(KERN_DEBUG " c0 badvaddr 0x%lx\n", read_vpe_c0_badvaddr());
6882600990eSRalf Baechle  	printk(KERN_DEBUG " c0 epc 0x%lx\n", read_vpe_c0_epc());
689e01402b1SRalf Baechle }
690e01402b1SRalf Baechle 
691e01402b1SRalf Baechle static void dump_tclist(void)
692e01402b1SRalf Baechle {
693e01402b1SRalf Baechle 	struct tc *t;
694e01402b1SRalf Baechle 
695e01402b1SRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list) {
696e01402b1SRalf Baechle 		dump_tc(t);
697e01402b1SRalf Baechle 	}
698e01402b1SRalf Baechle }
699e01402b1SRalf Baechle 
700e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
701be6e1437SRalf Baechle static int vpe_run(struct vpe * v)
702e01402b1SRalf Baechle {
7032600990eSRalf Baechle 	struct vpe_notifications *n;
7042600990eSRalf Baechle 	unsigned long val, dmt_flag;
705e01402b1SRalf Baechle 	struct tc *t;
706e01402b1SRalf Baechle 
707e01402b1SRalf Baechle 	/* check we are the Master VPE */
708e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
709e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
710e01402b1SRalf Baechle 		printk(KERN_WARNING
7112600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
712e01402b1SRalf Baechle 		return -1;
713e01402b1SRalf Baechle 	}
714e01402b1SRalf Baechle 
715e01402b1SRalf Baechle 	/* disable MT (using dvpe) */
716e01402b1SRalf Baechle 	dvpe();
717e01402b1SRalf Baechle 
718e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
719e01402b1SRalf Baechle 		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
7202600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: TC %d is already in use.\n",
721e01402b1SRalf Baechle 			       t->index);
722e01402b1SRalf Baechle 			return -ENOEXEC;
723e01402b1SRalf Baechle 		}
724e01402b1SRalf Baechle 	} else {
7252600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: No TC's associated with VPE %d\n",
726e01402b1SRalf Baechle 		       v->minor);
727e01402b1SRalf Baechle 		return -ENOEXEC;
728e01402b1SRalf Baechle 	}
729e01402b1SRalf Baechle 
7302600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7312600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
732e01402b1SRalf Baechle 
7332600990eSRalf Baechle 	settc(t->index);
734e01402b1SRalf Baechle 
735e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
736e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
7372600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already doing something!\n",
738e01402b1SRalf Baechle 		       t->index);
739e01402b1SRalf Baechle 		dump_tclist();
740e01402b1SRalf Baechle 		return -ENOEXEC;
741e01402b1SRalf Baechle 	}
742e01402b1SRalf Baechle 
7432600990eSRalf Baechle 	/*
7442600990eSRalf Baechle 	 * Disable multi-threaded execution whilst we activate, clear the
7452600990eSRalf Baechle 	 * halt bit and bound the tc to the other VPE...
7462600990eSRalf Baechle 	 */
7472600990eSRalf Baechle 	dmt_flag = dmt();
7482600990eSRalf Baechle 
749e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
750e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
751e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
7522600990eSRalf Baechle 	/*
7532600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7542600990eSRalf Baechle 	 * allocatable
7552600990eSRalf Baechle 	 */
756e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
757e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
758e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
759e01402b1SRalf Baechle 
760e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
761e01402b1SRalf Baechle 
762e01402b1SRalf Baechle 	/*
763e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7642600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
765e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
766e01402b1SRalf Baechle 	 */
7672600990eSRalf Baechle  	mttgpr(7, physical_memsize);
768e01402b1SRalf Baechle 
769e01402b1SRalf Baechle 
7702600990eSRalf Baechle 	/* set up VPE1 */
7712600990eSRalf Baechle 	/*
7722600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7732600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7742600990eSRalf Baechle 	 */
7752600990eSRalf Baechle  	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | v->minor);
7762600990eSRalf Baechle 
777a94d7020SElizabeth Oldham 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
778a94d7020SElizabeth Oldham 
779a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
780a94d7020SElizabeth Oldham 
7812600990eSRalf Baechle 	/* Set up the XTC bit in vpeconf0 to point at our tc */
7822600990eSRalf Baechle 	write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7832600990eSRalf Baechle 	                      | (t->index << VPECONF0_XTC_SHIFT));
7842600990eSRalf Baechle 
785a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
786a94d7020SElizabeth Oldham 
7872600990eSRalf Baechle 	/* enable this VPE */
7882600990eSRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
789e01402b1SRalf Baechle 
790e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7912600990eSRalf Baechle 	write_vpe_c0_status(0);
792e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
793e01402b1SRalf Baechle 
794e01402b1SRalf Baechle 	/* take system out of configuration state */
795340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
796e01402b1SRalf Baechle 
7972600990eSRalf Baechle 	/* now safe to re-enable multi-threading */
7982600990eSRalf Baechle 	emt(dmt_flag);
799e01402b1SRalf Baechle 
800e01402b1SRalf Baechle 	/* set it running */
801e01402b1SRalf Baechle 	evpe(EVPE_ENABLE);
802e01402b1SRalf Baechle 
8032600990eSRalf Baechle 	list_for_each_entry(n, &v->notify, list) {
8042600990eSRalf Baechle 		n->start(v->minor);
8052600990eSRalf Baechle 	}
8062600990eSRalf Baechle 
807e01402b1SRalf Baechle 	return 0;
808e01402b1SRalf Baechle }
809e01402b1SRalf Baechle 
8102600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
811e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
812e01402b1SRalf Baechle 				      struct module *mod)
813e01402b1SRalf Baechle {
814e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
815e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
816e01402b1SRalf Baechle 
817e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
818e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
819e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
820e01402b1SRalf Baechle 		}
821e01402b1SRalf Baechle 
822e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
823e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
824e01402b1SRalf Baechle 		}
825e01402b1SRalf Baechle 	}
826e01402b1SRalf Baechle 
8272600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8282600990eSRalf Baechle 		return -1;
8292600990eSRalf Baechle 
830e01402b1SRalf Baechle 	return 0;
831e01402b1SRalf Baechle }
832e01402b1SRalf Baechle 
833307bd284SRalf Baechle /*
8342600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8352600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8362600990eSRalf Baechle  * when finished.
837e01402b1SRalf Baechle  */
838be6e1437SRalf Baechle static int vpe_elfload(struct vpe * v)
839e01402b1SRalf Baechle {
840e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
841e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
842e01402b1SRalf Baechle 	long err = 0;
843e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8442600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
845e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
846e01402b1SRalf Baechle 
847e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8482600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
849e01402b1SRalf Baechle 
850e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
851e01402b1SRalf Baechle 	len = v->plen;
852e01402b1SRalf Baechle 
853e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
854e01402b1SRalf Baechle 	   weird elf version */
855e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
8562600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8572600990eSRalf Baechle 	    || !elf_check_arch(hdr)
858e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
859e01402b1SRalf Baechle 		printk(KERN_WARNING
8602600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
861e01402b1SRalf Baechle 
862e01402b1SRalf Baechle 		return -ENOEXEC;
863e01402b1SRalf Baechle 	}
864e01402b1SRalf Baechle 
8652600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8662600990eSRalf Baechle 		relocate = 1;
8672600990eSRalf Baechle 
868e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8692600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8702600990eSRalf Baechle 		       len);
8712600990eSRalf Baechle 
872e01402b1SRalf Baechle 		return -ENOEXEC;
873e01402b1SRalf Baechle 	}
874e01402b1SRalf Baechle 
875e01402b1SRalf Baechle 	/* Convenience variables */
876e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
877e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
878e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
879e01402b1SRalf Baechle 
880e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
881e01402b1SRalf Baechle 	symindex = strindex = 0;
882e01402b1SRalf Baechle 
8832600990eSRalf Baechle 	if (relocate) {
884e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
885e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
886e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
887e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
888e01402b1SRalf Baechle 				       len);
889e01402b1SRalf Baechle 				return -ENOEXEC;
890e01402b1SRalf Baechle 			}
891e01402b1SRalf Baechle 
892e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
893e01402b1SRalf Baechle 			   temporary image. */
894e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
895e01402b1SRalf Baechle 
896e01402b1SRalf Baechle 			/* Internal symbols and strings. */
897e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
898e01402b1SRalf Baechle 				symindex = i;
899e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
900e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
901e01402b1SRalf Baechle 			}
902e01402b1SRalf Baechle 		}
903e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
9042600990eSRalf Baechle 	}
905e01402b1SRalf Baechle 
906e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
907e01402b1SRalf Baechle 	memset(v->load_addr, 0, mod.core_size);
908e01402b1SRalf Baechle 
9092600990eSRalf Baechle 	printk("VPE loader: loading to %p\n", v->load_addr);
910e01402b1SRalf Baechle 
9112600990eSRalf Baechle 	if (relocate) {
912e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
913e01402b1SRalf Baechle 			void *dest;
914e01402b1SRalf Baechle 
915e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
916e01402b1SRalf Baechle 				continue;
917e01402b1SRalf Baechle 
918e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
919e01402b1SRalf Baechle 
920e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
921e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
922e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
923e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
924e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9252600990eSRalf Baechle 
9262600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9272600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
928e01402b1SRalf Baechle 		}
929e01402b1SRalf Baechle 
930e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
931e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
932e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
933e01402b1SRalf Baechle 
934e01402b1SRalf Baechle  		/* Now do relocations. */
935e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
936e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
937e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
938e01402b1SRalf Baechle 
939e01402b1SRalf Baechle  			/* Not a valid relocation section? */
940e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
941e01402b1SRalf Baechle  				continue;
942e01402b1SRalf Baechle 
943e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
944e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
945e01402b1SRalf Baechle  				continue;
946e01402b1SRalf Baechle 
947e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9482600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9492600990eSRalf Baechle  							&mod);
950e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
951e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
952e01402b1SRalf Baechle  							 &mod);
9532600990eSRalf Baechle  			if (err < 0)
9542600990eSRalf Baechle  				return err;
9552600990eSRalf Baechle 
9562600990eSRalf Baechle   		}
9572600990eSRalf Baechle   	} else {
9582600990eSRalf Baechle   		for (i = 0; i < hdr->e_shnum; i++) {
9592600990eSRalf Baechle 
9602600990eSRalf Baechle  			/* Internal symbols and strings. */
9612600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9622600990eSRalf Baechle  				symindex = i;
9632600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9642600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9652600990eSRalf Baechle 
9662600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9672600990eSRalf Baechle  				   magic symbols */
9682600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9692600990eSRalf Baechle  			}
9702600990eSRalf Baechle 
9712600990eSRalf Baechle  			/* filter sections we dont want in the final image */
9722600990eSRalf Baechle  			if (!(sechdrs[i].sh_flags & SHF_ALLOC) ||
9732600990eSRalf Baechle  			    (sechdrs[i].sh_type == SHT_MIPS_REGINFO)) {
9742600990eSRalf Baechle  				printk( KERN_DEBUG " ignoring section, "
9752600990eSRalf Baechle  					"name %s type %x address 0x%x \n",
9762600990eSRalf Baechle  					secstrings + sechdrs[i].sh_name,
9772600990eSRalf Baechle  					sechdrs[i].sh_type, sechdrs[i].sh_addr);
9782600990eSRalf Baechle  				continue;
9792600990eSRalf Baechle  			}
9802600990eSRalf Baechle 
9812600990eSRalf Baechle   			if (sechdrs[i].sh_addr < (unsigned int)v->load_addr) {
9822600990eSRalf Baechle  				printk( KERN_WARNING "VPE loader: "
9832600990eSRalf Baechle  					"fully linked image has invalid section, "
9842600990eSRalf Baechle  					"name %s type %x address 0x%x, before load "
9852600990eSRalf Baechle  					"address of 0x%x\n",
9862600990eSRalf Baechle  					secstrings + sechdrs[i].sh_name,
9872600990eSRalf Baechle  					sechdrs[i].sh_type, sechdrs[i].sh_addr,
9882600990eSRalf Baechle  					(unsigned int)v->load_addr);
9892600990eSRalf Baechle   				return -ENOEXEC;
9902600990eSRalf Baechle   			}
9912600990eSRalf Baechle 
9922600990eSRalf Baechle  			printk(KERN_DEBUG " copying section sh_name %s, sh_addr 0x%x "
9932600990eSRalf Baechle 			       "size 0x%x0 from x%p\n",
9942600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr,
9952600990eSRalf Baechle 			       sechdrs[i].sh_size, hdr + sechdrs[i].sh_offset);
9962600990eSRalf Baechle 
9972600990eSRalf Baechle   			if (sechdrs[i].sh_type != SHT_NOBITS)
9982600990eSRalf Baechle 				memcpy((void *)sechdrs[i].sh_addr,
9992600990eSRalf Baechle 				       (char *)hdr + sechdrs[i].sh_offset,
10002600990eSRalf Baechle  				       sechdrs[i].sh_size);
10012600990eSRalf Baechle 			else
10022600990eSRalf Baechle 				memset((void *)sechdrs[i].sh_addr, 0, sechdrs[i].sh_size);
1003e01402b1SRalf Baechle 		}
1004e01402b1SRalf Baechle 	}
1005e01402b1SRalf Baechle 
1006e01402b1SRalf Baechle 	/* make sure it's physically written out */
1007e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
1008e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
1009e01402b1SRalf Baechle 
1010e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
10112600990eSRalf Baechle 		if (v->__start == 0) {
10122600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
10132600990eSRalf Baechle 			       "a __start symbol\n");
10142600990eSRalf Baechle 			return -ENOEXEC;
10152600990eSRalf Baechle 		}
1016e01402b1SRalf Baechle 
10172600990eSRalf Baechle 		if (v->shared_ptr == NULL)
10182600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
10192600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
10202600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
1021e01402b1SRalf Baechle 	}
1022e01402b1SRalf Baechle 
1023e01402b1SRalf Baechle 	printk(" elf loaded\n");
10242600990eSRalf Baechle 	return 0;
1025e01402b1SRalf Baechle }
1026e01402b1SRalf Baechle 
10272600990eSRalf Baechle __attribute_used__ void dump_vpe(struct vpe * v)
1028e01402b1SRalf Baechle {
1029e01402b1SRalf Baechle 	struct tc *t;
1030e01402b1SRalf Baechle 
10312600990eSRalf Baechle 	settc(v->minor);
10322600990eSRalf Baechle 
1033e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol());
1034e01402b1SRalf Baechle 	printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0());
1035e01402b1SRalf Baechle 
10362600990eSRalf Baechle 	list_for_each_entry(t, &vpecontrol.tc_list, list)
1037e01402b1SRalf Baechle 		dump_tc(t);
1038e01402b1SRalf Baechle }
1039e01402b1SRalf Baechle 
10402600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
1041e01402b1SRalf Baechle {
10422600990eSRalf Baechle 	int tmp;
1043e01402b1SRalf Baechle 
10442600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
10452600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1046e01402b1SRalf Baechle 
10472600990eSRalf Baechle 	settc(tc->index);
1048e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
1049e01402b1SRalf Baechle 
1050e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1051e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1052e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1053e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1054e01402b1SRalf Baechle 
1055e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
1056e01402b1SRalf Baechle 
10572600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
10582600990eSRalf Baechle 	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10592600990eSRalf Baechle 
10602600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
10612600990eSRalf Baechle }
10622600990eSRalf Baechle 
10632600990eSRalf Baechle static int getcwd(char *buff, int size)
10642600990eSRalf Baechle {
10652600990eSRalf Baechle 	mm_segment_t old_fs;
10662600990eSRalf Baechle 	int ret;
10672600990eSRalf Baechle 
10682600990eSRalf Baechle 	old_fs = get_fs();
10692600990eSRalf Baechle 	set_fs(KERNEL_DS);
10702600990eSRalf Baechle 
10712600990eSRalf Baechle 	ret = sys_getcwd(buff,size);
10722600990eSRalf Baechle 
10732600990eSRalf Baechle 	set_fs(old_fs);
10742600990eSRalf Baechle 
10752600990eSRalf Baechle 	return ret;
10762600990eSRalf Baechle }
10772600990eSRalf Baechle 
10782600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10792600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10802600990eSRalf Baechle {
10812600990eSRalf Baechle 	int minor, ret;
1082c4c4018bSRalf Baechle 	enum vpe_state state;
10832600990eSRalf Baechle 	struct vpe *v;
10842600990eSRalf Baechle 	struct vpe_notifications *not;
10852600990eSRalf Baechle 
10862600990eSRalf Baechle 	/* assume only 1 device at the mo. */
108779e55bcfSRalf Baechle 	if ((minor = iminor(inode)) != 1) {
10882600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
10892600990eSRalf Baechle 		return -ENODEV;
10902600990eSRalf Baechle 	}
10912600990eSRalf Baechle 
10922600990eSRalf Baechle 	if ((v = get_vpe(minor)) == NULL) {
10932600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: unable to get vpe\n");
10942600990eSRalf Baechle 		return -ENODEV;
10952600990eSRalf Baechle 	}
10962600990eSRalf Baechle 
1097c4c4018bSRalf Baechle 	state = xchg(&v->state, VPE_STATE_INUSE);
1098c4c4018bSRalf Baechle 	if (state != VPE_STATE_UNUSED) {
10992600990eSRalf Baechle 		dvpe();
11002600990eSRalf Baechle 
11012600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
11022600990eSRalf Baechle 
11032600990eSRalf Baechle 		dump_tc(get_tc(minor));
11042600990eSRalf Baechle 
11052600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
11062600990eSRalf Baechle 			not->stop(minor);
11072600990eSRalf Baechle 		}
11082600990eSRalf Baechle 
11092600990eSRalf Baechle 		release_progmem(v->load_addr);
11102600990eSRalf Baechle 		cleanup_tc(get_tc(minor));
1111e01402b1SRalf Baechle 	}
1112e01402b1SRalf Baechle 
1113e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1114e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1115e01402b1SRalf Baechle 	v->plen = P_SIZE;
1116e01402b1SRalf Baechle 	v->load_addr = NULL;
1117e01402b1SRalf Baechle 	v->len = 0;
1118e01402b1SRalf Baechle 
11192600990eSRalf Baechle 	v->uid = filp->f_uid;
11202600990eSRalf Baechle 	v->gid = filp->f_gid;
11212600990eSRalf Baechle 
11222600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
11232600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
11242600990eSRalf Baechle 	if (!kspd_events_reqd) {
11252600990eSRalf Baechle 		kspd_notify(&kspd_events);
11262600990eSRalf Baechle 		kspd_events_reqd++;
11272600990eSRalf Baechle 	}
11282600990eSRalf Baechle #endif
11292600990eSRalf Baechle 
11302600990eSRalf Baechle 	v->cwd[0] = 0;
11312600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
11322600990eSRalf Baechle 	if (ret < 0)
11332600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
11342600990eSRalf Baechle 
11352600990eSRalf Baechle 	v->shared_ptr = NULL;
11362600990eSRalf Baechle 	v->__start = 0;
1137e01402b1SRalf Baechle 	return 0;
1138e01402b1SRalf Baechle }
1139e01402b1SRalf Baechle 
1140e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1141e01402b1SRalf Baechle {
1142e01402b1SRalf Baechle 	int minor, ret = 0;
1143307bd284SRalf Baechle 	struct vpe *v;
1144e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
1145e01402b1SRalf Baechle 
114679e55bcfSRalf Baechle 	minor = iminor(inode);
1147e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL)
1148e01402b1SRalf Baechle 		return -ENODEV;
1149e01402b1SRalf Baechle 
1150e01402b1SRalf Baechle 	// simple case of fire and forget, so tell the VPE to run...
1151e01402b1SRalf Baechle 
1152e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1153e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) {
1154e01402b1SRalf Baechle 		if (vpe_elfload(v) >= 0)
1155e01402b1SRalf Baechle 			vpe_run(v);
1156e01402b1SRalf Baechle 		else {
11572600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1158e01402b1SRalf Baechle 			ret = -ENOEXEC;
1159e01402b1SRalf Baechle 		}
1160e01402b1SRalf Baechle 	} else {
11612600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1162e01402b1SRalf Baechle 		ret = -ENOEXEC;
1163e01402b1SRalf Baechle 	}
1164e01402b1SRalf Baechle 
11652600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11662600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11672600990eSRalf Baechle 	   loose what has happened. So perhaps if garbage is sent to the vpe
11682600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11692600990eSRalf Baechle 	   executable will be along shortly. */
11702600990eSRalf Baechle 	if (ret < 0)
11712600990eSRalf Baechle 		v->shared_ptr = NULL;
11722600990eSRalf Baechle 
1173e01402b1SRalf Baechle 	// cleanup any temp buffers
1174e01402b1SRalf Baechle 	if (v->pbuffer)
1175e01402b1SRalf Baechle 		vfree(v->pbuffer);
1176e01402b1SRalf Baechle 	v->plen = 0;
1177e01402b1SRalf Baechle 	return ret;
1178e01402b1SRalf Baechle }
1179e01402b1SRalf Baechle 
1180e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1181e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1182e01402b1SRalf Baechle {
1183e01402b1SRalf Baechle 	int minor;
1184e01402b1SRalf Baechle 	size_t ret = count;
1185307bd284SRalf Baechle 	struct vpe *v;
1186e01402b1SRalf Baechle 
11871b04fe9aSJosef Sipek 	minor = iminor(file->f_path.dentry->d_inode);
1188e01402b1SRalf Baechle 	if ((v = get_vpe(minor)) == NULL)
1189e01402b1SRalf Baechle 		return -ENODEV;
1190e01402b1SRalf Baechle 
1191e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
11922600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: no buffer for program\n");
1193e01402b1SRalf Baechle 		return -ENOMEM;
1194e01402b1SRalf Baechle 	}
1195e01402b1SRalf Baechle 
1196e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1197e01402b1SRalf Baechle 		printk(KERN_WARNING
11982600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1199e01402b1SRalf Baechle 		return -ENOMEM;
1200e01402b1SRalf Baechle 	}
1201e01402b1SRalf Baechle 
1202e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
12032600990eSRalf Baechle 	if (!count)
1204e01402b1SRalf Baechle 		return -EFAULT;
1205e01402b1SRalf Baechle 
1206e01402b1SRalf Baechle 	v->len += count;
1207e01402b1SRalf Baechle 	return ret;
1208e01402b1SRalf Baechle }
1209e01402b1SRalf Baechle 
12105dfe4c96SArjan van de Ven static const struct file_operations vpe_fops = {
1211e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1212e01402b1SRalf Baechle 	.open = vpe_open,
1213e01402b1SRalf Baechle 	.release = vpe_release,
1214e01402b1SRalf Baechle 	.write = vpe_write
1215e01402b1SRalf Baechle };
1216e01402b1SRalf Baechle 
1217e01402b1SRalf Baechle /* module wrapper entry points */
1218e01402b1SRalf Baechle /* give me a vpe */
1219e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1220e01402b1SRalf Baechle {
1221e01402b1SRalf Baechle 	int i;
1222e01402b1SRalf Baechle 	struct vpe *v;
1223e01402b1SRalf Baechle 
1224e01402b1SRalf Baechle 	/* find a vpe */
1225e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1226e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1227e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1228e01402b1SRalf Baechle 			return v;
1229e01402b1SRalf Baechle 		}
1230e01402b1SRalf Baechle 	}
1231e01402b1SRalf Baechle 	return NULL;
1232e01402b1SRalf Baechle }
1233e01402b1SRalf Baechle 
1234e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1235e01402b1SRalf Baechle 
1236e01402b1SRalf Baechle /* start running from here */
1237e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1238e01402b1SRalf Baechle {
1239e01402b1SRalf Baechle 	struct vpe *v = vpe;
1240e01402b1SRalf Baechle 
1241e01402b1SRalf Baechle 	v->__start = start;
1242e01402b1SRalf Baechle 	return vpe_run(v);
1243e01402b1SRalf Baechle }
1244e01402b1SRalf Baechle 
1245e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1246e01402b1SRalf Baechle 
1247e01402b1SRalf Baechle /* halt it for now */
1248e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1249e01402b1SRalf Baechle {
1250e01402b1SRalf Baechle 	struct vpe *v = vpe;
1251e01402b1SRalf Baechle 	struct tc *t;
1252e01402b1SRalf Baechle 	unsigned int evpe_flags;
1253e01402b1SRalf Baechle 
1254e01402b1SRalf Baechle 	evpe_flags = dvpe();
1255e01402b1SRalf Baechle 
1256e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1257e01402b1SRalf Baechle 
1258e01402b1SRalf Baechle 		settc(t->index);
1259e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1260e01402b1SRalf Baechle 	}
1261e01402b1SRalf Baechle 
1262e01402b1SRalf Baechle 	evpe(evpe_flags);
1263e01402b1SRalf Baechle 
1264e01402b1SRalf Baechle 	return 0;
1265e01402b1SRalf Baechle }
1266e01402b1SRalf Baechle 
1267e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1268e01402b1SRalf Baechle 
1269e01402b1SRalf Baechle /* I've done with it thank you */
1270e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1271e01402b1SRalf Baechle {
1272e01402b1SRalf Baechle 	struct vpe *v = vpe;
1273e01402b1SRalf Baechle 	struct tc *t;
1274e01402b1SRalf Baechle 	unsigned int evpe_flags;
1275e01402b1SRalf Baechle 
1276e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1277e01402b1SRalf Baechle 		return -ENOEXEC;
1278e01402b1SRalf Baechle 	}
1279e01402b1SRalf Baechle 
1280e01402b1SRalf Baechle 	evpe_flags = dvpe();
1281e01402b1SRalf Baechle 
1282e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1283340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1284e01402b1SRalf Baechle 
1285e01402b1SRalf Baechle 	settc(t->index);
1286e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1287e01402b1SRalf Baechle 
1288e01402b1SRalf Baechle 	/* mark the TC unallocated and halt'ed */
1289e01402b1SRalf Baechle 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1290e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
1291e01402b1SRalf Baechle 
1292e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1293e01402b1SRalf Baechle 
1294340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1295e01402b1SRalf Baechle 	evpe(evpe_flags);
1296e01402b1SRalf Baechle 
1297e01402b1SRalf Baechle 	return 0;
1298e01402b1SRalf Baechle }
1299e01402b1SRalf Baechle 
1300e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1301e01402b1SRalf Baechle 
1302e01402b1SRalf Baechle void *vpe_get_shared(int index)
1303e01402b1SRalf Baechle {
1304e01402b1SRalf Baechle 	struct vpe *v;
1305e01402b1SRalf Baechle 
13062600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1307e01402b1SRalf Baechle 		return NULL;
1308e01402b1SRalf Baechle 
1309e01402b1SRalf Baechle 	return v->shared_ptr;
1310e01402b1SRalf Baechle }
1311e01402b1SRalf Baechle 
1312e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1313e01402b1SRalf Baechle 
13142600990eSRalf Baechle int vpe_getuid(int index)
13152600990eSRalf Baechle {
13162600990eSRalf Baechle 	struct vpe *v;
13172600990eSRalf Baechle 
13182600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13192600990eSRalf Baechle 		return -1;
13202600990eSRalf Baechle 
13212600990eSRalf Baechle 	return v->uid;
13222600990eSRalf Baechle }
13232600990eSRalf Baechle 
13242600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
13252600990eSRalf Baechle 
13262600990eSRalf Baechle int vpe_getgid(int index)
13272600990eSRalf Baechle {
13282600990eSRalf Baechle 	struct vpe *v;
13292600990eSRalf Baechle 
13302600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13312600990eSRalf Baechle 		return -1;
13322600990eSRalf Baechle 
13332600990eSRalf Baechle 	return v->gid;
13342600990eSRalf Baechle }
13352600990eSRalf Baechle 
13362600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
13372600990eSRalf Baechle 
13382600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
13392600990eSRalf Baechle {
13402600990eSRalf Baechle 	struct vpe *v;
13412600990eSRalf Baechle 
13422600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13432600990eSRalf Baechle 		return -1;
13442600990eSRalf Baechle 
13452600990eSRalf Baechle 	list_add(&notify->list, &v->notify);
13462600990eSRalf Baechle 	return 0;
13472600990eSRalf Baechle }
13482600990eSRalf Baechle 
13492600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13502600990eSRalf Baechle 
13512600990eSRalf Baechle char *vpe_getcwd(int index)
13522600990eSRalf Baechle {
13532600990eSRalf Baechle 	struct vpe *v;
13542600990eSRalf Baechle 
13552600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13562600990eSRalf Baechle 		return NULL;
13572600990eSRalf Baechle 
13582600990eSRalf Baechle 	return v->cwd;
13592600990eSRalf Baechle }
13602600990eSRalf Baechle 
13612600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13622600990eSRalf Baechle 
13632600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13642600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13652600990eSRalf Baechle {
13662600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13672600990eSRalf Baechle }
13682600990eSRalf Baechle #endif
13692600990eSRalf Baechle 
137027a3bbafSRalf Baechle static struct device *vpe_dev;
137127a3bbafSRalf Baechle 
1372e01402b1SRalf Baechle static int __init vpe_module_init(void)
1373e01402b1SRalf Baechle {
1374e01402b1SRalf Baechle 	struct vpe *v = NULL;
137527a3bbafSRalf Baechle 	struct device *dev;
1376e01402b1SRalf Baechle 	struct tc *t;
1377e01402b1SRalf Baechle 	unsigned long val;
137827a3bbafSRalf Baechle 	int i, err;
1379e01402b1SRalf Baechle 
1380e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1381e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1382e01402b1SRalf Baechle 		return -ENODEV;
1383e01402b1SRalf Baechle 	}
1384e01402b1SRalf Baechle 
1385682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1386682e852eSAlexey Dobriyan 	if (major < 0) {
1387e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1388307bd284SRalf Baechle 		return major;
1389e01402b1SRalf Baechle 	}
1390e01402b1SRalf Baechle 
139127a3bbafSRalf Baechle 	dev = device_create(mt_class, NULL, MKDEV(major, minor),
139227a3bbafSRalf Baechle 	                    "tc%d", minor);
139327a3bbafSRalf Baechle 	if (IS_ERR(dev)) {
139427a3bbafSRalf Baechle 		err = PTR_ERR(dev);
139527a3bbafSRalf Baechle 		goto out_chrdev;
139627a3bbafSRalf Baechle 	}
139727a3bbafSRalf Baechle 	vpe_dev = dev;
139827a3bbafSRalf Baechle 
1399e01402b1SRalf Baechle 	dmt();
1400e01402b1SRalf Baechle 	dvpe();
1401e01402b1SRalf Baechle 
1402e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1403340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1404e01402b1SRalf Baechle 
1405e01402b1SRalf Baechle 	/* dump_mtregs(); */
1406e01402b1SRalf Baechle 
1407e01402b1SRalf Baechle 
1408e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
1409e01402b1SRalf Baechle 	for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) {
1410e01402b1SRalf Baechle 		t = alloc_tc(i);
1411e01402b1SRalf Baechle 
1412e01402b1SRalf Baechle 		/* VPE's */
1413e01402b1SRalf Baechle 		if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) {
1414e01402b1SRalf Baechle 			settc(i);
1415e01402b1SRalf Baechle 
1416e01402b1SRalf Baechle 			if ((v = alloc_vpe(i)) == NULL) {
1417e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
1418e01402b1SRalf Baechle 				return -ENODEV;
1419e01402b1SRalf Baechle 			}
1420e01402b1SRalf Baechle 
14212600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
14222600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1423e01402b1SRalf Baechle 
1424e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
1425e01402b1SRalf Baechle 			if (i != 0) {
1426e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1427e01402b1SRalf Baechle 
1428e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1429e01402b1SRalf Baechle 
1430e01402b1SRalf Baechle 				/* master VPE */
1431e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1432e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1433e01402b1SRalf Baechle 			}
1434e01402b1SRalf Baechle 
1435e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1436e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1437e01402b1SRalf Baechle 
1438e01402b1SRalf Baechle 			if (i != 0) {
1439e01402b1SRalf Baechle 				write_vpe_c0_status((read_c0_status() &
1440e01402b1SRalf Baechle 						     ~(ST0_IM | ST0_IE | ST0_KSU))
1441e01402b1SRalf Baechle 						    | ST0_CU0);
1442e01402b1SRalf Baechle 
14432600990eSRalf Baechle 				/*
14442600990eSRalf Baechle 				 * Set config to be the same as vpe0,
14452600990eSRalf Baechle 				 * particularly kseg0 coherency alg
14462600990eSRalf Baechle 				 */
1447e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1448e01402b1SRalf Baechle 			}
1449e01402b1SRalf Baechle 		}
1450e01402b1SRalf Baechle 
1451e01402b1SRalf Baechle 		/* TC's */
1452e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1453e01402b1SRalf Baechle 
1454e01402b1SRalf Baechle 		if (i != 0) {
1455e01402b1SRalf Baechle 			unsigned long tmp;
1456e01402b1SRalf Baechle 
1457e01402b1SRalf Baechle 			settc(i);
1458e01402b1SRalf Baechle 
14592600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
14602600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
14612600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
14622600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
14632600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
14642600990eSRalf Baechle 
14652600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
14662600990eSRalf Baechle 			   notify GDB...
14672600990eSRalf Baechle 			*/
14682600990eSRalf Baechle 
14692600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
14702600990eSRalf Baechle 				/* tc is bound >vpe0 */
14712600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
14722600990eSRalf Baechle 
14732600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
14742600990eSRalf Baechle 			}
1475e01402b1SRalf Baechle 
1476e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1477e01402b1SRalf Baechle 
14782600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1479e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1480e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1481e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1482e01402b1SRalf Baechle 
1483e01402b1SRalf Baechle 			write_tc_c0_tchalt(TCHALT_H);
1484e01402b1SRalf Baechle 		}
1485e01402b1SRalf Baechle 	}
1486e01402b1SRalf Baechle 
1487e01402b1SRalf Baechle 	/* release config state */
1488340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1489e01402b1SRalf Baechle 
14902600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
14912600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
14922600990eSRalf Baechle #endif
1493e01402b1SRalf Baechle 	return 0;
149427a3bbafSRalf Baechle 
149527a3bbafSRalf Baechle out_chrdev:
149627a3bbafSRalf Baechle 	unregister_chrdev(major, module_name);
149727a3bbafSRalf Baechle 
149827a3bbafSRalf Baechle 	return err;
1499e01402b1SRalf Baechle }
1500e01402b1SRalf Baechle 
1501e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1502e01402b1SRalf Baechle {
1503e01402b1SRalf Baechle 	struct vpe *v, *n;
1504e01402b1SRalf Baechle 
1505e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1506e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1507e01402b1SRalf Baechle 			release_vpe(v);
1508e01402b1SRalf Baechle 		}
1509e01402b1SRalf Baechle 	}
1510e01402b1SRalf Baechle 
151127a3bbafSRalf Baechle 	device_destroy(mt_class, MKDEV(major, minor));
1512e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1513e01402b1SRalf Baechle }
1514e01402b1SRalf Baechle 
1515e01402b1SRalf Baechle module_init(vpe_module_init);
1516e01402b1SRalf Baechle module_exit(vpe_module_exit);
1517e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
15182600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1519e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1520