xref: /openbmc/linux/arch/mips/kernel/vpe.c (revision b1e3afa0)
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  */
30e01402b1SRalf Baechle #include <linux/kernel.h>
3127a3bbafSRalf Baechle #include <linux/device.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>
5127a3bbafSRalf Baechle #include <asm/mips_mt.h>
52e01402b1SRalf Baechle #include <asm/processor.h>
53e01402b1SRalf Baechle #include <asm/system.h>
542600990eSRalf Baechle #include <asm/vpe.h>
552600990eSRalf Baechle #include <asm/kspd.h>
5607cc0c9eSRalf Baechle #include <asm/mips_mt.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 
6741790e04SRalf Baechle /*
6841790e04SRalf Baechle  * The number of TCs and VPEs physically available on the core
6941790e04SRalf Baechle  */
7041790e04SRalf Baechle static int hw_tcs, hw_vpes;
71e01402b1SRalf Baechle static char module_name[] = "vpe";
72307bd284SRalf Baechle static int major;
7327a3bbafSRalf Baechle static const int minor = 1;	/* fixed for now  */
74e01402b1SRalf Baechle 
752600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
762600990eSRalf Baechle  static struct kspd_notifications kspd_events;
772600990eSRalf Baechle static int kspd_events_reqd = 0;
782600990eSRalf Baechle #endif
792600990eSRalf Baechle 
80e01402b1SRalf Baechle /* grab the likely amount of memory we will need. */
81e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
82e01402b1SRalf Baechle #define P_SIZE (2 * 1024 * 1024)
83e01402b1SRalf Baechle #else
84e01402b1SRalf Baechle /* add an overhead to the max kmalloc size for non-striped symbols/etc */
85e01402b1SRalf Baechle #define P_SIZE (256 * 1024)
86e01402b1SRalf Baechle #endif
87e01402b1SRalf Baechle 
882600990eSRalf Baechle extern unsigned long physical_memsize;
892600990eSRalf Baechle 
90e01402b1SRalf Baechle #define MAX_VPES 16
912600990eSRalf Baechle #define VPE_PATH_MAX 256
92e01402b1SRalf Baechle 
93e01402b1SRalf Baechle enum vpe_state {
94e01402b1SRalf Baechle 	VPE_STATE_UNUSED = 0,
95e01402b1SRalf Baechle 	VPE_STATE_INUSE,
96e01402b1SRalf Baechle 	VPE_STATE_RUNNING
97e01402b1SRalf Baechle };
98e01402b1SRalf Baechle 
99e01402b1SRalf Baechle enum tc_state {
100e01402b1SRalf Baechle 	TC_STATE_UNUSED = 0,
101e01402b1SRalf Baechle 	TC_STATE_INUSE,
102e01402b1SRalf Baechle 	TC_STATE_RUNNING,
103e01402b1SRalf Baechle 	TC_STATE_DYNAMIC
104e01402b1SRalf Baechle };
105e01402b1SRalf Baechle 
106307bd284SRalf Baechle struct vpe {
107e01402b1SRalf Baechle 	enum vpe_state state;
108e01402b1SRalf Baechle 
109e01402b1SRalf Baechle 	/* (device) minor associated with this vpe */
110e01402b1SRalf Baechle 	int minor;
111e01402b1SRalf Baechle 
112e01402b1SRalf Baechle 	/* elfloader stuff */
113e01402b1SRalf Baechle 	void *load_addr;
114571e0bedSRalf Baechle 	unsigned long len;
115e01402b1SRalf Baechle 	char *pbuffer;
116571e0bedSRalf Baechle 	unsigned long plen;
1172600990eSRalf Baechle 	unsigned int uid, gid;
1182600990eSRalf Baechle 	char cwd[VPE_PATH_MAX];
119e01402b1SRalf Baechle 
120e01402b1SRalf Baechle 	unsigned long __start;
121e01402b1SRalf Baechle 
122e01402b1SRalf Baechle 	/* tc's associated with this vpe */
123e01402b1SRalf Baechle 	struct list_head tc;
124e01402b1SRalf Baechle 
125e01402b1SRalf Baechle 	/* The list of vpe's */
126e01402b1SRalf Baechle 	struct list_head list;
127e01402b1SRalf Baechle 
128e01402b1SRalf Baechle 	/* shared symbol address */
129e01402b1SRalf Baechle 	void *shared_ptr;
1302600990eSRalf Baechle 
1312600990eSRalf Baechle 	/* the list of who wants to know when something major happens */
1322600990eSRalf Baechle 	struct list_head notify;
13341790e04SRalf Baechle 
13441790e04SRalf Baechle 	unsigned int ntcs;
135307bd284SRalf Baechle };
136307bd284SRalf Baechle 
137307bd284SRalf Baechle struct tc {
138307bd284SRalf Baechle 	enum tc_state state;
139307bd284SRalf Baechle 	int index;
140307bd284SRalf Baechle 
14107cc0c9eSRalf Baechle 	struct vpe *pvpe;	/* parent VPE */
14207cc0c9eSRalf Baechle 	struct list_head tc;	/* The list of TC's with this VPE */
14307cc0c9eSRalf Baechle 	struct list_head list;	/* The global list of tc's */
144307bd284SRalf Baechle };
145e01402b1SRalf Baechle 
1469cfdf6f1SRalf Baechle struct {
147e01402b1SRalf Baechle 	/* Virtual processing elements */
148e01402b1SRalf Baechle 	struct list_head vpe_list;
149e01402b1SRalf Baechle 
150e01402b1SRalf Baechle 	/* Thread contexts */
151e01402b1SRalf Baechle 	struct list_head tc_list;
1529cfdf6f1SRalf Baechle } vpecontrol = {
1539cfdf6f1SRalf Baechle 	.vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list),
1549cfdf6f1SRalf Baechle 	.tc_list = LIST_HEAD_INIT(vpecontrol.tc_list)
1559cfdf6f1SRalf Baechle };
156e01402b1SRalf Baechle 
157e01402b1SRalf Baechle static void release_progmem(void *ptr);
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 {
22107cc0c9eSRalf Baechle 	struct tc *tc;
222e01402b1SRalf Baechle 
22307cc0c9eSRalf Baechle 	if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL)
22407cc0c9eSRalf Baechle 		goto out;
225e01402b1SRalf Baechle 
22607cc0c9eSRalf Baechle 	INIT_LIST_HEAD(&tc->tc);
22707cc0c9eSRalf Baechle 	tc->index = index;
22807cc0c9eSRalf Baechle 	list_add_tail(&tc->list, &vpecontrol.tc_list);
229e01402b1SRalf Baechle 
23007cc0c9eSRalf Baechle out:
23107cc0c9eSRalf Baechle 	return tc;
232e01402b1SRalf Baechle }
233e01402b1SRalf Baechle 
234e01402b1SRalf Baechle /* clean up and free everything */
235e01402b1SRalf Baechle void release_vpe(struct vpe *v)
236e01402b1SRalf Baechle {
237e01402b1SRalf Baechle 	list_del(&v->list);
238e01402b1SRalf Baechle 	if (v->load_addr)
239e01402b1SRalf Baechle 		release_progmem(v);
240e01402b1SRalf Baechle 	kfree(v);
241e01402b1SRalf Baechle }
242e01402b1SRalf Baechle 
243e01402b1SRalf Baechle void dump_mtregs(void)
244e01402b1SRalf Baechle {
245e01402b1SRalf Baechle 	unsigned long val;
246e01402b1SRalf Baechle 
247e01402b1SRalf Baechle 	val = read_c0_config3();
248e01402b1SRalf Baechle 	printk("config3 0x%lx MT %ld\n", val,
249e01402b1SRalf Baechle 	       (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT);
250e01402b1SRalf Baechle 
251e01402b1SRalf Baechle 	val = read_c0_mvpcontrol();
252e01402b1SRalf Baechle 	printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val,
253e01402b1SRalf Baechle 	       (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT,
254e01402b1SRalf Baechle 	       (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT,
255e01402b1SRalf Baechle 	       (val & MVPCONTROL_EVP));
256e01402b1SRalf Baechle 
2572600990eSRalf Baechle 	val = read_c0_mvpconf0();
2582600990eSRalf Baechle 	printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val,
2592600990eSRalf Baechle 	       (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT,
2602600990eSRalf Baechle 	       val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT);
261e01402b1SRalf Baechle }
262e01402b1SRalf Baechle 
263e01402b1SRalf Baechle /* Find some VPE program space  */
264571e0bedSRalf Baechle static void *alloc_progmem(unsigned long len)
265e01402b1SRalf Baechle {
266e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM
267e01402b1SRalf Baechle 	/* this means you must tell linux to use less memory than you physically have */
268571e0bedSRalf Baechle 	return pfn_to_kaddr(max_pfn);
269e01402b1SRalf Baechle #else
270e01402b1SRalf Baechle 	// simple grab some mem for now
271e01402b1SRalf Baechle 	return kmalloc(len, GFP_KERNEL);
272e01402b1SRalf Baechle #endif
273e01402b1SRalf Baechle }
274e01402b1SRalf Baechle 
275e01402b1SRalf Baechle static void release_progmem(void *ptr)
276e01402b1SRalf Baechle {
277e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM
278e01402b1SRalf Baechle 	kfree(ptr);
279e01402b1SRalf Baechle #endif
280e01402b1SRalf Baechle }
281e01402b1SRalf Baechle 
282e01402b1SRalf Baechle /* Update size with this section: return offset. */
283e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr * sechdr)
284e01402b1SRalf Baechle {
285e01402b1SRalf Baechle 	long ret;
286e01402b1SRalf Baechle 
287e01402b1SRalf Baechle 	ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
288e01402b1SRalf Baechle 	*size = ret + sechdr->sh_size;
289e01402b1SRalf Baechle 	return ret;
290e01402b1SRalf Baechle }
291e01402b1SRalf Baechle 
292e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
293e01402b1SRalf Baechle    might -- code, read-only data, read-write data, small data.  Tally
294e01402b1SRalf Baechle    sizes, and place the offsets into sh_entsize fields: high bit means it
295e01402b1SRalf Baechle    belongs in init. */
296e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
297e01402b1SRalf Baechle 			    Elf_Shdr * sechdrs, const char *secstrings)
298e01402b1SRalf Baechle {
299e01402b1SRalf Baechle 	static unsigned long const masks[][2] = {
300e01402b1SRalf Baechle 		/* NOTE: all executable code must be the first section
301e01402b1SRalf Baechle 		 * in this array; otherwise modify the text_size
302e01402b1SRalf Baechle 		 * finder in the two loops below */
303e01402b1SRalf Baechle 		{SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
304e01402b1SRalf Baechle 		{SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
305e01402b1SRalf Baechle 		{SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
306e01402b1SRalf Baechle 		{ARCH_SHF_SMALL | SHF_ALLOC, 0}
307e01402b1SRalf Baechle 	};
308e01402b1SRalf Baechle 	unsigned int m, i;
309e01402b1SRalf Baechle 
310e01402b1SRalf Baechle 	for (i = 0; i < hdr->e_shnum; i++)
311e01402b1SRalf Baechle 		sechdrs[i].sh_entsize = ~0UL;
312e01402b1SRalf Baechle 
313e01402b1SRalf Baechle 	for (m = 0; m < ARRAY_SIZE(masks); ++m) {
314e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; ++i) {
315e01402b1SRalf Baechle 			Elf_Shdr *s = &sechdrs[i];
316e01402b1SRalf Baechle 
317e01402b1SRalf Baechle 			//  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
318e01402b1SRalf Baechle 			if ((s->sh_flags & masks[m][0]) != masks[m][0]
319e01402b1SRalf Baechle 			    || (s->sh_flags & masks[m][1])
320e01402b1SRalf Baechle 			    || s->sh_entsize != ~0UL)
321e01402b1SRalf Baechle 				continue;
322e01402b1SRalf Baechle 			s->sh_entsize = get_offset(&mod->core_size, s);
323e01402b1SRalf Baechle 		}
324e01402b1SRalf Baechle 
325e01402b1SRalf Baechle 		if (m == 0)
326e01402b1SRalf Baechle 			mod->core_text_size = mod->core_size;
327e01402b1SRalf Baechle 
328e01402b1SRalf Baechle 	}
329e01402b1SRalf Baechle }
330e01402b1SRalf Baechle 
331e01402b1SRalf Baechle 
332e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */
333e01402b1SRalf Baechle 
334e01402b1SRalf Baechle struct mips_hi16 {
335e01402b1SRalf Baechle 	struct mips_hi16 *next;
336e01402b1SRalf Baechle 	Elf32_Addr *addr;
337e01402b1SRalf Baechle 	Elf32_Addr value;
338e01402b1SRalf Baechle };
339e01402b1SRalf Baechle 
340e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list;
341e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr;
342e01402b1SRalf Baechle 
343e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location,
344e01402b1SRalf Baechle 			     Elf32_Addr v)
345e01402b1SRalf Baechle {
346e01402b1SRalf Baechle 	return 0;
347e01402b1SRalf Baechle }
348e01402b1SRalf Baechle 
349e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
350e01402b1SRalf Baechle 				Elf32_Addr v)
351e01402b1SRalf Baechle {
352e01402b1SRalf Baechle 	int rel;
353e01402b1SRalf Baechle 
354e01402b1SRalf Baechle 	if( !(*location & 0xffff) ) {
355e01402b1SRalf Baechle 		rel = (int)v - gp_addr;
356e01402b1SRalf Baechle 	}
357e01402b1SRalf Baechle 	else {
358e01402b1SRalf Baechle 		/* .sbss + gp(relative) + offset */
359e01402b1SRalf Baechle 		/* kludge! */
360e01402b1SRalf Baechle 		rel =  (int)(short)((int)v + gp_offs +
361e01402b1SRalf Baechle 				    (int)(short)(*location & 0xffff) - gp_addr);
362e01402b1SRalf Baechle 	}
363e01402b1SRalf Baechle 
364e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3652600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: "
3662600990eSRalf Baechle 		       "relative address 0x%x out of range of gp register\n",
3672600990eSRalf Baechle 		       rel);
368e01402b1SRalf Baechle 		return -ENOEXEC;
369e01402b1SRalf Baechle 	}
370e01402b1SRalf Baechle 
371e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
372e01402b1SRalf Baechle 
373e01402b1SRalf Baechle 	return 0;
374e01402b1SRalf Baechle }
375e01402b1SRalf Baechle 
376e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location,
377e01402b1SRalf Baechle 			     Elf32_Addr v)
378e01402b1SRalf Baechle {
379e01402b1SRalf Baechle 	int rel;
380e01402b1SRalf Baechle 	rel = (((unsigned int)v - (unsigned int)location));
381e01402b1SRalf Baechle 	rel >>= 2;		// because the offset is in _instructions_ not bytes.
382e01402b1SRalf Baechle 	rel -= 1;		// and one instruction less due to the branch delay slot.
383e01402b1SRalf Baechle 
384e01402b1SRalf Baechle 	if( (rel > 32768) || (rel < -32768) ) {
3852600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: "
386e01402b1SRalf Baechle  		       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
387e01402b1SRalf Baechle 		return -ENOEXEC;
388e01402b1SRalf Baechle 	}
389e01402b1SRalf Baechle 
390e01402b1SRalf Baechle 	*location = (*location & 0xffff0000) | (rel & 0xffff);
391e01402b1SRalf Baechle 
392e01402b1SRalf Baechle 	return 0;
393e01402b1SRalf Baechle }
394e01402b1SRalf Baechle 
395e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location,
396e01402b1SRalf Baechle 			   Elf32_Addr v)
397e01402b1SRalf Baechle {
398e01402b1SRalf Baechle 	*location += v;
399e01402b1SRalf Baechle 
400e01402b1SRalf Baechle 	return 0;
401e01402b1SRalf Baechle }
402e01402b1SRalf Baechle 
403e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location,
404e01402b1SRalf Baechle 			   Elf32_Addr v)
405e01402b1SRalf Baechle {
406e01402b1SRalf Baechle 	if (v % 4) {
4072600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: apply_r_mips_26 "
4082600990eSRalf Baechle 		       " unaligned relocation\n");
409e01402b1SRalf Baechle 		return -ENOEXEC;
410e01402b1SRalf Baechle 	}
411e01402b1SRalf Baechle 
412307bd284SRalf Baechle /*
413307bd284SRalf Baechle  * Not desperately convinced this is a good check of an overflow condition
414307bd284SRalf Baechle  * anyway. But it gets in the way of handling undefined weak symbols which
415307bd284SRalf Baechle  * we want to set to zero.
416307bd284SRalf Baechle  * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
417307bd284SRalf Baechle  * printk(KERN_ERR
418307bd284SRalf Baechle  * "module %s: relocation overflow\n",
419307bd284SRalf Baechle  * me->name);
420307bd284SRalf Baechle  * return -ENOEXEC;
421307bd284SRalf Baechle  * }
422e01402b1SRalf Baechle  */
423e01402b1SRalf Baechle 
424e01402b1SRalf Baechle 	*location = (*location & ~0x03ffffff) |
425e01402b1SRalf Baechle 		((*location + (v >> 2)) & 0x03ffffff);
426e01402b1SRalf Baechle 	return 0;
427e01402b1SRalf Baechle }
428e01402b1SRalf Baechle 
429e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location,
430e01402b1SRalf Baechle 			     Elf32_Addr v)
431e01402b1SRalf Baechle {
432e01402b1SRalf Baechle 	struct mips_hi16 *n;
433e01402b1SRalf Baechle 
434e01402b1SRalf Baechle 	/*
435e01402b1SRalf Baechle 	 * We cannot relocate this one now because we don't know the value of
436e01402b1SRalf Baechle 	 * the carry we need to add.  Save the information, and let LO16 do the
437e01402b1SRalf Baechle 	 * actual relocation.
438e01402b1SRalf Baechle 	 */
439e01402b1SRalf Baechle 	n = kmalloc(sizeof *n, GFP_KERNEL);
440e01402b1SRalf Baechle 	if (!n)
441e01402b1SRalf Baechle 		return -ENOMEM;
442e01402b1SRalf Baechle 
443e01402b1SRalf Baechle 	n->addr = location;
444e01402b1SRalf Baechle 	n->value = v;
445e01402b1SRalf Baechle 	n->next = mips_hi16_list;
446e01402b1SRalf Baechle 	mips_hi16_list = n;
447e01402b1SRalf Baechle 
448e01402b1SRalf Baechle 	return 0;
449e01402b1SRalf Baechle }
450e01402b1SRalf Baechle 
451e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location,
452e01402b1SRalf Baechle 			     Elf32_Addr v)
453e01402b1SRalf Baechle {
454e01402b1SRalf Baechle 	unsigned long insnlo = *location;
455e01402b1SRalf Baechle 	Elf32_Addr val, vallo;
456e01402b1SRalf Baechle 
457e01402b1SRalf Baechle 	/* Sign extend the addend we extract from the lo insn.  */
458e01402b1SRalf Baechle 	vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
459e01402b1SRalf Baechle 
460e01402b1SRalf Baechle 	if (mips_hi16_list != NULL) {
461e01402b1SRalf Baechle 		struct mips_hi16 *l;
462e01402b1SRalf Baechle 
463e01402b1SRalf Baechle 		l = mips_hi16_list;
464e01402b1SRalf Baechle 		while (l != NULL) {
465e01402b1SRalf Baechle 			struct mips_hi16 *next;
466e01402b1SRalf Baechle 			unsigned long insn;
467e01402b1SRalf Baechle 
468e01402b1SRalf Baechle 			/*
469e01402b1SRalf Baechle 			 * The value for the HI16 had best be the same.
470e01402b1SRalf Baechle 			 */
471e01402b1SRalf Baechle  			if (v != l->value) {
4722600990eSRalf Baechle 				printk(KERN_DEBUG "VPE loader: "
473b1e3afa0SJoe Perches 				       "apply_r_mips_lo16/hi16: \t"
4742600990eSRalf Baechle 				       "inconsistent value information\n");
4752600990eSRalf Baechle 				return -ENOEXEC;
476e01402b1SRalf Baechle 			}
477e01402b1SRalf Baechle 
478e01402b1SRalf Baechle 			/*
479e01402b1SRalf Baechle 			 * Do the HI16 relocation.  Note that we actually don't
480e01402b1SRalf Baechle 			 * need to know anything about the LO16 itself, except
481e01402b1SRalf Baechle 			 * where to find the low 16 bits of the addend needed
482e01402b1SRalf Baechle 			 * by the LO16.
483e01402b1SRalf Baechle 			 */
484e01402b1SRalf Baechle 			insn = *l->addr;
485e01402b1SRalf Baechle 			val = ((insn & 0xffff) << 16) + vallo;
486e01402b1SRalf Baechle 			val += v;
487e01402b1SRalf Baechle 
488e01402b1SRalf Baechle 			/*
489e01402b1SRalf Baechle 			 * Account for the sign extension that will happen in
490e01402b1SRalf Baechle 			 * the low bits.
491e01402b1SRalf Baechle 			 */
492e01402b1SRalf Baechle 			val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
493e01402b1SRalf Baechle 
494e01402b1SRalf Baechle 			insn = (insn & ~0xffff) | val;
495e01402b1SRalf Baechle 			*l->addr = insn;
496e01402b1SRalf Baechle 
497e01402b1SRalf Baechle 			next = l->next;
498e01402b1SRalf Baechle 			kfree(l);
499e01402b1SRalf Baechle 			l = next;
500e01402b1SRalf Baechle 		}
501e01402b1SRalf Baechle 
502e01402b1SRalf Baechle 		mips_hi16_list = NULL;
503e01402b1SRalf Baechle 	}
504e01402b1SRalf Baechle 
505e01402b1SRalf Baechle 	/*
506e01402b1SRalf Baechle 	 * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
507e01402b1SRalf Baechle 	 */
508e01402b1SRalf Baechle 	val = v + vallo;
509e01402b1SRalf Baechle 	insnlo = (insnlo & ~0xffff) | (val & 0xffff);
510e01402b1SRalf Baechle 	*location = insnlo;
511e01402b1SRalf Baechle 
512e01402b1SRalf Baechle 	return 0;
513e01402b1SRalf Baechle }
514e01402b1SRalf Baechle 
515e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
516e01402b1SRalf Baechle 				Elf32_Addr v) = {
517e01402b1SRalf Baechle 	[R_MIPS_NONE]	= apply_r_mips_none,
518e01402b1SRalf Baechle 	[R_MIPS_32]	= apply_r_mips_32,
519e01402b1SRalf Baechle 	[R_MIPS_26]	= apply_r_mips_26,
520e01402b1SRalf Baechle 	[R_MIPS_HI16]	= apply_r_mips_hi16,
521e01402b1SRalf Baechle 	[R_MIPS_LO16]	= apply_r_mips_lo16,
522e01402b1SRalf Baechle 	[R_MIPS_GPREL16] = apply_r_mips_gprel16,
523e01402b1SRalf Baechle 	[R_MIPS_PC16] = apply_r_mips_pc16
524e01402b1SRalf Baechle };
525e01402b1SRalf Baechle 
5262600990eSRalf Baechle static char *rstrs[] = {
5272600990eSRalf Baechle 	[R_MIPS_NONE]	= "MIPS_NONE",
5282600990eSRalf Baechle 	[R_MIPS_32]	= "MIPS_32",
5292600990eSRalf Baechle 	[R_MIPS_26]	= "MIPS_26",
5302600990eSRalf Baechle 	[R_MIPS_HI16]	= "MIPS_HI16",
5312600990eSRalf Baechle 	[R_MIPS_LO16]	= "MIPS_LO16",
5322600990eSRalf Baechle 	[R_MIPS_GPREL16] = "MIPS_GPREL16",
5332600990eSRalf Baechle 	[R_MIPS_PC16] = "MIPS_PC16"
5342600990eSRalf Baechle };
535e01402b1SRalf Baechle 
536e01402b1SRalf Baechle int apply_relocations(Elf32_Shdr *sechdrs,
537e01402b1SRalf Baechle 		      const char *strtab,
538e01402b1SRalf Baechle 		      unsigned int symindex,
539e01402b1SRalf Baechle 		      unsigned int relsec,
540e01402b1SRalf Baechle 		      struct module *me)
541e01402b1SRalf Baechle {
542e01402b1SRalf Baechle 	Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
543e01402b1SRalf Baechle 	Elf32_Sym *sym;
544e01402b1SRalf Baechle 	uint32_t *location;
545e01402b1SRalf Baechle 	unsigned int i;
546e01402b1SRalf Baechle 	Elf32_Addr v;
547e01402b1SRalf Baechle 	int res;
548e01402b1SRalf Baechle 
549e01402b1SRalf Baechle 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
550e01402b1SRalf Baechle 		Elf32_Word r_info = rel[i].r_info;
551e01402b1SRalf Baechle 
552e01402b1SRalf Baechle 		/* This is where to make the change */
553e01402b1SRalf Baechle 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
554e01402b1SRalf Baechle 			+ rel[i].r_offset;
555e01402b1SRalf Baechle 		/* This is the symbol it is referring to */
556e01402b1SRalf Baechle 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
557e01402b1SRalf Baechle 			+ ELF32_R_SYM(r_info);
558e01402b1SRalf Baechle 
559e01402b1SRalf Baechle 		if (!sym->st_value) {
560e01402b1SRalf Baechle 			printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
561e01402b1SRalf Baechle 			       me->name, strtab + sym->st_name);
562e01402b1SRalf Baechle 			/* just print the warning, dont barf */
563e01402b1SRalf Baechle 		}
564e01402b1SRalf Baechle 
565e01402b1SRalf Baechle 		v = sym->st_value;
566e01402b1SRalf Baechle 
567e01402b1SRalf Baechle 		res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
568e01402b1SRalf Baechle 		if( res ) {
5692600990eSRalf Baechle 			char *r = rstrs[ELF32_R_TYPE(r_info)];
5702600990eSRalf Baechle 		    	printk(KERN_WARNING "VPE loader: .text+0x%x "
5712600990eSRalf Baechle 			       "relocation type %s for symbol \"%s\" failed\n",
5722600990eSRalf Baechle 			       rel[i].r_offset, r ? r : "UNKNOWN",
5732600990eSRalf Baechle 			       strtab + sym->st_name);
574e01402b1SRalf Baechle 			return res;
575e01402b1SRalf Baechle 		}
5762600990eSRalf Baechle 	}
577e01402b1SRalf Baechle 
578e01402b1SRalf Baechle 	return 0;
579e01402b1SRalf Baechle }
580e01402b1SRalf Baechle 
581e01402b1SRalf Baechle void save_gp_address(unsigned int secbase, unsigned int rel)
582e01402b1SRalf Baechle {
583e01402b1SRalf Baechle 	gp_addr = secbase + rel;
584e01402b1SRalf Baechle 	gp_offs = gp_addr - (secbase & 0xffff0000);
585e01402b1SRalf Baechle }
586e01402b1SRalf Baechle /* end module-elf32.c */
587e01402b1SRalf Baechle 
588e01402b1SRalf Baechle 
589e01402b1SRalf Baechle 
590e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */
5912600990eSRalf Baechle static void simplify_symbols(Elf_Shdr * sechdrs,
592e01402b1SRalf Baechle 			    unsigned int symindex,
593e01402b1SRalf Baechle 			    const char *strtab,
594e01402b1SRalf Baechle 			    const char *secstrings,
595e01402b1SRalf Baechle 			    unsigned int nsecs, struct module *mod)
596e01402b1SRalf Baechle {
597e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
598e01402b1SRalf Baechle 	unsigned long secbase, bssbase = 0;
599e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
6002600990eSRalf Baechle 	int size;
601e01402b1SRalf Baechle 
602e01402b1SRalf Baechle 	/* find the .bss section for COMMON symbols */
603e01402b1SRalf Baechle 	for (i = 0; i < nsecs; i++) {
6042600990eSRalf Baechle 		if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
605e01402b1SRalf Baechle 			bssbase = sechdrs[i].sh_addr;
6062600990eSRalf Baechle 			break;
6072600990eSRalf Baechle 		}
608e01402b1SRalf Baechle 	}
609e01402b1SRalf Baechle 
610e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
611e01402b1SRalf Baechle 		switch (sym[i].st_shndx) {
612e01402b1SRalf Baechle 		case SHN_COMMON:
6132600990eSRalf Baechle 			/* Allocate space for the symbol in the .bss section.
6142600990eSRalf Baechle 			   st_value is currently size.
615e01402b1SRalf Baechle 			   We want it to have the address of the symbol. */
616e01402b1SRalf Baechle 
617e01402b1SRalf Baechle 			size = sym[i].st_value;
618e01402b1SRalf Baechle 			sym[i].st_value = bssbase;
619e01402b1SRalf Baechle 
620e01402b1SRalf Baechle 			bssbase += size;
621e01402b1SRalf Baechle 			break;
622e01402b1SRalf Baechle 
623e01402b1SRalf Baechle 		case SHN_ABS:
624e01402b1SRalf Baechle 			/* Don't need to do anything */
625e01402b1SRalf Baechle 			break;
626e01402b1SRalf Baechle 
627e01402b1SRalf Baechle 		case SHN_UNDEF:
628e01402b1SRalf Baechle 			/* ret = -ENOENT; */
629e01402b1SRalf Baechle 			break;
630e01402b1SRalf Baechle 
631e01402b1SRalf Baechle 		case SHN_MIPS_SCOMMON:
6322600990eSRalf Baechle 			printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "
6332600990eSRalf Baechle 			       "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
6342600990eSRalf Baechle 			       sym[i].st_shndx);
635e01402b1SRalf Baechle 			// .sbss section
636e01402b1SRalf Baechle 			break;
637e01402b1SRalf Baechle 
638e01402b1SRalf Baechle 		default:
639e01402b1SRalf Baechle 			secbase = sechdrs[sym[i].st_shndx].sh_addr;
640e01402b1SRalf Baechle 
641e01402b1SRalf Baechle 			if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
642e01402b1SRalf Baechle 				save_gp_address(secbase, sym[i].st_value);
643e01402b1SRalf Baechle 			}
644e01402b1SRalf Baechle 
645e01402b1SRalf Baechle 			sym[i].st_value += secbase;
646e01402b1SRalf Baechle 			break;
647e01402b1SRalf Baechle 		}
648e01402b1SRalf Baechle 	}
649e01402b1SRalf Baechle }
650e01402b1SRalf Baechle 
651e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER
652e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
653e01402b1SRalf Baechle 			    const char *strtab, struct module *mod)
654e01402b1SRalf Baechle {
655e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
656e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
657e01402b1SRalf Baechle 
658e01402b1SRalf Baechle 	printk(KERN_DEBUG "dump_elfsymbols: n %d\n", n);
659e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
660e01402b1SRalf Baechle 		printk(KERN_DEBUG " i %d name <%s> 0x%x\n", i,
661e01402b1SRalf Baechle 		       strtab + sym[i].st_name, sym[i].st_value);
662e01402b1SRalf Baechle 	}
663e01402b1SRalf Baechle }
664e01402b1SRalf Baechle #endif
665e01402b1SRalf Baechle 
666e01402b1SRalf Baechle /* We are prepared so configure and start the VPE... */
667be6e1437SRalf Baechle static int vpe_run(struct vpe * v)
668e01402b1SRalf Baechle {
66907cc0c9eSRalf Baechle 	unsigned long flags, val, dmt_flag;
6702600990eSRalf Baechle 	struct vpe_notifications *n;
67107cc0c9eSRalf Baechle 	unsigned int vpeflags;
672e01402b1SRalf Baechle 	struct tc *t;
673e01402b1SRalf Baechle 
674e01402b1SRalf Baechle 	/* check we are the Master VPE */
67507cc0c9eSRalf Baechle 	local_irq_save(flags);
676e01402b1SRalf Baechle 	val = read_c0_vpeconf0();
677e01402b1SRalf Baechle 	if (!(val & VPECONF0_MVP)) {
678e01402b1SRalf Baechle 		printk(KERN_WARNING
6792600990eSRalf Baechle 		       "VPE loader: only Master VPE's are allowed to configure MT\n");
68007cc0c9eSRalf Baechle 		local_irq_restore(flags);
68107cc0c9eSRalf Baechle 
682e01402b1SRalf Baechle 		return -1;
683e01402b1SRalf Baechle 	}
684e01402b1SRalf Baechle 
68507cc0c9eSRalf Baechle 	dmt_flag = dmt();
68607cc0c9eSRalf Baechle 	vpeflags = dvpe();
687e01402b1SRalf Baechle 
688e01402b1SRalf Baechle 	if (!list_empty(&v->tc)) {
689e01402b1SRalf Baechle 		if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
69007cc0c9eSRalf Baechle 			evpe(vpeflags);
69107cc0c9eSRalf Baechle 			emt(dmt_flag);
69207cc0c9eSRalf Baechle 			local_irq_restore(flags);
69307cc0c9eSRalf Baechle 
69407cc0c9eSRalf Baechle 			printk(KERN_WARNING
69507cc0c9eSRalf Baechle 			       "VPE loader: TC %d is already in use.\n",
696e01402b1SRalf Baechle                                t->index);
697e01402b1SRalf Baechle 			return -ENOEXEC;
698e01402b1SRalf Baechle 		}
699e01402b1SRalf Baechle 	} else {
70007cc0c9eSRalf Baechle 		evpe(vpeflags);
70107cc0c9eSRalf Baechle 		emt(dmt_flag);
70207cc0c9eSRalf Baechle 		local_irq_restore(flags);
70307cc0c9eSRalf Baechle 
70407cc0c9eSRalf Baechle 		printk(KERN_WARNING
70507cc0c9eSRalf Baechle 		       "VPE loader: No TC's associated with VPE %d\n",
706e01402b1SRalf Baechle 		       v->minor);
70707cc0c9eSRalf Baechle 
708e01402b1SRalf Baechle 		return -ENOEXEC;
709e01402b1SRalf Baechle 	}
710e01402b1SRalf Baechle 
7112600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
7122600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
713e01402b1SRalf Baechle 
7142600990eSRalf Baechle 	settc(t->index);
715e01402b1SRalf Baechle 
716e01402b1SRalf Baechle 	/* should check it is halted, and not activated */
717e01402b1SRalf Baechle 	if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
71807cc0c9eSRalf Baechle 		evpe(vpeflags);
71907cc0c9eSRalf Baechle 		emt(dmt_flag);
72007cc0c9eSRalf Baechle 		local_irq_restore(flags);
72107cc0c9eSRalf Baechle 
72207cc0c9eSRalf Baechle 		printk(KERN_WARNING "VPE loader: TC %d is already active!\n",
723e01402b1SRalf Baechle 		       t->index);
72407cc0c9eSRalf Baechle 
725e01402b1SRalf Baechle 		return -ENOEXEC;
726e01402b1SRalf Baechle 	}
727e01402b1SRalf Baechle 
728e01402b1SRalf Baechle 	/* Write the address we want it to start running from in the TCPC register. */
729e01402b1SRalf Baechle 	write_tc_c0_tcrestart((unsigned long)v->__start);
730e01402b1SRalf Baechle 	write_tc_c0_tccontext((unsigned long)0);
73107cc0c9eSRalf Baechle 
7322600990eSRalf Baechle 	/*
7332600990eSRalf Baechle 	 * Mark the TC as activated, not interrupt exempt and not dynamically
7342600990eSRalf Baechle 	 * allocatable
7352600990eSRalf Baechle 	 */
736e01402b1SRalf Baechle 	val = read_tc_c0_tcstatus();
737e01402b1SRalf Baechle 	val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A;
738e01402b1SRalf Baechle 	write_tc_c0_tcstatus(val);
739e01402b1SRalf Baechle 
740e01402b1SRalf Baechle 	write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H);
741e01402b1SRalf Baechle 
742e01402b1SRalf Baechle 	/*
743e01402b1SRalf Baechle 	 * The sde-kit passes 'memsize' to __start in $a3, so set something
7442600990eSRalf Baechle 	 * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
745e01402b1SRalf Baechle 	 * DFLT_HEAP_SIZE when you compile your program
746e01402b1SRalf Baechle 	 */
74741790e04SRalf Baechle 	mttgpr(6, v->ntcs);
7482600990eSRalf Baechle 	mttgpr(7, physical_memsize);
749e01402b1SRalf Baechle 
7502600990eSRalf Baechle 	/* set up VPE1 */
7512600990eSRalf Baechle 	/*
7522600990eSRalf Baechle 	 * bind the TC to VPE 1 as late as possible so we only have the final
7532600990eSRalf Baechle 	 * VPE registers to set up, and so an EJTAG probe can trigger on it
7542600990eSRalf Baechle 	 */
75507cc0c9eSRalf Baechle 	write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
7562600990eSRalf Baechle 
757a94d7020SElizabeth Oldham 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
758a94d7020SElizabeth Oldham 
759a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
760a94d7020SElizabeth Oldham 
7612600990eSRalf Baechle 	/* Set up the XTC bit in vpeconf0 to point at our tc */
7622600990eSRalf Baechle 	write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC))
7632600990eSRalf Baechle 	                      | (t->index << VPECONF0_XTC_SHIFT));
7642600990eSRalf Baechle 
765a94d7020SElizabeth Oldham 	back_to_back_c0_hazard();
766a94d7020SElizabeth Oldham 
7672600990eSRalf Baechle 	/* enable this VPE */
7682600990eSRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
769e01402b1SRalf Baechle 
770e01402b1SRalf Baechle 	/* clear out any left overs from a previous program */
7712600990eSRalf Baechle 	write_vpe_c0_status(0);
772e01402b1SRalf Baechle 	write_vpe_c0_cause(0);
773e01402b1SRalf Baechle 
774e01402b1SRalf Baechle 	/* take system out of configuration state */
775340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
776e01402b1SRalf Baechle 
77707cc0c9eSRalf Baechle #ifdef CONFIG_SMP
778e01402b1SRalf Baechle 	evpe(EVPE_ENABLE);
77907cc0c9eSRalf Baechle #else
78007cc0c9eSRalf Baechle 	evpe(vpeflags);
78107cc0c9eSRalf Baechle #endif
78207cc0c9eSRalf Baechle 	emt(dmt_flag);
78307cc0c9eSRalf Baechle 	local_irq_restore(flags);
784e01402b1SRalf Baechle 
78507cc0c9eSRalf Baechle 	list_for_each_entry(n, &v->notify, list)
78607cc0c9eSRalf Baechle 		n->start(minor);
7872600990eSRalf Baechle 
788e01402b1SRalf Baechle 	return 0;
789e01402b1SRalf Baechle }
790e01402b1SRalf Baechle 
7912600990eSRalf Baechle static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs,
792e01402b1SRalf Baechle 				      unsigned int symindex, const char *strtab,
793e01402b1SRalf Baechle 				      struct module *mod)
794e01402b1SRalf Baechle {
795e01402b1SRalf Baechle 	Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
796e01402b1SRalf Baechle 	unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
797e01402b1SRalf Baechle 
798e01402b1SRalf Baechle 	for (i = 1; i < n; i++) {
799e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "__start") == 0) {
800e01402b1SRalf Baechle 			v->__start = sym[i].st_value;
801e01402b1SRalf Baechle 		}
802e01402b1SRalf Baechle 
803e01402b1SRalf Baechle 		if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) {
804e01402b1SRalf Baechle 			v->shared_ptr = (void *)sym[i].st_value;
805e01402b1SRalf Baechle 		}
806e01402b1SRalf Baechle 	}
807e01402b1SRalf Baechle 
8082600990eSRalf Baechle 	if ( (v->__start == 0) || (v->shared_ptr == NULL))
8092600990eSRalf Baechle 		return -1;
8102600990eSRalf Baechle 
811e01402b1SRalf Baechle 	return 0;
812e01402b1SRalf Baechle }
813e01402b1SRalf Baechle 
814307bd284SRalf Baechle /*
8152600990eSRalf Baechle  * Allocates a VPE with some program code space(the load address), copies the
8162600990eSRalf Baechle  * contents of the program (p)buffer performing relocatations/etc, free's it
8172600990eSRalf Baechle  * when finished.
818e01402b1SRalf Baechle  */
819be6e1437SRalf Baechle static int vpe_elfload(struct vpe * v)
820e01402b1SRalf Baechle {
821e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
822e01402b1SRalf Baechle 	Elf_Shdr *sechdrs;
823e01402b1SRalf Baechle 	long err = 0;
824e01402b1SRalf Baechle 	char *secstrings, *strtab = NULL;
8252600990eSRalf Baechle 	unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
826e01402b1SRalf Baechle 	struct module mod;	// so we can re-use the relocations code
827e01402b1SRalf Baechle 
828e01402b1SRalf Baechle 	memset(&mod, 0, sizeof(struct module));
8292600990eSRalf Baechle 	strcpy(mod.name, "VPE loader");
830e01402b1SRalf Baechle 
831e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
832e01402b1SRalf Baechle 	len = v->plen;
833e01402b1SRalf Baechle 
834e01402b1SRalf Baechle 	/* Sanity checks against insmoding binaries or wrong arch,
835e01402b1SRalf Baechle 	   weird elf version */
836e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
8372600990eSRalf Baechle 	    || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
8382600990eSRalf Baechle 	    || !elf_check_arch(hdr)
839e01402b1SRalf Baechle 	    || hdr->e_shentsize != sizeof(*sechdrs)) {
840e01402b1SRalf Baechle 		printk(KERN_WARNING
8412600990eSRalf Baechle 		       "VPE loader: program wrong arch or weird elf version\n");
842e01402b1SRalf Baechle 
843e01402b1SRalf Baechle 		return -ENOEXEC;
844e01402b1SRalf Baechle 	}
845e01402b1SRalf Baechle 
8462600990eSRalf Baechle 	if (hdr->e_type == ET_REL)
8472600990eSRalf Baechle 		relocate = 1;
8482600990eSRalf Baechle 
849e01402b1SRalf Baechle 	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
8502600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: program length %u truncated\n",
8512600990eSRalf Baechle 		       len);
8522600990eSRalf Baechle 
853e01402b1SRalf Baechle 		return -ENOEXEC;
854e01402b1SRalf Baechle 	}
855e01402b1SRalf Baechle 
856e01402b1SRalf Baechle 	/* Convenience variables */
857e01402b1SRalf Baechle 	sechdrs = (void *)hdr + hdr->e_shoff;
858e01402b1SRalf Baechle 	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
859e01402b1SRalf Baechle 	sechdrs[0].sh_addr = 0;
860e01402b1SRalf Baechle 
861e01402b1SRalf Baechle 	/* And these should exist, but gcc whinges if we don't init them */
862e01402b1SRalf Baechle 	symindex = strindex = 0;
863e01402b1SRalf Baechle 
8642600990eSRalf Baechle 	if (relocate) {
865e01402b1SRalf Baechle 		for (i = 1; i < hdr->e_shnum; i++) {
866e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS
867e01402b1SRalf Baechle 			    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
868e01402b1SRalf Baechle 				printk(KERN_ERR "VPE program length %u truncated\n",
869e01402b1SRalf Baechle 				       len);
870e01402b1SRalf Baechle 				return -ENOEXEC;
871e01402b1SRalf Baechle 			}
872e01402b1SRalf Baechle 
873e01402b1SRalf Baechle 			/* Mark all sections sh_addr with their address in the
874e01402b1SRalf Baechle 			   temporary image. */
875e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
876e01402b1SRalf Baechle 
877e01402b1SRalf Baechle 			/* Internal symbols and strings. */
878e01402b1SRalf Baechle 			if (sechdrs[i].sh_type == SHT_SYMTAB) {
879e01402b1SRalf Baechle 				symindex = i;
880e01402b1SRalf Baechle 				strindex = sechdrs[i].sh_link;
881e01402b1SRalf Baechle 				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
882e01402b1SRalf Baechle 			}
883e01402b1SRalf Baechle 		}
884e01402b1SRalf Baechle 		layout_sections(&mod, hdr, sechdrs, secstrings);
8852600990eSRalf Baechle 	}
886e01402b1SRalf Baechle 
887e01402b1SRalf Baechle 	v->load_addr = alloc_progmem(mod.core_size);
888e01402b1SRalf Baechle 	memset(v->load_addr, 0, mod.core_size);
889e01402b1SRalf Baechle 
8902600990eSRalf Baechle 	printk("VPE loader: loading to %p\n", v->load_addr);
891e01402b1SRalf Baechle 
8922600990eSRalf Baechle 	if (relocate) {
893e01402b1SRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
894e01402b1SRalf Baechle 			void *dest;
895e01402b1SRalf Baechle 
896e01402b1SRalf Baechle 			if (!(sechdrs[i].sh_flags & SHF_ALLOC))
897e01402b1SRalf Baechle 				continue;
898e01402b1SRalf Baechle 
899e01402b1SRalf Baechle 			dest = v->load_addr + sechdrs[i].sh_entsize;
900e01402b1SRalf Baechle 
901e01402b1SRalf Baechle 			if (sechdrs[i].sh_type != SHT_NOBITS)
902e01402b1SRalf Baechle 				memcpy(dest, (void *)sechdrs[i].sh_addr,
903e01402b1SRalf Baechle 				       sechdrs[i].sh_size);
904e01402b1SRalf Baechle 			/* Update sh_addr to point to copy in image. */
905e01402b1SRalf Baechle 			sechdrs[i].sh_addr = (unsigned long)dest;
9062600990eSRalf Baechle 
9072600990eSRalf Baechle 			printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
9082600990eSRalf Baechle 			       secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
909e01402b1SRalf Baechle 		}
910e01402b1SRalf Baechle 
911e01402b1SRalf Baechle  		/* Fix up syms, so that st_value is a pointer to location. */
912e01402b1SRalf Baechle  		simplify_symbols(sechdrs, symindex, strtab, secstrings,
913e01402b1SRalf Baechle  				 hdr->e_shnum, &mod);
914e01402b1SRalf Baechle 
915e01402b1SRalf Baechle  		/* Now do relocations. */
916e01402b1SRalf Baechle  		for (i = 1; i < hdr->e_shnum; i++) {
917e01402b1SRalf Baechle  			const char *strtab = (char *)sechdrs[strindex].sh_addr;
918e01402b1SRalf Baechle  			unsigned int info = sechdrs[i].sh_info;
919e01402b1SRalf Baechle 
920e01402b1SRalf Baechle  			/* Not a valid relocation section? */
921e01402b1SRalf Baechle  			if (info >= hdr->e_shnum)
922e01402b1SRalf Baechle  				continue;
923e01402b1SRalf Baechle 
924e01402b1SRalf Baechle  			/* Don't bother with non-allocated sections */
925e01402b1SRalf Baechle  			if (!(sechdrs[info].sh_flags & SHF_ALLOC))
926e01402b1SRalf Baechle  				continue;
927e01402b1SRalf Baechle 
928e01402b1SRalf Baechle  			if (sechdrs[i].sh_type == SHT_REL)
9292600990eSRalf Baechle  				err = apply_relocations(sechdrs, strtab, symindex, i,
9302600990eSRalf Baechle  							&mod);
931e01402b1SRalf Baechle  			else if (sechdrs[i].sh_type == SHT_RELA)
932e01402b1SRalf Baechle  				err = apply_relocate_add(sechdrs, strtab, symindex, i,
933e01402b1SRalf Baechle  							 &mod);
9342600990eSRalf Baechle  			if (err < 0)
9352600990eSRalf Baechle  				return err;
9362600990eSRalf Baechle 
9372600990eSRalf Baechle   		}
9382600990eSRalf Baechle   	} else {
939bdf5d42cSRalf Baechle 		struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);
9402600990eSRalf Baechle 
941bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_phnum; i++) {
942bdf5d42cSRalf Baechle 			if (phdr->p_type != PT_LOAD)
943bdf5d42cSRalf Baechle 				continue;
944bdf5d42cSRalf Baechle 
94582923663SRalf Baechle 			memcpy((void *)phdr->p_paddr, (char *)hdr + phdr->p_offset, phdr->p_filesz);
94682923663SRalf Baechle 			memset((void *)phdr->p_paddr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
947bdf5d42cSRalf Baechle 			phdr++;
948bdf5d42cSRalf Baechle 		}
949bdf5d42cSRalf Baechle 
950bdf5d42cSRalf Baechle 		for (i = 0; i < hdr->e_shnum; i++) {
9512600990eSRalf Baechle  			/* Internal symbols and strings. */
9522600990eSRalf Baechle  			if (sechdrs[i].sh_type == SHT_SYMTAB) {
9532600990eSRalf Baechle  				symindex = i;
9542600990eSRalf Baechle  				strindex = sechdrs[i].sh_link;
9552600990eSRalf Baechle  				strtab = (char *)hdr + sechdrs[strindex].sh_offset;
9562600990eSRalf Baechle 
9572600990eSRalf Baechle  				/* mark the symtab's address for when we try to find the
9582600990eSRalf Baechle  				   magic symbols */
9592600990eSRalf Baechle  				sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
9602600990eSRalf Baechle  			}
961e01402b1SRalf Baechle 		}
962e01402b1SRalf Baechle 	}
963e01402b1SRalf Baechle 
964e01402b1SRalf Baechle 	/* make sure it's physically written out */
965e01402b1SRalf Baechle 	flush_icache_range((unsigned long)v->load_addr,
966e01402b1SRalf Baechle 			   (unsigned long)v->load_addr + v->len);
967e01402b1SRalf Baechle 
968e01402b1SRalf Baechle 	if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) {
9692600990eSRalf Baechle 		if (v->__start == 0) {
9702600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: program does not contain "
9712600990eSRalf Baechle 			       "a __start symbol\n");
9722600990eSRalf Baechle 			return -ENOEXEC;
9732600990eSRalf Baechle 		}
974e01402b1SRalf Baechle 
9752600990eSRalf Baechle 		if (v->shared_ptr == NULL)
9762600990eSRalf Baechle 			printk(KERN_WARNING "VPE loader: "
9772600990eSRalf Baechle 			       "program does not contain vpe_shared symbol.\n"
9782600990eSRalf Baechle 			       " Unable to use AMVP (AP/SP) facilities.\n");
979e01402b1SRalf Baechle 	}
980e01402b1SRalf Baechle 
981e01402b1SRalf Baechle 	printk(" elf loaded\n");
9822600990eSRalf Baechle 	return 0;
983e01402b1SRalf Baechle }
984e01402b1SRalf Baechle 
9852600990eSRalf Baechle static void cleanup_tc(struct tc *tc)
986e01402b1SRalf Baechle {
98707cc0c9eSRalf Baechle 	unsigned long flags;
98807cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
9892600990eSRalf Baechle 	int tmp;
990e01402b1SRalf Baechle 
99107cc0c9eSRalf Baechle 	local_irq_save(flags);
99207cc0c9eSRalf Baechle 	mtflags = dmt();
99307cc0c9eSRalf Baechle 	vpflags = dvpe();
9942600990eSRalf Baechle 	/* Put MVPE's into 'configuration state' */
9952600990eSRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
996e01402b1SRalf Baechle 
9972600990eSRalf Baechle 	settc(tc->index);
998e01402b1SRalf Baechle 	tmp = read_tc_c0_tcstatus();
999e01402b1SRalf Baechle 
1000e01402b1SRalf Baechle 	/* mark not allocated and not dynamically allocatable */
1001e01402b1SRalf Baechle 	tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1002e01402b1SRalf Baechle 	tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1003e01402b1SRalf Baechle 	write_tc_c0_tcstatus(tmp);
1004e01402b1SRalf Baechle 
1005e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
10067c3a622dSNigel Stephens 	mips_ihb();
1007e01402b1SRalf Baechle 
10082600990eSRalf Baechle 	/* bind it to anything other than VPE1 */
100907cc0c9eSRalf Baechle //	write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
10102600990eSRalf Baechle 
10112600990eSRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
101207cc0c9eSRalf Baechle 	evpe(vpflags);
101307cc0c9eSRalf Baechle 	emt(mtflags);
101407cc0c9eSRalf Baechle 	local_irq_restore(flags);
10152600990eSRalf Baechle }
10162600990eSRalf Baechle 
10172600990eSRalf Baechle static int getcwd(char *buff, int size)
10182600990eSRalf Baechle {
10192600990eSRalf Baechle 	mm_segment_t old_fs;
10202600990eSRalf Baechle 	int ret;
10212600990eSRalf Baechle 
10222600990eSRalf Baechle 	old_fs = get_fs();
10232600990eSRalf Baechle 	set_fs(KERNEL_DS);
10242600990eSRalf Baechle 
10252600990eSRalf Baechle 	ret = sys_getcwd(buff, size);
10262600990eSRalf Baechle 
10272600990eSRalf Baechle 	set_fs(old_fs);
10282600990eSRalf Baechle 
10292600990eSRalf Baechle 	return ret;
10302600990eSRalf Baechle }
10312600990eSRalf Baechle 
10322600990eSRalf Baechle /* checks VPE is unused and gets ready to load program  */
10332600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp)
10342600990eSRalf Baechle {
1035c4c4018bSRalf Baechle 	enum vpe_state state;
10362600990eSRalf Baechle 	struct vpe_notifications *not;
103707cc0c9eSRalf Baechle 	struct vpe *v;
103807cc0c9eSRalf Baechle 	int ret;
10392600990eSRalf Baechle 
104007cc0c9eSRalf Baechle 	if (minor != iminor(inode)) {
104107cc0c9eSRalf Baechle 		/* assume only 1 device at the moment. */
10422600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
10432600990eSRalf Baechle 		return -ENODEV;
10442600990eSRalf Baechle 	}
10452600990eSRalf Baechle 
104607cc0c9eSRalf Baechle 	if ((v = get_vpe(tclimit)) == NULL) {
10472600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: unable to get vpe\n");
10482600990eSRalf Baechle 		return -ENODEV;
10492600990eSRalf Baechle 	}
10502600990eSRalf Baechle 
1051c4c4018bSRalf Baechle 	state = xchg(&v->state, VPE_STATE_INUSE);
1052c4c4018bSRalf Baechle 	if (state != VPE_STATE_UNUSED) {
10532600990eSRalf Baechle 		printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
10542600990eSRalf Baechle 
10552600990eSRalf Baechle 		list_for_each_entry(not, &v->notify, list) {
105607cc0c9eSRalf Baechle 			not->stop(tclimit);
10572600990eSRalf Baechle 		}
10582600990eSRalf Baechle 
10592600990eSRalf Baechle 		release_progmem(v->load_addr);
106007cc0c9eSRalf Baechle 		cleanup_tc(get_tc(tclimit));
1061e01402b1SRalf Baechle 	}
1062e01402b1SRalf Baechle 
1063e01402b1SRalf Baechle 	/* this of-course trashes what was there before... */
1064e01402b1SRalf Baechle 	v->pbuffer = vmalloc(P_SIZE);
1065e01402b1SRalf Baechle 	v->plen = P_SIZE;
1066e01402b1SRalf Baechle 	v->load_addr = NULL;
1067e01402b1SRalf Baechle 	v->len = 0;
1068e01402b1SRalf Baechle 
10692600990eSRalf Baechle 	v->uid = filp->f_uid;
10702600990eSRalf Baechle 	v->gid = filp->f_gid;
10712600990eSRalf Baechle 
10722600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
10732600990eSRalf Baechle 	/* get kspd to tell us when a syscall_exit happens */
10742600990eSRalf Baechle 	if (!kspd_events_reqd) {
10752600990eSRalf Baechle 		kspd_notify(&kspd_events);
10762600990eSRalf Baechle 		kspd_events_reqd++;
10772600990eSRalf Baechle 	}
10782600990eSRalf Baechle #endif
10792600990eSRalf Baechle 
10802600990eSRalf Baechle 	v->cwd[0] = 0;
10812600990eSRalf Baechle 	ret = getcwd(v->cwd, VPE_PATH_MAX);
10822600990eSRalf Baechle 	if (ret < 0)
10832600990eSRalf Baechle 		printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret);
10842600990eSRalf Baechle 
10852600990eSRalf Baechle 	v->shared_ptr = NULL;
10862600990eSRalf Baechle 	v->__start = 0;
108707cc0c9eSRalf Baechle 
1088e01402b1SRalf Baechle 	return 0;
1089e01402b1SRalf Baechle }
1090e01402b1SRalf Baechle 
1091e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp)
1092e01402b1SRalf Baechle {
1093307bd284SRalf Baechle 	struct vpe *v;
1094e01402b1SRalf Baechle 	Elf_Ehdr *hdr;
109507cc0c9eSRalf Baechle 	int ret = 0;
1096e01402b1SRalf Baechle 
109707cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
109807cc0c9eSRalf Baechle 	if (v == NULL)
1099e01402b1SRalf Baechle 		return -ENODEV;
1100e01402b1SRalf Baechle 
1101e01402b1SRalf Baechle 	hdr = (Elf_Ehdr *) v->pbuffer;
1102e01402b1SRalf Baechle 	if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) {
110307cc0c9eSRalf Baechle 		if (vpe_elfload(v) >= 0) {
1104e01402b1SRalf Baechle 			vpe_run(v);
110507cc0c9eSRalf Baechle 		} else {
11062600990eSRalf Baechle  			printk(KERN_WARNING "VPE loader: ELF load failed.\n");
1107e01402b1SRalf Baechle 			ret = -ENOEXEC;
1108e01402b1SRalf Baechle 		}
1109e01402b1SRalf Baechle 	} else {
11102600990eSRalf Baechle  		printk(KERN_WARNING "VPE loader: only elf files are supported\n");
1111e01402b1SRalf Baechle 		ret = -ENOEXEC;
1112e01402b1SRalf Baechle 	}
1113e01402b1SRalf Baechle 
11142600990eSRalf Baechle 	/* It's good to be able to run the SP and if it chokes have a look at
11152600990eSRalf Baechle 	   the /dev/rt?. But if we reset the pointer to the shared struct we
11162600990eSRalf Baechle 	   loose what has happened. So perhaps if garbage is sent to the vpe
11172600990eSRalf Baechle 	   device, use it as a trigger for the reset. Hopefully a nice
11182600990eSRalf Baechle 	   executable will be along shortly. */
11192600990eSRalf Baechle 	if (ret < 0)
11202600990eSRalf Baechle 		v->shared_ptr = NULL;
11212600990eSRalf Baechle 
1122e01402b1SRalf Baechle 	// cleanup any temp buffers
1123e01402b1SRalf Baechle 	if (v->pbuffer)
1124e01402b1SRalf Baechle 		vfree(v->pbuffer);
1125e01402b1SRalf Baechle 	v->plen = 0;
1126e01402b1SRalf Baechle 	return ret;
1127e01402b1SRalf Baechle }
1128e01402b1SRalf Baechle 
1129e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user * buffer,
1130e01402b1SRalf Baechle 			 size_t count, loff_t * ppos)
1131e01402b1SRalf Baechle {
1132e01402b1SRalf Baechle 	size_t ret = count;
1133307bd284SRalf Baechle 	struct vpe *v;
1134e01402b1SRalf Baechle 
113507cc0c9eSRalf Baechle 	if (iminor(file->f_path.dentry->d_inode) != minor)
113607cc0c9eSRalf Baechle 		return -ENODEV;
113707cc0c9eSRalf Baechle 
113807cc0c9eSRalf Baechle 	v = get_vpe(tclimit);
113907cc0c9eSRalf Baechle 	if (v == NULL)
1140e01402b1SRalf Baechle 		return -ENODEV;
1141e01402b1SRalf Baechle 
1142e01402b1SRalf Baechle 	if (v->pbuffer == NULL) {
11432600990eSRalf Baechle 		printk(KERN_ERR "VPE loader: no buffer for program\n");
1144e01402b1SRalf Baechle 		return -ENOMEM;
1145e01402b1SRalf Baechle 	}
1146e01402b1SRalf Baechle 
1147e01402b1SRalf Baechle 	if ((count + v->len) > v->plen) {
1148e01402b1SRalf Baechle 		printk(KERN_WARNING
11492600990eSRalf Baechle 		       "VPE loader: elf size too big. Perhaps strip uneeded symbols\n");
1150e01402b1SRalf Baechle 		return -ENOMEM;
1151e01402b1SRalf Baechle 	}
1152e01402b1SRalf Baechle 
1153e01402b1SRalf Baechle 	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
11542600990eSRalf Baechle 	if (!count)
1155e01402b1SRalf Baechle 		return -EFAULT;
1156e01402b1SRalf Baechle 
1157e01402b1SRalf Baechle 	v->len += count;
1158e01402b1SRalf Baechle 	return ret;
1159e01402b1SRalf Baechle }
1160e01402b1SRalf Baechle 
11615dfe4c96SArjan van de Ven static const struct file_operations vpe_fops = {
1162e01402b1SRalf Baechle 	.owner = THIS_MODULE,
1163e01402b1SRalf Baechle 	.open = vpe_open,
1164e01402b1SRalf Baechle 	.release = vpe_release,
1165e01402b1SRalf Baechle 	.write = vpe_write
1166e01402b1SRalf Baechle };
1167e01402b1SRalf Baechle 
1168e01402b1SRalf Baechle /* module wrapper entry points */
1169e01402b1SRalf Baechle /* give me a vpe */
1170e01402b1SRalf Baechle vpe_handle vpe_alloc(void)
1171e01402b1SRalf Baechle {
1172e01402b1SRalf Baechle 	int i;
1173e01402b1SRalf Baechle 	struct vpe *v;
1174e01402b1SRalf Baechle 
1175e01402b1SRalf Baechle 	/* find a vpe */
1176e01402b1SRalf Baechle 	for (i = 1; i < MAX_VPES; i++) {
1177e01402b1SRalf Baechle 		if ((v = get_vpe(i)) != NULL) {
1178e01402b1SRalf Baechle 			v->state = VPE_STATE_INUSE;
1179e01402b1SRalf Baechle 			return v;
1180e01402b1SRalf Baechle 		}
1181e01402b1SRalf Baechle 	}
1182e01402b1SRalf Baechle 	return NULL;
1183e01402b1SRalf Baechle }
1184e01402b1SRalf Baechle 
1185e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_alloc);
1186e01402b1SRalf Baechle 
1187e01402b1SRalf Baechle /* start running from here */
1188e01402b1SRalf Baechle int vpe_start(vpe_handle vpe, unsigned long start)
1189e01402b1SRalf Baechle {
1190e01402b1SRalf Baechle 	struct vpe *v = vpe;
1191e01402b1SRalf Baechle 
1192e01402b1SRalf Baechle 	v->__start = start;
1193e01402b1SRalf Baechle 	return vpe_run(v);
1194e01402b1SRalf Baechle }
1195e01402b1SRalf Baechle 
1196e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_start);
1197e01402b1SRalf Baechle 
1198e01402b1SRalf Baechle /* halt it for now */
1199e01402b1SRalf Baechle int vpe_stop(vpe_handle vpe)
1200e01402b1SRalf Baechle {
1201e01402b1SRalf Baechle 	struct vpe *v = vpe;
1202e01402b1SRalf Baechle 	struct tc *t;
1203e01402b1SRalf Baechle 	unsigned int evpe_flags;
1204e01402b1SRalf Baechle 
1205e01402b1SRalf Baechle 	evpe_flags = dvpe();
1206e01402b1SRalf Baechle 
1207e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) != NULL) {
1208e01402b1SRalf Baechle 
1209e01402b1SRalf Baechle 		settc(t->index);
1210e01402b1SRalf Baechle 		write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1211e01402b1SRalf Baechle 	}
1212e01402b1SRalf Baechle 
1213e01402b1SRalf Baechle 	evpe(evpe_flags);
1214e01402b1SRalf Baechle 
1215e01402b1SRalf Baechle 	return 0;
1216e01402b1SRalf Baechle }
1217e01402b1SRalf Baechle 
1218e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_stop);
1219e01402b1SRalf Baechle 
1220e01402b1SRalf Baechle /* I've done with it thank you */
1221e01402b1SRalf Baechle int vpe_free(vpe_handle vpe)
1222e01402b1SRalf Baechle {
1223e01402b1SRalf Baechle 	struct vpe *v = vpe;
1224e01402b1SRalf Baechle 	struct tc *t;
1225e01402b1SRalf Baechle 	unsigned int evpe_flags;
1226e01402b1SRalf Baechle 
1227e01402b1SRalf Baechle 	if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
1228e01402b1SRalf Baechle 		return -ENOEXEC;
1229e01402b1SRalf Baechle 	}
1230e01402b1SRalf Baechle 
1231e01402b1SRalf Baechle 	evpe_flags = dvpe();
1232e01402b1SRalf Baechle 
1233e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1234340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1235e01402b1SRalf Baechle 
1236e01402b1SRalf Baechle 	settc(t->index);
1237e01402b1SRalf Baechle 	write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~VPECONF0_VPA);
1238e01402b1SRalf Baechle 
12397c3a622dSNigel Stephens 	/* halt the TC */
1240e01402b1SRalf Baechle 	write_tc_c0_tchalt(TCHALT_H);
12417c3a622dSNigel Stephens 	mips_ihb();
12427c3a622dSNigel Stephens 
12437c3a622dSNigel Stephens 	/* mark the TC unallocated */
12447c3a622dSNigel Stephens 	write_tc_c0_tcstatus(read_tc_c0_tcstatus() & ~TCSTATUS_A);
1245e01402b1SRalf Baechle 
1246e01402b1SRalf Baechle 	v->state = VPE_STATE_UNUSED;
1247e01402b1SRalf Baechle 
1248340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1249e01402b1SRalf Baechle 	evpe(evpe_flags);
1250e01402b1SRalf Baechle 
1251e01402b1SRalf Baechle 	return 0;
1252e01402b1SRalf Baechle }
1253e01402b1SRalf Baechle 
1254e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_free);
1255e01402b1SRalf Baechle 
1256e01402b1SRalf Baechle void *vpe_get_shared(int index)
1257e01402b1SRalf Baechle {
1258e01402b1SRalf Baechle 	struct vpe *v;
1259e01402b1SRalf Baechle 
12602600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
1261e01402b1SRalf Baechle 		return NULL;
1262e01402b1SRalf Baechle 
1263e01402b1SRalf Baechle 	return v->shared_ptr;
1264e01402b1SRalf Baechle }
1265e01402b1SRalf Baechle 
1266e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared);
1267e01402b1SRalf Baechle 
12682600990eSRalf Baechle int vpe_getuid(int index)
12692600990eSRalf Baechle {
12702600990eSRalf Baechle 	struct vpe *v;
12712600990eSRalf Baechle 
12722600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
12732600990eSRalf Baechle 		return -1;
12742600990eSRalf Baechle 
12752600990eSRalf Baechle 	return v->uid;
12762600990eSRalf Baechle }
12772600990eSRalf Baechle 
12782600990eSRalf Baechle EXPORT_SYMBOL(vpe_getuid);
12792600990eSRalf Baechle 
12802600990eSRalf Baechle int vpe_getgid(int index)
12812600990eSRalf Baechle {
12822600990eSRalf Baechle 	struct vpe *v;
12832600990eSRalf Baechle 
12842600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
12852600990eSRalf Baechle 		return -1;
12862600990eSRalf Baechle 
12872600990eSRalf Baechle 	return v->gid;
12882600990eSRalf Baechle }
12892600990eSRalf Baechle 
12902600990eSRalf Baechle EXPORT_SYMBOL(vpe_getgid);
12912600990eSRalf Baechle 
12922600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify)
12932600990eSRalf Baechle {
12942600990eSRalf Baechle 	struct vpe *v;
12952600990eSRalf Baechle 
12962600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
12972600990eSRalf Baechle 		return -1;
12982600990eSRalf Baechle 
12992600990eSRalf Baechle 	list_add(&notify->list, &v->notify);
13002600990eSRalf Baechle 	return 0;
13012600990eSRalf Baechle }
13022600990eSRalf Baechle 
13032600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify);
13042600990eSRalf Baechle 
13052600990eSRalf Baechle char *vpe_getcwd(int index)
13062600990eSRalf Baechle {
13072600990eSRalf Baechle 	struct vpe *v;
13082600990eSRalf Baechle 
13092600990eSRalf Baechle 	if ((v = get_vpe(index)) == NULL)
13102600990eSRalf Baechle 		return NULL;
13112600990eSRalf Baechle 
13122600990eSRalf Baechle 	return v->cwd;
13132600990eSRalf Baechle }
13142600990eSRalf Baechle 
13152600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd);
13162600990eSRalf Baechle 
13172600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
13182600990eSRalf Baechle static void kspd_sp_exit( int sp_id)
13192600990eSRalf Baechle {
13202600990eSRalf Baechle 	cleanup_tc(get_tc(sp_id));
13212600990eSRalf Baechle }
13222600990eSRalf Baechle #endif
13232600990eSRalf Baechle 
1324736fad17SKay Sievers static ssize_t store_kill(struct device *dev, struct device_attribute *attr,
1325736fad17SKay Sievers 			  const char *buf, size_t len)
13260f5d0df3SRalf Baechle {
13270f5d0df3SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
13280f5d0df3SRalf Baechle 	struct vpe_notifications *not;
13290f5d0df3SRalf Baechle 
13300f5d0df3SRalf Baechle 	list_for_each_entry(not, &vpe->notify, list) {
13310f5d0df3SRalf Baechle 		not->stop(tclimit);
13320f5d0df3SRalf Baechle 	}
13330f5d0df3SRalf Baechle 
13340f5d0df3SRalf Baechle 	release_progmem(vpe->load_addr);
13350f5d0df3SRalf Baechle 	cleanup_tc(get_tc(tclimit));
13360f5d0df3SRalf Baechle 	vpe_stop(vpe);
13370f5d0df3SRalf Baechle 	vpe_free(vpe);
13380f5d0df3SRalf Baechle 
13390f5d0df3SRalf Baechle 	return len;
13400f5d0df3SRalf Baechle }
13410f5d0df3SRalf Baechle 
1342736fad17SKay Sievers static ssize_t show_ntcs(struct device *cd, struct device_attribute *attr,
1343736fad17SKay Sievers 			 char *buf)
134441790e04SRalf Baechle {
134541790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
134641790e04SRalf Baechle 
134741790e04SRalf Baechle 	return sprintf(buf, "%d\n", vpe->ntcs);
134841790e04SRalf Baechle }
134941790e04SRalf Baechle 
1350736fad17SKay Sievers static ssize_t store_ntcs(struct device *dev, struct device_attribute *attr,
1351736fad17SKay Sievers 			  const char *buf, size_t len)
135241790e04SRalf Baechle {
135341790e04SRalf Baechle 	struct vpe *vpe = get_vpe(tclimit);
135441790e04SRalf Baechle 	unsigned long new;
135541790e04SRalf Baechle 	char *endp;
135641790e04SRalf Baechle 
135741790e04SRalf Baechle 	new = simple_strtoul(buf, &endp, 0);
135841790e04SRalf Baechle 	if (endp == buf)
135941790e04SRalf Baechle 		goto out_einval;
136041790e04SRalf Baechle 
136141790e04SRalf Baechle 	if (new == 0 || new > (hw_tcs - tclimit))
136241790e04SRalf Baechle 		goto out_einval;
136341790e04SRalf Baechle 
136441790e04SRalf Baechle 	vpe->ntcs = new;
136541790e04SRalf Baechle 
136641790e04SRalf Baechle 	return len;
136741790e04SRalf Baechle 
136841790e04SRalf Baechle out_einval:
136941790e04SRalf Baechle 	return -EINVAL;;
137041790e04SRalf Baechle }
137141790e04SRalf Baechle 
1372736fad17SKay Sievers static struct device_attribute vpe_class_attributes[] = {
13730f5d0df3SRalf Baechle 	__ATTR(kill, S_IWUSR, NULL, store_kill),
137441790e04SRalf Baechle 	__ATTR(ntcs, S_IRUGO | S_IWUSR, show_ntcs, store_ntcs),
137541790e04SRalf Baechle 	{}
137641790e04SRalf Baechle };
137741790e04SRalf Baechle 
1378736fad17SKay Sievers static void vpe_device_release(struct device *cd)
137941790e04SRalf Baechle {
138041790e04SRalf Baechle 	kfree(cd);
138141790e04SRalf Baechle }
138241790e04SRalf Baechle 
138341790e04SRalf Baechle struct class vpe_class = {
138441790e04SRalf Baechle 	.name = "vpe",
138541790e04SRalf Baechle 	.owner = THIS_MODULE,
1386736fad17SKay Sievers 	.dev_release = vpe_device_release,
1387736fad17SKay Sievers 	.dev_attrs = vpe_class_attributes,
138841790e04SRalf Baechle };
138941790e04SRalf Baechle 
1390736fad17SKay Sievers struct device vpe_device;
139127a3bbafSRalf Baechle 
1392e01402b1SRalf Baechle static int __init vpe_module_init(void)
1393e01402b1SRalf Baechle {
139407cc0c9eSRalf Baechle 	unsigned int mtflags, vpflags;
139507cc0c9eSRalf Baechle 	unsigned long flags, val;
1396e01402b1SRalf Baechle 	struct vpe *v = NULL;
1397e01402b1SRalf Baechle 	struct tc *t;
139841790e04SRalf Baechle 	int tc, err;
1399e01402b1SRalf Baechle 
1400e01402b1SRalf Baechle 	if (!cpu_has_mipsmt) {
1401e01402b1SRalf Baechle 		printk("VPE loader: not a MIPS MT capable processor\n");
1402e01402b1SRalf Baechle 		return -ENODEV;
1403e01402b1SRalf Baechle 	}
1404e01402b1SRalf Baechle 
140507cc0c9eSRalf Baechle 	if (vpelimit == 0) {
140607cc0c9eSRalf Baechle 		printk(KERN_WARNING "No VPEs reserved for AP/SP, not "
140707cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxvpes=<n> argument as "
140807cc0c9eSRalf Baechle 		       "kernel argument\n");
140907cc0c9eSRalf Baechle 
141007cc0c9eSRalf Baechle 		return -ENODEV;
141107cc0c9eSRalf Baechle 	}
141207cc0c9eSRalf Baechle 
141307cc0c9eSRalf Baechle 	if (tclimit == 0) {
141407cc0c9eSRalf Baechle 		printk(KERN_WARNING "No TCs reserved for AP/SP, not "
141507cc0c9eSRalf Baechle 		       "initializing VPE loader.\nPass maxtcs=<n> argument as "
141607cc0c9eSRalf Baechle 		       "kernel argument\n");
141707cc0c9eSRalf Baechle 
141807cc0c9eSRalf Baechle 		return -ENODEV;
141907cc0c9eSRalf Baechle 	}
142007cc0c9eSRalf Baechle 
1421682e852eSAlexey Dobriyan 	major = register_chrdev(0, module_name, &vpe_fops);
1422682e852eSAlexey Dobriyan 	if (major < 0) {
1423e01402b1SRalf Baechle 		printk("VPE loader: unable to register character device\n");
1424307bd284SRalf Baechle 		return major;
1425e01402b1SRalf Baechle 	}
1426e01402b1SRalf Baechle 
142741790e04SRalf Baechle 	err = class_register(&vpe_class);
142841790e04SRalf Baechle 	if (err) {
142941790e04SRalf Baechle 		printk(KERN_ERR "vpe_class registration failed\n");
143027a3bbafSRalf Baechle 		goto out_chrdev;
143127a3bbafSRalf Baechle 	}
143241790e04SRalf Baechle 
1433736fad17SKay Sievers 	device_initialize(&vpe_device);
143441790e04SRalf Baechle 	vpe_device.class	= &vpe_class,
143541790e04SRalf Baechle 	vpe_device.parent	= NULL,
1436736fad17SKay Sievers 	strlcpy(vpe_device.bus_id, "vpe1", BUS_ID_SIZE);
143741790e04SRalf Baechle 	vpe_device.devt = MKDEV(major, minor);
1438736fad17SKay Sievers 	err = device_add(&vpe_device);
143941790e04SRalf Baechle 	if (err) {
144041790e04SRalf Baechle 		printk(KERN_ERR "Adding vpe_device failed\n");
144141790e04SRalf Baechle 		goto out_class;
144241790e04SRalf Baechle 	}
144327a3bbafSRalf Baechle 
144407cc0c9eSRalf Baechle 	local_irq_save(flags);
144507cc0c9eSRalf Baechle 	mtflags = dmt();
144607cc0c9eSRalf Baechle 	vpflags = dvpe();
1447e01402b1SRalf Baechle 
1448e01402b1SRalf Baechle 	/* Put MVPE's into 'configuration state' */
1449340ee4b9SRalf Baechle 	set_c0_mvpcontrol(MVPCONTROL_VPC);
1450e01402b1SRalf Baechle 
1451e01402b1SRalf Baechle 	/* dump_mtregs(); */
1452e01402b1SRalf Baechle 
1453e01402b1SRalf Baechle 	val = read_c0_mvpconf0();
145407cc0c9eSRalf Baechle 	hw_tcs = (val & MVPCONF0_PTC) + 1;
145507cc0c9eSRalf Baechle 	hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
145607cc0c9eSRalf Baechle 
145707cc0c9eSRalf Baechle 	for (tc = tclimit; tc < hw_tcs; tc++) {
145807cc0c9eSRalf Baechle 		/*
145907cc0c9eSRalf Baechle 		 * Must re-enable multithreading temporarily or in case we
146007cc0c9eSRalf Baechle 		 * reschedule send IPIs or similar we might hang.
146107cc0c9eSRalf Baechle 		 */
146207cc0c9eSRalf Baechle 		clear_c0_mvpcontrol(MVPCONTROL_VPC);
146307cc0c9eSRalf Baechle 		evpe(vpflags);
146407cc0c9eSRalf Baechle 		emt(mtflags);
146507cc0c9eSRalf Baechle 		local_irq_restore(flags);
146607cc0c9eSRalf Baechle 		t = alloc_tc(tc);
146707cc0c9eSRalf Baechle 		if (!t) {
146807cc0c9eSRalf Baechle 			err = -ENOMEM;
146907cc0c9eSRalf Baechle 			goto out;
147007cc0c9eSRalf Baechle 		}
147107cc0c9eSRalf Baechle 
147207cc0c9eSRalf Baechle 		local_irq_save(flags);
147307cc0c9eSRalf Baechle 		mtflags = dmt();
147407cc0c9eSRalf Baechle 		vpflags = dvpe();
147507cc0c9eSRalf Baechle 		set_c0_mvpcontrol(MVPCONTROL_VPC);
1476e01402b1SRalf Baechle 
1477e01402b1SRalf Baechle 		/* VPE's */
147807cc0c9eSRalf Baechle 		if (tc < hw_tcs) {
147907cc0c9eSRalf Baechle 			settc(tc);
1480e01402b1SRalf Baechle 
148107cc0c9eSRalf Baechle 			if ((v = alloc_vpe(tc)) == NULL) {
1482e01402b1SRalf Baechle 				printk(KERN_WARNING "VPE: unable to allocate VPE\n");
148307cc0c9eSRalf Baechle 
148407cc0c9eSRalf Baechle 				goto out_reenable;
1485e01402b1SRalf Baechle 			}
1486e01402b1SRalf Baechle 
148741790e04SRalf Baechle 			v->ntcs = hw_tcs - tclimit;
148841790e04SRalf Baechle 
14892600990eSRalf Baechle 			/* add the tc to the list of this vpe's tc's. */
14902600990eSRalf Baechle 			list_add(&t->tc, &v->tc);
1491e01402b1SRalf Baechle 
1492e01402b1SRalf Baechle 			/* deactivate all but vpe0 */
149307cc0c9eSRalf Baechle 			if (tc >= tclimit) {
1494e01402b1SRalf Baechle 				unsigned long tmp = read_vpe_c0_vpeconf0();
1495e01402b1SRalf Baechle 
1496e01402b1SRalf Baechle 				tmp &= ~VPECONF0_VPA;
1497e01402b1SRalf Baechle 
1498e01402b1SRalf Baechle 				/* master VPE */
1499e01402b1SRalf Baechle 				tmp |= VPECONF0_MVP;
1500e01402b1SRalf Baechle 				write_vpe_c0_vpeconf0(tmp);
1501e01402b1SRalf Baechle 			}
1502e01402b1SRalf Baechle 
1503e01402b1SRalf Baechle 			/* disable multi-threading with TC's */
1504e01402b1SRalf Baechle 			write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
1505e01402b1SRalf Baechle 
150607cc0c9eSRalf Baechle 			if (tc >= vpelimit) {
15072600990eSRalf Baechle 				/*
15082600990eSRalf Baechle 				 * Set config to be the same as vpe0,
15092600990eSRalf Baechle 				 * particularly kseg0 coherency alg
15102600990eSRalf Baechle 				 */
1511e01402b1SRalf Baechle 				write_vpe_c0_config(read_c0_config());
1512e01402b1SRalf Baechle 			}
1513e01402b1SRalf Baechle 		}
1514e01402b1SRalf Baechle 
1515e01402b1SRalf Baechle 		/* TC's */
1516e01402b1SRalf Baechle 		t->pvpe = v;	/* set the parent vpe */
1517e01402b1SRalf Baechle 
151807cc0c9eSRalf Baechle 		if (tc >= tclimit) {
1519e01402b1SRalf Baechle 			unsigned long tmp;
1520e01402b1SRalf Baechle 
152107cc0c9eSRalf Baechle 			settc(tc);
1522e01402b1SRalf Baechle 
15232600990eSRalf Baechle 			/* Any TC that is bound to VPE0 gets left as is - in case
15242600990eSRalf Baechle 			   we are running SMTC on VPE0. A TC that is bound to any
15252600990eSRalf Baechle 			   other VPE gets bound to VPE0, ideally I'd like to make
15262600990eSRalf Baechle 			   it homeless but it doesn't appear to let me bind a TC
15272600990eSRalf Baechle 			   to a non-existent VPE. Which is perfectly reasonable.
15282600990eSRalf Baechle 
15292600990eSRalf Baechle 			   The (un)bound state is visible to an EJTAG probe so may
15302600990eSRalf Baechle 			   notify GDB...
15312600990eSRalf Baechle 			*/
15322600990eSRalf Baechle 
15332600990eSRalf Baechle 			if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) {
15342600990eSRalf Baechle 				/* tc is bound >vpe0 */
15352600990eSRalf Baechle 				write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE);
15362600990eSRalf Baechle 
15372600990eSRalf Baechle 				t->pvpe = get_vpe(0);	/* set the parent vpe */
15382600990eSRalf Baechle 			}
1539e01402b1SRalf Baechle 
15407c3a622dSNigel Stephens 			/* halt the TC */
15417c3a622dSNigel Stephens 			write_tc_c0_tchalt(TCHALT_H);
15427c3a622dSNigel Stephens 			mips_ihb();
15437c3a622dSNigel Stephens 
1544e01402b1SRalf Baechle 			tmp = read_tc_c0_tcstatus();
1545e01402b1SRalf Baechle 
15462600990eSRalf Baechle 			/* mark not activated and not dynamically allocatable */
1547e01402b1SRalf Baechle 			tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
1548e01402b1SRalf Baechle 			tmp |= TCSTATUS_IXMT;	/* interrupt exempt */
1549e01402b1SRalf Baechle 			write_tc_c0_tcstatus(tmp);
1550e01402b1SRalf Baechle 		}
1551e01402b1SRalf Baechle 	}
1552e01402b1SRalf Baechle 
155307cc0c9eSRalf Baechle out_reenable:
1554e01402b1SRalf Baechle 	/* release config state */
1555340ee4b9SRalf Baechle 	clear_c0_mvpcontrol(MVPCONTROL_VPC);
1556e01402b1SRalf Baechle 
155707cc0c9eSRalf Baechle 	evpe(vpflags);
155807cc0c9eSRalf Baechle 	emt(mtflags);
155907cc0c9eSRalf Baechle 	local_irq_restore(flags);
156007cc0c9eSRalf Baechle 
15612600990eSRalf Baechle #ifdef CONFIG_MIPS_APSP_KSPD
15622600990eSRalf Baechle 	kspd_events.kspd_sp_exit = kspd_sp_exit;
15632600990eSRalf Baechle #endif
1564e01402b1SRalf Baechle 	return 0;
156527a3bbafSRalf Baechle 
156641790e04SRalf Baechle out_class:
156741790e04SRalf Baechle 	class_unregister(&vpe_class);
156827a3bbafSRalf Baechle out_chrdev:
156927a3bbafSRalf Baechle 	unregister_chrdev(major, module_name);
157027a3bbafSRalf Baechle 
157107cc0c9eSRalf Baechle out:
157227a3bbafSRalf Baechle 	return err;
1573e01402b1SRalf Baechle }
1574e01402b1SRalf Baechle 
1575e01402b1SRalf Baechle static void __exit vpe_module_exit(void)
1576e01402b1SRalf Baechle {
1577e01402b1SRalf Baechle 	struct vpe *v, *n;
1578e01402b1SRalf Baechle 
1579e01402b1SRalf Baechle 	list_for_each_entry_safe(v, n, &vpecontrol.vpe_list, list) {
1580e01402b1SRalf Baechle 		if (v->state != VPE_STATE_UNUSED) {
1581e01402b1SRalf Baechle 			release_vpe(v);
1582e01402b1SRalf Baechle 		}
1583e01402b1SRalf Baechle 	}
1584e01402b1SRalf Baechle 
1585736fad17SKay Sievers 	device_del(&vpe_device);
1586e01402b1SRalf Baechle 	unregister_chrdev(major, module_name);
1587e01402b1SRalf Baechle }
1588e01402b1SRalf Baechle 
1589e01402b1SRalf Baechle module_init(vpe_module_init);
1590e01402b1SRalf Baechle module_exit(vpe_module_exit);
1591e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader");
15922600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
1593e01402b1SRalf Baechle MODULE_LICENSE("GPL");
1594